1 // Copyright 2004 "Gilles Degottex", 2007 "Florian Hars"
2 
3 // This file is part of "Music"
4 
5 // "Music" is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published by
7 // the Free Software Foundation; either version 2.1 of the License, or
8 // (at your option) any later version.
9 //
10 // "Music" is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19 
20 #ifndef _Music_h_
21 #define _Music_h_
22 
23 #include <assert.h>
24 #include <cmath>
25 #include <string>
26 #include <list>
27 #include <vector>
28 #include <limits>
29 using namespace std;
30 #include <CppAddons/CAMath.h>
31 #include <CppAddons/StringAddons.h>
32 
33 #include <iostream>
34 
35 namespace Music
36 {
37 	enum NotesName{LOCAL_ANGLO, LOCAL_LATIN, LOCAL_GERMAN, LOCAL_HINDUSTANI, LOCAL_BYZANTINE};
38 	extern NotesName s_notes_name;
GetNotesName()39 	inline NotesName GetNotesName()					{return s_notes_name;}
SetNotesName(NotesName type)40 	inline void SetNotesName(NotesName type)		{s_notes_name = type;}
41 
42 	extern int s_transposition;
GetTransposition()43 	inline int GetTransposition()						{return s_transposition;}
SetTransposition(int transposition)44 	inline void SetTransposition(int transposition)	    {s_transposition = transposition;}
45 
46 	enum Tuning{CHROMATIC,WERCKMEISTER3,KIRNBERGER3,DIATONIC,MEANTONE};
47 	extern Tuning s_tuning;
48 	extern double s_semitones[13];
GetTuning()49 	inline Tuning GetTuning()						{return s_tuning;}
50 	void SetTuning(Tuning tuning);
51 
52 
53 	extern int s_sampling_rate;
GetSamplingRate()54 	inline int GetSamplingRate()					{return s_sampling_rate;}
55 	void SetSamplingRate(int sampling_rate);
56 
57 	extern double s_AFreq;
GetAFreq()58 	inline double GetAFreq()						{return s_AFreq;}
59 	void SetAFreq(double AFreq);
60 
61 	extern const int UNDEFINED_SEMITONE;
62 	extern int s_semitone_min;
63 	extern int s_semitone_max;
GetSemitoneMin()64 	inline int GetSemitoneMin()						{return s_semitone_min;}
GetSemitoneMax()65 	inline int GetSemitoneMax()						{return s_semitone_max;}
GetNbSemitones()66 	inline int GetNbSemitones()						{return s_semitone_max-s_semitone_min+1;}
67 	void SetSemitoneBounds(int semitone_min, int semitone_max);
68 
69 	struct SettingsListener
70 	{
samplingRateChangedSettingsListener71 		virtual void samplingRateChanged()			{}
AFreqChangedSettingsListener72 		virtual void AFreqChanged()					{}
semitoneBoundsChangedSettingsListener73 		virtual void semitoneBoundsChanged()		{}
74 
75 		SettingsListener();
76 		virtual ~SettingsListener();
77 	};
78 
79 	extern list<SettingsListener*>	s_settings_listeners;
80 	void AddSettingsListener(SettingsListener* l);
81 	void RemoveSettingsListener(SettingsListener* l);
82 
83 	//! convert frequency to a float number of chromatic half-tones from A3
84 	/*!
85 	* \param freq the frequency to convert to \f$\in R+\f$ {Hz}
86 	* \param AFreq tuning frequency of the A3 (Usualy 440) {Hz}
87 	* \return the float number of half-tones from A3 \f$\in R\f$
88 	*/
89 	inline double f2hf(double freq, double AFreq=GetAFreq())
90 	{
91 			return 17.3123404906675624 * log(freq/AFreq); //12.0*(log(freq)-log(AFreq))/log(2.0)
92 	}
93 	//! find the halftone in the array for non-chromatic tunings
94 	// TODO:
95 	// Decide wether the step from 12/2 for linar search to log_2(12) for a
96 	// binary search really matters in a FFT-bound program
97 	/*!
98 	* \param relFreq the frequency divided by the frequency of the next lower A
99 	* \return the number of halftones above this A
100 	*/
f2h_find(double relFreq)101 	inline int f2h_find(double relFreq)
102 	{
103 		if (relFreq < s_semitones[1])
104 		{
105 			if (s_semitones[1] / relFreq > relFreq / s_semitones[0])
106 				return 0;
107 			else
108 				return 1;
109 		}
110 		else
111 		{
112 			int i;
113 			for (i = 2; i < 12; i += 1)
114 				if (relFreq < s_semitones[i]) { break; }
115 
116 			if (s_semitones[i] / relFreq > relFreq / s_semitones[i - 1])
117 				return i - 1;
118 			else
119 				return i;
120 		}
121 	}
122 	// TODO VERIF
123 	// le ht doit �re le ht le plus proche de freq !! et pas un simple arrondi en dessous de la valeur r�l !!
124 	//! convert frequency to number of half-tones from A3
125 	/*!
126 	* \param freq the frequency to convert to \f$\in R+\f$ {Hz}
127 	* \param AFreq tuning frequency of the A3 (Usualy 440) {Hz}
128 	* \return the number of half-tones from A3. Rounded to the nearest half-tones(
129 	* not a simple integer convertion of \ref f2hf ) \f$\in R\f$
130 	*/
131 	inline int f2h(double freq, double AFreq=GetAFreq(), int tuning=GetTuning())
132 	{
133 		if (CHROMATIC == tuning)
134 		{
135 			double ht = f2hf(freq, AFreq);
136 			if(ht>0)	return int(ht+0.5);
137 			if(ht<0)	return int(ht-0.5);
138 			return	0;
139 		}
140 		else
141 		{
142 			if (freq <= 1.0) return UNDEFINED_SEMITONE;
143 			int oct = 0;
144 			while (freq < AFreq)        { freq *= 2.0; oct -= 1; }
145 			while (freq >= 2.0 * AFreq) { freq /= 2.0; oct += 1; }
146 			int ht = f2h_find(freq/AFreq);
147 			return (12 * oct + ht);
148 		}
149 	}
150 	//! convert number of chromatic half-tones to frequency
151 	/*!
152 	* \param ht number of half-tones to convert to \f
153 	* \param AFreq tuning frequency of the A3 (Usualy 440) {Hz}
154 	* \return the converted frequency
155 	*/
156 	inline double h2f(double ht, double AFreq=GetAFreq())
157 	{
158 		return AFreq * pow(2.0, ht/12.0);
159 	}
160 	//! convert number of half-tones to frequency
161 	/*!
162 	* \param ht number of half-tones to convert to \f$\in Z\f$
163 	* \param AFreq tuning frequency of the A3 (Usualy 440) {Hz}
164 	* \return the converted frequency
165 	*/
166 	inline double h2cf(int ht, double AFreq=GetAFreq(), int tuning=GetTuning())
167 	{
168 		if (CHROMATIC == tuning)
169 		{
170 			return AFreq * pow(2.0, ht/12.0);
171 		}
172 		else if (ht >= s_semitone_min)
173 		{
174 			int oct = 0;
175 			while(ht<0)
176 			{
177 				ht += 12;
178 				oct--;
179 			}
180 			while(ht>11)
181 			{
182 				ht -= 12;
183 				oct++;
184 			}
185 			return AFreq * pow(2.0, oct) * s_semitones[ht];
186 		}
187 		else
188 		{
189 			return 0.0;
190 		}
191 	}
192 	//! convert frequency to the frequency of the nearest half tone
193 	/*!
194 	* \param freq the frequency to convert to \f$\in R+\f$ {Hz}
195 	* \param AFreq tuning frequency of the A3 (Usualy 440) {Hz}
196 	* \return the converted frequency
197 	*/
198 	inline double f2cf(double freq, double AFreq=GetAFreq(), int tuning=GetTuning())
199 	{
200 		return h2cf(f2h(freq, AFreq, tuning), AFreq, tuning);
201 	}
202 
203 	//! convert half-tones from A3 to the corresponding note name
204 	/*!
205 	* \param ht number of half-tone to convert to \f$\in Z\f$
206 	* \param local
207 	* \return its name (Do, Re, Mi, Fa, Sol, La, Si; with '#' or 'b' if needed)
208 	*/
209 	inline string h2n(int ht, NotesName local=GetNotesName(), int transposition=GetTransposition(), int tunig=GetTuning(), bool show_oct=true)
210 	{
211         (void)tunig;
212 
213 		ht += transposition;
214 
215 		int oct = 4;
216 		while(ht<0)
217 		{
218 			ht += 12;
219 			oct--;
220 		}
221 		while(ht>11)
222 		{
223 			ht -= 12;
224 			oct++;
225 		}
226 
227 		if(ht>2)	oct++;	// octave start from C
228 	// 	if(oct<=0)	oct--;	// skip 0-octave in occidental notations ??
229 
230 	//	char coct[3];
231 	//	sprintf(coct, "%d", oct);
232 	//	string soct = coct;
233 
234 		string soct;
235 		if(show_oct)
236 			soct = StringAddons::toString(oct);
237 
238 		if(local==LOCAL_ANGLO)
239 		{
240 			if(ht==0)	return "A"+soct;
241 			else if(ht==1)	return "B♭"+soct;
242 			else if(ht==2)	return "B"+soct;
243 			else if(ht==3)	return "C"+soct;
244 			else if(ht==4)	return "C♯"+soct;
245 			else if(ht==5)	return "D"+soct;
246 			else if(ht==6)	return "E♭"+soct;
247 			else if(ht==7)	return "E"+soct;
248 			else if(ht==8)	return "F"+soct;
249 			else if(ht==9)	return "F♯"+soct;
250 			else if(ht==10)	return "G"+soct;
251 			else if(ht==11)	return "G♯"+soct;
252 		}
253 		else if(local==LOCAL_LATIN)
254 		{
255 			if(ht==0)	return "La"+soct;
256 			else if(ht==1)	return "Si♭"+soct;
257 			else if(ht==2)	return "Si"+soct;
258 			else if(ht==3)	return "Do"+soct;
259 			else if(ht==4)	return "Do♯"+soct;
260 			else if(ht==5)	return "Re"+soct;
261 			else if(ht==6)	return "Mi♭"+soct;
262 			else if(ht==7)	return "Mi"+soct;
263 			else if(ht==8)	return "Fa"+soct;
264 			else if(ht==9)	return "Fa♯"+soct;
265 			else if(ht==10)	return "Sol"+soct;
266 			else if(ht==11)	return "Sol♯"+soct;
267 		}
268 		else if(local==LOCAL_GERMAN)
269 		{
270 			if(ht==0)	return "A"+soct;
271 			else if(ht==1)	return "B"+soct;
272 			else if(ht==2)	return "H"+soct;
273 			else if(ht==3)	return "C"+soct;
274 			else if(ht==4)	return "C♯"+soct;
275 			else if(ht==5)	return "D"+soct;
276 			else if(ht==6)	return "E♭"+soct;
277 			else if(ht==7)	return "E"+soct;
278 			else if(ht==8)	return "F"+soct;
279 			else if(ht==9)	return "F♯"+soct;
280 			else if(ht==10)	return "G"+soct;
281 			else if(ht==11)	return "G♯"+soct;
282 		}
283 		else if(local==LOCAL_HINDUSTANI)
284 		{
285 			if(ht==0)	return "D"+soct;
286 			else if(ht==1)	return "n"+soct;
287 			else if(ht==2)	return "N"+soct;
288 			else if(ht==3)	return "S"+soct;
289 			else if(ht==4)	return "r"+soct;
290 			else if(ht==5)	return "R"+soct;
291 			else if(ht==6)	return "g"+soct;
292 			else if(ht==7)	return "G"+soct;
293 			else if(ht==8)	return "m"+soct;
294 			else if(ht==9)	return "M"+soct;
295 			else if(ht==10)	return "P"+soct;
296 			else if(ht==11)	return "d"+soct;
297 		}
298 		else if(local==LOCAL_BYZANTINE)
299 		{
300 			if(ht==0)	return "Ke"+soct;
301 			else if(ht==1)	return "Zo♭"+soct;
302 			else if(ht==2)	return "Zo"+soct;
303 			else if(ht==3)	return "Ni"+soct;
304 			else if(ht==4)	return "Ni♯"+soct;
305 			else if(ht==5)	return "Pa"+soct;
306 			else if(ht==6)	return "Vou♭"+soct;
307 			else if(ht==7)	return "Vou"+soct;
308 			else if(ht==8)	return "Ga"+soct;
309 			else if(ht==9)	return "Ga♯"+soct;
310 			else if(ht==10)	return "Di"+soct;
311 			else if(ht==11)	return "Di♯"+soct;
312 		}
313 
314 		return "Th#1138";
315 	}
316 
317 //	inline int n2h(const std::string& note, NotesName local=LOCAL_ANGLO, int transposition=GetTransposition())
318 //	{
319 //		// TODO
320 //		return -1;
321 //	}
322 
323     //! convert amplitude to dB
324     template<class TYPE>
lp(TYPE value)325     TYPE lp(TYPE value)
326     {
327         return 20*log10(abs(value));
328 //         return 20*log10(abs(value)+numeric_limits<TYPE>::epsilon());
329     }
330 
331     //! convert dB to amplitude
332     // TODO cannot create a template so easily because the pow10 is not defined for all types
invlp(double value)333     inline double invlp(double value)
334     {
335         return std::pow(10, value/20.0);
336 //         return pow(TYPE(10), value/TYPE(20));
337     }
338 
339     std::vector<double> conv(const std::vector<double>& u, const std::vector<double>& v);
340 
341 	// TODO freq reffinement
342 }
343 
344 #endif // _Music_h_
345