diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index f3d36ec..e666dca 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -1,7 +1,6 @@ package com.lukas.music.song import com.lukas.music.instruments.Instrument -import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression import com.lukas.music.song.note.Note import com.lukas.music.util.Cycle @@ -11,22 +10,19 @@ val beats: Int ) : Cycle<Int>(beats) { val chordProgression = ChordProgression() - private lateinit var chord: Chord init { for (i in 0 until beats) { this += i } wraparoundListeners += { - chordProgression.step()?.let { chord = it.currentItem } + chordProgression.step() } } override fun step(): Int { super.step() - if (!this::chord.isInitialized) { - return index - } + val chord = chordProgression.currentItem?.currentItem ?: return index val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { voice.step(root, chordNotes) diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index f3d36ec..e666dca 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -1,7 +1,6 @@ package com.lukas.music.song import com.lukas.music.instruments.Instrument -import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression import com.lukas.music.song.note.Note import com.lukas.music.util.Cycle @@ -11,22 +10,19 @@ val beats: Int ) : Cycle<Int>(beats) { val chordProgression = ChordProgression() - private lateinit var chord: Chord init { for (i in 0 until beats) { this += i } wraparoundListeners += { - chordProgression.step()?.let { chord = it.currentItem } + chordProgression.step() } } override fun step(): Int { super.step() - if (!this::chord.isInitialized) { - return index - } + val chord = chordProgression.currentItem?.currentItem ?: return index val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { voice.step(root, chordNotes) diff --git a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt index 597c100..ba3d0b1 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt @@ -31,6 +31,23 @@ if (Rhythm.on) android.R.drawable.ic_media_pause else android.R.drawable.ic_media_play ) } + binding.advancePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigStep(true) + } + binding.reversePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigReverse(true) + } + binding.advanceMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.step() + } + binding.reverseMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.currentItem?.let { + chordDisplays[it.index].setCardBackgroundColor( + ContextCompat.getColor(binding.root.context, R.color.gray_0x40) + ) + } + Song.currentSong.chordProgression.reverse() + } setupSlider(binding.masterVolumeSlider, 0, 100, 100) { setMasterVolume(it.toDouble() / 100.0) binding.masterVolumeText.text = "Master volume: $it%" @@ -80,13 +97,10 @@ if (chordDisplays.isEmpty()) { putChords() } - chordDisplays[Song.currentSong.chordProgression.currentItem.index].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.index].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.purple_700) ) - if (Song.currentSong.chordProgression.currentItem.index == 0) { - return - } - chordDisplays[Song.currentSong.chordProgression.currentItem.indexBehind].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.indexBehind].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.gray_0x40) ) } @@ -121,7 +135,7 @@ private fun putChords() { binding.phraseDisplay.removeAllViews() chordDisplays.clear() - for (chord in Song.currentSong.chordProgression.currentItem) { + for (chord in Song.currentSong.chordProgression.currentItem ?: return) { val card = CardView(binding.root.context) card.layoutParams = SongFragment.tableRowLayout card.radius = 10f diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index f3d36ec..e666dca 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -1,7 +1,6 @@ package com.lukas.music.song import com.lukas.music.instruments.Instrument -import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression import com.lukas.music.song.note.Note import com.lukas.music.util.Cycle @@ -11,22 +10,19 @@ val beats: Int ) : Cycle<Int>(beats) { val chordProgression = ChordProgression() - private lateinit var chord: Chord init { for (i in 0 until beats) { this += i } wraparoundListeners += { - chordProgression.step()?.let { chord = it.currentItem } + chordProgression.step() } } override fun step(): Int { super.step() - if (!this::chord.isInitialized) { - return index - } + val chord = chordProgression.currentItem?.currentItem ?: return index val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { voice.step(root, chordNotes) diff --git a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt index 597c100..ba3d0b1 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt @@ -31,6 +31,23 @@ if (Rhythm.on) android.R.drawable.ic_media_pause else android.R.drawable.ic_media_play ) } + binding.advancePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigStep(true) + } + binding.reversePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigReverse(true) + } + binding.advanceMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.step() + } + binding.reverseMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.currentItem?.let { + chordDisplays[it.index].setCardBackgroundColor( + ContextCompat.getColor(binding.root.context, R.color.gray_0x40) + ) + } + Song.currentSong.chordProgression.reverse() + } setupSlider(binding.masterVolumeSlider, 0, 100, 100) { setMasterVolume(it.toDouble() / 100.0) binding.masterVolumeText.text = "Master volume: $it%" @@ -80,13 +97,10 @@ if (chordDisplays.isEmpty()) { putChords() } - chordDisplays[Song.currentSong.chordProgression.currentItem.index].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.index].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.purple_700) ) - if (Song.currentSong.chordProgression.currentItem.index == 0) { - return - } - chordDisplays[Song.currentSong.chordProgression.currentItem.indexBehind].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.indexBehind].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.gray_0x40) ) } @@ -121,7 +135,7 @@ private fun putChords() { binding.phraseDisplay.removeAllViews() chordDisplays.clear() - for (chord in Song.currentSong.chordProgression.currentItem) { + for (chord in Song.currentSong.chordProgression.currentItem ?: return) { val card = CardView(binding.root.context) card.layoutParams = SongFragment.tableRowLayout card.radius = 10f diff --git a/app/src/main/java/com/lukas/music/util/Cycle.kt b/app/src/main/java/com/lukas/music/util/Cycle.kt index 4bf3ec5..7e49239 100644 --- a/app/src/main/java/com/lukas/music/util/Cycle.kt +++ b/app/src/main/java/com/lukas/music/util/Cycle.kt @@ -8,8 +8,8 @@ val indexBehind: Int get() = (index - 1 + size) % size - val currentItem: T - get() = this[index] + val currentItem: T? + get() = if (size == 0) null else this[index] open fun step(): T? { if (size == 0) { @@ -28,6 +28,22 @@ return this[index] } + open fun reset() { + index = size - 1 + step() + } + + open fun reverse() { + if (size == 0) { + return + } + index = indexBehind + // TODO: back around handlers + for (callback in stepCallback) { + callback() + } + } + fun lookahead(distance: Int): T { return this[(index + distance) % size] } @@ -53,4 +69,52 @@ } return this[index] } + + override fun reset() { + this[index].reset() + super.reset() + } + + fun bigStep(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem?.indexBehind ?: return + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } + + override fun reverse() { + currentItem?.reverse() ?: return + if (currentItem!!.index == currentItem!!.size - 1) { + currentItem!!.reset() + super.reverse() + currentItem!!.index = currentItem!!.size - 1 + } + for (callback in miniStepCallback) { + callback() + } + } + + fun bigReverse(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.reset() + index = indexBehind + index = indexBehind + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem!!.indexBehind + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index f3d36ec..e666dca 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -1,7 +1,6 @@ package com.lukas.music.song import com.lukas.music.instruments.Instrument -import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression import com.lukas.music.song.note.Note import com.lukas.music.util.Cycle @@ -11,22 +10,19 @@ val beats: Int ) : Cycle<Int>(beats) { val chordProgression = ChordProgression() - private lateinit var chord: Chord init { for (i in 0 until beats) { this += i } wraparoundListeners += { - chordProgression.step()?.let { chord = it.currentItem } + chordProgression.step() } } override fun step(): Int { super.step() - if (!this::chord.isInitialized) { - return index - } + val chord = chordProgression.currentItem?.currentItem ?: return index val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { voice.step(root, chordNotes) diff --git a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt index 597c100..ba3d0b1 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt @@ -31,6 +31,23 @@ if (Rhythm.on) android.R.drawable.ic_media_pause else android.R.drawable.ic_media_play ) } + binding.advancePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigStep(true) + } + binding.reversePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigReverse(true) + } + binding.advanceMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.step() + } + binding.reverseMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.currentItem?.let { + chordDisplays[it.index].setCardBackgroundColor( + ContextCompat.getColor(binding.root.context, R.color.gray_0x40) + ) + } + Song.currentSong.chordProgression.reverse() + } setupSlider(binding.masterVolumeSlider, 0, 100, 100) { setMasterVolume(it.toDouble() / 100.0) binding.masterVolumeText.text = "Master volume: $it%" @@ -80,13 +97,10 @@ if (chordDisplays.isEmpty()) { putChords() } - chordDisplays[Song.currentSong.chordProgression.currentItem.index].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.index].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.purple_700) ) - if (Song.currentSong.chordProgression.currentItem.index == 0) { - return - } - chordDisplays[Song.currentSong.chordProgression.currentItem.indexBehind].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.indexBehind].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.gray_0x40) ) } @@ -121,7 +135,7 @@ private fun putChords() { binding.phraseDisplay.removeAllViews() chordDisplays.clear() - for (chord in Song.currentSong.chordProgression.currentItem) { + for (chord in Song.currentSong.chordProgression.currentItem ?: return) { val card = CardView(binding.root.context) card.layoutParams = SongFragment.tableRowLayout card.radius = 10f diff --git a/app/src/main/java/com/lukas/music/util/Cycle.kt b/app/src/main/java/com/lukas/music/util/Cycle.kt index 4bf3ec5..7e49239 100644 --- a/app/src/main/java/com/lukas/music/util/Cycle.kt +++ b/app/src/main/java/com/lukas/music/util/Cycle.kt @@ -8,8 +8,8 @@ val indexBehind: Int get() = (index - 1 + size) % size - val currentItem: T - get() = this[index] + val currentItem: T? + get() = if (size == 0) null else this[index] open fun step(): T? { if (size == 0) { @@ -28,6 +28,22 @@ return this[index] } + open fun reset() { + index = size - 1 + step() + } + + open fun reverse() { + if (size == 0) { + return + } + index = indexBehind + // TODO: back around handlers + for (callback in stepCallback) { + callback() + } + } + fun lookahead(distance: Int): T { return this[(index + distance) % size] } @@ -53,4 +69,52 @@ } return this[index] } + + override fun reset() { + this[index].reset() + super.reset() + } + + fun bigStep(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem?.indexBehind ?: return + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } + + override fun reverse() { + currentItem?.reverse() ?: return + if (currentItem!!.index == currentItem!!.size - 1) { + currentItem!!.reset() + super.reverse() + currentItem!!.index = currentItem!!.size - 1 + } + for (callback in miniStepCallback) { + callback() + } + } + + fun bigReverse(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.reset() + index = indexBehind + index = indexBehind + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem!!.indexBehind + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_play.xml b/app/src/main/res/layout/fragment_play.xml index fe27cd5..e4182bf 100644 --- a/app/src/main/res/layout/fragment_play.xml +++ b/app/src/main/res/layout/fragment_play.xml @@ -33,6 +33,63 @@ tools:layout_conversion_absoluteHeight="56dp" tools:layout_conversion_absoluteWidth="56dp" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/reverseMeasureButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_marginEnd="16dp" + android:clickable="true" + android:contentDescription="@string/reverse_measure" + android:src="@android:drawable/ic_media_previous" + app:fabSize="mini" + app:layout_constraintEnd_toStartOf="@+id/playButton" + app:layout_constraintTop_toTopOf="parent" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/reversePhraseButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:clickable="true" + android:contentDescription="@string/reverse_phrase" + android:src="@android:drawable/ic_media_rew" + app:fabSize="mini" + app:layout_constraintEnd_toStartOf="@+id/reverseMeasureButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/advanceMeasureButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:clickable="true" + android:contentDescription="@string/advance_measure" + android:src="@android:drawable/ic_media_next" + app:fabSize="mini" + app:layout_constraintStart_toEndOf="@+id/playButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/advancePhraseButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:clickable="true" + android:contentDescription="@string/advance_phrase" + android:src="@android:drawable/ic_media_ff" + app:fabSize="mini" + app:layout_constraintStart_toEndOf="@+id/advanceMeasureButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + <TextView android:id="@+id/tempoText" android:layout_width="wrap_content" @@ -122,7 +179,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" - android:text="Next chord not known yet . . ." + android:text="@string/unknown_chord" android:textSize="20sp" tools:layout_conversion_absoluteHeight="27dp" tools:layout_conversion_absoluteWidth="258dp" /> diff --git a/.idea/misc.xml b/.idea/misc.xml index e3b7c48..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ <entry key="app/src/main/res/layout/fragment_instrument.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_instrument_list.xml" value="0.2805755395683453" /> <entry key="app/src/main/res/layout/fragment_main.xml" value="0.1" /> - <entry key="app/src/main/res/layout/fragment_play.xml" value="0.24644549763033174" /> + <entry key="app/src/main/res/layout/fragment_play.xml" value="0.33" /> <entry key="app/src/main/res/layout/fragment_song.xml" value="0.24644549763033174" /> <entry key="app/src/main/res/layout/sample_credits_tab.xml" value="0.25" /> <entry key="app/src/main/res/layout/sample_instrument_view.xml" value="0.33" /> diff --git a/app/src/main/java/com/lukas/music/song/Song.kt b/app/src/main/java/com/lukas/music/song/Song.kt index f3d36ec..e666dca 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -1,7 +1,6 @@ package com.lukas.music.song import com.lukas.music.instruments.Instrument -import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression import com.lukas.music.song.note.Note import com.lukas.music.util.Cycle @@ -11,22 +10,19 @@ val beats: Int ) : Cycle<Int>(beats) { val chordProgression = ChordProgression() - private lateinit var chord: Chord init { for (i in 0 until beats) { this += i } wraparoundListeners += { - chordProgression.step()?.let { chord = it.currentItem } + chordProgression.step() } } override fun step(): Int { super.step() - if (!this::chord.isInitialized) { - return index - } + val chord = chordProgression.currentItem?.currentItem ?: return index val chordNotes = chord.getNotes(root) for (voice in Instrument.voice) { voice.step(root, chordNotes) diff --git a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt index 597c100..ba3d0b1 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/PlayFragment.kt @@ -31,6 +31,23 @@ if (Rhythm.on) android.R.drawable.ic_media_pause else android.R.drawable.ic_media_play ) } + binding.advancePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigStep(true) + } + binding.reversePhraseButton.setOnClickListener { + Song.currentSong.chordProgression.bigReverse(true) + } + binding.advanceMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.step() + } + binding.reverseMeasureButton.setOnClickListener { + Song.currentSong.chordProgression.currentItem?.let { + chordDisplays[it.index].setCardBackgroundColor( + ContextCompat.getColor(binding.root.context, R.color.gray_0x40) + ) + } + Song.currentSong.chordProgression.reverse() + } setupSlider(binding.masterVolumeSlider, 0, 100, 100) { setMasterVolume(it.toDouble() / 100.0) binding.masterVolumeText.text = "Master volume: $it%" @@ -80,13 +97,10 @@ if (chordDisplays.isEmpty()) { putChords() } - chordDisplays[Song.currentSong.chordProgression.currentItem.index].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.index].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.purple_700) ) - if (Song.currentSong.chordProgression.currentItem.index == 0) { - return - } - chordDisplays[Song.currentSong.chordProgression.currentItem.indexBehind].setCardBackgroundColor( + chordDisplays[Song.currentSong.chordProgression.currentItem!!.indexBehind].setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.gray_0x40) ) } @@ -121,7 +135,7 @@ private fun putChords() { binding.phraseDisplay.removeAllViews() chordDisplays.clear() - for (chord in Song.currentSong.chordProgression.currentItem) { + for (chord in Song.currentSong.chordProgression.currentItem ?: return) { val card = CardView(binding.root.context) card.layoutParams = SongFragment.tableRowLayout card.radius = 10f diff --git a/app/src/main/java/com/lukas/music/util/Cycle.kt b/app/src/main/java/com/lukas/music/util/Cycle.kt index 4bf3ec5..7e49239 100644 --- a/app/src/main/java/com/lukas/music/util/Cycle.kt +++ b/app/src/main/java/com/lukas/music/util/Cycle.kt @@ -8,8 +8,8 @@ val indexBehind: Int get() = (index - 1 + size) % size - val currentItem: T - get() = this[index] + val currentItem: T? + get() = if (size == 0) null else this[index] open fun step(): T? { if (size == 0) { @@ -28,6 +28,22 @@ return this[index] } + open fun reset() { + index = size - 1 + step() + } + + open fun reverse() { + if (size == 0) { + return + } + index = indexBehind + // TODO: back around handlers + for (callback in stepCallback) { + callback() + } + } + fun lookahead(distance: Int): T { return this[(index + distance) % size] } @@ -53,4 +69,52 @@ } return this[index] } + + override fun reset() { + this[index].reset() + super.reset() + } + + fun bigStep(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem?.indexBehind ?: return + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } + + override fun reverse() { + currentItem?.reverse() ?: return + if (currentItem!!.index == currentItem!!.size - 1) { + currentItem!!.reset() + super.reverse() + currentItem!!.index = currentItem!!.size - 1 + } + for (callback in miniStepCallback) { + callback() + } + } + + fun bigReverse(keepSubindex: Boolean = false) { + val subindex = currentItem?.index ?: return + currentItem?.reset() + index = indexBehind + index = indexBehind + currentItem?.index = currentItem!!.size - 1 + step() + if (keepSubindex) { + currentItem?.index = subindex + currentItem?.index = currentItem!!.indexBehind + currentItem?.step() + for (callback in miniStepCallback) { + callback() + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_play.xml b/app/src/main/res/layout/fragment_play.xml index fe27cd5..e4182bf 100644 --- a/app/src/main/res/layout/fragment_play.xml +++ b/app/src/main/res/layout/fragment_play.xml @@ -33,6 +33,63 @@ tools:layout_conversion_absoluteHeight="56dp" tools:layout_conversion_absoluteWidth="56dp" /> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/reverseMeasureButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_marginEnd="16dp" + android:clickable="true" + android:contentDescription="@string/reverse_measure" + android:src="@android:drawable/ic_media_previous" + app:fabSize="mini" + app:layout_constraintEnd_toStartOf="@+id/playButton" + app:layout_constraintTop_toTopOf="parent" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/reversePhraseButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="16dp" + android:clickable="true" + android:contentDescription="@string/reverse_phrase" + android:src="@android:drawable/ic_media_rew" + app:fabSize="mini" + app:layout_constraintEnd_toStartOf="@+id/reverseMeasureButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/advanceMeasureButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:clickable="true" + android:contentDescription="@string/advance_measure" + android:src="@android:drawable/ic_media_next" + app:fabSize="mini" + app:layout_constraintStart_toEndOf="@+id/playButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/advancePhraseButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:clickable="true" + android:contentDescription="@string/advance_phrase" + android:src="@android:drawable/ic_media_ff" + app:fabSize="mini" + app:layout_constraintStart_toEndOf="@+id/advanceMeasureButton" + app:layout_constraintTop_toTopOf="@+id/reverseMeasureButton" + tools:layout_conversion_absoluteHeight="56dp" + tools:layout_conversion_absoluteWidth="56dp" /> + <TextView android:id="@+id/tempoText" android:layout_width="wrap_content" @@ -122,7 +179,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp" - android:text="Next chord not known yet . . ." + android:text="@string/unknown_chord" android:textSize="20sp" tools:layout_conversion_absoluteHeight="27dp" tools:layout_conversion_absoluteWidth="258dp" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ba02a4..c125463 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,4 +19,9 @@ <string name="chord_pitch">Chord pitch</string> <string name="chord_type">Chord type</string> <string name="exit">Exit this menu</string> + <string name="advance_measure">Go to the next measure</string> + <string name="reverse_measure">Go to the start of the current measure</string> + <string name="advance_phrase">Go to the next phrase</string> + <string name="reverse_phrase">Go to the start of the current phrase</string> + <string name="unknown_chord">Next chord not known yet . . .</string> </resources> \ No newline at end of file