1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
pub mod scale;
pub mod chord;
mod util;
mod attribute;

use self::chord::Chord;

/// Returns notes in a given key and scale
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);

    ///Return notes filtered by scale
    let scale = scale::get_scale(scalestr);

    chromatic_notes.into_iter().enumerate()
        .filter(|&(index, _)| scale.contains(&(index as u8)))
        .map(|(_, e)| e)
        .collect()
}

//Returns list of chords a given rootnote can create with given list of notes
fn get_chords(root_note: (char, i8), notes: &Vec<(char, i8)>, extended: bool) -> Vec<Chord> {
    let mut chords = vec![];

    //Root note as string presentation
    let root_str = util::note_to_str(root_note);

    //Flip vec to root note
    let root_index = notes.iter().position(|&note| 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[1..] {
        let interval = chromatic_notes.iter()
            .position(|&note| note == *v || note == util::alt_note(*v))
            .expect("Failed to find interval position for note");
        intervals.push(interval as u8);
    }

    // Triads
    if intervals.len() > 3 {
        chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[3]], false));
        chords.push(Chord::new(&root_str, vec![0, intervals[2], intervals[3]], false));
    }

    // +1
    if intervals.len() > 4 {
        chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[3], intervals[4]], false));
        chords.push(Chord::new(&root_str, vec![0, intervals[2], intervals[3], intervals[4]], false));

        if extended {
            chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[2], intervals[3], intervals[4]], true));
        }
    }

    // +2
    if intervals.len() > 5 {
        chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[3], intervals[5]], false));

        if extended {
            chords.push(Chord::new(&root_str, vec![0, intervals[2], intervals[3], intervals[5]], true));

            // +3
            chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[3], intervals[4], intervals[5]], true));
            chords.push(Chord::new(&root_str, vec![0, intervals[2], intervals[3], intervals[4], intervals[5]], true));
            chords.push(Chord::new(&root_str, vec![0, intervals[1], intervals[2], intervals[3], intervals[4], intervals[5]], true));
        }
    }

    if intervals.len() > 6 {
        if extended {
            // +4
            chords.push(Chord::new(&root_str, vec![0, 
                                   intervals[1], 
                                   intervals[2], 
                                   intervals[3], 
                                   intervals[4], 
                                   intervals[5], 
                                   intervals[6]],
                                   true));
        }
    }

    for chord in chords.iter_mut() {
        chord.format_notes(notes);
    }

    //Return chords
    chords
}

pub fn analyze(key: &str, scale: &str, extended: bool) -> (Vec<String>,Vec<Chord>) {
    //Notes in scale
    let mut notes = get_notes(&key, &scale);

    //Format notes for readability
    match scale {
        "chromatic" => {},
        _ => notes = util::formatted_notes(notes)
    }

    //Chords in scale
    let mut chords: Vec<Chord> = vec![]; 
    if scale != "chromatic" {
        for v in &notes {
            chords.extend(get_chords(*v, &notes, extended));
        }
    }

    //Return values
    (notes.into_iter().map(|note| util::note_to_str(note).to_uppercase()).collect::<Vec<String>>(), chords)
}