diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ 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 899cefe..e72a20d 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 @@ -64,6 +64,7 @@ Song.currentSong.stepButtons += child binding.beatIndicator.addView(child) } + Song.currentSong.chordDisplay = binding.currentChord return binding.root } diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ 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 899cefe..e72a20d 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 @@ -64,6 +64,7 @@ Song.currentSong.stepButtons += child binding.beatIndicator.addView(child) } + Song.currentSong.chordDisplay = binding.currentChord return binding.root } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt index 1791310..ef0af81 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt @@ -45,7 +45,7 @@ return binding.root } - private fun updateChords() { + fun updateChords() { binding.chords.removeAllViews() for (phrase in Song.currentSong.chordProgression.phrases) { val row = TableRow(binding.root.context) @@ -53,6 +53,9 @@ val card = CardView(binding.root.context) card.radius = 10f card.layoutParams = layout + card.setOnClickListener { + EditChordFragment(chord, this).showNow(childFragmentManager, "") + } val text = TextView(binding.root.context) text.text = chord.toString(displayChordNames, Song.currentSong.root) text.layoutParams = layout diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ 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 899cefe..e72a20d 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 @@ -64,6 +64,7 @@ Song.currentSong.stepButtons += child binding.beatIndicator.addView(child) } + Song.currentSong.chordDisplay = binding.currentChord return binding.root } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt index 1791310..ef0af81 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt @@ -45,7 +45,7 @@ return binding.root } - private fun updateChords() { + fun updateChords() { binding.chords.removeAllViews() for (phrase in Song.currentSong.chordProgression.phrases) { val row = TableRow(binding.root.context) @@ -53,6 +53,9 @@ val card = CardView(binding.root.context) card.radius = 10f card.layoutParams = layout + card.setOnClickListener { + EditChordFragment(chord, this).showNow(childFragmentManager, "") + } val text = TextView(binding.root.context) text.text = chord.toString(displayChordNames, Song.currentSong.root) text.layoutParams = layout diff --git a/app/src/main/res/layout/fragment_edit_chord.xml b/app/src/main/res/layout/fragment_edit_chord.xml new file mode 100644 index 0000000..8de2950 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_chord.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ 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 899cefe..e72a20d 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 @@ -64,6 +64,7 @@ Song.currentSong.stepButtons += child binding.beatIndicator.addView(child) } + Song.currentSong.chordDisplay = binding.currentChord return binding.root } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt index 1791310..ef0af81 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt @@ -45,7 +45,7 @@ return binding.root } - private fun updateChords() { + fun updateChords() { binding.chords.removeAllViews() for (phrase in Song.currentSong.chordProgression.phrases) { val row = TableRow(binding.root.context) @@ -53,6 +53,9 @@ val card = CardView(binding.root.context) card.radius = 10f card.layoutParams = layout + card.setOnClickListener { + EditChordFragment(chord, this).showNow(childFragmentManager, "") + } val text = TextView(binding.root.context) text.text = chord.toString(displayChordNames, Song.currentSong.root) text.layoutParams = layout diff --git a/app/src/main/res/layout/fragment_edit_chord.xml b/app/src/main/res/layout/fragment_edit_chord.xml new file mode 100644 index 0000000..8de2950 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_chord.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + \ 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 5b0cc4a..136de13 100644 --- a/app/src/main/res/layout/fragment_play.xml +++ b/app/src/main/res/layout/fragment_play.xml @@ -58,5 +58,15 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/masterVolumeSlider" /> + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 447e0d3..5c9d3cd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,10 +8,11 @@ + - + diff --git a/app/src/main/java/com/lukas/music/song/Scale.kt b/app/src/main/java/com/lukas/music/song/Scale.kt new file mode 100644 index 0000000..bc016cb --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/Scale.kt @@ -0,0 +1,19 @@ +package com.lukas.music.song + +import com.lukas.music.song.chords.ChordType + +enum class Scale(val identifier: String, val steps: Array, val chordTypes: Array) { + MAJOR( + "major", + arrayOf(0, 2, 4, 5, 7, 9, 11, 12), + arrayOf( + ChordType.MAJOR, + ChordType.MINOR, + ChordType.MINOR, + ChordType.MAJOR, + ChordType.MAJOR, + ChordType.MINOR, + ChordType.DIMINISHED + ) + ) +} \ No newline at end of file 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 9dd3cb8..4b23eac 100644 --- a/app/src/main/java/com/lukas/music/song/Song.kt +++ b/app/src/main/java/com/lukas/music/song/Song.kt @@ -3,6 +3,7 @@ import android.os.Handler import android.os.Looper import android.widget.RadioButton +import android.widget.TextView import com.lukas.music.instruments.Instrument import com.lukas.music.song.chords.Chord import com.lukas.music.song.chords.ChordProgression @@ -16,6 +17,7 @@ private var beat = 0 private var chord: Chord = chordProgression.step() val stepButtons = mutableListOf() + lateinit var chordDisplay: TextView fun step() { Handler(Looper.getMainLooper()).post { @@ -31,6 +33,7 @@ for (voice in Instrument.voice) { voice.step(root, chordNotes) } + chordDisplay.text = chord.toString(true, root) } } diff --git a/app/src/main/java/com/lukas/music/song/chords/Chord.kt b/app/src/main/java/com/lukas/music/song/chords/Chord.kt index 61dac11..e0dd8fc 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Chord.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Chord.kt @@ -2,8 +2,13 @@ import com.lukas.music.song.note.Note -class Chord(val note: Int, private val chordType: ChordType) { - private val interval = Interval(note) +class Chord(note: Int, var chordType: ChordType) { + var note: Int = note + set(value) { + field = value + interval = Interval(value) + } + var interval = Interval(note) fun getNotes(root: Note): Array { return Array(chordType.notes.size) { root + note + chordType.notes[it] } diff --git a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt index e53189a..95b69a0 100644 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ b/app/src/main/java/com/lukas/music/song/chords/ChordType.kt @@ -5,11 +5,16 @@ private val asString: String, val transform: (String) -> String ) { - Major(arrayOf(0, 4, 7), "major", { it.uppercase() }), - Minor(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + MAJOR(arrayOf(0, 4, 7), "major", { it.uppercase() }), + MINOR(arrayOf(0, 3, 7), "minor", { it.lowercase() }), + DIMINISHED(arrayOf(0, 3, 6), "diminished", { it.lowercase() + "0" }), ; override fun toString(): String { return asString } + + companion object { + val VALUES = values() + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Interval.kt b/app/src/main/java/com/lukas/music/song/chords/Interval.kt index b3b4480..60a6ced 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Interval.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Interval.kt @@ -1,7 +1,7 @@ package com.lukas.music.song.chords class Interval(private val distance: Int) { - private val name: IntervalName = when (distance) { + val name: IntervalName = when (distance) { 0 -> IntervalName.UNISON 1, 2 -> IntervalName.SECOND 3, 4 -> IntervalName.THIRD @@ -13,7 +13,7 @@ else -> throw IllegalArgumentException("cannot make interval from distance $distance") } private val modifier: Modifier = when (distance) { - 0, 5, 12 -> Modifier.PERFECT + 0, 7, 12 -> Modifier.PERFECT 1, 3, 5, 8, 10 -> Modifier.MINOR else -> Modifier.MAJOR } @@ -36,12 +36,20 @@ override fun toString(): String { return romanVersion } + + companion object { + val VALUES = values() + val NAMES = Array(VALUES.size) { VALUES[it].romanVersion } + } } enum class Modifier(val descriptor: String, val offset: Int) { PERFECT("", 0), MINOR("", 0), MAJOR("", 1); - } + companion object { + val VALUES = values() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 76d4259..611d1b8 100644 --- a/app/src/main/java/com/lukas/music/song/chords/Phrase.kt +++ b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt @@ -2,20 +2,21 @@ class Phrase { val chords = mutableListOf( - Chord(0, ChordType.Major), - Chord(5, ChordType.Major), - Chord(2, ChordType.Minor), - Chord(7, ChordType.Major), + Chord(0, ChordType.MAJOR), + Chord(5, ChordType.MAJOR), + Chord(2, ChordType.MINOR), + Chord(7, ChordType.MAJOR), ) var position = 0 fun step(parent: ChordProgression): Chord { var parent: ChordProgression = parent + val result = chords[position] position++ if (position >= chords.size) { position = 0 parent++ } - return chords[position] + return result } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt new file mode 100644 index 0000000..2f3bbb2 --- /dev/null +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -0,0 +1,92 @@ +package com.lukas.music.ui.fragments + +import android.R +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.fragment.app.DialogFragment +import com.lukas.music.databinding.FragmentEditChordBinding +import com.lukas.music.song.Scale +import com.lukas.music.song.Song +import com.lukas.music.song.chords.Chord +import com.lukas.music.song.chords.ChordType +import com.lukas.music.song.chords.Interval + +class EditChordFragment(val chord: Chord, val songFragment: SongFragment) : DialogFragment() { + lateinit var binding: FragmentEditChordBinding + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentEditChordBinding.inflate(inflater) + setupPitchSpinner() + setupTypeSpinner() + binding.exitButton.setOnClickListener { + dismiss() + } + return binding.root + } + + private fun setupPitchSpinner() { + val pitches = if (songFragment.displayChordNames) { + Array(Scale.MAJOR.steps.size) { (Song.currentSong.root + Scale.MAJOR.steps[it]).noteName.toString() } + } else Interval.IntervalName.NAMES + val pitchAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, pitches + ) + binding.pitchSpinner.adapter = pitchAdapter + binding.pitchSpinner.setSelection(chord.interval.name.ordinal) + binding.pitchSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + chord.note = Scale.MAJOR.steps[position] + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } + + private fun setupTypeSpinner() { + val values = mutableListOf("default") + for (chordType in ChordType.VALUES) { + values += chordType.toString() + } + val modifierAdapter = ArrayAdapter( + binding.root.context, + R.layout.simple_spinner_dropdown_item, values + ) + binding.typeSpinner.adapter = modifierAdapter + binding.typeSpinner.setSelection( + if (chord.chordType == Scale.MAJOR.chordTypes[chord.interval.name.ordinal]) + 0 + else chord.chordType.ordinal + 1 + ) + binding.typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long + ) { + if (position == 0) { + chord.chordType = Scale.MAJOR.chordTypes[chord.interval.name.ordinal] + } else { + chord.chordType = ChordType.VALUES[position - 1] + } + songFragment.updateChords() + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + } +} \ 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 899cefe..e72a20d 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 @@ -64,6 +64,7 @@ Song.currentSong.stepButtons += child binding.beatIndicator.addView(child) } + Song.currentSong.chordDisplay = binding.currentChord return binding.root } diff --git a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt index 1791310..ef0af81 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/SongFragment.kt @@ -45,7 +45,7 @@ return binding.root } - private fun updateChords() { + fun updateChords() { binding.chords.removeAllViews() for (phrase in Song.currentSong.chordProgression.phrases) { val row = TableRow(binding.root.context) @@ -53,6 +53,9 @@ val card = CardView(binding.root.context) card.radius = 10f card.layoutParams = layout + card.setOnClickListener { + EditChordFragment(chord, this).showNow(childFragmentManager, "") + } val text = TextView(binding.root.context) text.text = chord.toString(displayChordNames, Song.currentSong.root) text.layoutParams = layout diff --git a/app/src/main/res/layout/fragment_edit_chord.xml b/app/src/main/res/layout/fragment_edit_chord.xml new file mode 100644 index 0000000..8de2950 --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_chord.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + \ 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 5b0cc4a..136de13 100644 --- a/app/src/main/res/layout/fragment_play.xml +++ b/app/src/main/res/layout/fragment_play.xml @@ -58,5 +58,15 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/masterVolumeSlider" /> + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a200009..1ba02a4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,4 +13,10 @@ Add a new phrase Actual chords select the song\'s key + Edit chord + Edit this chord\'s pitch + Edit this chord\'s type + Chord pitch + Chord type + Exit this menu \ No newline at end of file