-
Key: { props.songKey.getName() }
-
+ const [allChordsKind, setAllChordsKind] = React.useState(-1)
+ const [allChordsAccidental, setAllChordsAccidental] = React.useState(0)
+ const [inKeyChordType, setInKeyChordType] = React.useState(0)
+ const [sus2, setSus2] = React.useState(false)
+ const [sus4, setSus4] = React.useState(false)
+ const [add9, setAdd9] = React.useState(false)
+ const [add11, setAdd11] = React.useState(false)
+ const [add13, setAdd13] = React.useState(false)
+ const [no3, setNo3] = React.useState(false)
+ const [no5, setNo5] = React.useState(false)
+
+ const ChordButton = (props2) =>
+ {
+ const color = props2.chord.getColor(props.songKey)
+ const baseStr = props2.chord.getNameBase(props.songKey)
+ const supStr = props2.chord.getNameSup(props.songKey)
+ const subStr = props2.chord.getNameSub(props.songKey)
+
+ return
+ }
- { [0, 1, 2, 3, 4, 5, 6].map(degree =>
+ const TabButton = (props2) =>
+ {
+ return
props2.onClick(props2.value) } style={{ minWidth:"2em", display:"inline-block", fontFamily:"Verdana", fontSize:"15px", backgroundColor:(props2.value == props2.variable ? "#ddd" : "#fff"), padding:"0.25em", borderRadius:"0.25em", marginRight:"0.5em", cursor:"pointer" }}>
+ { props2.children }
+
+ }
+
+ const AllChordsMenu = () =>
+ {
+ return
+ }
+
+ const AllChordsMatrix = () =>
+ {
+ let groupIndex = 0
+
+ return
+ {
+ chords.map((chord, index) =>
+ {
+ if (chord.startGroup)
+ groupIndex = 1
- let baseStr = getRomanNumeralScaleDegreeStr(degree, 0)
- if (chordKind.symbol[0])
- baseStr = baseStr.toLowerCase()
+ return
+ { (groupIndex++) % 9 == 0 || chord.startGroup ?
: null }
+
+ { (chord.symbol[0] ? "i" : "I") + chord.symbol[1] }{ chord.symbol[2] }
+
+
+ })
+ }
+
+ }
- baseStr += chordKind.symbol[1]
+ let modifiers = {}
+ if (sus2) modifiers.sus2 = true
+ if (sus4) modifiers.sus4 = true
+ if (add9) modifiers.add9 = true
+ if (add11) modifiers.add11 = true
+ if (add13) modifiers.add13 = true
+ if (no3) modifiers.no3 = true
+ if (no5) modifiers.no5 = true
+
+ let chordButtons = null
+
+ switch (allChordsKind)
+ {
+ case -1:
+ chordButtons = [0, 1, 2, 3, 4, 5, 6].map(degree =>
+ {
+ const root = getPitchForScaleDegree(props.songKey, degree)
- return
- }
- )}
+ let pitches = [0]
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 2) - root)
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 4) - root)
+
+ if (inKeyChordType >= 1)
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 6) - root)
+
+ if (inKeyChordType >= 2)
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 8) - root)
+
+ if (inKeyChordType >= 3)
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 10) - root)
+
+ if (inKeyChordType >= 4)
+ pitches.push(getPitchForScaleDegree(props.songKey, degree + 12) - root)
+
+ const kind = getChordKindFromPitches(pitches)
+
+ return
+ })
+ break
+
+ default:
+ chordButtons =
+ ♭
+ ♮
+ ♯
+
+ { [0, 1, 2, 3, 4, 5, 6].map(degree => {
+ const kind = getChordKindFromPitches(chords[allChordsKind].pitches)
+ const root = getPitchForScaleDegree(props.songKey, degree)
+
+ return
+ })}
+
+ break
+ }
+ const Checkbox = (props2) =>
+ {
+ return
props2.onClick(ev.target.checked) }/> | |
+ }
+
+ const RadioButton = (props2) =>
+ {
+ return
props2.onClick(props2.value) }/> | |
+ }
+
+ return
+
+
+
+
+ |
+
+ |
+
+ { props.songKey.getName() }
+ |
+
+
+
+
+ |
+
+
+ { allChordsKind >= 0 ? null :
+ |
+ }
+ |
+
+ |
+
+ { chordButtons }
+ |
+
+
}
@@ -62,8 +238,17 @@ export default function Toolbox(props)
{
const keyChange = props.editor.song.keyChanges.findActiveAt(props.editor.cursorTime.start) || new KeyChange(new Rational(0), new Key(0, 0, scales.major.pitches))
+ const onSelectNote = (pitch) => props.editor.insertNoteAtCursor(pitch)
+ const onSelectChord = (chord) => props.editor.insertChordAtCursor(chord)
+
+ const callbacks =
+ {
+ onSelectNote,
+ onSelectChord,
+ }
+
return
- { props.editor.cursorTrack.start != 1 ? null : }
- { props.editor.cursorTrack.start != 2 ? null : }
+ { props.editor.cursorTrack.start != 1 ? null : }
+ { props.editor.cursorTrack.start != 2 ? null : }
}
\ No newline at end of file
diff --git a/src/util/theory.js b/src/util/theory.js
index 8e8ae3e..6543af7 100644
--- a/src/util/theory.js
+++ b/src/util/theory.js
@@ -13,52 +13,45 @@ export const scales =
mixolydian: { pitches: [0, 2, 4, 5, 7, 9, 10], mode: 4, name: "Mixolydian" },
minor: { pitches: [0, 2, 3, 5, 7, 8, 10], mode: 5, name: "Natural Minor" },
locrian: { pitches: [0, 1, 3, 5, 6, 8, 10], mode: 6, name: "Locrian" },
+
+ doubleHarmonic: { pitches: [0, 1, 4, 5, 7, 8, 11], mode: 0, name: "Double Harmonic Major" },
}
export const chords =
-{
- // `symbol` = [isLowercase, complement, superscriptComplement]
-
- major: { pitches: [0, 4, 7], code: "", symbol: [false, "", null], name: "Major", startGroup: "Triads" },
- minor: { pitches: [0, 3, 7], code: "m", symbol: [true, "", null], name: "Minor" },
- augmented: { pitches: [0, 4, 8], code: "+", symbol: [false, "", "+"], name: "Augmented" },
- diminished: { pitches: [0, 3, 6], code: "o", symbol: [true, "", "o"], name: "Diminished" },
-
- power: { pitches: [0, 0, 7, 12], code: "5", symbol: [false, "", "5"], name: "Power" },
-
- major6: { pitches: [0, 4, 7, 9], code: "6", symbol: [false, "", "6"], name: "Major Sixth", startGroup: "Sixths" },
- minor6: { pitches: [0, 3, 7, 9], code: "m6", symbol: [true, "", "6"], name: "Minor Sixth" },
-
- dominant7: { pitches: [0, 4, 7, 10], code: "7", symbol: [false, "", "7"], name: "Dominant Seventh", startGroup: "Sevenths" },
- major7: { pitches: [0, 4, 7, 11], code: "maj7", symbol: [false, "", "M7"], name: "Major Seventh" },
- minor7: { pitches: [0, 3, 7, 10], code: "m7", symbol: [true, "", "7"], name: "Minor Seventh" },
- minorMajor7: { pitches: [0, 3, 7, 11], code: "mmaj7", symbol: [true, "", "M7"], name: "Minor-Major Seventh" },
- augmented7: { pitches: [0, 4, 8, 10], code: "+7", symbol: [false, "+", "7"], name: "Augmented Seventh" },
- augmentedMajor7: { pitches: [0, 4, 8, 11], code: "+maj7", symbol: [false, "+", "M7"], name: "Augmented Major Seventh" },
- diminished7: { pitches: [0, 3, 6, 9], code: "o7", symbol: [true, "", "o7"], name: "Diminished Seventh" },
- halfDiminished7: { pitches: [0, 3, 6, 10], code: "%7", symbol: [true, "", "ø7"], name: "Half-Diminished Seventh" },
-
- dominant9: { pitches: [0, 4, 7, 10, 14], code: "9", symbol: [false, "", "9"], name: "Dominant Ninth", startGroup: "Ninths" },
- major9: { pitches: [0, 4, 7, 11, 14], code: "maj9", symbol: [false, "", "M9"], name: "Major Ninth" },
- minor9: { pitches: [0, 3, 7, 10, 14], code: "m9", symbol: [true, "", "9"], name: "Minor Ninth" },
- minorMajor9: { pitches: [0, 3, 7, 11, 14], code: "mmaj9", symbol: [true, "", "M9"], name: "Minor-Major Ninth" },
- augmented9: { pitches: [0, 4, 8, 10, 14], code: "+9", symbol: [false, "+", "9"], name: "Augmented Ninth" },
- augmentedMajor9: { pitches: [0, 4, 8, 11, 14], code: "+maj9", symbol: [false, "+", "M9"], name: "Augmented Major Ninth" },
- diminished9: { pitches: [0, 3, 6, 9, 14], code: "o9", symbol: [true, "", "o9"], name: "Diminished Ninth" },
- diminishedMinor9: { pitches: [0, 3, 6, 9, 13], code: "ob9", symbol: [true, "", "o♭9"], name: "Diminished Minor Ninth" },
- halfDiminished9: { pitches: [0, 3, 6, 10, 14], code: "%9", symbol: [true, "", "ø9"], name: "Half-Diminished Ninth" },
- halfDiminishedMinor9: { pitches: [0, 3, 6, 10, 13], code: "%b9", symbol: [true, "", "ø♭9"], name: "Half-Diminished Minor Ninth" },
-}
-
-
-export const chordList =
[
- chords.major, chords.minor, chords.augmented, chords.diminished, chords.power, chords.major6, chords.minor6,
- chords.dominant7, chords.major7, chords.minor7, chords.minorMajor7, chords.augmented7, chords.augmentedMajor7,
- chords.diminished7, chords.halfDiminished7,
- chords.dominant9, chords.major9, chords.minor9, chords.minorMajor9, chords.augmented9, chords.augmentedMajor9,
- chords.diminished9, chords.diminishedMinor9, chords.halfDiminished9, chords.halfDiminishedMinor9,
+ { pitches: [0, 4, 7], code: "", symbol: [false, "", null], name: "Major", startGroup: "Triads" },
+ { pitches: [0, 3, 7], code: "m", symbol: [true, "", null], name: "Minor" },
+ { pitches: [0, 4, 8], code: "+", symbol: [false, "", "+"], name: "Augmented" },
+ { pitches: [0, 3, 6], code: "o", symbol: [true, "", "o"], name: "Diminished" },
+ { pitches: [0, 2, 6], code: "oo", symbol: [true, "", "oo"], name: "Doubly-Diminished" },
+ { pitches: [0, 4, 6], code: "b5", symbol: [false, "", "(b5)"], name: "Flat-Fifth" },
+
+ { pitches: [0, 0, 7, 12], code: "5", symbol: [false, "", "5"], name: "Power" },
+
+ { pitches: [0, 4, 7, 9], code: "6", symbol: [false, "", "6"], name: "Major Sixth", startGroup: "Sixths" },
+ { pitches: [0, 3, 7, 9], code: "m6", symbol: [true, "", "6"], name: "Minor Sixth" },
+
+ { pitches: [0, 4, 7, 10], code: "7", symbol: [false, "", "7"], name: "Dominant Seventh", startGroup: "Sevenths" },
+ { pitches: [0, 4, 7, 11], code: "maj7", symbol: [false, "", "M7"], name: "Major Seventh" },
+ { pitches: [0, 3, 7, 10], code: "m7", symbol: [true, "", "7"], name: "Minor Seventh" },
+ { pitches: [0, 3, 7, 11], code: "mmaj7", symbol: [true, "", "M7"], name: "Minor-Major Seventh" },
+ { pitches: [0, 4, 8, 10], code: "+7", symbol: [false, "+", "7"], name: "Augmented Seventh" },
+ { pitches: [0, 4, 8, 11], code: "+maj7", symbol: [false, "+", "M7"], name: "Augmented Major Seventh" },
+ { pitches: [0, 3, 6, 9], code: "o7", symbol: [true, "", "o7"], name: "Diminished Seventh" },
+ { pitches: [0, 3, 6, 10], code: "%7", symbol: [true, "", "ø7"], name: "Half-Diminished Seventh" },
+
+ { pitches: [0, 4, 7, 10, 14], code: "9", symbol: [false, "", "9"], name: "Dominant Ninth", startGroup: "Ninths" },
+ { pitches: [0, 4, 7, 11, 14], code: "maj9", symbol: [false, "", "M9"], name: "Major Ninth" },
+ { pitches: [0, 3, 7, 10, 14], code: "m9", symbol: [true, "", "9"], name: "Minor Ninth" },
+ { pitches: [0, 3, 7, 11, 14], code: "mmaj9", symbol: [true, "", "M9"], name: "Minor-Major Ninth" },
+ { pitches: [0, 3, 7, 10, 13], code: "9?", symbol: [true, "", "9?"], name: "???" },
+ { pitches: [0, 4, 8, 10, 14], code: "+9", symbol: [false, "+", "9"], name: "Augmented Ninth" },
+ { pitches: [0, 4, 8, 11, 14], code: "+maj9", symbol: [false, "+", "M9"], name: "Augmented Major Ninth" },
+ { pitches: [0, 3, 6, 9, 14], code: "o9", symbol: [true, "", "o9"], name: "Diminished Ninth" },
+ { pitches: [0, 3, 6, 9, 13], code: "ob9", symbol: [true, "", "o♭9"], name: "Diminished Minor Ninth" },
+ { pitches: [0, 3, 6, 10, 14], code: "%9", symbol: [true, "", "ø9"], name: "Half-Diminished Ninth" },
+ { pitches: [0, 3, 6, 10, 13], code: "%b9", symbol: [true, "", "ø♭9"], name: "Half-Diminished Minor Ninth" },
]
@@ -82,6 +75,16 @@ export function getChordRecordFromPitches(pitches)
}
+export function getChordKindFromPitches(pitches)
+{
+ return Object.values(chords).findIndex(chord =>
+ (
+ chord.pitches.length == pitches.length &&
+ chord.pitches.every((p, index) => p == pitches[index])
+ ))
+}
+
+
export function getAbsolutePitchStr(pitch, accidental)
{
const baseLabels = [0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6]
@@ -240,4 +243,87 @@ export class Key
return getAbsolutePitchStr(this.tonicPitch, this.tonicAccidental) + " " + scaleRecord.name
}
+}
+
+
+export class Chord
+{
+ constructor(rootPitch, rootAccidental, kind, modifiers = {})
+ {
+ this.rootPitch = rootPitch
+ this.rootAccidental = rootAccidental
+ this.kind = kind
+ this.modifiers = modifiers
+ }
+
+
+ getNameBase(key)
+ {
+ const degree = getScaleDegreeForPitch(key, this.rootPitch)
+ const chordKind = chords[this.kind] || { symbol: [false, "", "?"] }
+
+ let baseStr = getRomanNumeralScaleDegreeStr(degree, this.rootAccidental)
+ if (chordKind.symbol[0])
+ baseStr = baseStr.toLowerCase()
+
+ return baseStr + chordKind.symbol[1]
+ }
+
+
+ getNameSup(key)
+ {
+ const chordKind = chords[this.kind] || { symbol: [false, "", "?"] }
+
+ let supStr = chordKind.symbol[2] || ""
+
+ if (this.modifiers)
+ {
+ if (this.modifiers.add9)
+ supStr += "(add9)"
+
+ if (this.modifiers.add11)
+ supStr += "(add11)"
+
+ if (this.modifiers.add13)
+ supStr += "(add13)"
+
+ if (this.modifiers.no3)
+ supStr += "(no3)"
+
+ if (this.modifiers.no5)
+ supStr += "(no5)"
+ }
+
+ return supStr
+ }
+
+
+ getNameSub(key)
+ {
+ let subStr = ""
+
+ if (this.modifiers)
+ {
+ if (this.modifiers.sus2)
+ {
+ if (this.modifiers.sus4)
+ subStr += "sus24"
+ else
+ subStr += "sus2"
+ }
+ else if (this.modifiers.sus4)
+ subStr += "sus4"
+ }
+
+ return subStr
+ }
+
+
+ getColor(key)
+ {
+ const degree = getScaleDegreeForPitch(key, this.rootPitch)
+ const colorRotation = getColorRotationForScale(key.scalePitches)
+ const color = getColorForScaleDegree(colorRotation + degree)
+ return color
+ }
}
\ No newline at end of file
diff --git a/watch.bat b/watch.bat
new file mode 100644
index 0000000..7946708
--- /dev/null
+++ b/watch.bat
@@ -0,0 +1 @@
+npm run watch
\ No newline at end of file