1mod file_reader;
2pub mod chord_generator;
3pub mod song;
4
5#[cfg(feature = "song_library")]
6pub mod song_library;
7
8use std::collections::BTreeMap;
9use std::fmt;
10use serde::{Serialize, Deserialize};
11use crossterm::style::Color;
12
13use crate::Note::*;
14pub use crate::chord_generator::chord_fingerings::Fingering;
15pub use crate::chord_generator::chord_fingerings::StringState::{self, *};
16pub use crate::chord_generator::chord_fingerings::sum_text_in_fingerings;
17pub use crate::chord_generator::get_fretboard;
18pub use crate::chord_generator::STRINGS;
19pub use crate::song::{Song, Metadata};
20pub use crate::song::chord::Chord;
21
22
23pub const STANDART_TUNING: [Note; STRINGS] = [E, B, G, D, A, E];
24
25
26const METADATA_START: &str = "{metadata:}";
27const METADATA_END: &str = "{:metadata}";
28
29const SONG_TITLE_SYMBOL: &str = "{song_title:} ";
30const SONG_ARTIST_SYMBOL: &str = "{song_artist:} ";
31const SONG_KEY_SYMBOL: &str = "{song_key:} ";
32const SONG_CAPO_SYMBOL: &str = "{song_capo:} ";
33const SONG_AUTOSCROLL_SPEED_SYMBOL: &str = "{song_autoscroll_speed:} ";
34
35
36const BLOCK_START: &str = "{block:}";
37const BLOCK_END: &str = "{:block}";
38
39const TITLE_SYMBOL: &str = "{title:} ";
40
41const CHORDS_LINE_SYMBOL: &str = "{chords_line:} ";
42const EMPTY_LINE_SYMBOL: &str = "{empty_line}";
43
44const PLAIN_TEXT_START: &str = "{plain_text:}";
45const PLAIN_TEXT_END: &str = "{:plain_text}";
46
47const TAB_START_SYMBOL: &str = "{tab:}";
48const TAB_END_SYMBOL: &str = "{:tab}";
49
50const CHORDS_SYMBOL: &str = "{C}|";
51const RHYTHM_SYMBOL: &str = "{R}|";
52const TEXT_SYMBOL: &str = "{T}|";
53
54const SONG_NOTE_START_SYMBOL: &str = "{song_note:} ";
55const SONG_NOTE_END_SYMBOL: &str = "{:song_note}";
56
57const BLOCK_NOTE_SYMBOL: &str = "{note:} ";
58
59
60const TITLE_COLOR: Color = Color::DarkGreen;
61const CHORDS_COLOR: Color = Color::Cyan;
62const RHYTHM_COLOR: Color = Color::Yellow;
63const NOTES_COLOR: Color = Color::DarkGrey;
64
65
66const KEYS: [[Note; 6]; 12] = [
67 [C, D, E, F, G, A],
68 [G, A, B, C, D, E],
69 [D, E, FSharp, G, A, B],
70 [A, B, CSharp, D, E, FSharp],
71 [E, FSharp, GSharp, A, B, CSharp],
72 [B, CSharp, DSharp, E, FSharp, GSharp],
73 [FSharp, GSharp, ASharp, B, CSharp, DSharp],
74 [CSharp, DSharp, F, FSharp, GSharp, ASharp],
75 [GSharp, ASharp, C, CSharp, FSharp, F],
76 [DSharp, F, G, GSharp, ASharp, C],
77 [ASharp, C, D, DSharp, F, G],
78 [F, G, A, ASharp, C, D]
79];
80
81
82#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
83pub struct Key {
84 keynote: Note,
85 is_minor: bool
86}
87
88impl Key {
89 pub fn new(text: &str) -> Option<Self> {
90 Some( match text.to_lowercase().as_str() {
91 "c" => Self { keynote: Note::new("C")?, is_minor: false },
92 "am" => Self { keynote: Note::new("C")?, is_minor: true },
93
94 "c#" | "db" => Self { keynote: Note::new("C#")?, is_minor: false },
95 "a#m" | "bbm" => Self { keynote: Note::new("C#")?, is_minor: true },
96
97 "d" => Self { keynote: Note::new("D")?, is_minor: false },
98 "bm" => Self { keynote: Note::new("D")?, is_minor: true },
99
100 "d#" | "eb" => Self { keynote: Note::new("D#")?, is_minor: false },
101 "cm" => Self { keynote: Note::new("D#")?, is_minor: true },
102
103 "e" => Self { keynote: Note::new("E")?, is_minor: false },
104 "c#m" | "dbm" => Self { keynote: Note::new("E")?, is_minor: true },
105
106 "f" => Self { keynote: Note::new("F")?, is_minor: false },
107 "dm" => Self { keynote: Note::new("F")?, is_minor: true },
108
109 "f#" | "gb" => Self { keynote: Note::new("F#")?, is_minor: false },
110 "d#m" | "ebm" => Self { keynote: Note::new("F#")?, is_minor: true },
111
112 "g" => Self { keynote: Note::new("G")?, is_minor: false },
113 "em" => Self { keynote: Note::new("G")?, is_minor: true },
114
115 "g#" | "ab" => Self { keynote: Note::new("G#")?, is_minor: false },
116 "fm" => Self { keynote: Note::new("G#")?, is_minor: true },
117
118 "a" => Self { keynote: Note::new("A")?, is_minor: false },
119 "f#m" | "gbm" => Self { keynote: Note::new("A")?, is_minor: true },
120
121 "a#" | "bb" => Self { keynote: Note::new("A#")?, is_minor: false },
122 "gm" => Self { keynote: Note::new("A#")?, is_minor: true },
123
124 "b" => Self { keynote: Note::new("B")?, is_minor: false },
125 "g#m" | "abm" => Self { keynote: Note::new("B")?, is_minor: true },
126 _ => return None
127 })
128 }
129
130 pub fn from_note(note: Note) -> Self {
131 Self { keynote: note, is_minor: false }
132 }
133
134 pub fn transpose(&self, steps: i32) -> Self {
135 Self { keynote: self.keynote.transpose(steps), is_minor: self.is_minor }
136 }
137
138 pub fn get_note(&self) -> Note {
139 self.keynote
140 }
141}
142
143impl fmt::Display for Key {
144 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145 let s = match self.keynote {
146 A if self.is_minor => String::from("F#m"),
147 A => self.keynote.get_text(),
148
149 ASharp if self.is_minor => String::from("Gm"),
150 ASharp => self.keynote.get_text(),
151
152 B if self.is_minor => String::from("G#m"),
153 B => self.keynote.get_text(),
154
155 C if self.is_minor => String::from("Am"),
156 C => self.keynote.get_text(),
157
158 CSharp if self.is_minor => String::from("A#m"),
159 CSharp => self.keynote.get_text(),
160
161 D if self.is_minor => String::from("Bm"),
162 D => self.keynote.get_text(),
163
164 DSharp if self.is_minor => String::from("Cm"),
165 DSharp => self.keynote.get_text(),
166
167 E if self.is_minor => String::from("C#m"),
168 E => self.keynote.get_text(),
169
170 F if self.is_minor => String::from("Dm"),
171 F => self.keynote.get_text(),
172
173 FSharp if self.is_minor => String::from("D#m"),
174 FSharp => self.keynote.get_text(),
175
176 G if self.is_minor => String::from("Em"),
177 G => self.keynote.get_text(),
178
179 GSharp if self.is_minor =>String::from("Fm"),
180 GSharp => self.keynote.get_text()
181 };
182
183 write!(f, "{}", s)
184 }
185}
186
187
188#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
189pub enum Note {
190 A,
191 ASharp,
192 B,
193 C,
194 CSharp,
195 D,
196 DSharp,
197 E,
198 F,
199 FSharp,
200 G,
201 GSharp
202}
203
204impl Note {
205 pub fn new(s: &str) -> Option<Self> {
206 Some( match s {
207 "A#" | "Bb" => ASharp,
208 "C#" | "Db" => CSharp,
209 "D#" | "Eb" => DSharp,
210 "F#" | "Gb" => FSharp,
211 "G#" | "Ab" => GSharp,
212
213 "B" | "Cb" => B,
214 "E" | "Fb" => E,
215
216 "A" => A,
217 "C" => C,
218 "D" => D,
219 "F" => F,
220 "G" => G,
221 _ => return None
222 } )
223 }
224
225 pub fn get_text(&self) -> String {
226 (
227 match self {
228 A => "A",
229 ASharp => "A#",
230 B => "B",
231 C => "C",
232 CSharp => "C#",
233 D => "D",
234 DSharp => "D#",
235 E => "E",
236 F => "F",
237 FSharp => "F#",
238 G => "G",
239 GSharp => "G#",
240 }
241 ).to_string()
242 }
243
244 pub fn transpose(&self, steps: i32) -> Self {
245 let steps = steps % 12;
246 if steps == 0 { return self.clone() }
247 let mut note = self.clone();
248
249 if steps > 0 {
250 for _ in 0..steps { note.increase() }
251 } else if steps < 0 {
252 for _ in steps..0 { note.decrease() }
253 }
254
255 return note
256 }
257 fn increase(&mut self) {
258 *self = match self {
259 A => ASharp,
260 ASharp => B,
261 B => C,
262 C => CSharp,
263 CSharp => D,
264 D => DSharp,
265 DSharp => E,
266 E => F,
267 F => FSharp,
268 FSharp => G,
269 G => GSharp,
270 GSharp => A
271 }
272 }
273
274 fn decrease(&mut self) {
275 *self = match self {
276 A => GSharp,
277 ASharp => A,
278 B => ASharp,
279 C => B,
280 CSharp => C,
281 D => CSharp,
282 DSharp => D,
283 E => DSharp,
284 F => E,
285 FSharp => F,
286 G => FSharp,
287 GSharp => G
288 }
289 }
290}
291
292
293
294pub fn print_fretboard(tuning: &[Note; STRINGS]) {
295 let fretboard = crate::chord_generator::get_fretboard(tuning);
296 let mut s = String::new();
297
298 let note_width = 4;
299 let line_width = fretboard.len() * note_width;
300 let string_line = "| ".repeat(fretboard.len());
301 for fret_num in 0..fretboard[0].len() {
302 if fret_num != 0 {
303 s.push_str(&string_line);
304 s.push('\n');
305 } else { s.push('\n') }
306
307 for string_num in (0..fretboard.len()).rev() {
308 let note = &fretboard[string_num][fret_num].get_text();
309 s.push_str(note);
310 s.push_str( &" ".repeat(note_width - note.len()) );
311
312 }
313
314 if fret_num != 0 {
315 s.push('\n');
316 s.push_str(&string_line);
317 s.push('\n');
318
319 s.push_str( &"-".repeat(line_width - (note_width - 1)) );
320 s.push(' ');
321 s.push_str(&fret_num.to_string());
322 s.push('\n');
323 } else {
324 s.push('\n');
325 s.push_str( &"=".repeat(line_width - (note_width - 1)) );
326 s.push('\n');
327 }
328 }
329
330 println!("{s}");
331}
332
333
334pub fn print_circle_of_fifth(needed_key: Option<Key>) {
335 let mut s = String::new();
336 let one_key_width = 18;
337 let width = if let Ok( (cols, _rows) ) = crossterm::terminal::size() {
338 <u16 as Into<usize>>::into(cols)
339 } else { one_key_width };
340 let max_keys = width / one_key_width;
341
342 let mut keys_already_in_line = 0;
343 let mut keys_first_line = String::new();
344 let mut keys_second_line = String::new();
345
346 let mut keys = BTreeMap::new();
347 for k in KEYS {
348 let first = k[0].get_text();
349 let second = k[1].get_text() + "m";
350 let third = k[2].get_text() + "m";
351 let fourth = k[3].get_text();
352 let fifth = k[4].get_text();
353 let sixth = k[5].get_text() + "m";
354
355 let width: usize = 5;
356
357
358 let mut first_line = String::new();
359 first_line.push_str(&fourth);
360 first_line.push_str( &" ".repeat(width - fourth.len()) );
361
362 first_line.push_str(&first);
363 first_line.push_str( &" ".repeat(width - first.len()) );
364
365 first_line.push_str(&fifth);
366 first_line.push_str( &" ".repeat(width - fifth.len()) );
367
368
369 let mut second_line = String::new();
370 second_line.push_str(&second);
371 second_line.push_str( &" ".repeat(width - second.len()) );
372
373 second_line.push_str(&sixth);
374 second_line.push_str( &" ".repeat(width - sixth.len()) );
375
376 second_line.push_str(&third);
377 second_line.push_str( &" ".repeat(width - third.len()) );
378
379
380 if keys_already_in_line < max_keys {
381 keys_already_in_line += 1;
382
383 keys_first_line.push_str(&first_line);
384 keys_first_line.push_str("| ");
385
386 keys_second_line.push_str(&second_line);
387 keys_second_line.push_str("| ");
388 } else {
389 keys_already_in_line = 0;
390
391 s.push_str(&keys_first_line);
392 s.push('\n');
393
394 s.push_str(&keys_second_line);
395 s.push('\n');
396
397 s.push_str( &"-".repeat((max_keys * one_key_width) - 2) );
398 s.push('\n');
399
400 keys_first_line.clear();
401 keys_second_line.clear();
402 }
403
404 keys.insert(k[0], (first_line, second_line));
405 }
406
407 if !keys_first_line.is_empty() && !keys_second_line.is_empty() {
409 s.push_str(&keys_first_line);
410 s.push('\n');
411 s.push_str(&keys_second_line);
412 s.push('\n');
413
414 keys_first_line.clear();
415 keys_second_line.clear();
416 }
417
418
419 if let Some(needed_k) = needed_key {
420 if let Some( (f_line, s_line) ) = keys.get(&needed_k.get_note()) {
421 println!("| {f_line}|\n| {s_line}|");
422 }
423 } else {
424 println!("{s}");
425 }
426}