Skip to content

Commit

Permalink
add Lua bindings to fetch supported chord and scale names
Browse files Browse the repository at this point in the history
  • Loading branch information
emuell committed Dec 10, 2024
1 parent 63f6695 commit 4f01727
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 14 deletions.
16 changes: 16 additions & 0 deletions src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use self::{
};

use crate::{
chord::unique_chord_names,
event::InstrumentId,
rhythm::{beat_time::BeatTimeRhythm, second_time::SecondTimeRhythm, Rhythm},
time::BeatTimeBase,
Expand Down Expand Up @@ -218,6 +219,13 @@ fn register_global_bindings(
)?,
)?;

globals.raw_set(
"scale_names",
lua.create_function(|lua, _args: ()| -> LuaResult<LuaTable> {
lua.create_sequence_from(Scale::mode_names())
})?,
)?;

// function note(args...)
globals.raw_set(
"note",
Expand All @@ -236,6 +244,14 @@ fn register_global_bindings(
)?,
)?;

// function chord_names()
globals.raw_set(
"chord_names",
lua.create_function(|lua, _args: LuaMultiValue| -> LuaResult<LuaTable> {
lua.create_sequence_from(unique_chord_names())
})?,
)?;

// function sequence(args...)
globals.raw_set(
"sequence",
Expand Down
5 changes: 5 additions & 0 deletions src/bindings/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ mod test {
assert!(evaluate_note_userdata(&lua, r#"note("c4'maj'")"#).is_err());
assert!(evaluate_note_userdata(&lua, r#"note("c4'maj xx")"#).is_err());

assert!(
evaluate_note_userdata(&lua, r#"note(string.format("c4'%s", chord_names()[1]))"#)
.is_ok()
);

assert_eq!(
evaluate_note_userdata(&lua, r#"note("c'maj")"#)?.notes,
vec![new_note("c4"), new_note("e4"), new_note("g4"),]
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/scale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ mod test {
fn scale() -> LuaResult<()> {
let lua = new_test_engine()?;

// scale_names ()
assert!(lua
.load(r#"scale_names()[1]"#)
.eval::<LuaString>()
.is_ok_and(|v| v.to_str().is_ok_and(|s| s == "chromatic")));

// Scale (note, mode_name)
assert!(lua
.load(r#"scale("c", "wurst")"#)
Expand Down
36 changes: 28 additions & 8 deletions src/chord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ lazy_static! {
("minorMajor7", Vec::from(MINOR_MAJOR7)),
("minMaj7", Vec::from(MINOR_MAJOR7)),
("mM7", Vec::from(MINOR_MAJOR7)),
("^7", Vec::from(MINOR_MAJOR7)),
("-^7", Vec::from(MINOR_MAJOR7)),
("five", Vec::from(FIVE)),
("5", Vec::from(FIVE)),
("sus2", Vec::from(SUS2)),
Expand All @@ -183,12 +183,32 @@ pub fn chords() -> HashMap<&'static str, Vec<u8>> {
}

/// return list of all known chord names.
pub fn chord_names() -> String {
CHORD_TABLE
pub fn chord_names() -> Vec<String> {
let mut chords = CHORD_TABLE
.keys()
.map(|name| String::from(*name))
.collect::<Vec<_>>()
.join(", ")
.collect::<Vec<_>>();
chords.sort();
chords
}

/// return list of all known chord names with unique intervals.
pub fn unique_chord_names() -> Vec<String> {
let mut unique_chords = CHORD_TABLE.iter().collect::<Vec<_>>();
// prefere longer names, then dedup
unique_chords.sort_by(|(an, _), (bn, _)| bn.len().cmp(&an.len()));
unique_chords.sort_by(|(_, ai), (_, bi)| ai.cmp(bi));
// dedup, but keep add/dom duplicates
unique_chords.dedup_by(|(an, ai), (_, bi)| {
ai == bi && !(an.starts_with("dom") || an.starts_with("add"))
});
// get names and sort
let mut chords = unique_chords
.into_iter()
.map(|(name, _)| String::from(*name))
.collect::<Vec<_>>();
chords.sort();
chords
}

/// return chord intervals for the given chord string or []
Expand Down Expand Up @@ -241,8 +261,8 @@ impl TryFrom<&str> for Chord {
}
let note = Note::try_from(note_part)?;
let intervals = CHORD_TABLE.get(chord_part).ok_or(format!(
"invalid mode, valid modes are: {}",
chord_names()
"invalid chord mode, valid modes are: {}",
chord_names().join(",")
))?;
return Ok(Self::new(note, intervals.clone()));
}
Expand All @@ -263,7 +283,7 @@ where
fn try_from((note, mode): (N, &str)) -> Result<Self, String> {
let intervals = CHORD_TABLE.get(mode).ok_or(format!(
"Invalid chord mode, valid chords are: {}",
chord_names()
chord_names().join(",")
))?;
Ok(Self::new(note, intervals.clone()))
}
Expand Down
10 changes: 7 additions & 3 deletions types/nerdo/library/chord.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ error("Do not try to execute this file. It's just a type definition file.")
----------------------------------------------------------------------------------------------------

---Available chords.
---@alias ChordName "major"|"augmented"|"six"|"sixNine"|"major7"|"major9"|"add9"|"major11"|"add11"|"major13"|"add13"|"dom7"|"dom9"|"dom11"|"dom13"|"7b5"|"7#5"|"7b9"|"9"|"nine"|"eleven"|"thirteen"|"minor"|"diminished"|"dim"|"minor#5"|"minor6"|"minor69"|"minor7b5"|"minor7"|"minor7#5"|"minor7b9"|"minor7#9"|"diminished7"|"minor9"|"minor11"|"minor13"|"minorMajor7"|"five"|"sus2"|"sus4"|"7sus2"|"7sus4"|"9sus2"|"9sus4"|string
---@alias ChordName "major"|"major7"|"major9"|"major11"|"major13"|"minor"|"minor#5"|"minor6"|"minor69"|"minor7b5"|"minor7"|"minor7#5"|"minor7b9"|"minor7#9"|"minor9"|"minor11"|"minor13"|"minorMajor7"|"add9"|"add11"|"add13"|"dom7"|"dom9"|"dom11"|"dom13"|"7b5"|"7#5"|"7b9"|"five"|"six"|"sixNine"|"nine"|"eleven"|"thirteen"|"augmented"|"diminished"|"diminished7"|"sus2"|"sus4"|"7sus2"|"7sus4"|"9sus2"|"9sus4"|string

---Create a new chord from the given key notes and a chord name or an array of custom intervals.
---
---NB: Chords also can also be defined via strings in function `note` and via the a scale's
---`chord` function. See examples below.
---NB: Chords also can also be defined via strings in function `note` and via the a scale's
---`chord` function. See examples below.
---
---Chord names also can be shortened by using the following synonyms:
---- "min" | "m" | "-" -> "minor"
Expand Down Expand Up @@ -46,3 +46,7 @@ error("Do not try to execute this file. It's just a type definition file.")
---@nodiscard
---@overload fun(key: NoteValue, intervals: integer[]): Note
function chord(key, mode) end

---Return supported chord names.
---@return string[]
function chord_names() end
10 changes: 7 additions & 3 deletions types/nerdo/library/scale.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function Scale:degree(...) end

---Create an iterator function that returns up to `count` notes from the scale.
---If the count exceeds the number of notes in the scale, then notes from the next
---octave are taken.
---octave are taken.
---
---The iterator function returns nil when the maximum number of MIDI notes has been
---reached, or when the given optional `count` parameter has been exceeded.
Expand Down Expand Up @@ -83,12 +83,12 @@ function Scale:fit(...) end

----------------------------------------------------------------------------------------------------

---Available scales.
---Available scale mode names.
---@alias ScaleMode "chromatic"|"major"|"minor"|"natural major"|"natural minor"|"pentatonic major"|"pentatonic minor"|"pentatonic egyptian"|"blues major"|"blues minor"|"whole tone"|"augmented"|"prometheus"|"tritone"|"harmonic major"|"harmonic minor"|"melodic minor"|"all minor"|"dorian"|"phrygian"|"phrygian dominant"|"lydian"|"lydian augmented"|"mixolydian"|"locrian"|"locrian major"|"super locrian"|"neapolitan major"|"neapolitan minor"|"romanian minor"|"spanish gypsy"|"hungarian gypsy"|"enigmatic"|"overtone"|"diminished half"|"diminished whole"|"spanish eight-tone"|"nine-tone"|string

---Create a new scale from the given key notes and a mode name.
---
---Scale names can also be shortened by using the following synonyms:
---Mode names can also be shortened by using the following synonyms:
---- "8-tone" -> "eight-tone"
---- "9-tone" -> "nine-tone"
---- "aug" -> "augmented"
Expand Down Expand Up @@ -125,3 +125,7 @@ function scale(key, mode) end
---@return Scale
---@nodiscard
function scale(key, intervals) end

---Return supported scale mode names.
---@return string[]
function scale_names() end

0 comments on commit 4f01727

Please sign in to comment.