From c7031066e6ee1d0cf90d1e9c1cd096c2a0e40120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Waldisp=C3=BChl?= Date: Thu, 19 Jul 2018 17:07:46 +0200 Subject: [PATCH] Reduce / adapt volume of break track (fixes #47). --- .../src/main/resources/CHANGELOG.txt | 1 + .../compilation/Compilation.java | 2 +- .../compilation/CompilationGenerator.java | 2 +- .../player/ExtractMusicPlayer.java | 2 +- .../playlist/BreakPlaylistItemFragment.java | 2 +- .../playlist/Playlist.java | 22 +++++----- .../playlist/PlaylistItemFragment.java | 8 +++- .../util/SoundHelper.java | 12 +++++- .../compilation/CompilationTest.java | 42 ++++++++++++++++++- .../playlist/PlaylistItemFragmentTest.java | 4 +- .../playlist/PlaylistTest.java | 32 +++++++------- .../ui/mainscreen/MainScreenController.java | 29 ++++++++++++- .../ui/preferences/UiUserPreferences.java | 9 ++++ .../main/resources/layouts/MainScreen.fxml | 9 ++-- .../src/main/resources/styles/MainScreen.css | 5 +++ .../src/main/resources/ui_imc_de.properties | 7 ++-- .../src/main/resources/ui_imc_en.properties | 1 + .../compilation/CompilationParameters.java | 12 +++++- 18 files changed, 156 insertions(+), 45 deletions(-) diff --git a/intervalmusiccompositor.app/src/main/resources/CHANGELOG.txt b/intervalmusiccompositor.app/src/main/resources/CHANGELOG.txt index fd453f7..e2a98e3 100644 --- a/intervalmusiccompositor.app/src/main/resources/CHANGELOG.txt +++ b/intervalmusiccompositor.app/src/main/resources/CHANGELOG.txt @@ -3,6 +3,7 @@ retorte laboratories (retorte.ch) IntervalMusicCompositor change log 2018-xx-xx 2.x.x (not yet released) -------------------------------------------------------------------------------- +ADD #47 Reduce / adapt volume of break track. FIX #46 BPM dialog: Music should stop when dialog is closed. diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/Compilation.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/Compilation.java index 043b07e..759c637 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/Compilation.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/Compilation.java @@ -254,7 +254,7 @@ private byte[] getByteArrayFrom(PlaylistItemFragment playlistItemFragment) throw return soundHelper.generateSilenceOfLength((playlistItemFragment.getExtractDurationInSeconds())); } - AudioInputStream leveledStream = soundHelper.getLeveledStream(playlistItemFragment.getAudioFile().getAudioInputStream(), getVolumeRatioOf(playlistItemFragment)); + AudioInputStream leveledStream = soundHelper.getLeveledStream(playlistItemFragment.getAudioFile().getAudioInputStream(), getVolumeRatioOf(playlistItemFragment), playlistItemFragment.getVolume()); return soundHelper.getStreamPart(leveledStream, playlistItemFragment.getExtractStartInMilliseconds(), playlistItemFragment.getExtractDurationInMilliseconds()); } diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/CompilationGenerator.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/CompilationGenerator.java index feddf5d..2f510f2 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/CompilationGenerator.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/compilation/CompilationGenerator.java @@ -222,7 +222,7 @@ private void collectBreakTracks() { private void createPlaylist() { playlist = new Playlist(compilationParameters, messageProducer); - playlist.generatePlaylist(musicPlaylistCandidates, compilationParameters.getMusicPattern(), breakPlaylistCandidates, compilationParameters.getBreakPattern(), compilationParameters.getIterations(), compilationParameters.getSoundEffectOccurrences()); + playlist.generatePlaylist(musicPlaylistCandidates, compilationParameters.getMusicPattern(), breakPlaylistCandidates, compilationParameters.getBreakPattern(), compilationParameters.getBreakVolume(), compilationParameters.getIterations(), compilationParameters.getSoundEffectOccurrences()); } private void createPlaylistReport() { diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/player/ExtractMusicPlayer.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/player/ExtractMusicPlayer.java index b7049d8..aed725f 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/player/ExtractMusicPlayer.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/player/ExtractMusicPlayer.java @@ -51,7 +51,7 @@ public void play(IAudioFile audioFile) { AudioInputStream inputStream = null; try { inputStream = soundHelper.getLeveledStream(soundHelper.getStreamExtract(audioFile.getAudioInputStream(), extractStart, extractLength), - audioFile.getVolumeRatio()); + audioFile.getVolumeRatio(), 1); } catch (IOException e) { messageProducer.send(new ErrorMessage(e.getMessage())); diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/BreakPlaylistItemFragment.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/BreakPlaylistItemFragment.java index d5fc32f..12afaab 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/BreakPlaylistItemFragment.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/BreakPlaylistItemFragment.java @@ -6,7 +6,7 @@ public class BreakPlaylistItemFragment extends PlaylistItemFragment { public BreakPlaylistItemFragment(PlaylistItemFragment playlistItemFragment) { - super(playlistItemFragment.getAudioFile(), playlistItemFragment.getExtractStartInMilliseconds(), playlistItemFragment.getExtractEndInMilliseconds()); + super(playlistItemFragment.getAudioFile(), playlistItemFragment.getVolume(), playlistItemFragment.getExtractStartInMilliseconds(), playlistItemFragment.getExtractEndInMilliseconds()); } } diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/Playlist.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/Playlist.java index 7f331ed..333d050 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/Playlist.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/Playlist.java @@ -76,9 +76,10 @@ public void generatePlaylist(List musicFiles, List musicPattern, List breakFiles, List breakPattern, + double breakVolume, Integer iterations, List soundEffectOccurrences) { - playlistItems = generatePlaylistFrom(musicFiles, musicPattern, breakFiles, breakPattern, iterations, soundEffectOccurrences); + playlistItems = generatePlaylistFrom(musicFiles, musicPattern, breakFiles, breakPattern, breakVolume, iterations, soundEffectOccurrences); } @VisibleForTesting @@ -86,6 +87,7 @@ List generatePlaylistFrom(List musicFiles, List musicPattern, List breakFiles, List breakPattern, + double breakVolume, Integer iterations, List soundEffectOccurrences) { @@ -99,7 +101,7 @@ List generatePlaylistFrom(List musicFiles, } List musicTracks = createMusicPlaylist(musicFiles, musicPattern, iterations); - List breakTracks = createBreakPlaylist(breakFiles, breakPattern, iterations, musicPattern.size()); + List breakTracks = createBreakPlaylist(breakFiles, breakPattern, iterations, musicPattern.size(), breakVolume); return createItemsWith(musicTracks, breakTracks, soundEffectOccurrences); } @@ -133,7 +135,7 @@ private List createMusicPlaylist(List musicFil IAudioFile currentAudioFile = musicFiles.get((musicTrackCounter) % musicFiles.size()); int currentSoundPattern = musicPattern.get(musicPatternCounter % musicPattern.size()); - PlaylistItemFragment newMusicTrack = createPlaylistItemFrom(currentAudioFile, currentSoundPattern * 1000); + PlaylistItemFragment newMusicTrack = createPlaylistItemFrom(currentAudioFile, 1, currentSoundPattern * 1000); if (newMusicTrack != null) { musicPlaylist.add(newMusicTrack); musicPatternCounter++; @@ -165,7 +167,7 @@ private void shuffle(List audioFiles) { Collections.shuffle(audioFiles, random); } - private List createBreakPlaylist(List breakFiles, List breakPattern, Integer iterations, int musicPatternSize) { + private List createBreakPlaylist(List breakFiles, List breakPattern, Integer iterations, int musicPatternSize, double volume) { List breakPlaylist = Lists.newArrayList(); if (breakPattern.isEmpty() || hasSingleZero(breakPattern)) { @@ -182,7 +184,7 @@ private List createBreakPlaylist(List breakFil if (!breakFiles.isEmpty()) { IAudioFile currentBreakFile = breakFiles.get((breakTrackCounter % musicPatternSize) % breakFiles.size()); - PlaylistItemFragment newBreakTrack = createPlaylistItemFrom(currentBreakFile, currentBreakPatternMs); + PlaylistItemFragment newBreakTrack = createPlaylistItemFrom(currentBreakFile, volume, currentBreakPatternMs); if (newBreakTrack != null) { breakPlaylist.add(new BreakPlaylistItemFragment(newBreakTrack)); } @@ -195,7 +197,7 @@ private List createBreakPlaylist(List breakFil if (isCrossFadingMode()) { currentBreakPatternMs += (long) (blendTime * 1000); } - breakPlaylist.add(new BreakPlaylistItemFragment(createPlaylistItem(null, 0L, currentBreakPatternMs))); + breakPlaylist.add(new BreakPlaylistItemFragment(createPlaylistItem(null, volume, 0L, currentBreakPatternMs))); } breakTrackCounter++; @@ -213,7 +215,7 @@ private boolean hasSingleZero(List breakPattern) { return breakPattern.size() == 1 && breakPattern.iterator().next() == 0; } - private PlaylistItemFragment createPlaylistItemFrom(IAudioFile audioFile, long extractLengthInMilliseconds) { + private PlaylistItemFragment createPlaylistItemFrom(IAudioFile audioFile, double volume, long extractLengthInMilliseconds) { long maximalRangeForDuration; long trackStart = startCutOffInMilliseconds; @@ -249,15 +251,15 @@ private PlaylistItemFragment createPlaylistItemFrom(IAudioFile audioFile, long e currentProgress.put(audioFile, trackStart + extractLengthInMilliseconds); } - return createPlaylistItem(audioFile, trackStart, trackStart + extractLengthInMilliseconds); + return createPlaylistItem(audioFile, volume, trackStart, trackStart + extractLengthInMilliseconds); } boolean hasSoundEffects() { return playlistItems.stream().anyMatch(PlaylistItem::hasSoundEffects); } - private PlaylistItemFragment createPlaylistItem(IAudioFile audioFile, Long startInMilliseconds, Long endInMilliseconds) { - return new PlaylistItemFragment(audioFile, startInMilliseconds, endInMilliseconds); + private PlaylistItemFragment createPlaylistItem(IAudioFile audioFile, double volume, Long startInMilliseconds, Long endInMilliseconds) { + return new PlaylistItemFragment(audioFile, volume, startInMilliseconds, endInMilliseconds); } long getTotalLength(Playlist playlist, List playlistItems) { diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragment.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragment.java index be51110..3e74dd4 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragment.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragment.java @@ -15,14 +15,16 @@ public class PlaylistItemFragment { //---- Fields private final IAudioFile audioFile; + private final double volume; private final long extractStartInMilliseconds; private final long extractEndInMilliseconds; //---- Constructor - PlaylistItemFragment(IAudioFile audioFile, long extractStartInMilliseconds, long extractEndInMilliseconds) { + PlaylistItemFragment(IAudioFile audioFile, double volume, long extractStartInMilliseconds, long extractEndInMilliseconds) { this.audioFile = audioFile; + this.volume = volume; this.extractStartInMilliseconds = extractStartInMilliseconds; this.extractEndInMilliseconds = extractEndInMilliseconds; @@ -50,6 +52,10 @@ public IAudioFile getAudioFile() { return audioFile; } + public double getVolume() { + return volume; + } + public long getExtractStartInMilliseconds() { return extractStartInMilliseconds; } diff --git a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/util/SoundHelper.java b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/util/SoundHelper.java index 6a419ea..dd20ef5 100644 --- a/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/util/SoundHelper.java +++ b/intervalmusiccompositor.core/src/main/java/ch/retorte/intervalmusiccompositor/util/SoundHelper.java @@ -106,9 +106,17 @@ public int getAvgAmplitude(AudioInputStream inputBuffer, int sampleWindow) throw return maxSample; } - public AudioInputStream getLeveledStream(AudioInputStream audioInputStream, float desiredRelativeAmplitude) { + /** + * Adjusts the 'volume' (i.e. the amplitude) of the provided audio stream according to the measured volume ratio and an arbitrary volume factor. + * + * @param audioInputStream the stream holding the to be processed audio data. + * @param desiredRelativeAmplitude the relative amplitude to match. + * @param volume an arbitrary control factor, with e.g. 0.5 for half the volume. + * @return the amplified audio stream. + */ + public AudioInputStream getLeveledStream(AudioInputStream audioInputStream, float desiredRelativeAmplitude, double volume) { AmplitudeAudioInputStream amplitudeAudioInputStream = new AmplitudeAudioInputStream(audioInputStream); - amplitudeAudioInputStream.setAmplitudeLinear(desiredRelativeAmplitude); + amplitudeAudioInputStream.setAmplitudeLinear((float) (desiredRelativeAmplitude * volume)); return amplitudeAudioInputStream; } diff --git a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/compilation/CompilationTest.java b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/compilation/CompilationTest.java index c1a29bc..83c9b33 100644 --- a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/compilation/CompilationTest.java +++ b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/compilation/CompilationTest.java @@ -48,6 +48,7 @@ public class CompilationTest { @Before public void setup() throws IOException, UnsupportedAudioFileException { musicFiles.add(audioFileFrom(TEST_SOUND)); + breakFiles.add(audioFileFrom(TEST_SOUND)); } @After @@ -55,6 +56,9 @@ public void cleanup() throws IOException { for (IAudioFile f : musicFiles) { f.removeCache(); } + for (IAudioFile f : breakFiles) { + f.removeCache(); + } } //---- Test methods @@ -95,13 +99,49 @@ public void shouldGenerateLongerCompilation() throws IOException, UnsupportedAud assertThat(compilation.length, is(44100 * 2 * 12)); } + @Test + public void shouldSetBreakVolume() throws IOException, UnsupportedAudioFileException { + // given + Playlist playList = playlistWith(1, l(1), l(1), 0.5); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + // when + sut.generateCompilation(playList, baos); + + // then + // Somehow check byte array... + byte[] compilation = baos.toByteArray(); + assertThat(compilation.length, is(44100 * 2 * 2 * 2)); + + // Sound part + assertThat(compilation[0], is(b(0))); + assertThat(compilation[1], is(b(0))); + assertThat(compilation[88198], is(b(-20))); + assertThat(compilation[88199], is(b(-63))); + assertThat(compilation[176398], is(b(0))); + assertThat(compilation[176399], is(b(0))); + + // Break part + assertThat(compilation[176400], is(b(0))); + assertThat(compilation[176401], is(b(0))); + assertThat(compilation[264598], is(b(-10))); + assertThat(compilation[264599], is(b(-32))); + assertThat(compilation[352798], is(b(0))); + assertThat(compilation[352798], is(b(0))); + + } + //---- Helper methods private Playlist playlistWith(int iterations, List musicIntervals, List breakIntervals) { + return playlistWith(iterations, musicIntervals, breakIntervals, 1); + } + + private Playlist playlistWith(int iterations, List musicIntervals, List breakIntervals, double breakVolume) { CompilationParameters parameters = new CompilationParameters(); Playlist playlist = new Playlist(parameters, m -> {}); - playlist.generatePlaylist(musicFiles, musicIntervals, breakFiles, breakIntervals, iterations, newArrayList()); + playlist.generatePlaylist(musicFiles, musicIntervals, breakFiles, breakIntervals, breakVolume, iterations, newArrayList()); return playlist; } diff --git a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragmentTest.java b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragmentTest.java index 34eb1de..688f28b 100644 --- a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragmentTest.java +++ b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistItemFragmentTest.java @@ -19,7 +19,7 @@ public class PlaylistItemFragmentTest { @Test public void shouldConsiderItselfSilentBreakIfAudioFileIsNull() { // when - PlaylistItemFragment playlistItemFragment = new PlaylistItemFragment(null, 1, 2); + PlaylistItemFragment playlistItemFragment = new PlaylistItemFragment(null, 1, 1, 2); // then assertTrue(playlistItemFragment.isSilentBreak()); @@ -30,7 +30,7 @@ public void shouldReturnCorrectExtractDuration() { // given IAudioFile audioFile = mock(IAudioFile.class); when(audioFile.getDuration()).thenReturn(10000L); - PlaylistItemFragment playlistItemFragment = new PlaylistItemFragment(audioFile, 500L, 1500L); + PlaylistItemFragment playlistItemFragment = new PlaylistItemFragment(audioFile, 1,500L, 1500L); // when long duration = playlistItemFragment.getExtractDurationInMilliseconds(); diff --git a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistTest.java b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistTest.java index e6bd366..f303e3a 100644 --- a/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistTest.java +++ b/intervalmusiccompositor.core/src/test/java/ch/retorte/intervalmusiccompositor/playlist/PlaylistTest.java @@ -73,7 +73,7 @@ public void shouldProduceEmptyListOnEmptyPattern() { musicList.add(musicTrack20s); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1,3, soundEffectOccurrences); // then assertThat(tracks.size(), is(0)); @@ -86,7 +86,7 @@ public void shouldProduceEmptyListOnEmptyTracks() { musicPattern = pattern(1, 2, 3); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 3, soundEffectOccurrences); // then assertThat(tracks.size(), is(0)); @@ -100,7 +100,7 @@ public void shouldGenerateTracksOfRightLength() { musicPattern = pattern(5, 10); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 1, soundEffectOccurrences); // then Assert.assertThat(tracks.size(), CoreMatchers.is(2)); @@ -119,7 +119,7 @@ public void shouldGenerateBreaksOfRightLength() { breakPattern = pattern(2); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 1, soundEffectOccurrences); // then assertThat(tracks.size(), is(2)); @@ -138,7 +138,7 @@ public void shouldIgnoreSurplusBreakPatterns() { breakPattern = pattern(1, 2, 3, 4); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1,3, soundEffectOccurrences); // then assertThat(tracks.size(), is(6)); @@ -159,7 +159,7 @@ public void shouldPutTracksInOrder() { breakPattern = pattern(1); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 2, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 2, soundEffectOccurrences); // then assertThat(tracks.size(), is(6)); @@ -183,7 +183,7 @@ public void shouldIncreaseExtractLengthOnCrossFadeByTransitionDuration() { breakPattern = pattern(3); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 1, soundEffectOccurrences); // then assertThat(tracks.size(), is(3)); @@ -201,7 +201,7 @@ public void shouldRejectTooShortTracks() { musicPattern = pattern(11); // when - playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, soundEffectOccurrences); + playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 1, soundEffectOccurrences); } @Test @@ -214,7 +214,7 @@ public void shouldUseExtractFromInsideTheBoundaries() { musicPattern = pattern(25); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 1, soundEffectOccurrences); // then assertThat(tracks.size(), is(1)); @@ -238,7 +238,7 @@ public void shouldSkipTooShortTracksButNeverthelessCompletelyFillPattern() { breakPattern = pattern(1); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 3, soundEffectOccurrences); // then assertThat(tracks.size(), is(3)); @@ -263,7 +263,7 @@ public void shouldTakeTheSameTrackInContinuousMode() { musicPattern = pattern(10); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 10, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 10, soundEffectOccurrences); // then assertThat(tracks.size(), is(10)); @@ -294,7 +294,7 @@ public void shouldTakeTheSameTrackInContinuousShuffleMode() { musicPattern = pattern(10); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 9, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 9, soundEffectOccurrences); // then assertThat(tracks.size(), is(9)); @@ -321,7 +321,7 @@ public void shouldPlaceSpecialEffectsWithSimplePattern() { soundEffectOccurrences.add(new SoundEffectOccurrence(soundEffect1, 15000)); // when - playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 1, 3, soundEffectOccurrences); // then assertTrue(playlist.hasSoundEffects()); @@ -339,7 +339,7 @@ public void shouldPlaceSpecialEffectsWithComplexPattern() { soundEffectOccurrences.add(new SoundEffectOccurrence(soundEffect1, 6000)); // when - playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 5, soundEffectOccurrences); + playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 1, 5, soundEffectOccurrences); // then assertTrue(playlist.hasSoundEffects()); @@ -356,7 +356,7 @@ public void shouldNotPlaceSpecialEffectIfItExceedsTotalLength() { soundEffectOccurrences.add(new SoundEffectOccurrence(soundEffect1, 9000)); // when - playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 2, soundEffectOccurrences); + playlist.generatePlaylist(musicList, musicPattern, breakList, breakPattern, 1, 2, soundEffectOccurrences); // then assertTrue(playlist.hasSoundEffects()); @@ -377,7 +377,7 @@ public void shouldNotThrowExceptionInContinuousShuffleMode() { musicPattern = pattern(10); // when - List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3, soundEffectOccurrences); + List tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1, 3, soundEffectOccurrences); // then assertThat(tracks.size(), is(3)); diff --git a/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/mainscreen/MainScreenController.java b/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/mainscreen/MainScreenController.java index 09c82e6..19c0382 100644 --- a/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/mainscreen/MainScreenController.java +++ b/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/mainscreen/MainScreenController.java @@ -126,6 +126,12 @@ public class MainScreenController implements Initializable { @FXML private Button addBreakTrackButton; + @FXML + private Label breakVolumeLabel; + + @FXML + private Slider breakVolume; + @FXML private DraggableAudioFileBreakListView breakTrackListView; @@ -335,6 +341,17 @@ private void initializeTrackEnumeration() { private void initializeBreakTrackList() { breakTrackListView.initializeWith(musicListControl.getBreakList(), bundle, messageProducer, musicListControl, e -> Platform.runLater(this::updateTrackCount)); addBreakTrackButton.setOnAction(event -> openFileChooserFor(breakTrackListView)); + + breakVolume.valueProperty().addListener((observable, oldValue, newValue) -> { + double volume = newValue.doubleValue(); + if (((int)volume) == volume) { + /* The slider fires not only for the configured integer positions, but also in between (e.g. for 1.023323). We skip such data. */ + breakVolumeLabel.setText(bundle.getString("ui.form.break_list.volume", volume)); + compilationParameters.setBreakVolume(volume / 100.0); + } + }); + breakVolume.valueProperty().addListener(debugHandlerWith(breakVolume.getId())); + breakVolumeLabel.setText(bundle.getString("ui.form.break_list.volume", breakVolume.valueProperty().intValue())); } private void initializeDurationControls() { @@ -356,7 +373,6 @@ private void initializeDurationControls() { soundPeriod.valueProperty().addListener(debugHandlerWith(soundPeriod.getId())); soundPeriod.valueProperty().addListener(updateUiHandler()); - breakPeriod.valueProperty().addListener(debugHandlerWith(breakPeriod.getId())); breakPeriod.valueProperty().addListener(updateUiHandler()); @@ -455,6 +471,8 @@ private void loadStoredPreferenceValues() { soundPattern.textProperty().setValue(userPreferences.loadSoundPattern("")); breakPattern.textProperty().setValue(userPreferences.loadBreakPattern("")); + breakVolume.valueProperty().setValue(userPreferences.loadBreakVolume(100)); + Tab selectedTab = getPeriodTabFor(userPreferences.loadPeriodTab("simpleTab")); periodTabPane.getSelectionModel().select(selectedTab); updatePeriodSelectionWith(selectedTab); @@ -479,6 +497,15 @@ private void registerPreferenceSaveListeners() { breakPeriod.valueProperty().addListener((observable, oldValue, newValue) -> userPreferences.saveBreakPeriod(newValue)); soundPattern.textProperty().addListener((observable, oldValue, newValue) -> userPreferences.saveSoundPattern(newValue)); breakPattern.textProperty().addListener((observable, oldValue, newValue) -> userPreferences.saveBreakPattern(newValue)); + + breakVolume.valueProperty().addListener((observable, oldValue, newValue) -> { + double volume = newValue.doubleValue(); + if (((int)volume) == volume) { + /* The slider fires not only for the configured integer positions, but also in between (e.g. for 1.023323). We skip such data. */ + userPreferences.saveBreakVolume((int) volume); + } + }); + periodTabPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> userPreferences.savePeriodTab(newValue.getId())); iterations.valueProperty().addListener((observable, oldValue, newValue) -> userPreferences.saveIterations(newValue)); diff --git a/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/preferences/UiUserPreferences.java b/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/preferences/UiUserPreferences.java index 67a1f93..ea67789 100644 --- a/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/preferences/UiUserPreferences.java +++ b/intervalmusiccompositor.fxUi/src/main/java/ch/retorte/intervalmusiccompositor/ui/preferences/UiUserPreferences.java @@ -40,6 +40,7 @@ public class UiUserPreferences extends UserPreferences { private static final String BLEND_DURATION_KEY = "blendDuration"; private static final String SOUND_EFFECTS_KEY = "soundEffects"; + private static final String BREAK_VOLUME_KEY = "breakVolume"; private static final String OUTPUT_FILE_FORMAT_KEY = "outputFileFormat"; private static final String OUTPUT_DIRECTORY_KEY = "outputDirectory"; @@ -183,6 +184,14 @@ public List loadSoundEffectOccurrencesWith(SoundEffectsPr return deserializeSoundEffects(loadString(SOUND_EFFECTS_KEY, ""), soundEffectsProvider); } + public void saveBreakVolume(int breakVolume) { + saveInt(BREAK_VOLUME_KEY, breakVolume); + } + + public int loadBreakVolume(int defaultBreakVolume) { + return loadInt(BREAK_VOLUME_KEY, defaultBreakVolume); + } + public void saveOutputFileFormat(String outputFileFormat) { saveString(OUTPUT_FILE_FORMAT_KEY, outputFileFormat); } diff --git a/intervalmusiccompositor.fxUi/src/main/resources/layouts/MainScreen.fxml b/intervalmusiccompositor.fxUi/src/main/resources/layouts/MainScreen.fxml index b639644..d479680 100644 --- a/intervalmusiccompositor.fxUi/src/main/resources/layouts/MainScreen.fxml +++ b/intervalmusiccompositor.fxUi/src/main/resources/layouts/MainScreen.fxml @@ -115,12 +115,13 @@ - + -