1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #ifndef __unit_conv_h__
22 #define __unit_conv_h__
23 
24 
25 #include "../../config/common.h"
26 
27 /*
28  * Unit conversion functions
29  */
30 
31 #include <math.h>
32 #include <stdexcept>
33 #include <algorithm>
34 #include <istring>
35 #include "CSound_defs.h"
36 
37 // volume
scalar_to_dB(const double scalar)38 static inline const double scalar_to_dB(const double scalar) { return 20.0*log10(scalar); }
dB_to_scalar(const double dB)39 static inline const double dB_to_scalar(const double dB) { return pow(10.0,dB/20.0); }
40 
percent_to_amp(const double percent)41 static inline const mix_sample_t percent_to_amp(const double percent) { return (mix_sample_t)(percent*MAX_SAMPLE/100.0); }
42 									// ??? if I use this function should I abs the amp value?
amp_to_percent(const mix_sample_t amp)43 static inline const double amp_to_percent(const mix_sample_t amp) { return (double)amp*100.0/(double)MAX_SAMPLE; }
44 
45 
46 
dBFS_to_amp(const double dBFS,const type maxSample)47 template<class type> static inline const type dBFS_to_amp(const double dBFS,const type maxSample) { return (type)(maxSample*pow(10.0,dBFS/20.0)); }
dBFS_to_amp(const double dBFS)48 static inline const mix_sample_t dBFS_to_amp(const double dBFS) { return (mix_sample_t)(MAX_SAMPLE*pow(10.0,dBFS/20.0)); }
49 
amp_to_dBFS(const type amp,const type maxSample)50 template<class type> static inline const double amp_to_dBFS(const type amp,const type maxSample) { return 20.0*log10(fabs((double)amp/maxSample)); }
amp_to_dBFS(const mix_sample_t amp)51 static inline const double amp_to_dBFS(const mix_sample_t amp) { return 20.0*log10(fabs((double)amp/MAX_SAMPLE)); }
52 
53 
54 // rate
55 
56 
57 // times
ms_to_samples(const sample_fpos_t ms,const unsigned sampleRate)58 static inline const sample_pos_t ms_to_samples(const sample_fpos_t ms,const unsigned sampleRate) { return (sample_pos_t)sample_fpos_floor(((sample_fpos_t)sampleRate*ms/1000.0)+0.5); }
s_to_samples(const sample_fpos_t s,const unsigned sampleRate)59 static inline const sample_pos_t s_to_samples(const sample_fpos_t s,const unsigned sampleRate) { return (sample_pos_t)sample_fpos_floor(((sample_fpos_t)sampleRate*s)+0.5); }
samples_to_ms(const sample_pos_t samples,const unsigned sampleRate)60 static inline const sample_fpos_t samples_to_ms(const sample_pos_t samples,const unsigned sampleRate) { return (sample_fpos_t)samples/(sample_fpos_t)sampleRate*1000.0; }
samples_to_s(const sample_pos_t samples,const unsigned sampleRate)61 static inline const sample_fpos_t samples_to_s(const sample_pos_t samples,const unsigned sampleRate) { return (sample_fpos_t)samples/(sample_fpos_t)sampleRate; }
s_to_samples_offset(const float s,const unsigned sampleRate)62 static inline const int s_to_samples_offset(const float s,const unsigned sampleRate) { return (int)floor(((float)sampleRate*s)+0.5); }
63 
64 // sTime is in seconds
65 static inline const string seconds_to_string(const sample_fpos_t sTime,int secondsDecimalPlaces=0,bool includeUnits=false)
66 {
67 	string time;
68 
69 	if(sTime>=3600)
70 	{ // make it HH:MM:SS.sss
71 		const int hours=(int)(sTime/3600);
72 		const int mins=(int)((sTime-(hours*3600))/60);
73 		const double secs=sTime-((hours*3600)+(mins*60));
74 
75 		time=istring(hours,2,true)+":"+istring(mins,2,true)+":"+istring(secs,(secondsDecimalPlaces>0 ? 3+secondsDecimalPlaces : 2),secondsDecimalPlaces,true);
76 	}
77 	else
78 	{ // make it MM:SS.sss
79 		int mins=(int)(sTime/60);
80 		double secs=sTime-(mins*60);
81 
82 		/*
83 		 * if it's going to render (because of rounding in istring) as 3:60.000
84 		 * then make that 4:00.000 which would happen if the seconds had come out
85 		 * to 59.995 or more so that's (60 - .005) which is (60 - 5/(10^deciplaces))
86 		 * (this probably needs to be done slimiarly in the HH:MM:SS.sss case too)
87 		 */
88 		if(secs >= 60.0-(5.0/pow(10.0,secondsDecimalPlaces)))
89 		{
90 			mins++;
91 			secs=0;
92 		}
93 
94 		time=istring(mins,2,true)+":"+istring(secs,(secondsDecimalPlaces>0 ? 3+secondsDecimalPlaces : 2),secondsDecimalPlaces,true);
95 	}
96 
97 	if(includeUnits)
98 		return(time+"s");
99 	else
100 		return(time);
101 }
102 
103 
104 // angles
degrees_to_radians(const double degrees)105 static inline const double degrees_to_radians(const double degrees) { return degrees*(2.0*M_PI)/360.0; }
radians_to_degrees(const double radians)106 static inline const double radians_to_degrees(const double radians) { return radians*360.0/(2.0*M_PI); }
107 
108 
109 // frequency
110 static inline const double freq_to_fraction(const double frequency,const unsigned sampleRate,bool throwOnError=false) { if(throwOnError && (frequency<0.0 || frequency>sampleRate/2)) {throw runtime_error(string(__func__)+" -- frequency out of range, "+istring(frequency)+", for sample rate of, "+istring(sampleRate)); } return max(0.0,min(0.5,frequency/(double)sampleRate)); }
111 static inline const double fraction_to_freq(const double fraction,const unsigned sampleRate,bool throwOnError=false) { if(throwOnError && (fraction<0.0 || fraction>0.5)) { throw runtime_error(string(__func__)+" -- fraction out of range, "+istring(fraction)); } return max(0.0,min(sampleRate/2.0,fraction*(double)sampleRate)); }
112 
113 /* no longer used because I wanted to have evenly spaced octaves rather than going precisely from 0 to SR/2
114 	// given x [0,1] this returns a frequency in Hz except curved for human interface use on the frontend
115 static inline const double unitRange_to_curvedFreq(const double x,const unsigned sampleRate) { return pow(x,3.0)*(sampleRate/2.0); }
116 static inline const double curvedFreq_to_unitRange(const double freq,const unsigned sampleRate) { return pow(2.0*freq/sampleRate,1.0/3.0); }
117 */
118 
119 	// maps a given (real) octave number [0,number of octaves] to a frequency in Hz where the octave 0 returns the given baseFrequency
octave_to_freq(const double octave,const double baseFrequency)120 static inline const double octave_to_freq(const double octave,const double baseFrequency) { return baseFrequency*pow(2.0,octave); }
freq_to_octave(const double freq,const double baseFrequency)121 static inline const double freq_to_octave(const double freq,const double baseFrequency) { return log(freq/baseFrequency)/log(2.0); }
122 
123 
124 // range conversions -- generally for user interface convenience
125 
126 // [0,1] -> [0,1] patterned after f(x)=x^2
unitRange_to_unitRange_squared(const double x)127 static inline const double unitRange_to_unitRange_squared(const double x) { return  pow(x,2.0); }
unitRange_to_unitRange_unsquared(const double x)128 static inline const double unitRange_to_unitRange_unsquared(const double x) { return  sqrt(x); } // inverse of previous
129 
130 	// NOTE: this is a little different than ..._squared ??? I'm not sure if it's working right it seems to be recip-sym
131 // [0,1] -> [0,1] with more accuracy near 0.5 patterned after f(x)=x^3
unitRange_to_unitRange_cubed(const double x)132 static inline const double unitRange_to_unitRange_cubed(const double x) { return (pow(2.0*x-1.0,3.0)+1.0)/2.0; }
unitRange_to_unitRange_uncubed(const double x)133 static inline const double unitRange_to_unitRange_uncubed(const double x) { return (cbrt(2.0*x-1.0)+1.0)/2.0; } // inverse of previous
134 
135 // [0,1] -> [1/a,a] with exponential behavior .. recipsym means reciprocally symetric as the range of the function is reciprocally symetric around 1
unitRange_to_recipsymRange_exp(const double x,const double a)136 static inline const double unitRange_to_recipsymRange_exp(const double x,const double a) { return pow(a,2.0*x-1.0); }
recipsymRange_to_unitRange_exp(const double x,const double a)137 static inline const double recipsymRange_to_unitRange_exp(const double x,const double a) { return 0.5*log(x)/log(a)+0.5; } // inverse of previous
138 
139 // [0,1] -> [a,b] with linear behavior
unitRange_to_otherRange_linear(const double x,const double a,const double b)140 static inline const double unitRange_to_otherRange_linear(const double x,const double a,const double b) { return a+((b-a)*x); }
otherRange_to_unitRange_linear(const double x,const double a,const double b)141 static inline const double otherRange_to_unitRange_linear(const double x,const double a,const double b) { return (x-a)/(b-a); } // inverse of previous
142 
143 #endif
144