diff --git a/app/src/main/java/org/toop/app/menu/MainMenu.java b/app/src/main/java/org/toop/app/menu/MainMenu.java index b0eceba..2765083 100644 --- a/app/src/main/java/org/toop/app/menu/MainMenu.java +++ b/app/src/main/java/org/toop/app/menu/MainMenu.java @@ -23,8 +23,10 @@ public final class MainMenu extends Menu { public MainMenu() { final Region background = createBackground(); - tictactoe = createButton(loc.getString("mainMenuSelectTicTacToe",currentLocale), () -> { App.activate(new GameSelectMenu(GameType.TICTACTOE)); }); - reversi = createButton(loc.getString("mainMenuSelectReversi",currentLocale), () -> { App.activate(new GameSelectMenu(GameType.REVERSI)); }); + tictactoe = createButton( + loc.getString("mainMenuSelectTicTacToe",currentLocale), () -> { App.activate(new GameSelectMenu(GameType.TICTACTOE)); }); + reversi = createButton( + loc.getString("mainMenuSelectReversi",currentLocale), () -> { App.activate(new GameSelectMenu(GameType.REVERSI)); }); final VBox gamesBox = new VBox(10, tictactoe, reversi); gamesBox.setAlignment(Pos.TOP_LEFT); diff --git a/app/src/main/java/org/toop/app/menu/Menu.java b/app/src/main/java/org/toop/app/menu/Menu.java index 9e9ac29..6863d8e 100644 --- a/app/src/main/java/org/toop/app/menu/Menu.java +++ b/app/src/main/java/org/toop/app/menu/Menu.java @@ -4,6 +4,8 @@ import javafx.scene.control.Button; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.scene.text.Text; +import org.toop.framework.audio.events.AudioEvents; +import org.toop.framework.eventbus.EventFlow; public abstract class Menu { protected Pane pane; @@ -34,7 +36,10 @@ public abstract class Menu { public Button createButton(String css, String x, Runnable runnable) { final Button button = new Button(x); - button.setOnAction(_ -> runnable.run()); + button.setOnAction(_ -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); + runnable.run(); + }); button.getStyleClass().add(css); return button; diff --git a/app/src/main/java/org/toop/app/menu/OptionsMenu.java b/app/src/main/java/org/toop/app/menu/OptionsMenu.java index 0a96dbf..a707fe7 100644 --- a/app/src/main/java/org/toop/app/menu/OptionsMenu.java +++ b/app/src/main/java/org/toop/app/menu/OptionsMenu.java @@ -55,7 +55,7 @@ public final class OptionsMenu extends Menu { selectLanguage.getItems().add(locFile); } - selectLanguage.setConverter(new javafx.util.StringConverter() { + selectLanguage.setConverter(new javafx.util.StringConverter<>() { @Override public String toString(Locale locale) { return locale.getDisplayName(); @@ -67,7 +67,12 @@ public final class OptionsMenu extends Menu { } }); + selectLanguage.setOnShowing(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); + }); + selectLanguage.setOnAction(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); Locale selectedLocale = selectLanguage.getSelectionModel().getSelectedItem(); if (selectedLocale != null) { AppContext.setLocale(selectedLocale); @@ -87,7 +92,12 @@ public final class OptionsMenu extends Menu { selectScreen.getItems().add(screen); } + selectScreen.setOnShowing(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); + }); + selectScreen.setOnAction(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); int selectedIndex = selectScreen.getSelectionModel().getSelectedIndex(); Object selectedItem = selectScreen.getSelectionModel().getSelectedItem(); @@ -102,6 +112,17 @@ public final class OptionsMenu extends Menu { for (DisplayMode displayMode : currentScreenDevice.getDisplayModes()) { selectWindowSize.getItems().add(displayMode); } + selectWindowSize.setOnShowing(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); + }); + selectWindowSize.setOnAction(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); + int selectedIndex = selectWindowSize.getSelectionModel().getSelectedIndex(); + Object selectedItem = selectWindowSize.getSelectionModel().getSelectedItem(); + + System.out.println("Selection made: [" + selectedIndex + "] " + selectedItem); + System.out.println(" ChoiceBox.getValue(): " + selectWindowSize.getValue()); + }); return selectWindowSize; } @@ -109,6 +130,7 @@ public final class OptionsMenu extends Menu { final CheckBox setFullscreen = new CheckBox("Fullscreen"); setFullscreen.setSelected(App.isFullscreen()); setFullscreen.setOnAction(event -> { + new EventFlow().addPostEvent(new AudioEvents.clickButton()).asyncPostEvent(); boolean isSelected = setFullscreen.isSelected(); App.setFullscreen(isSelected); }); @@ -131,11 +153,24 @@ public final class OptionsMenu extends Menu { Label valueLabel = new Label(String.valueOf((int) volumeSlider.getValue())); + final long[] lastPlayed = {0}; + final long cooldown = 50; volumeSlider.valueProperty().addListener((obs, oldVal, newVal) -> { + long now = System.currentTimeMillis(); + + if (now - lastPlayed[0] >= cooldown) { + lastPlayed[0] = now; + + int value = newVal.intValue(); + valueLabel.setText(String.valueOf(value)); + + new EventFlow().addPostEvent(new AudioEvents.PlayEffect("soft-button-click.wav", false)).asyncPostEvent(); + } valueLabel.setText(String.valueOf(newVal.intValue())); new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(newVal.doubleValue()/100.0)) .asyncPostEvent(); }); + return volumeSlider; } diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java index 982caeb..8324b25 100644 --- a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java +++ b/framework/src/main/java/org/toop/framework/asset/ResourceManager.java @@ -1,5 +1,7 @@ package org.toop.framework.asset; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.toop.framework.asset.resources.*; import java.util.*; @@ -46,6 +48,7 @@ import java.util.concurrent.ConcurrentHashMap; * */ public class ResourceManager { + private static final Logger logger = LogManager.getLogger(ResourceManager.class); private static final ResourceManager INSTANCE = new ResourceManager(); private static final Map> assets = new ConcurrentHashMap<>(); diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java index 1d0cedd..3ca6386 100644 --- a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java +++ b/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java @@ -23,7 +23,10 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { Clip clip = AudioSystem.getClip(); // Insert a new audio stream into the clip - clip.open(this.getAudioStream()); + AudioInputStream inputStream = this.getAudioStream(); + AudioFormat baseFormat = inputStream.getFormat(); + if (baseFormat.getSampleSizeInBits() > 16) inputStream = downSampleAudio(inputStream, baseFormat); + clip.open(inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary. return clip; } @@ -32,6 +35,20 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource { return AudioSystem.getAudioInputStream(this.file); } + private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) { + AudioFormat decodedFormat = new AudioFormat( + AudioFormat.Encoding.PCM_SIGNED, + baseFormat.getSampleRate(), + 16, // force 16-bit + baseFormat.getChannels(), + baseFormat.getChannels() * 2, + baseFormat.getSampleRate(), + false // little-endian + ); + + return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream); + } + @Override public void load() { try { diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java index 2a0ec17..e65e613 100644 --- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java +++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java @@ -1,5 +1,7 @@ package org.toop.framework.audio; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.toop.framework.SnowflakeGenerator; import org.toop.framework.asset.ResourceManager; import org.toop.framework.asset.ResourceMeta; @@ -15,6 +17,7 @@ import java.util.*; import javax.sound.sampled.*; public class SoundManager { + private static final Logger logger = LogManager.getLogger(SoundManager.class); private final List activeMusic = new ArrayList<>(); private final Queue backgroundMusicQueue = new LinkedList<>(); private final Map activeSoundEffects = new HashMap<>(); @@ -38,16 +41,16 @@ public class SoundManager { .listen(this::handleMusicStart) .listen(this::handleVolumeChange) .listen(this::handleGetCurrentVolume) - .listen(AudioEvents.playOnClickButton.class, _ -> { + .listen(AudioEvents.clickButton.class, _ -> { try { - playSound("hitsound0.wav", false); + playSound("medium-button-click.wav", false); } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { - throw new RuntimeException(e); + logger.error(e); } }); } - private void handlePlaySound(AudioEvents.PlayAudio event) { + private void handlePlaySound(AudioEvents.PlayEffect event) { try { this.playSound(event.fileName(), event.loop()); } catch (UnsupportedAudioFileException | LineUnavailableException | IOException e) { @@ -55,7 +58,7 @@ public class SoundManager { } } - private void handleStopSound(AudioEvents.StopAudio event) { + private void handleStopSound(AudioEvents.StopEffect event) { this.stopSound(event.clipId()); } @@ -71,7 +74,6 @@ public class SoundManager { for (MediaPlayer mediaPlayer : this.activeMusic) { mediaPlayer.setVolume(this.volume); } - IO.println("Volume: " + this.volume); } private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) { @@ -125,13 +127,15 @@ public class SoundManager { mediaPlayer.setVolume(this.volume); mediaPlayer.play(); activeMusic.add(mediaPlayer); + logger.info("Playing background music: {}", ma.getFile().getName()); } private long playSound(String audioFileName, boolean loop) throws UnsupportedAudioFileException, LineUnavailableException, IOException { SoundEffectAsset asset = audioResources.get(audioFileName); // Return -1 which indicates resource wasn't available - if (asset == null){ + if (asset == null) { + logger.warn("Unable to load audio asset: {}", audioFileName); return -1; } @@ -146,6 +150,8 @@ public class SoundManager { clip.start(); } + logger.info("Playing sound: {}", asset.getFile().getName()); + // Generate id for clip long clipId = idGenerator.nextId(); diff --git a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java index 08fb67b..5aceb8c 100644 --- a/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java +++ b/framework/src/main/java/org/toop/framework/audio/events/AudioEvents.java @@ -1,6 +1,5 @@ package org.toop.framework.audio.events; -import org.toop.framework.asset.resources.MusicAsset; import org.toop.framework.eventbus.events.EventWithSnowflake; import org.toop.framework.eventbus.events.EventWithoutSnowflake; import org.toop.framework.eventbus.events.EventsBase; @@ -9,10 +8,10 @@ import java.util.Map; public class AudioEvents extends EventsBase { /** Starts playing a sound. */ - public record PlayAudio(String fileName, boolean loop) + public record PlayEffect(String fileName, boolean loop) implements EventWithoutSnowflake {} - public record StopAudio(long clipId) implements EventWithoutSnowflake {} + public record StopEffect(long clipId) implements EventWithoutSnowflake {} public record StartBackgroundMusic() implements EventWithoutSnowflake {} public record ChangeVolume(double newVolume) implements EventWithoutSnowflake {} @@ -38,5 +37,5 @@ public class AudioEvents extends EventsBase { return snowflakeId; } } - public record playOnClickButton() implements EventWithoutSnowflake {} + public record clickButton() implements EventWithoutSnowflake {} }