diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt index e06761a..40b1d14 100644 --- a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt +++ b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt @@ -11,6 +11,7 @@ package com.lukas.music.song.voice import com.lukas.music.song.ScaleType +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note import com.lukas.music.util.transform @@ -20,7 +21,7 @@ val getNotes: (Note, Array) -> Array ) { Bass("Bass note", 1, { _, chordNotes -> arrayOf(chordNotes[0]) }), - Chord("Chord notes", 3, { _, chordNotes -> chordNotes }), + ChordVoice("Chord notes", Chord.NOTE_COUNT, { _, chordNotes -> chordNotes }), Scale("Scale notes", 8, { root, _ -> ScaleType.MAJOR.steps.transform { root + it } }), Root("Root note", 1, { root, _ -> arrayOf(root) }), RootRelative("Song root relative", 12, { root, _ -> Array(12) { root + it } }), diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt index e06761a..40b1d14 100644 --- a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt +++ b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt @@ -11,6 +11,7 @@ package com.lukas.music.song.voice import com.lukas.music.song.ScaleType +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note import com.lukas.music.util.transform @@ -20,7 +21,7 @@ val getNotes: (Note, Array) -> Array ) { Bass("Bass note", 1, { _, chordNotes -> arrayOf(chordNotes[0]) }), - Chord("Chord notes", 3, { _, chordNotes -> chordNotes }), + ChordVoice("Chord notes", Chord.NOTE_COUNT, { _, chordNotes -> chordNotes }), Scale("Scale notes", 8, { root, _ -> ScaleType.MAJOR.steps.transform { root + it } }), Root("Root note", 1, { root, _ -> arrayOf(root) }), RootRelative("Song root relative", 12, { root, _ -> Array(12) { root + it } }), diff --git a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt index 3e129bc..3d7d1f4 100644 --- a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt +++ b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt @@ -39,7 +39,7 @@ Song.currentSong.soloInstrument = instrument } field = value - binding.soloButton.updateToggle(this::solo, R.color.blue) + binding.soloButton.updateToggle(this.solo, R.color.blue) } var instrument: Instrument? = null diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt index e06761a..40b1d14 100644 --- a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt +++ b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt @@ -11,6 +11,7 @@ package com.lukas.music.song.voice import com.lukas.music.song.ScaleType +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note import com.lukas.music.util.transform @@ -20,7 +21,7 @@ val getNotes: (Note, Array) -> Array ) { Bass("Bass note", 1, { _, chordNotes -> arrayOf(chordNotes[0]) }), - Chord("Chord notes", 3, { _, chordNotes -> chordNotes }), + ChordVoice("Chord notes", Chord.NOTE_COUNT, { _, chordNotes -> chordNotes }), Scale("Scale notes", 8, { root, _ -> ScaleType.MAJOR.steps.transform { root + it } }), Root("Root note", 1, { root, _ -> arrayOf(root) }), RootRelative("Song root relative", 12, { root, _ -> Array(12) { root + it } }), diff --git a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt index 3e129bc..3d7d1f4 100644 --- a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt +++ b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt @@ -39,7 +39,7 @@ Song.currentSong.soloInstrument = instrument } field = value - binding.soloButton.updateToggle(this::solo, R.color.blue) + binding.soloButton.updateToggle(this.solo, R.color.blue) } var instrument: Instrument? = null 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 index 6c3bae3..86d18ca 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -14,14 +14,21 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TableRow +import android.widget.TextView +import androidx.core.view.children +import androidx.core.view.setMargins import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton +import com.lukas.music.R import com.lukas.music.databinding.FragmentEditChordBinding import com.lukas.music.song.ScaleType import com.lukas.music.song.Song +import com.lukas.music.song.chords.Accidental import com.lukas.music.song.chords.Chord -import com.lukas.music.song.chords.ChordType import com.lukas.music.song.chords.Interval import com.lukas.music.util.setup +import com.lukas.music.util.updateToggle class EditChordFragment(private val chord: Chord, private val songFragment: SongFragment) : DialogFragment() { @@ -33,42 +40,89 @@ ): View? { binding = FragmentEditChordBinding.inflate(inflater) setupPitchSpinner() - setupTypeSpinner() + setupEditor() binding.exitButton.setOnClickListener { dismiss() } return binding.root } + private fun update() { + songFragment.updateChords() + binding.chordText.text = chord.toString(true, Song.currentSong.root) + updateEditor() + } + private fun setupPitchSpinner() { val pitches = if (songFragment.displayChordNames) { Array(ScaleType.MAJOR.steps.size) { (Song.currentSong.root + ScaleType.MAJOR.steps[it]).noteName.toString() } } else Interval.IntervalName.NAMES binding.pitchSpinner.setup(pitches, chord.interval.name.ordinal) { chord.note = ScaleType.MAJOR.steps[it] - if (binding.typeSpinner.selectedItemPosition == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } - songFragment.updateChords() + update() + // todo: setup chord to be the correct type } } - private fun setupTypeSpinner() { - val values = mutableListOf("default") - for (chordType in ChordType.VALUES) { - values += chordType.toString() + private fun setupEditor() { + binding.editorGrid.removeAllViews() + val row = TableRow(binding.root.context) + for (description in descriptions) { + val text = TextView(binding.root.context) + text.text = description + text.layoutParams = layout + text.textAlignment = TextView.TEXT_ALIGNMENT_CENTER + row.addView(text) } - binding.typeSpinner.setup( - values, - if (chord.chordType == ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal]) 0 - else chord.chordType.ordinal + 1 - ) { - if (it == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } else { - chord.chordType = ChordType.VALUES[it - 1] + binding.editorGrid.addView(row) + for (accidental in Accidental.VALUES) { + val row = TableRow(binding.root.context) + for (position in 0 until Chord.NOTE_COUNT - 1) { + val button = MaterialButton(binding.root.context) + button.text = accidental.toString() + button.layoutParams = layout + button.updateToggle(chord.accidentals[position] == accidental, R.color.blue) + button.setOnClickListener { + if (chord.accidentals[position] == accidental) { + chord.accidentals[position] = null + } else { + chord.accidentals[position] = accidental + } + update() + } + row.addView(button) } - songFragment.updateChords() + binding.editorGrid.addView(row) } } + + private fun updateEditor() { + for ((index, view) in binding.editorGrid.children.iterator().withIndex()) { + if (index == 0) { + continue + } + view as TableRow + for ((childIndex, childView) in view.children.iterator().withIndex()) { + childView as MaterialButton + childView.updateToggle( + chord.accidentals[childIndex] == Accidental.VALUES[index - 1], + R.color.blue + ) + } + } + } + + companion object { + val layout = TableRow.LayoutParams( + TableRow.LayoutParams.WRAP_CONTENT, + TableRow.LayoutParams.WRAP_CONTENT + ) + + init { + layout.weight = 1.0f + layout.setMargins(5) + } + + val descriptions = arrayOf("III", "V", "VII", "IX") + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt index e06761a..40b1d14 100644 --- a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt +++ b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt @@ -11,6 +11,7 @@ package com.lukas.music.song.voice import com.lukas.music.song.ScaleType +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note import com.lukas.music.util.transform @@ -20,7 +21,7 @@ val getNotes: (Note, Array) -> Array ) { Bass("Bass note", 1, { _, chordNotes -> arrayOf(chordNotes[0]) }), - Chord("Chord notes", 3, { _, chordNotes -> chordNotes }), + ChordVoice("Chord notes", Chord.NOTE_COUNT, { _, chordNotes -> chordNotes }), Scale("Scale notes", 8, { root, _ -> ScaleType.MAJOR.steps.transform { root + it } }), Root("Root note", 1, { root, _ -> arrayOf(root) }), RootRelative("Song root relative", 12, { root, _ -> Array(12) { root + it } }), diff --git a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt index 3e129bc..3d7d1f4 100644 --- a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt +++ b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt @@ -39,7 +39,7 @@ Song.currentSong.soloInstrument = instrument } field = value - binding.soloButton.updateToggle(this::solo, R.color.blue) + binding.soloButton.updateToggle(this.solo, R.color.blue) } var instrument: Instrument? = null 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 index 6c3bae3..86d18ca 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -14,14 +14,21 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TableRow +import android.widget.TextView +import androidx.core.view.children +import androidx.core.view.setMargins import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton +import com.lukas.music.R import com.lukas.music.databinding.FragmentEditChordBinding import com.lukas.music.song.ScaleType import com.lukas.music.song.Song +import com.lukas.music.song.chords.Accidental import com.lukas.music.song.chords.Chord -import com.lukas.music.song.chords.ChordType import com.lukas.music.song.chords.Interval import com.lukas.music.util.setup +import com.lukas.music.util.updateToggle class EditChordFragment(private val chord: Chord, private val songFragment: SongFragment) : DialogFragment() { @@ -33,42 +40,89 @@ ): View? { binding = FragmentEditChordBinding.inflate(inflater) setupPitchSpinner() - setupTypeSpinner() + setupEditor() binding.exitButton.setOnClickListener { dismiss() } return binding.root } + private fun update() { + songFragment.updateChords() + binding.chordText.text = chord.toString(true, Song.currentSong.root) + updateEditor() + } + private fun setupPitchSpinner() { val pitches = if (songFragment.displayChordNames) { Array(ScaleType.MAJOR.steps.size) { (Song.currentSong.root + ScaleType.MAJOR.steps[it]).noteName.toString() } } else Interval.IntervalName.NAMES binding.pitchSpinner.setup(pitches, chord.interval.name.ordinal) { chord.note = ScaleType.MAJOR.steps[it] - if (binding.typeSpinner.selectedItemPosition == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } - songFragment.updateChords() + update() + // todo: setup chord to be the correct type } } - private fun setupTypeSpinner() { - val values = mutableListOf("default") - for (chordType in ChordType.VALUES) { - values += chordType.toString() + private fun setupEditor() { + binding.editorGrid.removeAllViews() + val row = TableRow(binding.root.context) + for (description in descriptions) { + val text = TextView(binding.root.context) + text.text = description + text.layoutParams = layout + text.textAlignment = TextView.TEXT_ALIGNMENT_CENTER + row.addView(text) } - binding.typeSpinner.setup( - values, - if (chord.chordType == ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal]) 0 - else chord.chordType.ordinal + 1 - ) { - if (it == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } else { - chord.chordType = ChordType.VALUES[it - 1] + binding.editorGrid.addView(row) + for (accidental in Accidental.VALUES) { + val row = TableRow(binding.root.context) + for (position in 0 until Chord.NOTE_COUNT - 1) { + val button = MaterialButton(binding.root.context) + button.text = accidental.toString() + button.layoutParams = layout + button.updateToggle(chord.accidentals[position] == accidental, R.color.blue) + button.setOnClickListener { + if (chord.accidentals[position] == accidental) { + chord.accidentals[position] = null + } else { + chord.accidentals[position] = accidental + } + update() + } + row.addView(button) } - songFragment.updateChords() + binding.editorGrid.addView(row) } } + + private fun updateEditor() { + for ((index, view) in binding.editorGrid.children.iterator().withIndex()) { + if (index == 0) { + continue + } + view as TableRow + for ((childIndex, childView) in view.children.iterator().withIndex()) { + childView as MaterialButton + childView.updateToggle( + chord.accidentals[childIndex] == Accidental.VALUES[index - 1], + R.color.blue + ) + } + } + } + + companion object { + val layout = TableRow.LayoutParams( + TableRow.LayoutParams.WRAP_CONTENT, + TableRow.LayoutParams.WRAP_CONTENT + ) + + init { + layout.weight = 1.0f + layout.setMargins(5) + } + + val descriptions = arrayOf("III", "V", "VII", "IX") + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/util/UIUtil.kt b/app/src/main/java/com/lukas/music/util/UIUtil.kt index 72e0681..ea91abf 100644 --- a/app/src/main/java/com/lukas/music/util/UIUtil.kt +++ b/app/src/main/java/com/lukas/music/util/UIUtil.kt @@ -64,19 +64,19 @@ ) { setOnClickListener { target.set(!target.get()) - updateToggle(target, activeColor, inactiveColor) + updateToggle(target.get(), activeColor, inactiveColor) callback(target.get()) } - updateToggle(target, activeColor, inactiveColor) + updateToggle(target.get(), activeColor, inactiveColor) } fun Button.updateToggle( - target: KMutableProperty0, + value: Boolean, activeColor: Int, inactiveColor: Int = R.color.gray_0x60, ) { setBackgroundColor( - ContextCompat.getColor(context, if (target.get()) activeColor else inactiveColor) + ContextCompat.getColor(context, if (value) activeColor else inactiveColor) ) } diff --git a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt index b33c439..7f10ff2 100644 --- a/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt +++ b/app/src/main/java/com/lukas/music/instruments/PolyInstrument.kt @@ -10,11 +10,12 @@ package com.lukas.music.instruments +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note class PolyInstrument(name: String) : Instrument(name) { - private val internalInstruments = Array(3) { InternalInstrument() } - private val playing = Array(3) { false } + private val internalInstruments = Array(Chord.NOTE_COUNT) { InternalInstrument() } + private val playing = Array(Chord.NOTE_COUNT) { false } override var waveform: Waveform = Waveform.SINE set(value) { diff --git a/app/src/main/java/com/lukas/music/song/ScaleType.kt b/app/src/main/java/com/lukas/music/song/ScaleType.kt index 4c6a0d9..2f385dd 100644 --- a/app/src/main/java/com/lukas/music/song/ScaleType.kt +++ b/app/src/main/java/com/lukas/music/song/ScaleType.kt @@ -10,24 +10,12 @@ package com.lukas.music.song -import com.lukas.music.song.chords.ChordType - enum class ScaleType( 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/chords/Accidental.kt b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt new file mode 100644 index 0000000..688ae4e --- /dev/null +++ b/app/src/main/java/com/lukas/music/song/chords/Accidental.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 Lukas Eisenhauer + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. + * + * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + */ + +package com.lukas.music.song.chords + +enum class Accidental(val id: String, val short: String, val distance: Int) { + Flat("\u266D", "b", -1), + None("\u266E", "", 0), + Sharp("\u266F", "#", 1), + ; + + override fun toString(): String { + return id + } + + companion object { + val VALUES = values() + } +} \ No newline at end of file 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 4400dae..fedb465 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 @@ -10,14 +10,18 @@ package com.lukas.music.song.chords +import com.lukas.music.song.Song import com.lukas.music.song.note.Note -class Chord(note: Int, var chordType: ChordType) { - var note: Int = note +class Chord { + val accidentals: Array = arrayOf(Accidental.None, Accidental.None, null, null) + + var note: Int = 0 set(value) { field = value interval = Interval(value) } + var interval = Interval(note) set(value) { field = value @@ -27,19 +31,76 @@ } fun getNotes(root: Note): Array { - return Array(chordType.notes.size) { root + note + chordType.notes[it] } + val result = Array(NOTE_COUNT) { root } + var resultIndex = 0 + var accidentalIndex = 0 + var octave = 0 + while (resultIndex < NOTE_COUNT) { + if (accidentalIndex == 0) { + result[resultIndex] = root + note + 12 * octave + resultIndex++ + } else if (accidentals[accidentalIndex - 1] != null) { + result[resultIndex] = root + note + when (accidentalIndex) { + 1 -> 4 + 2 -> 7 + 3 -> 10 + 4 -> 14 + else -> 0 + } + accidentals[accidentalIndex - 1]!!.distance + 12 * octave + resultIndex++ + } + accidentalIndex++ + if (accidentalIndex > accidentals.size) { + octave++ + accidentalIndex = 0 + } + } + return result } override fun toString(): String { - return chordType.transform(interval.toString()) + return toString(false, Song.currentSong.root) } fun toString(displayChordNames: Boolean, root: Note): String { - val base = if (displayChordNames) { + var result = if (displayChordNames) { (root + note).noteName.toString() } else { interval.toString() } - return chordType.transform(base) + accidentals[0]?.let { + result += when (it) { + Accidental.Flat -> "-" + Accidental.Sharp -> "sus4" + else -> "" + } + } + accidentals[1]?.let { + if (accidentals[0] != null && it == Accidental.None) { + return@let + } + result += it.short + "5" + } + result = result.replace("-b5", "0") + result = result.replace("(?=[A-G])#5".toRegex(), "+") + accidentals[2]?.let { + result += when (it) { + Accidental.Sharp -> " maj7" + Accidental.None -> " 7" + Accidental.Flat -> " 6" + } + } + accidentals[3]?.let { + result += when (it) { + Accidental.Sharp -> " maj9" + Accidental.None -> " 9" + Accidental.Flat -> " b9" + } + } + return result + } + + companion object { + val NOTE_COUNT = 5 } } \ No newline at end of file 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 deleted file mode 100644 index 1fe4b40..0000000 --- a/app/src/main/java/com/lukas/music/song/chords/ChordType.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022 Lukas Eisenhauer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version. - * - * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see . - */ - -package com.lukas.music.song.chords - -enum class ChordType( - val notes: Array, - private val asString: String, - val transform: (String) -> String -) { - 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/Phrase.kt b/app/src/main/java/com/lukas/music/song/chords/Phrase.kt index 1cadb06..f4bcbc7 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 @@ -15,7 +15,7 @@ class Phrase : Cycle() { init { for (i in 0 until 4) { - this += Chord(0, ChordType.MAJOR) + this += Chord() } } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/song/note/Note.kt b/app/src/main/java/com/lukas/music/song/note/Note.kt index 17bfb2b..f17e322 100644 --- a/app/src/main/java/com/lukas/music/song/note/Note.kt +++ b/app/src/main/java/com/lukas/music/song/note/Note.kt @@ -12,7 +12,7 @@ import kotlin.math.pow -class Note(private val id: Int) { +class Note(val id: Int) { val noteName = NoteName.VALUES[id % 12] val octave = id / 12 - 1 val frequency = 440 * 2.0.pow((id - 69) / 12.0) @@ -28,6 +28,8 @@ return this + (-other) } + operator fun minus(other: Note): Int = id - other.id + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt index e06761a..40b1d14 100644 --- a/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt +++ b/app/src/main/java/com/lukas/music/song/voice/VoiceType.kt @@ -11,6 +11,7 @@ package com.lukas.music.song.voice import com.lukas.music.song.ScaleType +import com.lukas.music.song.chords.Chord import com.lukas.music.song.note.Note import com.lukas.music.util.transform @@ -20,7 +21,7 @@ val getNotes: (Note, Array) -> Array ) { Bass("Bass note", 1, { _, chordNotes -> arrayOf(chordNotes[0]) }), - Chord("Chord notes", 3, { _, chordNotes -> chordNotes }), + ChordVoice("Chord notes", Chord.NOTE_COUNT, { _, chordNotes -> chordNotes }), Scale("Scale notes", 8, { root, _ -> ScaleType.MAJOR.steps.transform { root + it } }), Root("Root note", 1, { root, _ -> arrayOf(root) }), RootRelative("Song root relative", 12, { root, _ -> Array(12) { root + it } }), diff --git a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt index 3e129bc..3d7d1f4 100644 --- a/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt +++ b/app/src/main/java/com/lukas/music/ui/adapters/InstrumentViewHolder.kt @@ -39,7 +39,7 @@ Song.currentSong.soloInstrument = instrument } field = value - binding.soloButton.updateToggle(this::solo, R.color.blue) + binding.soloButton.updateToggle(this.solo, R.color.blue) } var instrument: Instrument? = null 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 index 6c3bae3..86d18ca 100644 --- a/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt +++ b/app/src/main/java/com/lukas/music/ui/fragments/EditChordFragment.kt @@ -14,14 +14,21 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.TableRow +import android.widget.TextView +import androidx.core.view.children +import androidx.core.view.setMargins import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton +import com.lukas.music.R import com.lukas.music.databinding.FragmentEditChordBinding import com.lukas.music.song.ScaleType import com.lukas.music.song.Song +import com.lukas.music.song.chords.Accidental import com.lukas.music.song.chords.Chord -import com.lukas.music.song.chords.ChordType import com.lukas.music.song.chords.Interval import com.lukas.music.util.setup +import com.lukas.music.util.updateToggle class EditChordFragment(private val chord: Chord, private val songFragment: SongFragment) : DialogFragment() { @@ -33,42 +40,89 @@ ): View? { binding = FragmentEditChordBinding.inflate(inflater) setupPitchSpinner() - setupTypeSpinner() + setupEditor() binding.exitButton.setOnClickListener { dismiss() } return binding.root } + private fun update() { + songFragment.updateChords() + binding.chordText.text = chord.toString(true, Song.currentSong.root) + updateEditor() + } + private fun setupPitchSpinner() { val pitches = if (songFragment.displayChordNames) { Array(ScaleType.MAJOR.steps.size) { (Song.currentSong.root + ScaleType.MAJOR.steps[it]).noteName.toString() } } else Interval.IntervalName.NAMES binding.pitchSpinner.setup(pitches, chord.interval.name.ordinal) { chord.note = ScaleType.MAJOR.steps[it] - if (binding.typeSpinner.selectedItemPosition == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } - songFragment.updateChords() + update() + // todo: setup chord to be the correct type } } - private fun setupTypeSpinner() { - val values = mutableListOf("default") - for (chordType in ChordType.VALUES) { - values += chordType.toString() + private fun setupEditor() { + binding.editorGrid.removeAllViews() + val row = TableRow(binding.root.context) + for (description in descriptions) { + val text = TextView(binding.root.context) + text.text = description + text.layoutParams = layout + text.textAlignment = TextView.TEXT_ALIGNMENT_CENTER + row.addView(text) } - binding.typeSpinner.setup( - values, - if (chord.chordType == ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal]) 0 - else chord.chordType.ordinal + 1 - ) { - if (it == 0) { - chord.chordType = ScaleType.MAJOR.chordTypes[chord.interval.name.ordinal] - } else { - chord.chordType = ChordType.VALUES[it - 1] + binding.editorGrid.addView(row) + for (accidental in Accidental.VALUES) { + val row = TableRow(binding.root.context) + for (position in 0 until Chord.NOTE_COUNT - 1) { + val button = MaterialButton(binding.root.context) + button.text = accidental.toString() + button.layoutParams = layout + button.updateToggle(chord.accidentals[position] == accidental, R.color.blue) + button.setOnClickListener { + if (chord.accidentals[position] == accidental) { + chord.accidentals[position] = null + } else { + chord.accidentals[position] = accidental + } + update() + } + row.addView(button) } - songFragment.updateChords() + binding.editorGrid.addView(row) } } + + private fun updateEditor() { + for ((index, view) in binding.editorGrid.children.iterator().withIndex()) { + if (index == 0) { + continue + } + view as TableRow + for ((childIndex, childView) in view.children.iterator().withIndex()) { + childView as MaterialButton + childView.updateToggle( + chord.accidentals[childIndex] == Accidental.VALUES[index - 1], + R.color.blue + ) + } + } + } + + companion object { + val layout = TableRow.LayoutParams( + TableRow.LayoutParams.WRAP_CONTENT, + TableRow.LayoutParams.WRAP_CONTENT + ) + + init { + layout.weight = 1.0f + layout.setMargins(5) + } + + val descriptions = arrayOf("III", "V", "VII", "IX") + } } \ No newline at end of file diff --git a/app/src/main/java/com/lukas/music/util/UIUtil.kt b/app/src/main/java/com/lukas/music/util/UIUtil.kt index 72e0681..ea91abf 100644 --- a/app/src/main/java/com/lukas/music/util/UIUtil.kt +++ b/app/src/main/java/com/lukas/music/util/UIUtil.kt @@ -64,19 +64,19 @@ ) { setOnClickListener { target.set(!target.get()) - updateToggle(target, activeColor, inactiveColor) + updateToggle(target.get(), activeColor, inactiveColor) callback(target.get()) } - updateToggle(target, activeColor, inactiveColor) + updateToggle(target.get(), activeColor, inactiveColor) } fun Button.updateToggle( - target: KMutableProperty0, + value: Boolean, activeColor: Int, inactiveColor: Int = R.color.gray_0x60, ) { setBackgroundColor( - ContextCompat.getColor(context, if (target.get()) activeColor else inactiveColor) + ContextCompat.getColor(context, if (value) activeColor else inactiveColor) ) } diff --git a/app/src/main/res/layout/fragment_edit_chord.xml b/app/src/main/res/layout/fragment_edit_chord.xml index 16fa8f2..245172c 100644 --- a/app/src/main/res/layout/fragment_edit_chord.xml +++ b/app/src/main/res/layout/fragment_edit_chord.xml @@ -19,10 +19,10 @@ @@ -39,19 +39,6 @@ app:layout_constraintStart_toEndOf="@+id/textView5" app:layout_constraintTop_toBottomOf="@+id/textView4" /> - - - - + app:layout_constraintTop_toBottomOf="@+id/editorGrid" /> + + + + \ No newline at end of file