File: main.cpp

package info (click to toggle)
soundtouch 2.4.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 920 kB
  • sloc: cpp: 5,251; pascal: 453; makefile: 91; ansic: 64; sh: 50
file content (321 lines) | stat: -rw-r--r-- 10,150 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
////////////////////////////////////////////////////////////////////////////////
///
/// SoundStretch main routine.
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <cstdio>
#include <ctime>
#include "RunParameters.h"
#include "WavFile.h"
#include "SoundTouch.h"
#include "BPMDetect.h"

using namespace soundtouch;
using namespace std;

namespace soundstretch
{

// Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...)
#define BUFF_SIZE           6720

#if _WIN32
#include <io.h>
#include <fcntl.h>

// Macro for Win32 standard input/output stream support: Sets a file stream into binary mode
#define SET_STREAM_TO_BIN_MODE(f) (_setmode(_fileno(f), _O_BINARY))
#else
    // Not needed for GNU environment...
#define SET_STREAM_TO_BIN_MODE(f) {}
#endif


static const char _helloText[] =
    "\n"
    "   SoundStretch v%s -  Copyright (c) Olli Parviainen\n"
    "=========================================================\n"
    "author e-mail: <oparviai"
    "@"
    "iki.fi> - WWW: http://www.surina.net/soundtouch\n"
    "\n"
    "This program is subject to (L)GPL license. Run \"soundstretch -license\" for\n"
    "more information.\n"
    "\n";

static void openFiles(unique_ptr<WavInFile>& inFile, unique_ptr<WavOutFile>& outFile, const RunParameters& params)
{
    if (params.inFileName == STRING_CONST("stdin"))
    {
        // used 'stdin' as input file
        SET_STREAM_TO_BIN_MODE(stdin);
        inFile = make_unique<WavInFile>(stdin);
    }
    else
    {
        // open input file...
        inFile = make_unique<WavInFile>(params.inFileName.c_str());
    }

    // ... open output file with same sound parameters
    const int bits = (int)inFile->getNumBits();
    const int samplerate = (int)inFile->getSampleRate();
    const int channels = (int)inFile->getNumChannels();

    if (!params.outFileName.empty())
    {
        if (params.outFileName == STRING_CONST("stdout"))
        {
            SET_STREAM_TO_BIN_MODE(stdout);
            outFile = make_unique<WavOutFile>(stdout, samplerate, bits, channels);
        }
        else
        {
            outFile = make_unique<WavOutFile>(params.outFileName.c_str(), samplerate, bits, channels);
        }
    }
}


// Sets the 'SoundTouch' object up according to input file sound format &
// command line parameters
static void setup(SoundTouch& soundTouch, const WavInFile& inFile, const RunParameters& params)
{
    const int sampleRate = (int)inFile.getSampleRate();
    const int channels = (int)inFile.getNumChannels();
    soundTouch.setSampleRate(sampleRate);
    soundTouch.setChannels(channels);

    soundTouch.setTempoChange(params.tempoDelta);
    soundTouch.setPitchSemiTones(params.pitchDelta);
    soundTouch.setRateChange(params.rateDelta);

    soundTouch.setSetting(SETTING_USE_QUICKSEEK, params.quick);
    soundTouch.setSetting(SETTING_USE_AA_FILTER, !(params.noAntiAlias));

    if (params.speech)
    {
        // use settings for speech processing
        soundTouch.setSetting(SETTING_SEQUENCE_MS, 40);
        soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15);
        soundTouch.setSetting(SETTING_OVERLAP_MS, 8);
        fprintf(stderr, "Tune processing parameters for speech processing.\n");
    }

    // print processing information
    if (!params.outFileName.empty())
    {
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
        fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
#else
#ifndef SOUNDTOUCH_FLOAT_SAMPLES
#error "Sampletype not defined"
#endif
        fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
#endif
        // print processing information only if outFileName given i.e. some processing will happen
        fprintf(stderr, "Processing the file with the following changes:\n");
        fprintf(stderr, "  tempo change = %+lg %%\n", params.tempoDelta);
        fprintf(stderr, "  pitch change = %+lg semitones\n", params.pitchDelta);
        fprintf(stderr, "  rate change  = %+lg %%\n\n", params.rateDelta);
        fprintf(stderr, "Working...");
    }
    else
    {
        // outFileName not given
        fprintf(stderr, "Warning: output file name missing, won't output anything.\n\n");
    }

    fflush(stderr);
}


