music21_rs/pitch/
names.rs1use super::*;
5
6impl Pitch {
7 pub fn german(&self) -> Result<String> {
10 let alter = self.whole_alteration("german")?;
11 let mut step = self.step.as_char().to_string();
12 let mut alter = alter;
13 if self.step == StepName::B {
14 if alter == -1 {
15 alter = 0;
16 } else {
17 step = "H".to_string();
18 }
19 }
20 Ok(match alter {
21 0 => step,
22 alter if alter > 0 => step + &"is".repeat(alter as usize),
23 alter => {
24 let first = if matches!(step.as_str(), "C" | "D" | "F" | "G" | "H") {
25 "es"
26 } else {
27 "s"
28 };
29 step + first + &"es".repeat(alter.unsigned_abs() as usize - 1)
30 }
31 })
32 }
33
34 pub fn italian(&self) -> Result<String> {
38 let alter = self.whole_alteration("italian")?;
39 let solfege = match self.step {
40 StepName::C => "do",
41 StepName::D => "re",
42 StepName::E => "mi",
43 StepName::F => "fa",
44 StepName::G => "sol",
45 StepName::A => "la",
46 StepName::B => "si",
47 };
48 let cardinality = match alter.unsigned_abs() {
49 0 => return Ok(solfege.to_string()),
50 1 => " ",
51 2 => " doppio ",
52 3 => " triplo ",
53 4 => " quadruplo ",
54 _ => {
55 return Err(Error::Pitch(format!(
56 "entirely too many accidentals for an Italian name: {self}"
57 )));
58 }
59 };
60 let kind = if alter > 0 { "diesis" } else { "bemolle" };
61 Ok(format!("{solfege}{cardinality}{kind}"))
62 }
63
64 pub fn french(&self) -> Result<String> {
68 let alter = self.whole_alteration("french")?;
69 let solfege = match self.step {
70 StepName::D => "ré",
71 other => romance_solfege(other),
72 };
73 let multiplier = match alter.unsigned_abs() {
74 0 => return Ok(solfege.to_string()),
75 1 => "",
76 2 => " double",
77 3 => " triple",
78 4 => " quadruple",
79 _ => {
80 return Err(Error::Pitch(format!(
81 "entirely too many accidentals for a French name: {self}"
82 )));
83 }
84 };
85 let kind = if alter > 0 { "dièse" } else { "bémol" };
86 Ok(format!("{solfege}{multiplier} {kind}"))
87 }
88
89 pub fn spanish(&self) -> Result<String> {
93 let alter = self.whole_alteration("spanish")?;
94 let solfege = romance_solfege(self.step);
95 let multiplier = match alter.unsigned_abs() {
96 0 => return Ok(solfege.to_string()),
97 1 => "",
98 2 => " doble",
99 3 => " triple",
100 4 => " cuádruple",
101 _ => {
102 return Err(Error::Pitch(format!(
103 "entirely too many accidentals for a Spanish name: {self}"
104 )));
105 }
106 };
107 let kind = if alter > 0 { "sostenido" } else { "bemol" };
108 Ok(format!("{solfege}{multiplier} {kind}"))
109 }
110
111 pub fn unicode_name(&self) -> String {
114 if self.accidental.alter() == 0.0 {
115 return self.step.as_char().to_string();
116 }
117 format!("{}{}", self.step.as_char(), self.accidental.unicode())
118 }
119
120 pub fn full_name(&self) -> String {
123 let mut name = self.step.as_char().to_string();
124 if self.has_accidental {
128 name.push('-');
129 name.push_str(self.accidental.full_name());
130 }
131 if let Some(octave) = self.octave {
132 name.push_str(&format!(" in octave {octave}"));
133 }
134 if let Some(microtone) = &self.microtone
135 && microtone.cents() != 0.0
136 {
137 name.push(' ');
138 name.push_str(µtone.to_string());
139 }
140 name
141 }
142
143 pub fn name_with_octave_and_microtone(&self) -> String {
146 match &self.microtone {
147 Some(microtone) if microtone.cents() != 0.0 => {
148 format!("{}{microtone}", self.name_with_octave())
149 }
150 _ => self.name_with_octave(),
151 }
152 }
153
154 pub fn unicode_name_with_octave(&self) -> String {
156 match self.octave {
157 Some(octave) => format!("{}{octave}", self.unicode_name()),
158 None => self.unicode_name(),
159 }
160 }
161
162 pub(super) fn whole_alteration(&self, language: &str) -> Result<IntegerType> {
163 let alter = self.accidental.alter();
164 if alter.fract() != 0.0 {
165 return Err(Error::Pitch(match language {
166 "german" => {
167 "Es geht nicht \"german\" zu benutzen mit Microtönen. Schade!".to_string()
168 }
169 "italian" => "Non si puo usare `italian` con microtoni".to_string(),
170 "french" => {
171 "On ne peut pas utiliser les microtones avec \"french.\" Quelle Dommage!"
172 .to_string()
173 }
174 "spanish" => "Unsupported accidental type.".to_string(),
175 other => {
176 format!("{other} names cannot express the microtonal accidental of {self}")
177 }
178 }));
179 }
180 Ok(alter as IntegerType)
181 }
182}
183
184pub const CHROMATIC_PITCH_CLASS_NAMES: [&str; 12] = [
186 "C", "D-", "D", "E-", "E", "F", "F#", "G", "A-", "A", "B-", "B",
187];
188
189pub fn pitch_class_name(pitch_class: u8) -> &'static str {
191 CHROMATIC_PITCH_CLASS_NAMES[pitch_class as usize % 12]
192}
193
194pub(super) fn romance_solfege(step: StepName) -> &'static str {
195 match step {
196 StepName::C => "do",
197 StepName::D => "re",
198 StepName::E => "mi",
199 StepName::F => "fa",
200 StepName::G => "sol",
201 StepName::A => "la",
202 StepName::B => "si",
203 }
204}