#[macro_use]
extern crate serde_json;
mod attribute;
pub mod chord;
#[cfg(feature = "ffi_c")]
mod ffi;
mod json;
pub mod note;
pub mod scale;
pub mod util;
use self::chord::Chord;
#[cfg(feature = "ffi_c")]
pub use ffi::*;
use ordered_permutation as op;
fn get_notes(keystr: &str, scalestr: &str) -> Vec<(char, i8)> {
let key = util::str_to_note(keystr);
let chromatic_notes: Vec<(char, i8)> = scale::chromatic_notes(key);
let scale = scale::get_scale(scalestr);
chromatic_notes
.into_iter()
.enumerate()
.filter(|&(index, _)| scale.contains(&(index as u8)))
.map(|(_, e)| e)
.collect()
}
fn get_chords(root_note: (char, i8), notes: &Vec<(char, i8)>, extended: bool) -> Vec<Chord> {
let mut chords = vec![];
let root_str = util::note_to_str(root_note);
let root_index = notes
.iter()
.position(|¬e| note == root_note)
.expect("Failed to find root index!");
let a = notes[..root_index].to_vec();
let b = notes[root_index..].to_vec();
let mut flipped = vec![];
flipped.extend(b);
flipped.extend(a);
let chromatic_notes: Vec<(char, i8)> = scale::chromatic_notes(root_note);
let mut intervals = vec![];
for v in flipped {
let interval = chromatic_notes
.iter()
.position(|¬e| note == v || note == util::alt_note(v))
.expect("Failed to find interval position for note");
intervals.push(interval as u8);
}
let mut permutations = op::permutate(&intervals[1..]);
permutations.sort_by(|a, b| {
let aa = util::indexes(&a, &intervals);
let bb = util::indexes(&b, &intervals);
util::weight_levels(&aa).cmp(&util::weight_levels(&bb))
});
for mut p in permutations {
if p.len() > 1 {
let weight = util::weight_levels(&util::indexes(&p, &intervals));
let is_extended = weight > 4;
if extended || !is_extended {
p.insert(0, 0);
let chord = Chord::new(&root_str, p, is_extended, weight);
if chord.valid {
chords.push(chord);
}
}
}
}
for chord in chords.iter_mut() {
chord.format_notes(notes);
}
chords
}
pub fn analyze(key: &str, scale: &str, extended: bool) -> (Vec<String>, Vec<Chord>) {
let mut notes = get_notes(&key, &scale);
match scale {
"chromatic" => {}
_ => notes = util::formatted_notes(notes),
}
let mut chords: Vec<Chord> = vec![];
if scale != "chromatic" {
for v in ¬es {
chords.extend(get_chords(*v, ¬es, extended));
}
}
chords = util::deduplicate(chords);
(
notes
.into_iter()
.map(|note| util::note_to_str(note).to_uppercase())
.collect::<Vec<String>>(),
chords,
)
}
pub fn analyze_json(key: &str, scale: &str, extended: bool) -> String {
let result = analyze(key, scale, extended);
json::serialize(result.0, result.1)
}
pub fn supported_scales() -> Vec<String> {
scale::supported_scales()
}
pub fn supported_scales_json() -> String {
json!({ "scales": json!(scale::supported_scales()) }).to_string()
}