// Processes the sound
static void process(SoundTouch& soundTouch, WavInFile& inFile, WavOutFile& outFile)
{
    SAMPLETYPE sampleBuffer[BUFF_SIZE];
    int nSamples;

    const int nChannels = (int)inFile.getNumChannels();
    assert(nChannels > 0);
    const int buffSizeSamples = BUFF_SIZE / nChannels;

    // Process samples read from the input file
    while (inFile.eof() == 0)
    {
        // Read a chunk of samples from the input file
        const int num = inFile.read(sampleBuffer, BUFF_SIZE);
        int nSamples = num / (int)inFile.getNumChannels();

        // Feed the samples into SoundTouch processor
        soundTouch.putSamples(sampleBuffer, nSamples);

        // Read ready samples from SoundTouch processor & write them output file.
        // NOTES:
        // - 'receiveSamples' doesn't necessarily return any samples at all
        //   during some rounds!
        // - On the other hand, during some round 'receiveSamples' may have more
        //   ready samples than would fit into 'sampleBuffer', and for this reason
        //   the 'receiveSamples' call is iterated for as many times as it
        //   outputs samples.
        do
        {
            nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
            outFile.write(sampleBuffer, nSamples * nChannels);
        } while (nSamples != 0);
    }

    // Now the input file is processed, yet 'flush' few last samples that are
    // hiding in the SoundTouch's internal processing pipeline.
    soundTouch.flush();
    do
    {
        nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
        outFile.write(sampleBuffer, nSamples * nChannels);
    } while (nSamples != 0);
}


// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
static void detectBPM(WavInFile& inFile, RunParameters& params)
{
    BPMDetect bpm(inFile.getNumChannels(), inFile.getSampleRate());
    SAMPLETYPE sampleBuffer[BUFF_SIZE];

    // detect bpm rate
    fprintf(stderr, "Detecting BPM rate...");
    fflush(stderr);

    const int nChannels = (int)inFile.getNumChannels();
    int readSize = BUFF_SIZE - BUFF_SIZE % nChannels;   // round read size down to multiple of num.channels

    // Process the 'inFile' in small blocks, repeat until whole file has
    // been processed
    while (inFile.eof() == 0)
    {
        // Read sample data from input file
        const int num = inFile.read(sampleBuffer, readSize);

        // Enter the new samples to the bpm analyzer class
        const int samples = num / nChannels;
        bpm.inputSamples(sampleBuffer, samples);
    }

    // Now the whole song data has been analyzed. Read the resulting bpm.
    const float bpmValue = bpm.getBpm();
    fprintf(stderr, "Done!\n");

    // rewind the file after bpm detection
    inFile.rewind();

    if (bpmValue > 0)
    {
        fprintf(stderr, "Detected BPM rate %.1lf\n\n", bpmValue);
    }
    else
    {
        fprintf(stderr, "Couldn't detect BPM rate.\n\n");
        return;
    }

    if (params.goalBPM > 0)
    {
        // adjust tempo to given bpm
        params.tempoDelta = (params.goalBPM / bpmValue - 1.0f) * 100.0f;
        fprintf(stderr, "The file will be converted to %.1lf BPM\n\n", params.goalBPM);
    }
}

void printHelloText()
{
    SoundTouch soundTouch;
    fprintf(stderr, _helloText, soundTouch.getVersionString());
}

void ss_main(RunParameters& params)
{
    unique_ptr<WavInFile> inFile;
    unique_ptr<WavOutFile> outFile;
    SoundTouch soundTouch;

    // Open input & output files
    openFiles(inFile, outFile, params);

    if (params.detectBPM == true)
    {
        // detect sound BPM (and adjust processing parameters
        //  accordingly if necessary)
        detectBPM(*inFile, params);
    }

    // Setup the 'SoundTouch' object for processing the sound
    setup(soundTouch, *inFile, params);

    // clock_t cs = clock();    // for benchmarking processing duration
    // Process the sound
    if (inFile && outFile)
    {
        process(soundTouch, *inFile, *outFile);
    }
    // clock_t ce = clock();    // for benchmarking processing duration
    // printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC);

    fprintf(stderr, "Done!\n");
}

}

#if _WIN32
int wmain(int argc, const wchar_t* args[])
#else
int main(int argc, const char* args[])
#endif
{
    try
    {
        soundstretch::printHelloText();
        soundstretch::RunParameters params(argc, args);
        soundstretch::ss_main(params);
    }
    catch (const runtime_error& e)
    {
        fprintf(stderr, "%s\n", e.what());
        return -1;
    }
    catch (const string& e)
    {
        fprintf(stderr, "%s\n", e.c_str());
        return -1;
    }
    return 0;
}