'libs/rubberband' - Compiler specific includes
[ardour.git] / libs / rubberband / src / main.cpp
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
3 /*
4     Rubber Band
5     An audio time-stretching and pitch-shifting library.
6     Copyright 2007-2008 Chris Cannam.
7     
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.  See the file
12     COPYING included with this distribution for more information.
13 */
14
15 #include "RubberBandStretcher.h"
16
17 #include <cstring>
18 #include <iostream>
19 #include <sndfile.h>
20 #include <cmath>
21 #include <time.h>
22 #include <cstdlib>
23 #include <cstring>
24 #include "sysutils.h"
25
26 #ifdef COMPILER_MSVC
27 #include "bsd-3rdparty/getopt/getopt.h"
28 #else
29 #include <getopt.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #endif
33
34 #include "Profiler.h"
35
36 using namespace std;
37 using namespace RubberBand;
38
39 #ifdef _WIN32
40 using RubberBand::gettimeofday;
41 using RubberBand::usleep;
42 #endif
43
44 double tempo_convert(const char *str)
45 {
46     const char *d = strchr(str, ':');
47
48     if (!d || !*d) {
49         double m = atof(str);
50         if (m != 0.0) return 1.0 / m;
51         else return 1.0;
52     }
53
54     char *a = strdup(str);
55     char *b = strdup(d+1);
56     a[d-str] = '\0';
57     double m = atof(a);
58     double n = atof(b);
59     free(a);
60     free(b);
61     if (n != 0.0 && m != 0.0) return m / n;
62     else return 1.0;
63 }
64
65 int main(int argc, char **argv)
66 {
67     int c;
68
69     double ratio = 1.0;
70     double duration = 0.0;
71     double pitchshift = 0.0;
72     double frequencyshift = 1.0;
73     int debug = 0;
74     bool realtime = false;
75     bool precise = false;
76     int threading = 0;
77     bool lamination = true;
78     bool longwin = false;
79     bool shortwin = false;
80     bool hqpitch = false;
81     bool formant = false;
82     bool crispchanged = false;
83     int crispness = -1;
84     bool help = false;
85     bool version = false;
86     bool quiet = false;
87
88     bool haveRatio = false;
89
90     enum {
91         NoTransients,
92         BandLimitedTransients,
93         Transients
94     } transients = Transients;
95
96     while (1) {
97         int optionIndex = 0;
98
99         static struct option longOpts[] = {
100             { "help",          0, 0, 'h' },
101             { "version",       0, 0, 'V' },
102             { "time",          1, 0, 't' },
103             { "tempo",         1, 0, 'T' },
104             { "duration",      1, 0, 'D' },
105             { "pitch",         1, 0, 'p' },
106             { "frequency",     1, 0, 'f' },
107             { "crisp",         1, 0, 'c' },
108             { "crispness",     1, 0, 'c' },
109             { "debug",         1, 0, 'd' },
110             { "realtime",      0, 0, 'R' },
111             { "precise",       0, 0, 'P' },
112             { "formant",       0, 0, 'F' },
113             { "no-threads",    0, 0, '0' },
114             { "no-transients", 0, 0, '1' },
115             { "no-lamination", 0, 0, '2' },
116             { "window-long",   0, 0, '3' },
117             { "window-short",  0, 0, '4' },
118             { "bl-transients", 0, 0, '8' },
119             { "pitch-hq",      0, 0, '%' },
120             { "threads",       0, 0, '@' },
121             { "quiet",         0, 0, 'q' },
122             { 0, 0, 0, '\0' }
123         };
124
125         c = getopt_long(argc, argv, "t:p:d:RPFc:f:T:D:qhV", longOpts, &optionIndex);
126         if (c == -1) break;
127
128         switch (c) {
129         case 'h': help = true; break;
130         case 'V': version = true; break;
131         case 't': ratio *= atof(optarg); haveRatio = true; break;
132         case 'T': ratio *= tempo_convert(optarg); haveRatio = true; break;
133         case 'D': duration = atof(optarg); haveRatio = true; break;
134         case 'p': pitchshift = atof(optarg); haveRatio = true; break;
135         case 'f': frequencyshift = atof(optarg); haveRatio = true; break;
136         case 'd': debug = atoi(optarg); break;
137         case 'R': realtime = true; break;
138         case 'P': precise = true; break;
139         case 'F': formant = true; break;
140         case '0': threading = 1; break;
141         case '@': threading = 2; break;
142         case '1': transients = NoTransients; crispchanged = true; break;
143         case '2': lamination = false; crispchanged = true; break;
144         case '3': longwin = true; crispchanged = true; break;
145         case '4': shortwin = true; crispchanged = true; break;
146         case '8': transients = BandLimitedTransients; crispchanged = true; break;
147         case '%': hqpitch = true; break;
148         case 'c': crispness = atoi(optarg); break;
149         case 'q': quiet = true; break;
150         default:  help = true; break;
151         }
152     }
153
154     if (version) {
155         cerr << RUBBERBAND_VERSION << endl;
156         return 0;
157     }
158
159     if (help || !haveRatio || optind + 2 != argc) {
160         cerr << endl;
161         cerr << "Rubber Band" << endl;
162         cerr << "An audio time-stretching and pitch-shifting library and utility program." << endl;
163         cerr << "Copyright 2008 Chris Cannam.  Distributed under the GNU General Public License." << endl;
164         cerr << endl;
165         cerr << "   Usage: " << argv[0] << " [options] <infile.wav> <outfile.wav>" << endl;
166         cerr << endl;
167         cerr << "You must specify at least one of the following time and pitch ratio options." << endl;
168         cerr << endl;
169         cerr << "  -t<X>, --time <X>       Stretch to X times original duration, or" << endl;
170         cerr << "  -T<X>, --tempo <X>      Change tempo by multiple X (same as --time 1/X), or" << endl;
171         cerr << "  -T<X>, --tempo <X>:<Y>  Change tempo from X to Y (same as --time X/Y), or" << endl;
172         cerr << "  -D<X>, --duration <X>   Stretch or squash to make output file X seconds long" << endl;
173         cerr << endl;
174         cerr << "  -p<X>, --pitch <X>      Raise pitch by X semitones, or" << endl;
175         cerr << "  -f<X>, --frequency <X>  Change frequency by multiple X" << endl;
176         cerr << endl;
177         cerr << "The following options provide a simple way to adjust the sound.  See below" << endl;
178         cerr << "for more details." << endl;
179         cerr << endl;
180         cerr << "  -c<N>, --crisp <N>      Crispness (N = 0,1,2,3,4,5); default 4 (see below)" << endl;
181         cerr << "  -F,    --formant        Enable formant preservation when pitch shifting" << endl;
182         cerr << endl;
183         cerr << "The remaining options fine-tune the processing mode and stretch algorithm." << endl;
184         cerr << "These are mostly included for test purposes; the default settings and standard" << endl;
185         cerr << "crispness parameter are intended to provide the best sounding set of options" << endl;
186         cerr << "for most situations.  The default is to use none of these options." << endl;
187         cerr << endl;
188         cerr << "  -P,    --precise        Aim for minimal time distortion (implied by -R)" << endl;
189         cerr << "  -R,    --realtime       Select realtime mode (implies -P --no-threads)" << endl;
190         cerr << "         --no-threads     No extra threads regardless of CPU and channel count" << endl;
191         cerr << "         --threads        Assume multi-CPU even if only one CPU is identified" << endl;
192         cerr << "         --no-transients  Disable phase resynchronisation at transients" << endl;
193         cerr << "         --bl-transients  Band-limit phase resync to extreme frequencies" << endl;
194         cerr << "         --no-lamination  Disable phase lamination" << endl;
195         cerr << "         --window-long    Use longer processing window (actual size may vary)" << endl;
196         cerr << "         --window-short   Use shorter processing window" << endl;
197         cerr << "         --pitch-hq       In RT mode, use a slower, higher quality pitch shift" << endl;
198         cerr << endl;
199         cerr << "  -d<N>, --debug <N>      Select debug level (N = 0,1,2,3); default 0, full 3" << endl;
200         cerr << "                          (N.B. debug level 3 includes audible ticks in output)" << endl;
201         cerr << "  -q,    --quiet          Suppress progress output" << endl;
202         cerr << endl;
203         cerr << "  -V,    --version        Show version number and exit" << endl;
204         cerr << "  -h,    --help           Show this help" << endl;
205         cerr << endl;
206         cerr << "\"Crispness\" levels:" << endl;
207         cerr << "  -c 0   equivalent to --no-transients --no-lamination --window-long" << endl;
208         cerr << "  -c 1   equivalent to --no-transients --no-lamination" << endl;
209         cerr << "  -c 2   equivalent to --no-transients" << endl;
210         cerr << "  -c 3   equivalent to --bl-transients" << endl;
211         cerr << "  -c 4   default processing options" << endl;
212         cerr << "  -c 5   equivalent to --no-lamination --window-short (may be good for drums)" << endl;
213         cerr << endl;
214         return 2;
215     }
216
217     if (crispness >= 0 && crispchanged) {
218         cerr << "WARNING: Both crispness option and transients, lamination or window options" << endl;
219         cerr << "         provided -- crispness will override these other options" << endl;
220     }
221
222     switch (crispness) {
223     case -1: crispness = 4; break;
224     case 0: transients = NoTransients; lamination = false; longwin = true; shortwin = false; break;
225     case 1: transients = NoTransients; lamination = false; longwin = false; shortwin = false; break;
226     case 2: transients = NoTransients; lamination = true; longwin = false; shortwin = false; break;
227     case 3: transients = BandLimitedTransients; lamination = true; longwin = false; shortwin = false; break;
228     case 4: transients = Transients; lamination = true; longwin = false; shortwin = false; break;
229     case 5: transients = Transients; lamination = false; longwin = false; shortwin = true; break;
230     };
231
232     if (!quiet) {
233         cerr << "Using crispness level: " << crispness << " (";
234         switch (crispness) {
235         case 0: cerr << "Mushy"; break;
236         case 1: cerr << "Smooth"; break;
237         case 2: cerr << "Balanced multitimbral mixture"; break;
238         case 3: cerr << "Unpitched percussion with stable notes"; break;
239         case 4: cerr << "Crisp monophonic instrumental"; break;
240         case 5: cerr << "Unpitched solo percussion"; break;
241         }
242         cerr << ")" << endl;
243     }
244
245     char *fileName = strdup(argv[optind++]);
246     char *fileNameOut = strdup(argv[optind++]);
247
248     SNDFILE *sndfile;
249     SNDFILE *sndfileOut;
250     SF_INFO sfinfo;
251     SF_INFO sfinfoOut;
252     memset(&sfinfo, 0, sizeof(SF_INFO));
253
254     sndfile = sf_open(fileName, SFM_READ, &sfinfo);
255     if (!sndfile) {
256         cerr << "ERROR: Failed to open input file \"" << fileName << "\": "
257              << sf_strerror(sndfile) << endl;
258         return 1;
259     }
260
261     if (duration != 0.0) {
262         if (sfinfo.frames == 0 || sfinfo.samplerate == 0) {
263             cerr << "ERROR: File lacks frame count or sample rate in header, cannot use --duration" << endl;
264             return 1;
265         }
266         double induration = double(sfinfo.frames) / double(sfinfo.samplerate);
267         if (induration != 0.0) ratio = duration / induration;
268     }
269
270     sfinfoOut.channels = sfinfo.channels;
271     sfinfoOut.format = sfinfo.format;
272     sfinfoOut.frames = int(sfinfo.frames * ratio + 0.1);
273     sfinfoOut.samplerate = sfinfo.samplerate;
274     sfinfoOut.sections = sfinfo.sections;
275     sfinfoOut.seekable = sfinfo.seekable;
276
277     sndfileOut = sf_open(fileNameOut, SFM_WRITE, &sfinfoOut) ;
278     if (!sndfileOut) {
279         cerr << "ERROR: Failed to open output file \"" << fileNameOut << "\" for writing: "
280              << sf_strerror(sndfileOut) << endl;
281         return 1;
282     }
283     
284     int ibs = 1024;
285     size_t channels = sfinfo.channels;
286
287     RubberBandStretcher::Options options = 0;
288     if (realtime)    options |= RubberBandStretcher::OptionProcessRealTime;
289     if (precise)     options |= RubberBandStretcher::OptionStretchPrecise;
290     if (!lamination) options |= RubberBandStretcher::OptionPhaseIndependent;
291     if (longwin)     options |= RubberBandStretcher::OptionWindowLong;
292     if (shortwin)    options |= RubberBandStretcher::OptionWindowShort;
293     if (formant)     options |= RubberBandStretcher::OptionFormantPreserved;
294     if (hqpitch)     options |= RubberBandStretcher::OptionPitchHighQuality;
295
296     switch (threading) {
297     case 0:
298         options |= RubberBandStretcher::OptionThreadingAuto;
299         break;
300     case 1:
301         options |= RubberBandStretcher::OptionThreadingNever;
302         break;
303     case 2:
304         options |= RubberBandStretcher::OptionThreadingAlways;
305         break;
306     }
307
308     switch (transients) {
309     case NoTransients:
310         options |= RubberBandStretcher::OptionTransientsSmooth;
311         break;
312     case BandLimitedTransients:
313         options |= RubberBandStretcher::OptionTransientsMixed;
314         break;
315     case Transients:
316         options |= RubberBandStretcher::OptionTransientsCrisp;
317         break;
318     }
319
320     if (pitchshift != 0.0) {
321         frequencyshift *= pow(2.0, pitchshift / 12);
322     }
323
324     cerr << "Using time ratio " << ratio;
325     cerr << " and frequency ratio " << frequencyshift << endl;
326
327 #ifdef _WIN32
328     RubberBand::
329 #endif
330     timeval tv;
331     (void)gettimeofday(&tv, 0);
332
333     RubberBandStretcher::setDefaultDebugLevel(debug);
334
335     RubberBandStretcher ts(sfinfo.samplerate, channels, options,
336                            ratio, frequencyshift);
337
338     ts.setExpectedInputDuration(sfinfo.frames);
339
340     float *fbuf = new float[channels * ibs];
341     float **ibuf = new float *[channels];
342     for (size_t i = 0; i < channels; ++i) ibuf[i] = new float[ibs];
343
344     int frame = 0;
345     int percent = 0;
346
347     sf_seek(sndfile, 0, SEEK_SET);
348
349     if (!realtime) {
350
351         if (!quiet) {
352             cerr << "Pass 1: Studying..." << endl;
353         }
354
355         while (frame < sfinfo.frames) {
356
357             int count = -1;
358
359             if ((count = sf_readf_float(sndfile, fbuf, ibs)) <= 0) break;
360         
361             for (size_t c = 0; c < channels; ++c) {
362                 for (int i = 0; i < count; ++i) {
363                     float value = fbuf[i * channels + c];
364                     ibuf[c][i] = value;
365                 }
366             }
367
368             bool final = (frame + ibs >= sfinfo.frames);
369
370             ts.study(ibuf, count, final);
371
372             int p = int((double(frame) * 100.0) / sfinfo.frames);
373             if (p > percent || frame == 0) {
374                 percent = p;
375                 if (!quiet) {
376                     cerr << "\r" << percent << "% ";
377                 }
378             }
379
380             frame += ibs;
381         }
382
383         if (!quiet) {
384             cerr << "\rCalculating profile..." << endl;
385         }
386
387         sf_seek(sndfile, 0, SEEK_SET);
388     }
389
390     frame = 0;
391     percent = 0;
392     
393     size_t countIn = 0, countOut = 0;
394
395     while (frame < sfinfo.frames) {
396
397         int count = -1;
398
399         if ((count = sf_readf_float(sndfile, fbuf, ibs)) < 0) break;
400         
401         countIn += count;
402
403         for (size_t c = 0; c < channels; ++c) {
404             for (int i = 0; i < count; ++i) {
405                 float value = fbuf[i * channels + c];
406                 ibuf[c][i] = value;
407             }
408         }
409
410         bool final = (frame + ibs >= sfinfo.frames);
411
412         ts.process(ibuf, count, final);
413
414         int avail = ts.available();
415         if (debug > 1) cerr << "available = " << avail << endl;
416
417         if (avail > 0) {
418             float **obf = new float *[channels];
419             for (size_t i = 0; i < channels; ++i) {
420                 obf[i] = new float[avail];
421             }
422             ts.retrieve(obf, avail);
423             countOut += avail;
424             float *fobf = new float[channels * avail];
425             for (size_t c = 0; c < channels; ++c) {
426                 for (int i = 0; i < avail; ++i) {
427                     float value = obf[c][i];
428                     if (value > 1.f) value = 1.f;
429                     if (value < -1.f) value = -1.f;
430                     fobf[i * channels + c] = value;
431                 }
432             }
433 //            cout << "fobf mean: ";
434 //    double d = 0;
435 //    for (int i = 0; i < avail; ++i) {
436 //        d += fobf[i];
437 //    }
438 //    d /= avail;
439 //    cout << d << endl;
440             sf_writef_float(sndfileOut, fobf, avail);
441             delete[] fobf;
442             for (size_t i = 0; i < channels; ++i) {
443                 delete[] obf[i];
444             }
445             delete[] obf;
446         }
447
448         if (frame == 0 && !realtime && !quiet) {
449             cerr << "Pass 2: Processing..." << endl;
450         }
451
452         int p = int((double(frame) * 100.0) / sfinfo.frames);
453         if (p > percent || frame == 0) {
454             percent = p;
455             if (!quiet) {
456                 cerr << "\r" << percent << "% ";
457             }
458         }
459
460         frame += ibs;
461     }
462
463     if (!quiet) {
464         cerr << "\r    " << endl;
465     }
466     int avail;
467
468     while ((avail = ts.available()) >= 0) {
469
470         if (debug > 1) {
471             cerr << "(completing) available = " << avail << endl;
472         }
473
474         if (avail > 0) {
475             float **obf = new float *[channels];
476             for (size_t i = 0; i < channels; ++i) {
477                 obf[i] = new float[avail];
478             }
479             ts.retrieve(obf, avail);
480             countOut += avail;
481             float *fobf = new float[channels * avail];
482             for (size_t c = 0; c < channels; ++c) {
483                 for (int i = 0; i < avail; ++i) {
484                     float value = obf[c][i];
485                     if (value > 1.f) value = 1.f;
486                     if (value < -1.f) value = -1.f;
487                     fobf[i * channels + c] = value;
488                 }
489             }
490
491             sf_writef_float(sndfileOut, fobf, avail);
492             delete[] fobf;
493             for (size_t i = 0; i < channels; ++i) {
494                 delete[] obf[i];
495             }
496             delete[] obf;
497         } else {
498 #ifdef WIN32
499             RubberBand::usleep(10000);
500 #else
501             usleep(10000);
502 #endif
503         }
504     }
505
506     sf_close(sndfile);
507     sf_close(sndfileOut);
508
509     if (!quiet) {
510
511         cerr << "in: " << countIn << ", out: " << countOut << ", ratio: " << float(countOut)/float(countIn) << ", ideal output: " << lrint(countIn * ratio) << ", error: " << abs(lrint(countIn * ratio) - int(countOut)) << endl;
512
513 #ifdef _WIN32
514         RubberBand::
515 #endif
516         timeval etv;
517         (void)gettimeofday(&etv, 0);
518         
519         etv.tv_sec -= tv.tv_sec;
520         if (etv.tv_usec < tv.tv_usec) {
521             etv.tv_usec += 1000000;
522             etv.tv_sec -= 1;
523         }
524         etv.tv_usec -= tv.tv_usec;
525         
526         double sec = double(etv.tv_sec) + (double(etv.tv_usec) / 1000000.0);
527         cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl;
528     }
529
530     Profiler::dump();
531
532     return 0;
533 }
534
535