chordpro/
transpose.rs

1//! This module allows transposing chords in a `Song`
2//!
3use crate::song::{Chunk, Song};
4
5use crate::chords::Chord;
6
7pub struct Transposer {
8    s: i8,
9}
10
11pub fn map_to_chords<'a, F>(song: &mut Song, f: F)
12where
13    F: Fn(&mut Chord),
14{
15    for section in song.iter_mut() {
16        for line in section.iter_mut() {
17            for chunk in line.iter_mut() {
18                match chunk {
19                    Chunk::Chord(c) => f(c),
20                    _ => {}
21                }
22            }
23        }
24    }
25}
26
27impl Transposer {
28    /// Create a `Transposer` object which transposes song the specified
29    /// amount of `semitones` (positive or negative)
30    pub fn new(semitones: i8) -> Self {
31        Self { s: semitones }
32    }
33
34    /// Applies transposition in-place
35    pub fn apply_transpose(&self, song: &mut Song) {
36        if self.s.abs() % 12 != 0 {
37            map_to_chords(song, |chord| {
38                chord.root = chord.root + self.s;
39                chord.bass = chord.bass + self.s;
40            });
41        }
42    }
43
44    /// Applies transposition to song
45    pub fn transpose(&self, song: Song) -> Song {
46        let mut song = song;
47        self.apply_transpose(&mut song);
48        song
49    }
50}
51
52#[cfg(test)]
53mod test {
54
55    #[test]
56    fn transpose() {
57        use super::*;
58        use crate::chords::Note;
59        use crate::song::{Line, Song};
60        use std::str::FromStr;
61        let song = Song::from_str("[C]A [D#m]Cm").expect("Failed to parse song");
62        let song2 = Transposer::new(-3).transpose(song);
63
64        let line = song2.iter().nth(0).unwrap().iter().nth(0).unwrap();
65        assert_eq!(
66            line,
67            &Line(vec![
68                Chunk::Chord(Chord::major(Note::A)),
69                Chunk::Lyrics("A ".to_string()),
70                Chunk::Chord(Chord::minor(Note::C)),
71                Chunk::Lyrics("Cm".to_string()),
72            ])
73        );
74    }
75}