1 ////////////////////////////////////////////////////////////////////////////////
2 ///
3 /// A class for parsing the 'soundstretch' application command line parameters
4 ///
5 /// Author        : Copyright (c) Olli Parviainen
6 /// Author e-mail : oparviai 'at' iki.fi
7 /// SoundTouch WWW: http://www.surina.net/soundtouch
8 ///
9 ////////////////////////////////////////////////////////////////////////////////
10 //
11 // Last changed  : $Date: 2011-09-02 21:56:11 +0300 (Fri, 02 Sep 2011) $
12 // File revision : $Revision: 4 $
13 //
14 // $Id: RunParameters.cpp 131 2011-09-02 18:56:11Z oparviai $
15 //
16 ////////////////////////////////////////////////////////////////////////////////
17 //
18 // License :
19 //
20 //  SoundTouch audio processing library
21 //  Copyright (c) Olli Parviainen
22 //
23 //  This library is free software; you can redistribute it and/or
24 //  modify it under the terms of the GNU Lesser General Public
25 //  License as published by the Free Software Foundation; either
26 //  version 2.1 of the License, or (at your option) any later version.
27 //
28 //  This library is distributed in the hope that it will be useful,
29 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
30 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
31 //  Lesser General Public License for more details.
32 //
33 //  You should have received a copy of the GNU Lesser General Public
34 //  License along with this library; if not, write to the Free Software
35 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
36 //
37 ////////////////////////////////////////////////////////////////////////////////
38 
39 #include <string>
40 #include <stdlib.h>
41 
42 #include "RunParameters.h"
43 
44 using namespace std;
45 
46 // Program usage instructions
47 
48 static const char licenseText[] =
49     "    LICENSE:\n"
50     "    ========\n"
51     "    \n"
52     "    SoundTouch sound processing library\n"
53     "    Copyright (c) Olli Parviainen\n"
54     "    \n"
55     "    This library is free software; you can redistribute it and/or\n"
56     "    modify it under the terms of the GNU Lesser General Public\n"
57     "    License version 2.1 as published by the Free Software Foundation.\n"
58     "    \n"
59     "    This library is distributed in the hope that it will be useful,\n"
60     "    but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
61     "    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
62     "    Lesser General Public License for more details.\n"
63     "    \n"
64     "    You should have received a copy of the GNU Lesser General Public\n"
65     "    License along with this library; if not, write to the Free Software\n"
66     "    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n"
67     "    \n"
68     "This application is distributed with full source codes; however, if you\n"
69     "didn't receive them, please visit the author's homepage (see the link above).";
70 
71 static const char whatText[] =
72     "This application processes WAV audio files by modifying the sound tempo,\n"
73     "pitch and playback rate properties independently from each other.\n"
74     "\n";
75 
76 static const char usage[] =
77     "Usage :\n"
78     "    soundstretch infilename outfilename [switches]\n"
79     "\n"
80     "To use standard input/output pipes, give 'stdin' and 'stdout' as filenames.\n"
81     "\n"
82     "Available switches are:\n"
83     "  -tempo=n : Change sound tempo by n percents  (n=-95..+5000 %)\n"
84     "  -pitch=n : Change sound pitch by n semitones (n=-60..+60 semitones)\n"
85     "  -rate=n  : Change sound rate by n percents   (n=-95..+5000 %)\n"
86     "  -bpm=n   : Detect the BPM rate of sound and adjust tempo to meet 'n' BPMs.\n"
87     "             If '=n' is omitted, just detects the BPM rate.\n"
88     "  -quick   : Use quicker tempo change algorithm (gain speed, lose quality)\n"
89     "  -naa     : Don't use anti-alias filtering (gain speed, lose quality)\n"
90     "  -speech  : Tune algorithm for speech processing (default is for music)\n"
91     "  -license : Display the program license text (LGPL)\n";
92 
93 
94 // Converts a char into lower case
_toLowerCase(int c)95 static int _toLowerCase(int c)
96 {
97     if (c >= 'A' && c <= 'Z')
98     {
99         c += 'a' - 'A';
100     }
101     return c;
102 }
103 
104 
105 // Constructor
RunParameters(const int nParams,const char * const paramStr[])106 RunParameters::RunParameters(const int nParams, const char * const paramStr[])
107 {
108     int i;
109     int nFirstParam;
110 
111     if (nParams < 3)
112     {
113         // Too few parameters
114         if (nParams > 1 && paramStr[1][0] == '-' &&
115             _toLowerCase(paramStr[1][1]) == 'l')
116         {
117             // '-license' switch
118             throwLicense();
119         }
120         string msg = whatText;
121         msg += usage;
122         ST_THROW_RT_ERROR(msg.c_str());
123     }
124 
125     inFileName = NULL;
126     outFileName = NULL;
127     tempoDelta = 0;
128     pitchDelta = 0;
129     rateDelta = 0;
130     quick = 0;
131     noAntiAlias = 0;
132     goalBPM = 0;
133     speech = FALSE;
134     detectBPM = FALSE;
135 
136     // Get input & output file names
137     inFileName = (char*)paramStr[1];
138     outFileName = (char*)paramStr[2];
139 
140     if (outFileName[0] == '-')
141     {
142         // no outputfile name was given but parameters
143         outFileName = NULL;
144         nFirstParam = 2;
145     }
146     else
147     {
148         nFirstParam = 3;
149     }
150 
151     // parse switch parameters
152     for (i = nFirstParam; i < nParams; i ++)
153     {
154         parseSwitchParam(paramStr[i]);
155     }
156 
157     checkLimits();
158 }
159 
160 
161 
162 // Checks parameter limits
checkLimits()163 void RunParameters::checkLimits()
164 {
165     if (tempoDelta < -95.0f)
166     {
167         tempoDelta = -95.0f;
168     }
169     else if (tempoDelta > 5000.0f)
170     {
171         tempoDelta = 5000.0f;
172     }
173 
174     if (pitchDelta < -60.0f)
175     {
176         pitchDelta = -60.0f;
177     }
178     else if (pitchDelta > 60.0f)
179     {
180         pitchDelta = 60.0f;
181     }
182 
183     if (rateDelta < -95.0f)
184     {
185         rateDelta = -95.0f;
186     }
187     else if (rateDelta > 5000.0f)
188     {
189         rateDelta = 5000.0f;
190     }
191 }
192 
193 
194 
195 // Unknown switch parameter -- throws an exception with an error message
throwIllegalParamExp(const string & str) const196 void RunParameters::throwIllegalParamExp(const string &str) const
197 {
198     string msg = "ERROR : Illegal parameter \"";
199     msg += str;
200     msg += "\".\n\n";
201     msg += usage;
202     ST_THROW_RT_ERROR(msg.c_str());
203 }
204 
205 
206 
throwLicense() const207 void RunParameters::throwLicense() const
208 {
209     ST_THROW_RT_ERROR(licenseText);
210 }
211 
212 
parseSwitchValue(const string & str) const213 float RunParameters::parseSwitchValue(const string &str) const
214 {
215     int pos;
216 
217     pos = (int)str.find_first_of('=');
218     if (pos < 0)
219     {
220         // '=' missing
221         throwIllegalParamExp(str);
222     }
223 
224     // Read numerical parameter value after '='
225     return (float)atof(str.substr(pos + 1).c_str());
226 }
227 
228 
229 // Interprets a single switch parameter string of format "-switch=xx"
230 // Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
231 // switch values into 'params' structure.
parseSwitchParam(const string & str)232 void RunParameters::parseSwitchParam(const string &str)
233 {
234     int upS;
235 
236     if (str[0] != '-')
237     {
238         // leading hyphen missing => not a valid parameter
239         throwIllegalParamExp(str);
240     }
241 
242     // Take the first character of switch name & change to lower case
243     upS = _toLowerCase(str[1]);
244 
245     // interpret the switch name & operate accordingly
246     switch (upS)
247     {
248         case 't' :
249             // switch '-tempo=xx'
250             tempoDelta = parseSwitchValue(str);
251             break;
252 
253         case 'p' :
254             // switch '-pitch=xx'
255             pitchDelta = parseSwitchValue(str);
256             break;
257 
258         case 'r' :
259             // switch '-rate=xx'
260             rateDelta = parseSwitchValue(str);
261             break;
262 
263         case 'b' :
264             // switch '-bpm=xx'
265             detectBPM = TRUE;
266             try
267             {
268                 goalBPM = parseSwitchValue(str);
269             }
270             catch (const runtime_error)
271             {
272                 // illegal or missing bpm value => just calculate bpm
273                 goalBPM = 0;
274             }
275             break;
276 
277         case 'q' :
278             // switch '-quick'
279             quick = 1;
280             break;
281 
282         case 'n' :
283             // switch '-naa'
284             noAntiAlias = 1;
285             break;
286 
287         case 'l' :
288             // switch '-license'
289             throwLicense();
290             break;
291 
292         case 's' :
293             // switch '-speech'
294             speech = TRUE;
295             break;
296 
297         default:
298             // unknown switch
299             throwIllegalParamExp(str);
300     }
301 }
302