Feat: new harmony / melodic words and demo
This commit is contained in:
@@ -63,3 +63,6 @@ mod euclidean;
|
||||
|
||||
#[path = "forth/case_statement.rs"]
|
||||
mod case_statement;
|
||||
|
||||
#[path = "forth/harmony.rs"]
|
||||
mod harmony;
|
||||
|
||||
@@ -158,6 +158,68 @@ fn chord_dom7s5() {
|
||||
expect_stack("c4 dom7s5", &ints(&[60, 64, 68, 70]));
|
||||
}
|
||||
|
||||
// Power chord
|
||||
|
||||
#[test]
|
||||
fn chord_power() {
|
||||
expect_stack("c4 pwr", &ints(&[60, 67]));
|
||||
}
|
||||
|
||||
// Suspended seventh
|
||||
|
||||
#[test]
|
||||
fn chord_7sus4() {
|
||||
expect_stack("c4 7sus4", &ints(&[60, 65, 67, 70]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chord_9sus4() {
|
||||
expect_stack("c4 9sus4", &ints(&[60, 65, 67, 70, 74]));
|
||||
}
|
||||
|
||||
// Augmented major
|
||||
|
||||
#[test]
|
||||
fn chord_augmaj7() {
|
||||
expect_stack("c4 augmaj7", &ints(&[60, 64, 68, 71]));
|
||||
}
|
||||
|
||||
// 6/9 chords
|
||||
|
||||
#[test]
|
||||
fn chord_maj69() {
|
||||
expect_stack("c4 maj69", &ints(&[60, 64, 67, 69, 74]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chord_min69() {
|
||||
expect_stack("c4 min69", &ints(&[60, 63, 67, 69, 74]));
|
||||
}
|
||||
|
||||
// Extended - major
|
||||
|
||||
#[test]
|
||||
fn chord_maj11() {
|
||||
expect_stack("c4 maj11", &ints(&[60, 64, 67, 71, 74, 77]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chord_maj13() {
|
||||
expect_stack("c4 maj13", &ints(&[60, 64, 67, 71, 74, 81]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chord_min13() {
|
||||
expect_stack("c4 min13", &ints(&[60, 63, 67, 70, 74, 81]));
|
||||
}
|
||||
|
||||
// Altered - lydian dominant
|
||||
|
||||
#[test]
|
||||
fn chord_dom7s11() {
|
||||
expect_stack("c4 dom7s11", &ints(&[60, 64, 67, 70, 78]));
|
||||
}
|
||||
|
||||
// Different roots
|
||||
|
||||
#[test]
|
||||
|
||||
193
tests/forth/harmony.rs
Normal file
193
tests/forth/harmony.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use cagire::forth::Value;
|
||||
|
||||
use super::harness::{expect_error, expect_int, expect_stack};
|
||||
|
||||
fn ints(vals: &[i64]) -> Vec<Value> {
|
||||
vals.iter().map(|&v| Value::Int(v, None)).collect()
|
||||
}
|
||||
|
||||
// Inversions
|
||||
|
||||
#[test]
|
||||
fn invert_major_triad() {
|
||||
expect_stack("c4 maj inv", &ints(&[64, 67, 72]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invert_twice() {
|
||||
expect_stack("c4 maj inv inv", &ints(&[67, 72, 76]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn down_invert_major_triad() {
|
||||
expect_stack("c4 maj dinv", &ints(&[55, 60, 64]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn down_invert_min7() {
|
||||
expect_stack("c4 min7 dinv", &ints(&[58, 60, 63, 67]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invert_min7() {
|
||||
expect_stack("c4 min7 inv", &ints(&[63, 67, 70, 72]));
|
||||
}
|
||||
|
||||
// Voicings
|
||||
|
||||
#[test]
|
||||
fn drop2_maj7() {
|
||||
// c4 maj7 = [60, 64, 67, 71], 2nd from top = 67, drop to 55
|
||||
expect_stack("c4 maj7 drop2", &ints(&[55, 60, 64, 71]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop3_maj7() {
|
||||
// c4 maj7 = [60, 64, 67, 71], 3rd from top = 64, drop to 52
|
||||
expect_stack("c4 maj7 drop3", &ints(&[52, 60, 67, 71]));
|
||||
}
|
||||
|
||||
// Key
|
||||
|
||||
#[test]
|
||||
fn key_sets_tonal_center() {
|
||||
expect_int("g3 key! 0 major", 55);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_with_degree() {
|
||||
// G3=55, degree 4 of major = semitone 7, so 55+7=62
|
||||
expect_int("g3 key! 4 major", 62);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_default_is_c4() {
|
||||
expect_int("0 major", 60);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_a3_minor() {
|
||||
expect_int("a3 key! 0 minor", 57);
|
||||
}
|
||||
|
||||
// Diatonic triads
|
||||
|
||||
#[test]
|
||||
fn diatonic_triad_degree_0() {
|
||||
// C major: degrees 0,2,4 = C,E,G = 60,64,67
|
||||
expect_stack("0 major triad", &ints(&[60, 64, 67]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diatonic_triad_degree_1() {
|
||||
// D minor: degrees 1,3,5 = D,F,A = 62,65,69
|
||||
expect_stack("1 major triad", &ints(&[62, 65, 69]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diatonic_triad_degree_3() {
|
||||
// F major: degrees 3,5,7(=0+12) = F,A,C = 65,69,72
|
||||
expect_stack("3 major triad", &ints(&[65, 69, 72]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diatonic_triad_degree_4() {
|
||||
// G major: degrees 4,6,8(=1+12) = G,B,D = 67,71,74
|
||||
expect_stack("4 major triad", &ints(&[67, 71, 74]));
|
||||
}
|
||||
|
||||
// Diatonic sevenths
|
||||
|
||||
#[test]
|
||||
fn diatonic_seventh_degree_0() {
|
||||
// C major7: degrees 0,2,4,6 = C,E,G,B = 60,64,67,71
|
||||
expect_stack("0 major seventh", &ints(&[60, 64, 67, 71]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diatonic_seventh_degree_1() {
|
||||
// D minor7: degrees 1,3,5,7(=0+12) = D,F,A,C = 62,65,69,72
|
||||
expect_stack("1 major seventh", &ints(&[62, 65, 69, 72]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diatonic_seventh_degree_4() {
|
||||
// G dom7: degrees 4,6,8(=1+12),10(=3+12) = G,B,D,F = 67,71,74,77
|
||||
expect_stack("4 major seventh", &ints(&[67, 71, 74, 77]));
|
||||
}
|
||||
|
||||
// Combined
|
||||
|
||||
#[test]
|
||||
fn key_with_diatonic_triad() {
|
||||
// G3=55 key, degree 0 major triad = G,B,D = 55,59,62
|
||||
expect_stack("g3 key! 0 major triad", &ints(&[55, 59, 62]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_with_triad_inv() {
|
||||
// C4 key, degree 0 minor triad = C,Eb,G = 60,63,67, then inv = Eb,G,C+12 = 63,67,72
|
||||
expect_stack("0 minor triad inv", &ints(&[63, 67, 72]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_degree_4_triad_in_g() {
|
||||
// G3=55, degree 4 of major = notes at degrees 4,6,8(=1+12)
|
||||
// major pattern: [0,2,4,5,7,9,11]
|
||||
// degree 4 -> semitone 7, degree 6 -> semitone 11, degree 8(=1+12) -> 12+2=14
|
||||
// 55+7=62, 55+11=66, 55+14=69 -> D, F#, A
|
||||
expect_stack("g3 key! 4 major triad", &ints(&[62, 66, 69]));
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
|
||||
#[test]
|
||||
fn scale_degree_still_works() {
|
||||
expect_int("0 major", 60);
|
||||
expect_int("7 major", 72);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chords_still_work() {
|
||||
expect_stack("c4 maj", &ints(&[60, 64, 67]));
|
||||
expect_stack("c4 min7", &ints(&[60, 63, 67, 70]));
|
||||
}
|
||||
|
||||
// Transpose
|
||||
|
||||
#[test]
|
||||
fn transpose_major_triad() {
|
||||
expect_stack("c4 maj 3 tp", &ints(&[63, 67, 70]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_down() {
|
||||
expect_stack("c4 maj -2 tp", &ints(&[58, 62, 65]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_zero() {
|
||||
expect_stack("c4 maj 0 tp", &ints(&[60, 64, 67]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_single_note() {
|
||||
expect_int("60 7 tp", 67);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_octave() {
|
||||
expect_stack("c4 min7 12 tp", &ints(&[72, 75, 79, 82]));
|
||||
}
|
||||
|
||||
// Error cases
|
||||
|
||||
#[test]
|
||||
fn triad_without_scale_errors() {
|
||||
expect_error("0 triad", "unknown word");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seventh_without_scale_errors() {
|
||||
expect_error("0 seventh", "unknown word");
|
||||
}
|
||||
Reference in New Issue
Block a user