1
2#![allow(warnings)]
3
4use std::cell::OnceCell;
5use std::cmp;
6use std::cmp::PartialEq;
7use std::collections::HashMap;
8use std::fmt::{Debug, Display, Formatter};
9use std::ops::{Index, IndexMut};
10use std::string::ToString;
11use std::sync::Mutex;
12use std::vec::IntoIter;
13use ezstr::{EzStr, GraphemeMatch};
14use jazz_accidentals::Accidentals;
15use once_cell::sync::Lazy;
16
17use cached::proc_macro::cached;
18
19#[derive(Clone)]
20struct ChordQualityParseResult {
21 input: EzStr,
22 triad: Option<ChordTypeMatch>,
23 extension: Option<ExtensionTypeMatch>,
24 modification: Option<ChordModificationTokens>,
25
26}
27#[derive(Debug,Clone,PartialEq,Hash)]
28pub struct TriadType<'a> {
29 pub name: &'a str,
30 pub triggers: &'a [&'a str],
31 pub notes: Change,
32}
33impl TriadType<'_> {
34
35 pub const MAJOR: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Major",
36 notes: Change::from("1 3 5"),
37 triggers: &["maj","ma","Ma", "MA"]});
38 pub const MINOR: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Minor",
39 notes: Change::from("1 b3 5"),
40 triggers: &["minor", "min","mi","m", "-"]});
41 pub const DIMINISHED: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Diminished",
42 notes: Change::from("1 b3 b5"),
43 triggers: &["dim", "di", "o","0","O","o","o"]});
44
45
46 pub const HALF_DIMINISHED: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Half Diminished",
47 notes: Change::from("1 b3 b5"),
48 triggers: &["ø","⌀","halfdim", "hdim"]});
49 pub const AUGMENTED: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Augmented", notes: Change::from("1 3 #5"),
50 triggers: &["aug", "+"]});
51 pub const SUS_FOUR: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Suspended 4th", notes: Change::from("1 4 5"),
52 triggers: &["sus4","sus"]});
53 pub const SUS_SHARP_FOUR: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Suspended Sharp 4th", notes: Change::from("1 4 #5"),
54 triggers: &["sus♯4","sus#4"]});
55 pub const SUS_TWO: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Suspended 2nd", notes: Change::from("1 2 5"),
56 triggers: &["sus2"]});
57 pub const SUS_FLAT_TWO: Lazy<TriadType<'static>> = Lazy::new(|| TriadType { name: "Suspended ♭2nd",notes: Change::from("1 b2 5"),
58 triggers: &["sus♭2","susb2"]});
59 pub const TRIAD: Lazy<Vec<TriadType<'static>>> = Lazy::new(|| vec![
60 Self::MAJOR.clone(),
61 Self::MINOR.clone(),
62 Self::DIMINISHED.clone(),
63 Self::HALF_DIMINISHED.clone(),
64 Self::AUGMENTED.clone(),
65 Self::SUS_TWO.clone(),
66 Self::SUS_FLAT_TWO.clone(),
67 Self::SUS_FOUR.clone(),
68 Self::SUS_SHARP_FOUR.clone(),
69 ]);
70
71 pub const SUS_TRIADS: Lazy<Vec<TriadType<'static>>> = Lazy::new(|| vec![
72 Self::SUS_FOUR.clone(),
73 Self::SUS_TWO.clone(),
74 Self::SUS_FLAT_TWO.clone(),
75 Self::SUS_SHARP_FOUR.clone()
76 ]);
77
78 pub fn is_sus(&self) -> bool {
79 Self::SUS_TRIADS.contains(self)
80 }
81}
82
83enum BracketState {
84 Unknown,
85 Add,
86 Remove,
87}
88impl PartialEq for &BracketState {
89 fn eq(&self, other: &Self) -> bool {
90 self == other
91 }
92}
93
94#[derive(Clone, Debug, PartialEq)]
95enum ChordModificationTokenType {
96 BracketOpen,
97 BracketClose,
98 Add,
99 Remove,
100 Note,
101 Space,
102 Comma,
103}
104
105#[derive(Debug, Clone)]
106pub struct ChordModificationToken {
107 pub span: GraphemeMatch,
108 pub modification_type: ChordModificationTokenType
109}
110
111
112
113#[derive(Debug, Clone)]
114pub struct ChordModificationTokens {
115 tokens: Vec<ChordModificationToken>,
116}
117
118impl IntoIterator for ChordModificationTokens {
119 type Item = ChordModificationToken;
120 type IntoIter = std::vec::IntoIter<Self::Item>;
121 fn into_iter(self) -> Self::IntoIter {
122 self.tokens.into_iter()
123 }
124}
125
126
127pub struct ChordModificationParseResult {
128 tokens: ChordModificationTokens,
129}
130
131
132#[derive(Debug,Clone,PartialEq,Hash)]
133pub struct ChordTypeMatch {
134 pub chord_type: TriadType<'static>,
135 pub span: GraphemeMatch,
136}
137
138#[derive(Debug, Clone, PartialEq, Hash)]
139pub struct ExtensionType<'a> {
140 pub name: &'static str,
141 pub triggers: &'a [&'a str],
142 pub notes:Change,
143}
144
145impl ExtensionType<'static> {
146 pub const FIFTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
147 name: "Fifth",
148 notes: Change::from("1 5"),
149 triggers: &["5th","5",]
150 });
151 pub const SIXTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
152 name: "Major Six",
153 notes: Change::from("1 3 5 6"),
154 triggers: &["6"]
155 });
156 pub const SEVENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
157 name: "Dominant Seventh",
158 notes: Change::from("1 3 5 b7"),
159 triggers: &["7","dom","dominant"]
160 });
161 pub const MAJOR_SEVENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
162 name: "Major Seventh",
163 notes: Change::from("1 3 5 7"),
164 triggers: &["ma7","Ma7","MA7","triangle7",] });
166
167 pub const NINTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
168 name: "Ninth",
169 notes: Change::from("1 3 5 b7 9"),
170 triggers: &["9"] });
172
173 pub const MAJOR_NINTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
174 name: "Major Ninth",
175 notes: Change::from("1 3 5 7 9"),
176 triggers: &["ma9","Ma9","MA9","triangle9",] });
178
179 pub const ELEVENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
180 name: "Eleventh",
181 notes: Change::from("1 3 5 b7 9 11"),
182 triggers: &["11",] });
184
185 pub const MAJOR_ELEVENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
186 name: "Major Eleventh",
187 notes: Change::from("1 3 5 7 9 11"),
188 triggers: &["ma11","Ma11","MA11","triangle11",] });
190
191 pub const THIRTEENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
192 name: "Thirteenth",
193 notes: Change::from("1 3 5 b7 9 11 13"),
194 triggers: &["13",] });
196
197 pub const MAJOR_THIRTEENTH: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
198 name: "Major Thirteenth",
199 notes: Change::from("1 3 5 7 9 11 13"),
200 triggers: &["ma13","Ma13","MA13","triangle13",] });
202
203 pub const SIX_ADD_NINE: Lazy<ExtensionType<'static>> = Lazy::new(|| ExtensionType {
204 name: "Six Add Nine",
205 notes: Change::from("1 3 5 6 9"),
206 triggers: &["69","6/9"] });
208
209
210 pub const EXTENSIONS: Lazy<Vec<ExtensionType<'static>>> = Lazy::new(|| {
211 vec![
212 Self::FIFTH.clone(),
213 Self::SIX_ADD_NINE.clone(),
214 Self::SIXTH.clone(),
215 Self::MAJOR_SEVENTH.clone(),
216 Self::SEVENTH.clone(),
217 Self::MAJOR_NINTH.clone(),
218 Self::NINTH.clone(),
219 Self::MAJOR_ELEVENTH.clone(),
220 Self::ELEVENTH.clone(),
221 Self::MAJOR_THIRTEENTH.clone(),
222 Self::THIRTEENTH.clone(),
223
224 ]
225 });
226}
227#[derive(Debug,Clone,PartialEq,Hash)]
228pub struct ExtensionTypeMatch {
229 pub extension_type: ExtensionType<'static>,
230 pub span: GraphemeMatch,
231}
232
233#[derive(Debug)]
234#[derive(Clone)]
235pub struct ChordQuality {
236 pub input: EzStr,
237 pub triad: Option<ChordTypeMatch>,
238 pub extension: Option<ExtensionTypeMatch>,
239 pub modification: Option<ChordModificationTokens>,
240}
241
242impl Default for ChordQuality {
243 fn default() -> ChordQuality {
244 ChordQuality {
245 input: "".into(),
246 triad: None,
247 extension: None,
248 modification: None,
249 }
251 }
252}
253
254impl Display for ChordQuality {
255 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
256 let mut extension_str = String::new();
257 let mut triad_str = String::new();
258 let mut modification_str = String::new();
259 if let Some(triad) = &self.triad {
260 triad_str = triad.chord_type.triggers[0].parse().unwrap();
261 }
262 if let Some(extension) = &self.extension {
263 extension_str = extension.extension_type.triggers[0].parse().unwrap();
264 }
265 if let Some(modification) = &self.modification {
266 modification_str = modification.tokens.iter().map(|m| m.span.to_string()).collect::<String>();
267 }
269
270
271 if self.is_sus_chord() {
272 write!(f, "{}{}", extension_str, triad_str, );
273 } else {
274 write!(f, "{}{}", triad_str, extension_str, );
275 }
276 write!(f, "{}", modification_str)
277
278 }
279}
280
281
282impl ChordQuality {
283
284 fn new<S: Into<EzStr> + Clone>(s:S) -> Option<Self> {
298 let string = s.clone().into();
299 let result = Self::parse_string(string.data);
300 let mut triad = None;
301 let mut extension = None;
302 if result.is_some() {
303 if result.clone().unwrap().triad.is_some(){
304 triad = result.clone().unwrap().triad;
305 }
306 if result.clone().unwrap().extension.is_some(){
307 extension = result.clone().unwrap().extension;
308 }
309 }
310 if triad.is_none() && extension.is_none() {
311 return None;
312 }
313 Some(ChordQuality {
314 input: s.into(),
315 triad: triad,
316 extension: extension,
317 modification: result.unwrap().modification,
318 })
319 }
320
321 pub fn from_string<S: Into<EzStr>>(s: S) -> Option<Self> {
322 Self::new(s.into())
323 }
324
325 fn parse_string(input: String) -> Option<ChordQualityParseResult> {
326
327 let input = EzStr::from(input);
328 let mut triad_at_start_result = ChordQuality::triad_at_index(input.clone(), 0usize);
329 let mut extension_result = ChordQuality::extension_at_index(input.clone(), 0usize);
330 let mut modifications_result = None;
331 let mut start: usize = 0;
332 let mut end: usize = input.len();
333 if extension_result.is_some() {
334
335 start = extension_result.clone().unwrap().span.text.len();
336 if let Some(triad) = ChordQuality::triad_at_index(&input.data, start){
337 if triad.chord_type.is_sus(){
338 triad_at_start_result = ChordQuality::triad_at_index(&input.data, start);
339 }
340 }
341 }
342 else if triad_at_start_result.is_some() {
343
344 start = triad_at_start_result.clone().unwrap().span.text.len();
345
346 extension_result = ChordQuality::extension_at_index(input.clone(), start);
350 if extension_result.is_some() {
351 if input == EzStr::from("mima7"){
352 }
354 }
356
357 if input == EzStr::from("mima7") {
358 }
360 } else {
361 return None;
363 }
364
365 if extension_result.is_some() && triad_at_start_result.is_some() {
367 if extension_result.clone().unwrap().span.start == triad_at_start_result.clone().unwrap().span.start {
368 triad_at_start_result = None;
369 }
370 }
371
372 let mut brackets_start = 0usize;
373 if let Some(triad) = triad_at_start_result.clone() {
374 brackets_start = cmp::max(brackets_start,triad.span.end);
375 }
376 if let Some(extension) = extension_result.clone() {
377 brackets_start = cmp::max(brackets_start,extension.span.end);
378 }
379
380 modifications_result = ChordQuality::brackets_at_index(input.clone(),brackets_start);
392
393 Some(ChordQualityParseResult {
469 input: input.clone().into(),
470 triad: triad_at_start_result,
471 extension: extension_result,
472 modification: modifications_result,
473 })
474 }
477
478 fn triad_at_index<S: Into<EzStr> + Clone, I: Into<usize>>(s:S, index:I) -> Option<ChordTypeMatch> {
479 let start = index.into();
480 let s = s.clone().into();
481 for triad in TriadType::TRIAD.iter() {
482 for trigger in triad.triggers.iter() {
483 let trigger = EzStr::from(*trigger);
484 let end = start + trigger.len();
485
486 if end <= s.len() && s.slice(start as i32, end as i32) == trigger {
490 let grapheme_match = GraphemeMatch{start:start,
492 end:end,
493 text: trigger.clone(),
494 };
495
496 return Some(ChordTypeMatch{chord_type:triad.clone(), span:
499 grapheme_match
500 }
501 )
502 }
503 }
504 }
505 None
506 }
507
508 pub fn extension_at_index<S: Into<EzStr> + Clone, I: Into<usize>>(s:S, index:I) -> Option<ExtensionTypeMatch> {
509 let start = index.into();
510 let s = s.clone().into();
511 for extension in ExtensionType::EXTENSIONS.iter() {
512 for trigger in extension.triggers.iter() {
513 let trigger = EzStr::from(*trigger);
514 let end = start + trigger.len();
515 if end <= s.len() && s.slice(start as i32,end as i32) == trigger{
519 let grapheme_match = GraphemeMatch {
520 start,
521 end,
522 text: trigger.clone(),
523 };
524 return Some(ExtensionTypeMatch {
526 extension_type: extension.clone(),
527 span: grapheme_match
528 });
529 }
530
531 }
532 }
533 None
534 }
535
536 fn brackets_at_index<S: Into<EzStr> + Clone, I: Into<usize>>(s:S, index:I) -> Option<ChordModificationTokens> {
537 let mut tokens:Vec<ChordModificationToken> = Vec::new();
538 let mut start = index.into();
539 let s = s.clone().into();
540 let triggers_to_mod_type = vec![
541 ( vec!["add", "+"], ChordModificationTokenType::Add ),
542 ( vec!["no", "-"], ChordModificationTokenType::Remove ),
543 ( vec!["("], ChordModificationTokenType::BracketOpen ),
544 ( vec![" "], ChordModificationTokenType::Space ),
545 ( vec![","], ChordModificationTokenType::Comma ),
546 ( vec![")"], ChordModificationTokenType::BracketClose ),
547 ];
548 while start < s.len() {
549 let mut trigger_found = false;
550 for token_type in &triggers_to_mod_type {
551 if trigger_found { break; }
552 for trigger in token_type.0.iter() {
553 if trigger_found { break; }
554 let trigger = EzStr::from(*trigger);
555 let end = start + trigger.len();
556
557 if end <= s.len() && s.slice(start as i32, end as i32) == trigger {
561 let grapheme_match = GraphemeMatch {
563 start: start,
564 end: end,
565 text: trigger.clone(),
566 };
567
568 tokens.push(ChordModificationToken {
570 span:
571 GraphemeMatch { start, end, text: trigger.clone() },
572 modification_type: token_type.1.clone(),
573 });
574 start = end;
575 trigger_found = true;
576 }
577 }
578 }
579 if !trigger_found {
580 let mut note_found = false;
581
582
583 if let Some(note) = Note::parse_until_invalid(&s, start){
584 let end = start + note.text.len();
585 tokens.push(ChordModificationToken {
586 span:
587 GraphemeMatch { start, end, text: note.text.clone() },
588 modification_type: ChordModificationTokenType::Note,
589 });
590 start = end;
591 trigger_found = true;
592 } else{
593 start += 1;
594 break;
595
596 }
597 }
599 }
600 if tokens.len() > 0 {
601
602 return Some(ChordModificationTokens {tokens})
606 }
607 None
608 }
609
610 fn brackets_at_indexOLD<S: Into<EzStr> + Clone, I: Into<usize>>(s:S, index:I) -> Option<ChordModificationParseResult> {
611 let mut tokens:Vec<ChordModificationToken> = Vec::new();
612 let s = s.into();
613 let mut index = index.into();
614
615 let triggers_to_mod_type = vec![
616 ( vec!["add", "+"], ChordModificationTokenType::Add ),
617 ( vec!["no", "-"], ChordModificationTokenType::Remove ),
618 ( vec!["("], ChordModificationTokenType::BracketOpen ),
619 ( vec![" "], ChordModificationTokenType::Space ),
620 ( vec![")"], ChordModificationTokenType::BracketClose ),
621 ];
622
623 while index < s.len() - 1 {
624 let mut trigger_found = false;
625 for mod_type in triggers_to_mod_type.iter() {
626 let triggers = &mod_type.0;
627 let token_type = &mod_type.1;
628 for trigger in triggers.iter() {
629 let trigger = EzStr::from(*trigger);
634 if trigger == EzStr::from("(") {
635 let val1 = &trigger;
636 let val2 = s.slice(index as i32, (index + trigger.len()) as i32);
637 panic!("inside brackets_at_index(s:{}, index:{}) trigger:{} val1:{} val2:{} bool1:{} bool2:{}\ntokens {:?}", &s, index, trigger,val1,val2,
638 index + trigger.len() <= s.len(),
639 *val1 == val2,
640 tokens
641 );
642 }
643
644 if trigger == EzStr::from("(") {
645
646 }
647
648 if index + trigger.len() <= s.len() && trigger == s.slice(index as i32, (index + trigger.len()) as i32) {
649 trigger_found = true;
650 tokens.push(ChordModificationToken{span:
651 GraphemeMatch{start:index,end:index+ trigger.len(),text:trigger.clone()},
652 modification_type: token_type.clone() });
653 index += &trigger.len();
654 break;
655 }
656 }
657 if trigger_found {
658 break;
659 } else {
660 index += 1;
661 }
664 }
665 }
666 if !tokens.is_empty() {
667 panic!("inside brackets_at_index({}, {}) tokens: {:?}", s, index, tokens);
668 }
669
670
671
672
673
674
675 Some(ChordModificationParseResult{
676 tokens: ChordModificationTokens {tokens}
677 })
678 }
679
680 fn first_triad_in_str(s:&str) -> Option<TriadType> {
681 for triad in TriadType::TRIAD.iter() {
682 for trigger in triad.triggers.iter() {
683 if s.contains(trigger) {
684 return Some(triad.clone())
685 }
686 }
687 }
688 None
689 }
690
691 pub fn to_change(&self) -> Change {
692 let mut notes:Change = Change::new();
693 if self.extension.is_none() && let Some(triad) = &self.triad {
694 let triad_notes = &triad.chord_type.notes;
695 notes.extend(triad_notes);
696 }
697 if let Some(extension) = &self.extension {
698 let mut extension_notes = extension.clone().extension_type.notes;
699 if let Some(triad) = &self.triad {
701 let triad_notes = &triad.chord_type.notes;
702 let triad_third = &triad_notes[1];
703 let triad_fifth = &triad_notes[2];
704 if let Some(extension_third) = extension_notes.first_note_with_degree(3){
705 extension_notes[extension_third] = triad_third.clone();
706 }
707 if let Some(extension_fifth) = extension_notes.first_note_with_degree(5) {
708 extension_notes[extension_fifth] = triad_fifth.clone();
709 }
710 }
712 notes.extend(extension_notes);
713 }
714
715 if let Some(mods) = &self.modification {
716 let mut state = BracketState::Unknown;
718 for token in mods.clone().into_iter() {
719 match token.modification_type {
720 ChordModificationTokenType::Note =>
721 match state {
722 BracketState::Unknown => {
723 if let Some(mod_note) = Note::new( & token.span) {
724 if let Some(target_n) = notes.first_note_with_degree( & mod_note.degree){
725 notes[target_n] = mod_note;
727 } else {
728 notes.extend(Change::from_note(mod_note));
729 }
730 } else {
731 panic ! ("Can't parse note: {}", token.span);
732 }
733 },
734 BracketState::Remove => { if let Some(mod_note) = Note::new( & token.span) {
737 if let Some(target_n) = notes.first_note_with_degree( & mod_note.degree){
738 notes.remove(target_n);
739 }
740 }
741 },
743 BracketState::Add => {
744 if let Some(mod_note) = Note::new( & token.span) {
745 notes.extend(Change::from_note(mod_note));
746 }
747 }
748 },
749 ChordModificationTokenType::Remove =>
750 state = BracketState::Remove,
751 ChordModificationTokenType::Add =>
752 state = BracketState::Add,
753 _ => (),
754 }
755 }
756 }
757 if let Some(triad) = &self.triad {
759 if triad.chord_type == *TriadType::DIMINISHED {
760 if let Some(flat_seven) = notes.position("b7") {
761 notes[flat_seven] = "bb7".into();
762 }
764 }
765 }
766 notes
767 }
768
769
770 pub fn to_frets(&self) -> Vec<i32> {
771 self.to_change().to_frets()
772 }
773
774 pub fn short_debug(&self) -> String {
775 let mut ret = String::new();
776 ret += "ChordQuality {triad: ";
777 if let Some(triad) = self.triad.clone() {
778 ret.push_str(format!("{:?}", &self.clone().triad.unwrap().span.text).as_str());
779 } else {
780 ret.push_str("None");
781 }
782 ret.push_str(", extension: ");
783 if let Some(extension) = self.extension.clone() {
784 ret.push_str(format!("{:?}", &self.clone().extension.unwrap().span.text).as_str());
785 } else {
786 ret.push_str("None");
787 }
788 ret.push_str(", mods: ");
789 if let Some(modification) = self.modification.clone() {
790 ret.push_str(format!("{:?}", &self.clone().modification.unwrap().tokens).as_str());
791 } else {
792 ret.push_str("None");
793 }
794 ret.push_str("}");
795 ret.push_str(format!(" == {}", self).as_str());
796 ret
797 }
798
799 pub fn is_sus_chord(&self) -> bool {
800 if let Some(triad) = &self.triad {
801 if TriadType::SUS_TRIADS.contains(&triad.chord_type) {
802 return true;
803 }
804 }
805 return false;
806 }
807}
808
809
810
811 #[derive(Clone,PartialEq,Eq,Hash,Debug)]
818pub struct Note {
819 pub text: EzStr,
820 pub accidentals: EzStr,
821 pub degree: EzStr,
822}
823
824static FRETS_FROM_ONE_CACHE: Lazy<Mutex<HashMap<String, i32>>> = Lazy::new(|| Mutex::new(HashMap::with_capacity(256)));
825static PARSE_CACHE: Lazy<Mutex<HashMap<String, Option<(String, String)>>>> = Lazy::new(|| Mutex::new(HashMap::with_capacity(256)));
826
827impl From<&str> for Note {
828 fn from(s:&str) -> Note {
829 Note::new(s).expect("REASON")
830 }
831}
832
833impl Display for Note {
834 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
835 write!(f,"{}",self.unicode_accidentals(false))
836 }
837}
838
839impl Note {
840 pub const fn scale_degree_to_fret_first_octave(degree: u8) -> Option<u8> {
841 match degree {
842 1 => Some(0),
843 2 => Some(2),
844 3 => Some(4),
845 4 => Some(5),
846 5 => Some(7),
847 6 => Some(9),
848 7 => Some(11),
849 _ => None,
850 }
851 }
852 pub const fn fret_to_scale_degree_all_sharps(fret: u8) -> Option<u8> {
853 match fret {
854 0 | 1 => Some(1),
855 2 | 3 => Some(2),
856 4 => Some(3),
857 5 | 6 => Some(4),
858 7 | 8 => Some(5),
859 9 | 10 => Some(6),
860 11 => Some(7),
861 _ => None,
862 }
863 }
864
865 pub const fn fret_to_scale_degree_all_flats(fret: u8) -> Option<u8> {
866 match fret {
867 0 => Some(1),
868 1 | 2 => Some(2),
869 3 | 4 => Some(3),
870 5 => Some(4),
871 6 | 7 => Some(5),
872 8 | 9 => Some(6),
873 10 | 11 => Some(7),
874 _ => None,
875 }
876 }
877 pub fn new<T: Into<EzStr>>(text: T) -> Option<Self> {
878 let text = text.into();
879 let result = Self::parse(&text);
880 if result.is_none() {
881 None
882 } else {
883 Some(
884 Self { text: text,
885 accidentals: result.clone().unwrap().0.into(),
886 degree: result.unwrap().1.into() }
887 )
888 }
889 }
890
891 fn parse(text: &EzStr) -> Option<(String, String)> {
892 {
894 let cache = PARSE_CACHE.lock().unwrap();
895 if let Some(v) = cache.get(&text.data) {
896 return v.clone();
897 }
898 }
899 let mut accidentals = String::new();
900 let mut degree = String::new();
901 let mut is_parsing_accidentals = true;
902 let mut c_inactive_until = 0usize;
903 let mut ret:Option<(String, String)>;
904 for (c, char) in text.graphemes().iter().enumerate() {
905 if c < c_inactive_until {
906 continue;
907 }
908 if is_parsing_accidentals {
909 let mut trigger_found = false;
910 for trigger in Accidentals::ALL_TRIGGERS_LONGEST_FIRST.iter() {
911 let trigger = EzStr::from(*trigger);
912 let end = c as i32 + trigger.len() as i32;
913 if end < text.len() as i32 && trigger == text.slice(c as i32, end) {
914 accidentals.push_str(&trigger.data);
915 c_inactive_until = c_inactive_until.wrapping_add(trigger.len());
916 trigger_found = true;
917 break;
918 }
919 }
920 if !trigger_found {
921 is_parsing_accidentals = false;
922 }
923 }
924 if !is_parsing_accidentals {
925 if !text[c].value.chars().next().unwrap().is_numeric() {
926 ret = None;
927 let mut cache = PARSE_CACHE.lock().unwrap();
928 cache.insert(text.data.clone(), ret);
929 return None;
930
931
932 } else {
933 degree.push_str(&text[c].value);
934 }
935 }
936 }
937
938 ret = Some((accidentals, degree));
939 let mut cache = PARSE_CACHE.lock().unwrap();
940 cache.insert(text.data.clone(), ret.clone());
941 ret
942 }
943
944 fn in_first_octave(&self) -> Note {
945 let mut degree = self.degree.data.parse::<u8>().unwrap();
946 degree = (degree - 1 ) % 7 + 1;
947 let text = self.accidentals.as_ref().to_owned() + &*degree.to_string();
948 let note = Note::new(text).unwrap();
949 note
950 }
951
952 pub fn accidentals_dist(&self) -> i32 {
953 Accidentals::get_dist(&*self.accidentals.data)
954 }
955 pub fn degree_to_fret<N: Into<u8>>(degree:N) -> i32 {
956 let degree = degree.into();
957 let octave = (degree - 1) / 7; let degree_first_octave = (degree - 1) % 7 + 1;
959 (octave * 12 + Self::scale_degree_to_fret_first_octave(degree_first_octave).unwrap()) as i32
960 }
961 pub fn fret(&self) -> i32 {
962 {
964 let cache = FRETS_FROM_ONE_CACHE.lock().unwrap();
965 if let Some(v) = cache.get(&self.text.data) {
966 return *v;
967 }
968 }
969 let accidentals_frets:i32 = Accidentals::get_dist(&self.accidentals.data);
970 let degree_frets:i32 = Note::degree_to_fret((&self.degree.data).parse::<u8>().unwrap());
971
972 let ret = accidentals_frets + degree_frets;
973 let mut cache = FRETS_FROM_ONE_CACHE.lock().unwrap();
975 cache.insert(self.text.data.clone(), ret);
976 ret
977 }
978
979 pub fn from_fret<F: Into<u8>>(fret: F) -> Note {
980 let fret = fret.into();
981 let octave = fret / 12;
982 let first_octave_fret = fret % 12;
983 let first_octave_degree = Note::fret_to_scale_degree_all_flats(first_octave_fret).unwrap();
984 let accidental_dist = Self::scale_degree_to_fret_first_octave(first_octave_degree).unwrap() - first_octave_fret;
985 let degree = String::from((first_octave_degree + 7 * octave).to_string());
986 let text = Accidentals::from_dist(accidental_dist as i32,false,false) + &*degree;
987 let note = Note::new(text.clone());
988 if note.is_some(){
989 note.unwrap()
990 } else {
991 panic!("Failed {}", text)
992 }
993 }
994
995 pub fn unicode_accidentals(&self, using_double_accidentals: bool) -> String {
996 format!("{}", Accidentals::replace_with_unicode(&self.text.data, using_double_accidentals))
997 }
998
999 pub fn parse_until_invalid(text: &EzStr,start:usize) -> Option<Note>{
1000 let mut accidentals = String::new();
1001 let mut degree = String::new();
1002 let mut is_parsing_accidentals = true;
1003 let mut c_inactive_until = start;
1004 let mut ret:Option<Note>;
1005 for (c, char) in text.graphemes().iter().enumerate() {
1006 if c < c_inactive_until {
1007 continue;
1008 }
1009 if is_parsing_accidentals {
1010 let mut trigger_found = false;
1011 for trigger in Accidentals::ALL_TRIGGERS_LONGEST_FIRST.iter() {
1012 let trigger = EzStr::from(*trigger);
1013 let end = c as i32 + trigger.len() as i32;
1014 if end < text.len() as i32 && trigger == text.slice(c as i32, end) {
1015 accidentals.push_str(&trigger.data);
1016 c_inactive_until = c_inactive_until.wrapping_add(trigger.len());
1017 trigger_found = true;
1018 break;
1019 }
1020 }
1021 if !trigger_found {
1022 is_parsing_accidentals = false;
1023 if c == 0 {
1024 return None;
1025 }
1026 }
1027 }
1028 if !is_parsing_accidentals {
1029 if !text[c].value.chars().next().unwrap().is_numeric() {
1030 if degree.len() == 0 {
1031 return None;
1032 } else {
1033 break;
1034 }
1035 } else {
1036 degree.push_str(&text[c].value);
1037 }
1038 }
1039 }
1040
1041 ret = Some(Note{ text: EzStr::from(accidentals.clone() + &*degree.clone()),
1042 accidentals:accidentals.into(), degree: EzStr::from(degree)
1043 });
1044 ret
1045 }
1046
1047}
1048
1049#[derive(PartialEq, Debug, Hash, Clone)]
1050pub struct Change {
1051 pub notes: Vec<Note>,
1052}
1053
1054
1055impl Display for Change {
1056 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1057 write!(f,"{}",Accidentals::replace_with_unicode(&*self.join(" "), false))
1058 }
1059}
1060impl From<&str> for Change {
1061 fn from(text: &str) -> Self {
1062 let mut text = String::from(text);
1063 while text.contains(" "){
1064 text = text.replace(" ", " ");
1065 }
1066 text = text.replace(",","");
1067 let note_strs = text.split(' ').collect::<Vec<&str>>();
1068 Self {notes: note_strs.into_iter().map(|arg: &str| Note::from(arg)).collect()}
1069 }
1070}
1071
1072impl From<Vec<&str>> for Change {
1073 fn from(text: Vec<&str>) -> Change {
1074 Change { notes:text.into_iter().map(|arg| Note::from(arg)).collect::<Vec<Note>>() }
1075 }
1076}
1077
1078impl From<Vec<Note>> for Change {
1079 fn from(notes: Vec<Note>) -> Change {
1080 Change { notes }
1081 }
1082}
1083
1084impl IntoIterator for Change {
1085 type Item = Note;
1086 type IntoIter = IntoIter<Self::Item>;
1087
1088 fn into_iter(self) -> Self::IntoIter {
1089 self.notes.into_iter()
1090 }
1091}
1092
1093impl IntoIterator for &Change {
1094 type Item = Note;
1095 type IntoIter = IntoIter<Self::Item>;
1096
1097 fn into_iter(self) -> Self::IntoIter {
1098 self.clone().notes.into_iter()
1099 }
1100}
1101
1102impl Index<usize> for Change {
1103 type Output = Note;
1104 fn index(&self, index: usize) -> &Self::Output {
1105 &self.notes[index]
1106 }
1107}
1108
1109impl IndexMut<usize> for Change {
1110 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
1111 &mut self.notes[index]
1112 }
1113}
1114
1115impl Extend<Note> for Change {
1116 fn extend<T: IntoIterator<Item=Note>>(&mut self, iter: T) {
1117 self.notes.extend(iter);
1118 }
1119}
1120
1121impl Change {
1122 pub fn new() -> Self {
1123 Change {notes: Vec::new()}
1124 }
1125
1126 pub fn from_notes(notes: Vec<Note>) -> Self {
1127 Change {notes: notes}
1128 }
1129
1130 pub fn from_note(note: Note) -> Self {
1131 Change {notes: vec![note]}
1132 }
1133 pub fn first_note_with_degree<I: Into<i32>>(&self,degree:I) -> Option<usize>{
1134 let degree = degree.into();
1135 for (n,note) in self.into_iter().enumerate(){
1136 if note.degree == EzStr::new(degree.to_string()) {
1137 return Some(n)
1138 }
1139 }
1140 None
1141 }
1142 pub fn from_frets<F: IntoIterator<Item = u8>>(frets:F) -> Change {
1143 let mut notes = Vec::new();
1144 for fret in frets {
1145 notes.push(Note::from_fret(fret));
1146 }
1147 Change{notes}
1148 }
1149
1150 pub fn to_frets(&self) -> Vec<i32> {
1151 self.notes.iter().map(|i|i.fret()).collect()
1152 }
1153
1154 pub fn join(&self,sep:&str) -> String {
1155 if self.len() == 0 {
1156 return String::new();
1157 }
1158 let mut ret = String::new();
1159 let notes = self.clone().notes;
1160 for note in notes[0..self.notes.len() - 1].iter(){
1161 ret += &*note.unicode_accidentals(false);
1162 ret += sep.as_ref();
1163 }
1164 ret += &*notes[self.len() - 1].unicode_accidentals(false);
1165 ret
1166 }
1167
1168 pub fn len(&self) -> usize {
1169 self.notes.len()
1170 }
1171
1172 pub fn remove(&mut self, n:usize) {
1173 self.notes.remove(n);
1174 }
1175
1176 pub fn contains<N: Into<Note>>(&self,note:N) -> bool {
1177 return self.notes.contains(¬e.into());
1178 }
1179 pub fn position<N: Into<Note> + Clone>(&self,note:N) -> Option<usize> {
1180 return self.notes.iter().position(|n| *n == note.clone().into());
1181 }
1182
1183
1184 pub fn in_first_octave(&self) -> Change {
1185 Change {
1186 notes: self.clone().notes.iter().map(|note|
1187 note.in_first_octave()).collect(),
1188 }
1189 }
1190
1191 pub fn sorted_by_dist(&self) -> Change {
1192 let mut notes = self.clone().notes;
1193 notes.sort_by(|a,b| a.fret().cmp(&b.fret()));
1194 Change {
1195 notes
1196 }
1197 }
1198
1199 pub fn in_first_octave_sorted(&self) -> Change {
1200 Change {
1201 notes: self.in_first_octave().sorted_by_dist().notes.clone(),
1202 }
1203 }
1204
1205}
1206#[derive(Debug)]
1207struct KeyParseResult {
1208 accidentals: EzStr,
1209 letter: EzStr,
1210}
1211#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1212pub struct Key {
1213 pub data: String,
1214 pub accidentals: EzStr,
1215 pub letter: EzStr,
1216}
1217
1218
1219impl Key {
1220 pub const fn letter_to_frets_from_c(letter: char) -> Option<u8> {
1221 match letter {
1222 'C' => Some(0),
1223 'D' => Some(2),
1224 'E' => Some(4),
1225 'F' => Some(5),
1226 'G' => Some(7),
1227 'A' => Some(9),
1228 'B' => Some(11),
1229 _ => None,
1230 }
1231 }
1232
1233 pub fn new(data: &str) -> Option<Self> {
1234 let parse_result = Key::parse(data);
1235 match parse_result {
1237 Some(x) => {
1238 Some(Key {
1239 data: data.into(),
1240 accidentals: x.accidentals,
1241 letter: x.letter,
1242 })
1243 }
1244 _ => None,
1245 }
1246 }
1247
1248 fn parse(data: &str) -> Option<KeyParseResult> {
1249 let letter:char;
1250 let accidentals:&str;
1251 if "ABCDEFG".contains(&data.chars().nth(0).unwrap().to_string()) {
1252 letter = data.chars().nth(0).unwrap()
1253 } else {
1254 return None;
1255 }
1256 let ending = &data[1..];
1257 if Accidentals::is_accidentals_str(ending) {
1258 accidentals = ending;
1259 } else {
1260 return None
1261 }
1262 Some(KeyParseResult {
1263 accidentals: accidentals.into(),
1264 letter: letter.into(), })
1265 }
1266
1267 pub fn frets_from_c(&self) -> i32{
1269 let letter_dist_result = Key::letter_to_frets_from_c(self.letter.data.chars().nth(0).unwrap());
1270 if letter_dist_result.is_none() {
1271 panic!("{:?} {:?}",self,
1272 self.letter.data.chars().nth(0).unwrap().to_string());
1273 }
1274 let letter_dist = letter_dist_result.unwrap() as i32;
1275 let accidentals_dist = Accidentals::get_dist(&self.accidentals.data) as i32 ;
1276 (letter_dist + accidentals_dist).rem_euclid( 12)
1278 }
1279}