use crate::chord::{note::Note, quality::ChordQuality};
use interval::Interval;
use serde::{Deserialize, Serialize};
use serde_json;
use std::vec;
pub mod interval;
pub mod note;
pub mod quality;
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct Chord {
pub origin: String,
pub descriptor: String,
pub normalized: String,
pub root: Note,
pub bass: Option<Note>,
pub notes: Vec<Note>,
pub semitones: Vec<u8>,
pub intervals: Vec<Interval>,
pub quality: ChordQuality,
#[serde(skip_serializing)]
pub(crate) normalized_intervals: Vec<Interval>,
#[serde(skip_serializing)]
interval_degrees: Vec<u8>,
}
impl Chord {
pub fn builder(origin: &str, root: Note) -> ChordBuilder {
ChordBuilder::new(origin, root)
}
pub fn note_literals(&self) -> impl Iterator<Item = String> {
self.notes.iter().map(|n| n.to_string())
}
pub fn transpose(&self, transpose_to: &Note) -> Chord {
let bass = self
.bass
.as_ref()
.map(|bass| self.root.transpose(bass, transpose_to));
let mut notes = Vec::new();
let semitones = self.semitones.clone();
let interval_degrees = self.interval_degrees.clone();
for (st, degree) in semitones.iter().zip(&interval_degrees) {
let note = transpose_to.get_note(*st, *degree);
notes.push(note);
}
let mut origin = transpose_to.to_string();
if self.descriptor.starts_with("#5") {
let desc = &self.descriptor[0..2].replace("#5", "+5");
origin.push_str(desc);
} else {
origin.push_str(&self.descriptor);
}
Chord::builder(&origin, *transpose_to)
.descriptor(&self.descriptor)
.bass(bass)
.notes(notes)
.semitones(semitones)
.interval_degrees(interval_degrees)
.normalized_intervals(self.normalized_intervals.clone())
.intervals(self.intervals.clone())
.build()
}
pub fn to_midi_codes(&self) -> Vec<u8> {
let root = self.root.to_midi_code();
let mut codes = vec![];
if let Some(bass) = &self.bass {
codes.push(bass.to_midi_code() - 12);
codes.push(root);
} else {
codes.push(root - 12);
}
for note in self.normalized_intervals.iter().skip(1) {
codes.push(note.st() + root);
}
codes
}
pub fn to_json(&self) -> String {
let a = serde_json::to_string(self);
match a {
Ok(v) => v,
Err(_) => "{{}}".to_string(),
}
}
}
pub struct ChordBuilder {
origin: String,
normalized: String,
descriptor: String,
root: Note,
bass: Option<Note>,
notes: Vec<Note>,
semitones: Vec<u8>,
interval_degrees: Vec<u8>,
intervals: Vec<Interval>,
normalized_intervals: Vec<Interval>,
quality: ChordQuality,
}
impl ChordBuilder {
pub fn new(origin: &str, root: Note) -> ChordBuilder {
ChordBuilder {
origin: origin.to_string(),
normalized: "".to_string(),
descriptor: String::new(),
root,
bass: None,
notes: Vec::new(),
semitones: Vec::new(),
interval_degrees: Vec::new(),
normalized_intervals: Vec::new(),
intervals: Vec::new(),
quality: Default::default(),
}
}
pub fn quality(mut self, quality: ChordQuality) -> ChordBuilder {
self.quality = quality;
self
}
pub fn intervals(mut self, real_intervals: Vec<Interval>) -> ChordBuilder {
self.intervals = real_intervals;
self
}
pub fn normalized_intervals(mut self, normalized_intervals: Vec<Interval>) -> ChordBuilder {
self.normalized_intervals = normalized_intervals;
self
}
pub fn interval_degrees(mut self, semantic_intervals: Vec<u8>) -> ChordBuilder {
self.interval_degrees = semantic_intervals;
self
}
pub fn semitones(mut self, semitones: Vec<u8>) -> ChordBuilder {
self.semitones = semitones;
self
}
pub fn notes(mut self, notes: Vec<Note>) -> ChordBuilder {
self.notes = notes;
self
}
pub fn bass(mut self, bass: Option<Note>) -> ChordBuilder {
self.bass = bass;
self
}
pub fn descriptor(mut self, descriptor: &str) -> ChordBuilder {
self.descriptor = descriptor.to_string();
self
}
pub fn normalized(mut self, normalized: String) -> ChordBuilder {
self.normalized = normalized;
self
}
pub fn build(self) -> Chord {
Chord {
origin: self.origin,
descriptor: self.descriptor,
root: self.root,
bass: self.bass,
notes: self.notes,
interval_degrees: self.interval_degrees,
intervals: self.intervals,
normalized_intervals: self.normalized_intervals,
semitones: self.semitones,
normalized: self.normalized,
quality: self.quality,
}
}
}