diff --git a/.idea/misc.xml b/.idea/misc.xml index 5c9d3cd..c668cf4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 5c9d3cd..c668cf4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ - + 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 d40829a..ed56f52 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,22 +3,19 @@ import android.os.Handler import android.os.Looper import android.view.View -import android.widget.TextView -import androidx.core.view.doOnDetach 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.chords.Phrase import com.lukas.music.song.note.Note class Song( var root: Note, - val chordProgression: ChordProgression, val beats: Int ) { + val chordProgression = ChordProgression(this) private var beat = beats - 1 private lateinit var chord: Chord - lateinit var chordDisplay: TextView - private val beatCallback = mutableListOf<(Int, Int) -> Unit>() fun step() { if (chordProgression.phrases.isEmpty()) { @@ -29,7 +26,14 @@ beat++ if (beat >= beats) { beat = 0 + var oldChord: Chord? = null + if (this::chord.isInitialized) { + oldChord = chord + } chord = chordProgression.step() + for (callback in chordCallback) { + callback(oldChord ?: chord, chord) + } } for (callback in beatCallback) { callback(before, beat) @@ -39,22 +43,29 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } - chordDisplay.text = chord.toString(true, root) } } companion object { var currentSong = Song( Note.NOTES[69], - ChordProgression(), 4 ) + private val beatCallback = mutableListOf<(Int, Int) -> Unit>() + private val chordCallback = mutableListOf<(Chord, Chord) -> Unit>() + val phraseCallback = mutableListOf<(Phrase) -> Unit>() + fun View.setOnBeatCallback(callback: (Int, Int) -> Unit) { - currentSong.beatCallback += callback - doOnDetach { - currentSong.beatCallback.remove(callback) - } + beatCallback += callback + } + + fun View.setOnPhraseCallback(callback: (Phrase) -> Unit) { + phraseCallback += callback + } + + fun View.setOnChordCallback(callback: (Chord, Chord) -> Unit) { + chordCallback += callback } } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5c9d3cd..c668cf4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ - + 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 d40829a..ed56f52 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,22 +3,19 @@ import android.os.Handler import android.os.Looper import android.view.View -import android.widget.TextView -import androidx.core.view.doOnDetach 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.chords.Phrase import com.lukas.music.song.note.Note class Song( var root: Note, - val chordProgression: ChordProgression, val beats: Int ) { + val chordProgression = ChordProgression(this) private var beat = beats - 1 private lateinit var chord: Chord - lateinit var chordDisplay: TextView - private val beatCallback = mutableListOf<(Int, Int) -> Unit>() fun step() { if (chordProgression.phrases.isEmpty()) { @@ -29,7 +26,14 @@ beat++ if (beat >= beats) { beat = 0 + var oldChord: Chord? = null + if (this::chord.isInitialized) { + oldChord = chord + } chord = chordProgression.step() + for (callback in chordCallback) { + callback(oldChord ?: chord, chord) + } } for (callback in beatCallback) { callback(before, beat) @@ -39,22 +43,29 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } - chordDisplay.text = chord.toString(true, root) } } companion object { var currentSong = Song( Note.NOTES[69], - ChordProgression(), 4 ) + private val beatCallback = mutableListOf<(Int, Int) -> Unit>() + private val chordCallback = mutableListOf<(Chord, Chord) -> Unit>() + val phraseCallback = mutableListOf<(Phrase) -> Unit>() + fun View.setOnBeatCallback(callback: (Int, Int) -> Unit) { - currentSong.beatCallback += callback - doOnDetach { - currentSong.beatCallback.remove(callback) - } + beatCallback += callback + } + + fun View.setOnPhraseCallback(callback: (Phrase) -> Unit) { + phraseCallback += callback + } + + fun View.setOnChordCallback(callback: (Chord, Chord) -> Unit) { + chordCallback += callback } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt index ba9419e..56e42ec 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt @@ -1,6 +1,8 @@ package com.lukas.music.song.chords -class ChordProgression { +import com.lukas.music.song.Song + +class ChordProgression(val song: Song) { // TODO: special handler for increasing or decreasing measuresPerPhrase val measuresPerPhrase: Int = 4 val phrases = mutableListOf() @@ -14,6 +16,9 @@ operator fun inc(): ChordProgression { position++ position %= phrases.size + for (callback in Song.phraseCallback) { + callback(phrases[position]) + } return this } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 5c9d3cd..c668cf4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ - + 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 d40829a..ed56f52 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,22 +3,19 @@ import android.os.Handler import android.os.Looper import android.view.View -import android.widget.TextView -import androidx.core.view.doOnDetach 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.chords.Phrase import com.lukas.music.song.note.Note class Song( var root: Note, - val chordProgression: ChordProgression, val beats: Int ) { + val chordProgression = ChordProgression(this) private var beat = beats - 1 private lateinit var chord: Chord - lateinit var chordDisplay: TextView - private val beatCallback = mutableListOf<(Int, Int) -> Unit>() fun step() { if (chordProgression.phrases.isEmpty()) { @@ -29,7 +26,14 @@ beat++ if (beat >= beats) { beat = 0 + var oldChord: Chord? = null + if (this::chord.isInitialized) { + oldChord = chord + } chord = chordProgression.step() + for (callback in chordCallback) { + callback(oldChord ?: chord, chord) + } } for (callback in beatCallback) { callback(before, beat) @@ -39,22 +43,29 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } - chordDisplay.text = chord.toString(true, root) } } companion object { var currentSong = Song( Note.NOTES[69], - ChordProgression(), 4 ) + private val beatCallback = mutableListOf<(Int, Int) -> Unit>() + private val chordCallback = mutableListOf<(Chord, Chord) -> Unit>() + val phraseCallback = mutableListOf<(Phrase) -> Unit>() + fun View.setOnBeatCallback(callback: (Int, Int) -> Unit) { - currentSong.beatCallback += callback - doOnDetach { - currentSong.beatCallback.remove(callback) - } + beatCallback += callback + } + + fun View.setOnPhraseCallback(callback: (Phrase) -> Unit) { + phraseCallback += callback + } + + fun View.setOnChordCallback(callback: (Chord, Chord) -> Unit) { + chordCallback += callback } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt index ba9419e..56e42ec 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt @@ -1,6 +1,8 @@ package com.lukas.music.song.chords -class ChordProgression { +import com.lukas.music.song.Song + +class ChordProgression(val song: Song) { // TODO: special handler for increasing or decreasing measuresPerPhrase val measuresPerPhrase: Int = 4 val phrases = mutableListOf() @@ -14,6 +16,9 @@ operator fun inc(): ChordProgression { position++ position %= phrases.size + for (callback in Song.phraseCallback) { + callback(phrases[position]) + } return this } } \ No newline at end of file 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 48f9bcc..c8744cd 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 @@ -4,19 +4,23 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.RadioButton -import android.widget.RadioGroup -import android.widget.SeekBar -import android.widget.Space +import android.widget.* +import androidx.cardview.widget.CardView +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import com.lukas.music.R import com.lukas.music.databinding.FragmentPlayBinding import com.lukas.music.instruments.Rhythm import com.lukas.music.song.Song import com.lukas.music.song.Song.Companion.setOnBeatCallback +import com.lukas.music.song.Song.Companion.setOnChordCallback +import com.lukas.music.song.Song.Companion.setOnPhraseCallback +import com.lukas.music.song.chords.Chord class PlayFragment : Fragment() { lateinit var binding: FragmentPlayBinding - val beatIndicators = mutableListOf() + private val beatIndicators = mutableListOf() + private val chordDisplays = mutableMapOf() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -66,11 +70,38 @@ beatIndicators += child binding.beatIndicator.addView(child) } - Song.currentSong.chordDisplay = binding.currentChord binding.root.setOnBeatCallback { before, now -> beatIndicators[before].isChecked = false beatIndicators[now].isChecked = true } + binding.root.setOnPhraseCallback { + binding.phraseDisplay.removeAllViews() + chordDisplays.clear() + for (chord in it.chords) { + val card = CardView(binding.root.context) + val text = TextView(binding.root.context) + text.text = chord.toString(true, Song.currentSong.root) + card.layoutParams = SongFragment.layout + card.addView(text) + binding.phraseDisplay.addView(card) + chordDisplays[chord] = card + } + binding.phraseTable.isStretchAllColumns = true + } + binding.root.setOnChordCallback { old, new -> + chordDisplays[old]?.setBackgroundColor( + ContextCompat.getColor( + binding.root.context, + R.color.gray_600 + ) + ) + chordDisplays[new]?.setBackgroundColor( + ContextCompat.getColor( + binding.root.context, + R.color.gray_400 + ) + ) + } return binding.root } diff --git a/.idea/misc.xml b/.idea/misc.xml index 5c9d3cd..c668cf4 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -12,7 +12,7 @@ - + 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 d40829a..ed56f52 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,22 +3,19 @@ import android.os.Handler import android.os.Looper import android.view.View -import android.widget.TextView -import androidx.core.view.doOnDetach 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.chords.Phrase import com.lukas.music.song.note.Note class Song( var root: Note, - val chordProgression: ChordProgression, val beats: Int ) { + val chordProgression = ChordProgression(this) private var beat = beats - 1 private lateinit var chord: Chord - lateinit var chordDisplay: TextView - private val beatCallback = mutableListOf<(Int, Int) -> Unit>() fun step() { if (chordProgression.phrases.isEmpty()) { @@ -29,7 +26,14 @@ beat++ if (beat >= beats) { beat = 0 + var oldChord: Chord? = null + if (this::chord.isInitialized) { + oldChord = chord + } chord = chordProgression.step() + for (callback in chordCallback) { + callback(oldChord ?: chord, chord) + } } for (callback in beatCallback) { callback(before, beat) @@ -39,22 +43,29 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } - chordDisplay.text = chord.toString(true, root) } } companion object { var currentSong = Song( Note.NOTES[69], - ChordProgression(), 4 ) + private val beatCallback = mutableListOf<(Int, Int) -> Unit>() + private val chordCallback = mutableListOf<(Chord, Chord) -> Unit>() + val phraseCallback = mutableListOf<(Phrase) -> Unit>() + fun View.setOnBeatCallback(callback: (Int, Int) -> Unit) { - currentSong.beatCallback += callback - doOnDetach { - currentSong.beatCallback.remove(callback) - } + beatCallback += callback + } + + fun View.setOnPhraseCallback(callback: (Phrase) -> Unit) { + phraseCallback += callback + } + + fun View.setOnChordCallback(callback: (Chord, Chord) -> Unit) { + chordCallback += callback } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt index ba9419e..56e42ec 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordProgression.kt @@ -1,6 +1,8 @@ package com.lukas.music.song.chords -class ChordProgression { +import com.lukas.music.song.Song + +class ChordProgression(val song: Song) { // TODO: special handler for increasing or decreasing measuresPerPhrase val measuresPerPhrase: Int = 4 val phrases = mutableListOf() @@ -14,6 +16,9 @@ operator fun inc(): ChordProgression { position++ position %= phrases.size + for (callback in Song.phraseCallback) { + callback(phrases[position]) + } return this } } \ No newline at end of file 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 48f9bcc..c8744cd 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 @@ -4,19 +4,23 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.RadioButton -import android.widget.RadioGroup -import android.widget.SeekBar -import android.widget.Space +import android.widget.* +import androidx.cardview.widget.CardView +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment +import com.lukas.music.R import com.lukas.music.databinding.FragmentPlayBinding import com.lukas.music.instruments.Rhythm import com.lukas.music.song.Song import com.lukas.music.song.Song.Companion.setOnBeatCallback +import com.lukas.music.song.Song.Companion.setOnChordCallback +import com.lukas.music.song.Song.Companion.setOnPhraseCallback +import com.lukas.music.song.chords.Chord class PlayFragment : Fragment() { lateinit var binding: FragmentPlayBinding - val beatIndicators = mutableListOf() + private val beatIndicators = mutableListOf() + private val chordDisplays = mutableMapOf() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -66,11 +70,38 @@ beatIndicators += child binding.beatIndicator.addView(child) } - Song.currentSong.chordDisplay = binding.currentChord binding.root.setOnBeatCallback { before, now -> beatIndicators[before].isChecked = false beatIndicators[now].isChecked = true } + binding.root.setOnPhraseCallback { + binding.phraseDisplay.removeAllViews() + chordDisplays.clear() + for (chord in it.chords) { + val card = CardView(binding.root.context) + val text = TextView(binding.root.context) + text.text = chord.toString(true, Song.currentSong.root) + card.layoutParams = SongFragment.layout + card.addView(text) + binding.phraseDisplay.addView(card) + chordDisplays[chord] = card + } + binding.phraseTable.isStretchAllColumns = true + } + binding.root.setOnChordCallback { old, new -> + chordDisplays[old]?.setBackgroundColor( + ContextCompat.getColor( + binding.root.context, + R.color.gray_600 + ) + ) + chordDisplays[new]?.setBackgroundColor( + ContextCompat.getColor( + binding.root.context, + R.color.gray_400 + ) + ) + } return binding.root } diff --git a/app/src/main/res/layout/fragment_play.xml b/app/src/main/res/layout/fragment_play.xml index 136de13..ace467f 100644 --- a/app/src/main/res/layout/fragment_play.xml +++ b/app/src/main/res/layout/fragment_play.xml @@ -58,15 +58,21 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/masterVolumeSlider" /> - + app:layout_constraintTop_toBottomOf="@+id/beatIndicator"> + + + + \ No newline at end of file