~ubuntu-branches/ubuntu/oneiric/espeak/oneiric

« back to all changes in this revision

Viewing changes to src/sonic.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich
  • Date: 2011-05-04 11:25:46 UTC
  • mfrom: (1.1.24 upstream) (5.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20110504112546-ykijzihgc7ybgzn2
Tags: 1.45.04-1ubuntu1
* Merge from debian unstable, remaining changes:
  - Add gbp.conf for use with git buildpackage
  - Update the explanation of the -b command-line flag in the espeak manpage

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Sonic library
 
2
   Copyright 2010
 
3
   Bill Cox
 
4
   This file is part of the Sonic Library.
 
5
 
 
6
   The Sonic Library is free software; you can redistribute it and/or
 
7
   modify it under the terms of the GNU Lesser General Public
 
8
   License as published by the Free Software Foundation; either
 
9
   version 2.1 of the License, or (at your option) any later version.
 
10
 
 
11
   The GNU C Library is distributed in the hope that it will be useful,
 
12
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
   Lesser General Public License for more details.
 
15
 
 
16
   You should have received a copy of the GNU Lesser General Public
 
17
   License along with the GNU C Library; if not, write to the Free
 
18
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 
19
   02111-1307 USA.  */
 
20
 
 
21
#include "StdAfx.h"
 
22
 
 
23
#include <stdio.h>
 
24
#include <stdlib.h>
 
25
#include <string.h>
 
26
#include <stdarg.h>
 
27
#include "sonic.h"
 
28
 
 
29
struct sonicStreamStruct {
 
30
    short *inputBuffer;
 
31
    short *outputBuffer;
 
32
    short *pitchBuffer;
 
33
    short *downSampleBuffer;
 
34
    float speed;
 
35
    float volume;
 
36
    float pitch;
 
37
    int numChannels;
 
38
    int inputBufferSize;
 
39
    int pitchBufferSize;
 
40
    int outputBufferSize;
 
41
    int numInputSamples;
 
42
    int numOutputSamples;
 
43
    int numPitchSamples;
 
44
    int minPeriod;
 
45
    int maxPeriod;
 
46
    int maxRequired;
 
47
    int remainingInputToCopy;
 
48
    int sampleRate;
 
49
    int prevPeriod;
 
50
    int prevMaxDiff;
 
51
    int prevMinDiff;
 
52
};
 
53
 
 
54
/* Just used for debugging */
 
55
void sonicMSG(char *format, ...)
 
56
{
 
57
    char buffer[4096];
 
58
    va_list ap;
 
59
    FILE *file;
 
60
 
 
61
    va_start(ap, format);
 
62
    vsprintf((char *)buffer, (char *)format, ap);
 
63
    va_end(ap);
 
64
    file=fopen("/tmp/sonic.log", "a");
 
65
    fprintf(file, "%s", buffer);
 
66
    fclose(file);
 
67
}
 
68
 
 
69
/* Scale the samples by the factor. */
 
70
static void scaleSamples(
 
71
    short *samples,
 
72
    int numSamples,
 
73
    float volume)
 
74
{
 
75
    int fixedPointVolume = volume*4096.0f;
 
76
    int value;
 
77
 
 
78
    while(numSamples--) {
 
79
        value = (*samples*fixedPointVolume) >> 12;
 
80
        if(value > 32767) {
 
81
            value = 32767;
 
82
        } else if(value < -32767) {
 
83
            value = -32767;
 
84
        }
 
85
        *samples++ = value;
 
86
    }
 
87
}
 
88
 
 
89
/* Get the speed of the stream. */
 
90
float sonicGetSpeed(
 
91
    sonicStream stream)
 
92
{
 
93
    return stream->speed;
 
94
}
 
95
 
 
96
/* Set the speed of the stream. */
 
97
void sonicSetSpeed(
 
98
    sonicStream stream,
 
99
    float speed)
 
100
{
 
101
    stream->speed = speed;
 
102
}
 
103
 
 
104
/* Get the pitch of the stream. */
 
105
float sonicGetPitch(
 
106
    sonicStream stream)
 
107
{
 
108
    return stream->pitch;
 
109
}
 
110
 
 
111
/* Set the pitch of the stream. */
 
112
void sonicSetPitch(
 
113
    sonicStream stream,
 
114
    float pitch)
 
115
{
 
116
    stream->pitch = pitch;
 
117
}
 
118
 
 
119
/* Get the scaling factor of the stream. */
 
120
float sonicGetVolume(
 
121
    sonicStream stream)
 
122
{
 
123
    return stream->volume;
 
124
}
 
125
 
 
126
/* Set the scaling factor of the stream. */
 
127
void sonicSetVolume(
 
128
    sonicStream stream,
 
129
    float volume)
 
130
{
 
131
    stream->volume = volume;
 
132
}
 
133
 
 
134
/* Get the sample rate of the stream. */
 
135
int sonicGetSampleRate(
 
136
    sonicStream stream)
 
137
{
 
138
    return stream->sampleRate;
 
139
}
 
140
 
 
141
/* Get the number of channels. */
 
142
int sonicGetNumChannels(
 
143
    sonicStream stream)
 
144
{
 
145
    return stream->numChannels;
 
146
}
 
147
 
 
148
/* Destroy the sonic stream. */
 
149
void sonicDestroyStream(
 
150
    sonicStream stream)
 
151
{
 
152
    if(stream->inputBuffer != NULL) {
 
153
        free(stream->inputBuffer);
 
154
    }
 
155
    if(stream->outputBuffer != NULL) {
 
156
        free(stream->outputBuffer);
 
157
    }
 
158
    if(stream->pitchBuffer != NULL) {
 
159
        free(stream->pitchBuffer);
 
160
    }
 
161
    if(stream->downSampleBuffer != NULL) {
 
162
        free(stream->downSampleBuffer);
 
163
    }
 
164
    free(stream);
 
165
}
 
166
 
 
167
/* Create a sonic stream.  Return NULL only if we are out of memory and cannot
 
168
   allocate the stream. */
 
169
sonicStream sonicCreateStream(
 
170
    int sampleRate,
 
171
    int numChannels)
 
172
{
 
173
    sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
 
174
    int minPeriod = sampleRate/SONIC_MAX_PITCH;
 
175
    int maxPeriod = sampleRate/SONIC_MIN_PITCH;
 
176
    int maxRequired = 2*maxPeriod; 
 
177
 
 
178
    if(stream == NULL) {
 
179
        return NULL;
 
180
    }
 
181
    stream->inputBufferSize = maxRequired;
 
182
    stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
 
183
    if(stream->inputBuffer == NULL) {
 
184
        sonicDestroyStream(stream);
 
185
        return NULL;
 
186
    }
 
187
    stream->outputBufferSize = maxRequired;
 
188
    stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
 
189
    if(stream->outputBuffer == NULL) {
 
190
        sonicDestroyStream(stream);
 
191
        return NULL;
 
192
    }
 
193
    stream->pitchBufferSize = maxRequired;
 
194
    stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
 
195
    if(stream->pitchBuffer == NULL) {
 
196
        sonicDestroyStream(stream);
 
197
        return NULL;
 
198
    }
 
199
    stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
 
200
    stream->speed = 1.0f;
 
201
    stream->pitch = 1.0f;
 
202
    stream->volume = 1.0f;
 
203
    stream->sampleRate = sampleRate;
 
204
    stream->numChannels = numChannels;
 
205
    stream->minPeriod = minPeriod;
 
206
    stream->maxPeriod = maxPeriod;
 
207
    stream->maxRequired = maxRequired;
 
208
    return stream;
 
209
}
 
210
 
 
211
/* Enlarge the output buffer if needed. */
 
212
static int enlargeOutputBufferIfNeeded(
 
213
    sonicStream stream,
 
214
    int numSamples)
 
215
{
 
216
    if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
 
217
        stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
 
218
        stream->outputBuffer = (short *)realloc(stream->outputBuffer,
 
219
            stream->outputBufferSize*sizeof(short)*stream->numChannels);
 
220
        if(stream->outputBuffer == NULL) {
 
221
            return 0;
 
222
        }
 
223
    }
 
224
    return 1;
 
225
}
 
226
 
 
227
/* Enlarge the input buffer if needed. */
 
228
static int enlargeInputBufferIfNeeded(
 
229
    sonicStream stream,
 
230
    int numSamples)
 
231
{
 
232
    if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
 
233
        stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
 
234
        stream->inputBuffer = (short *)realloc(stream->inputBuffer,
 
235
            stream->inputBufferSize*sizeof(short)*stream->numChannels);
 
236
        if(stream->inputBuffer == NULL) {
 
237
            return 0;
 
238
        }
 
239
    }
 
240
    return 1;
 
241
}
 
242
 
 
243
/* Add the input samples to the input buffer. */
 
244
static int addFloatSamplesToInputBuffer(
 
245
    sonicStream stream,
 
246
    float *samples,
 
247
    int numSamples)
 
248
{
 
249
    short *buffer;
 
250
    int count = numSamples*stream->numChannels;
 
251
 
 
252
    if(numSamples == 0) {
 
253
        return 1;
 
254
    }
 
255
    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
 
256
        return 0;
 
257
    }
 
258
    buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
 
259
    while(count--) {
 
260
        *buffer++ = (*samples++)*32767.0f;
 
261
    }
 
262
    stream->numInputSamples += numSamples;
 
263
    return 1;
 
264
}
 
265
 
 
266
/* Add the input samples to the input buffer. */
 
267
static int addShortSamplesToInputBuffer(
 
268
    sonicStream stream,
 
269
    short *samples,
 
270
    int numSamples)
 
271
{
 
272
    if(numSamples == 0) {
 
273
        return 1;
 
274
    }
 
275
    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
 
276
        return 0;
 
277
    }
 
278
    memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
 
279
        numSamples*sizeof(short)*stream->numChannels);
 
280
    stream->numInputSamples += numSamples;
 
281
    return 1;
 
282
}
 
283
 
 
284
/* Add the input samples to the input buffer. */
 
285
static int addUnsignedCharSamplesToInputBuffer(
 
286
    sonicStream stream,
 
287
    unsigned char *samples,
 
288
    int numSamples)
 
289
{
 
290
    short *buffer;
 
291
    int count = numSamples*stream->numChannels;
 
292
 
 
293
    if(numSamples == 0) {
 
294
        return 1;
 
295
    }
 
296
    if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
 
297
        return 0;
 
298
    }
 
299
    buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
 
300
    while(count--) {
 
301
        *buffer++ = (*samples++ - 128) << 8;
 
302
    }
 
303
    stream->numInputSamples += numSamples;
 
304
    return 1;
 
305
}
 
306
 
 
307
/* Remove input samples that we have already processed. */
 
308
static void removeInputSamples(
 
309
    sonicStream stream,
 
310
    int position)
 
311
{
 
312
    int remainingSamples = stream->numInputSamples - position;
 
313
 
 
314
    if(remainingSamples > 0) {
 
315
        memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
 
316
            remainingSamples*sizeof(short)*stream->numChannels);
 
317
    }
 
318
    stream->numInputSamples = remainingSamples;
 
319
}
 
320
 
 
321
/* Just copy from the array to the output buffer */
 
322
static int copyToOutput(
 
323
    sonicStream stream,
 
324
    short *samples,
 
325
    int numSamples)
 
326
{
 
327
    if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
 
328
        return 0;
 
329
    }
 
330
    memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
 
331
        samples, numSamples*sizeof(short)*stream->numChannels);
 
332
    stream->numOutputSamples += numSamples;
 
333
    return numSamples;
 
334
}
 
335
 
 
336
/* Just copy from the input buffer to the output buffer.  Return 0 if we fail to
 
337
   resize the output buffer.  Otherwise, return numSamples */
 
338
static int copyInputToOutput(
 
339
    sonicStream stream,
 
340
    int position)
 
341
{
 
342
    int numSamples = stream->remainingInputToCopy;
 
343
 
 
344
    if(numSamples > stream->maxRequired) {
 
345
        numSamples = stream->maxRequired;
 
346
    }
 
347
    if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
 
348
            numSamples)) {
 
349
        return 0;
 
350
    }
 
351
    stream->remainingInputToCopy -= numSamples;
 
352
    return numSamples;
 
353
}
 
354
 
 
355
/* Read data out of the stream.  Sometimes no data will be available, and zero
 
356
   is returned, which is not an error condition. */
 
357
int sonicReadFloatFromStream(
 
358
    sonicStream stream,
 
359
    float *samples,
 
360
    int maxSamples)
 
361
{
 
362
    int numSamples = stream->numOutputSamples;
 
363
    int remainingSamples = 0;
 
364
    short *buffer;
 
365
    int count;
 
366
 
 
367
    if(numSamples == 0) {
 
368
        return 0;
 
369
    }
 
370
    if(numSamples > maxSamples) {
 
371
        remainingSamples = numSamples - maxSamples;
 
372
        numSamples = maxSamples;
 
373
    }
 
374
    buffer = stream->outputBuffer;
 
375
    count = numSamples*stream->numChannels;
 
376
    while(count--) {
 
377
        *samples++ = (*buffer++)/32767.0f;
 
378
    }
 
379
    if(remainingSamples > 0) {
 
380
        memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
 
381
            remainingSamples*sizeof(short)*stream->numChannels);
 
382
    }
 
383
    stream->numOutputSamples = remainingSamples;
 
384
    return numSamples;
 
385
}
 
386
 
 
387
/* Read short data out of the stream.  Sometimes no data will be available, and zero
 
388
   is returned, which is not an error condition. */
 
389
int sonicReadShortFromStream(
 
390
    sonicStream stream,
 
391
    short *samples,
 
392
    int maxSamples)
 
393
{
 
394
    int numSamples = stream->numOutputSamples;
 
395
    int remainingSamples = 0;
 
396
 
 
397
    if(numSamples == 0) {
 
398
        return 0;
 
399
    }
 
400
    if(numSamples > maxSamples) {
 
401
        remainingSamples = numSamples - maxSamples;
 
402
        numSamples = maxSamples;
 
403
    }
 
404
    memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
 
405
    if(remainingSamples > 0) {
 
406
        memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
 
407
            remainingSamples*sizeof(short)*stream->numChannels);
 
408
    }
 
409
    stream->numOutputSamples = remainingSamples;
 
410
    return numSamples;
 
411
}
 
412
 
 
413
/* Read unsigned char data out of the stream.  Sometimes no data will be available, and zero
 
414
   is returned, which is not an error condition. */
 
415
int sonicReadUnsignedCharFromStream(
 
416
    sonicStream stream,
 
417
    unsigned char *samples,
 
418
    int maxSamples)
 
419
{
 
420
    int numSamples = stream->numOutputSamples;
 
421
    int remainingSamples = 0;
 
422
    short *buffer;
 
423
    int count;
 
424
 
 
425
    if(numSamples == 0) {
 
426
        return 0;
 
427
    }
 
428
    if(numSamples > maxSamples) {
 
429
        remainingSamples = numSamples - maxSamples;
 
430
        numSamples = maxSamples;
 
431
    }
 
432
    buffer = stream->outputBuffer;
 
433
    count = numSamples*stream->numChannels;
 
434
    while(count--) {
 
435
        *samples++ = (char)((*buffer++) >> 8) + 128;
 
436
    }
 
437
    if(remainingSamples > 0) {
 
438
        memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
 
439
            remainingSamples*sizeof(short)*stream->numChannels);
 
440
    }
 
441
    stream->numOutputSamples = remainingSamples;
 
442
    return numSamples;
 
443
}
 
444
 
 
445
/* Force the sonic stream to generate output using whatever data it currently
 
446
   has.  No extra delay will be added to the output, but flushing in the middle of
 
447
   words could introduce distortion. */
 
448
int sonicFlushStream(
 
449
    sonicStream stream)
 
450
{
 
451
    int maxRequired = stream->maxRequired;
 
452
    int numSamples = stream->numInputSamples;
 
453
    int remainingSpace, numOutputSamples, expectedSamples;
 
454
 
 
455
    if(numSamples == 0) {
 
456
        return 1;
 
457
    }
 
458
    if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
 
459
        return 0;
 
460
    }
 
461
    numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
 
462
    if(numSamples == 0) {
 
463
        return 1;
 
464
    }
 
465
    remainingSpace = maxRequired - numSamples;
 
466
    memset(stream->inputBuffer + numSamples*stream->numChannels, 0,
 
467
        remainingSpace*sizeof(short)*stream->numChannels);
 
468
    stream->numInputSamples = maxRequired;
 
469
    numOutputSamples = stream->numOutputSamples;
 
470
    if(!sonicWriteShortToStream(stream, NULL, 0)) {
 
471
        return 0;
 
472
    }
 
473
    /* Throw away any extra samples we generated due to the silence we added */
 
474
    expectedSamples = (int)(numSamples*stream->speed + 0.5);
 
475
    if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
 
476
        stream->numOutputSamples = numOutputSamples + expectedSamples;
 
477
    }
 
478
    return 1;
 
479
}
 
480
 
 
481
/* Return the number of samples in the output buffer */
 
482
int sonicSamplesAvailable(
 
483
   sonicStream stream)
 
484
{
 
485
    return stream->numOutputSamples;
 
486
}
 
487
 
 
488
/* If skip is greater than one, average skip samples togther and write them to
 
489
   the down-sample buffer.  If numChannels is greater than one, mix the channels
 
490
   together as we down sample. */
 
491
static void downSampleInput(
 
492
    sonicStream stream,
 
493
    short *samples,
 
494
    int skip)
 
495
{
 
496
    int numSamples = stream->maxRequired/skip;
 
497
    int samplesPerValue = stream->numChannels*skip;
 
498
    int i, j;
 
499
    int value;
 
500
    short *downSamples = stream->downSampleBuffer;
 
501
 
 
502
    for(i = 0; i < numSamples; i++) {
 
503
        value = 0;
 
504
        for(j = 0; j < samplesPerValue; j++) {
 
505
            value += *samples++;
 
506
        }
 
507
        value /= samplesPerValue;
 
508
        *downSamples++ = value;
 
509
    }
 
510
}
 
511
 
 
512
/* Find the best frequency match in the range, and given a sample skip multiple.
 
513
   For now, just find the pitch of the first channel.  */
 
514
static int findPitchPeriodInRange(
 
515
    short *samples,
 
516
    int minPeriod,
 
517
    int maxPeriod,
 
518
    int *retMinDiff,
 
519
    int *retMaxDiff)
 
520
{
 
521
    int period, bestPeriod = 0;
 
522
    short *s, *p, sVal, pVal;
 
523
    unsigned long diff, minDiff = 1, maxDiff = 0;
 
524
    int i;
 
525
 
 
526
    for(period = minPeriod; period <= maxPeriod; period++) {
 
527
        diff = 0;
 
528
        s = samples;
 
529
        p = samples + period;
 
530
        for(i = 0; i < period; i++) {
 
531
            sVal = *s++;
 
532
            pVal = *p++;
 
533
            diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
 
534
                (unsigned short)(pVal - sVal);
 
535
        }
 
536
        /* Note that the highest number of samples we add into diff will be less
 
537
           than 256, since we skip samples.  Thus, diff is a 24 bit number, and
 
538
           we can safely multiply by numSamples without overflow */
 
539
        if(diff*bestPeriod < minDiff*period) {
 
540
            minDiff = diff;
 
541
            bestPeriod = period;
 
542
        }
 
543
        if(diff*bestPeriod > maxDiff*period) {
 
544
            maxDiff = diff;
 
545
        }
 
546
    }
 
547
    *retMinDiff = minDiff;
 
548
    *retMaxDiff = maxDiff;
 
549
    return bestPeriod;
 
550
}
 
551
 
 
552
/* At abrupt ends of voiced words, we can have pitch periods that are better
 
553
   aproximated by the previous pitch period estimate.  Try to detect this case.  */
 
554
static int prevPeriodBetter(
 
555
    sonicStream stream,
 
556
    int period,
 
557
    int minDiff,
 
558
    int maxDiff)
 
559
{
 
560
    if(maxDiff*3/2 < stream->prevMaxDiff && (maxDiff*3.0f)*stream->prevMinDiff < 
 
561
            (float)stream->prevMaxDiff*minDiff*2) {
 
562
        return 1;
 
563
    }
 
564
    return 0;
 
565
}
 
566
 
 
567
/* Find the pitch period.  This is a critical step, and we may have to try
 
568
   multiple ways to get a good answer.  This version uses AMDF.  To improve
 
569
   speed, we down sample by an integer factor get in the 11KHz range, and then
 
570
   do it again with a narrower frequency range without down sampling */
 
571
static int findPitchPeriod(
 
572
    sonicStream stream,
 
573
    short *samples)
 
574
{
 
575
    int minPeriod = stream->minPeriod;
 
576
    int maxPeriod = stream->maxPeriod;
 
577
    int sampleRate = stream->sampleRate;
 
578
    int minDiff, maxDiff, retPeriod;
 
579
    int skip = 1;
 
580
    int period;
 
581
 
 
582
    if(sampleRate > SONIC_AMDF_FREQ) {
 
583
        skip = sampleRate/SONIC_AMDF_FREQ;
 
584
    }
 
585
    if(stream->numChannels == 1 && skip == 1) {
 
586
        period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
 
587
    } else {
 
588
        downSampleInput(stream, samples, skip);
 
589
        period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
 
590
            maxPeriod/skip, &minDiff, &maxDiff);
 
591
        if(skip != 1) {
 
592
            period *= skip;
 
593
            minPeriod = period - (skip << 2);
 
594
            maxPeriod = period + (skip << 2);
 
595
            if(minPeriod < stream->minPeriod) {
 
596
                minPeriod = stream->minPeriod;
 
597
            }
 
598
            if(maxPeriod > stream->maxPeriod) {
 
599
                maxPeriod = stream->maxPeriod;
 
600
            }
 
601
            if(stream->numChannels == 1) {
 
602
                period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
 
603
                    &minDiff, &maxDiff);
 
604
            } else {
 
605
                downSampleInput(stream, samples, 1);
 
606
                period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
 
607
                    maxPeriod, &minDiff, &maxDiff);
 
608
            }
 
609
        }
 
610
    }
 
611
    if(prevPeriodBetter(stream, period, minDiff, maxDiff)) {
 
612
        retPeriod = stream->prevPeriod;
 
613
    } else {
 
614
        retPeriod = period;
 
615
    }
 
616
    stream->prevMinDiff = minDiff;
 
617
    stream->prevMaxDiff = maxDiff;
 
618
    stream->prevPeriod = period;
 
619
    return retPeriod;
 
620
}
 
621
 
 
622
/* Overlap two sound segments, ramp the volume of one down, while ramping the
 
623
   other one from zero up, and add them, storing the result at the output. */
 
624
static void overlapAdd(
 
625
    int numSamples,
 
626
    int numChannels,
 
627
    short *out,
 
628
    short *rampDown,
 
629
    short *rampUp)
 
630
{
 
631
    short *o, *u, *d;
 
632
    int i, t;
 
633
 
 
634
    for(i = 0; i < numChannels; i++) {
 
635
        o = out + i;
 
636
        u = rampUp + i;
 
637
        d = rampDown + i;
 
638
        for(t = 0; t < numSamples; t++) {
 
639
            *o = (*d*(numSamples - t) + *u*t)/numSamples;
 
640
            o += numChannels;
 
641
            d += numChannels;
 
642
            u += numChannels;
 
643
        }
 
644
    }
 
645
}
 
646
 
 
647
/* Overlap two sound segments, ramp the volume of one down, while ramping the
 
648
   other one from zero up, and add them, storing the result at the output. */
 
649
static void overlapAddWithSeparation(
 
650
    int numSamples,
 
651
    int numChannels,
 
652
    int separation,
 
653
    short *out,
 
654
    short *rampDown,
 
655
    short *rampUp)
 
656
{
 
657
    short *o, *u, *d;
 
658
    int i, t;
 
659
 
 
660
    for(i = 0; i < numChannels; i++) {
 
661
        o = out + i;
 
662
        u = rampUp + i;
 
663
        d = rampDown + i;
 
664
        for(t = 0; t < numSamples + separation; t++) {
 
665
            if(t < separation) {
 
666
                *o = *d*(numSamples - t)/numSamples;
 
667
                d += numChannels;
 
668
            } else if(t < numSamples) {
 
669
                *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
 
670
                d += numChannels;
 
671
                u += numChannels;
 
672
            } else {
 
673
                *o = *u*(t - separation)/numSamples;
 
674
                u += numChannels;
 
675
            }
 
676
            o += numChannels;
 
677
        }
 
678
    }
 
679
}
 
680
 
 
681
/* Just move the new samples in the output buffer to the pitch bufer */
 
682
static int moveNewSamplesToPitchBuffer(
 
683
    sonicStream stream,
 
684
    int originalNumOutputSamples)
 
685
{
 
686
    int numSamples = stream->numOutputSamples - originalNumOutputSamples;
 
687
    int numChannels = stream->numChannels;
 
688
 
 
689
    if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
 
690
        stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
 
691
        stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
 
692
            stream->pitchBufferSize*sizeof(short)*numChannels);
 
693
        if(stream->pitchBuffer == NULL) {
 
694
            return 0;
 
695
        }
 
696
    }
 
697
    memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
 
698
        stream->outputBuffer + originalNumOutputSamples*numChannels,
 
699
        numSamples*sizeof(short)*numChannels);
 
700
    stream->numOutputSamples = originalNumOutputSamples;
 
701
    stream->numPitchSamples += numSamples;
 
702
    return 1;
 
703
}
 
704
 
 
705
/* Remove processed samples from the pitch buffer. */
 
706
static void removePitchSamples(
 
707
    sonicStream stream,
 
708
    int numSamples)
 
709
{
 
710
    int numChannels = stream->numChannels;
 
711
    short *source = stream->pitchBuffer + numSamples*numChannels;
 
712
 
 
713
    if(numSamples == 0) {
 
714
        return;
 
715
    }
 
716
    if(numSamples != stream->numPitchSamples) {
 
717
        memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
 
718
            numSamples)*sizeof(short)*numChannels);
 
719
    }
 
720
    stream->numPitchSamples -= numSamples;
 
721
}
 
722
 
 
723
/* Change the pitch.  The latency this introduces could be reduced by looking at
 
724
   past samples to determine pitch, rather than future. */
 
725
static int adjustPitch(
 
726
    sonicStream stream,
 
727
    int originalNumOutputSamples)
 
728
{
 
729
    float pitch = stream->pitch;
 
730
    int numChannels = stream->numChannels;
 
731
    int period, newPeriod, separation;
 
732
    int position = 0;
 
733
    short *out, *rampDown, *rampUp;
 
734
 
 
735
    if(stream->numOutputSamples == originalNumOutputSamples) {
 
736
        return 1;
 
737
    }
 
738
    if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
 
739
        return 0;
 
740
    }
 
741
    while(stream->numPitchSamples - position >= stream->maxRequired) {
 
742
        period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels);
 
743
        newPeriod = period/pitch;
 
744
        if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
 
745
            return 0;
 
746
        }
 
747
        out = stream->outputBuffer + stream->numOutputSamples*numChannels;
 
748
        if(pitch >= 1.0f) {
 
749
            rampDown = stream->pitchBuffer + position*numChannels;
 
750
            rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
 
751
            overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
 
752
        } else {
 
753
            rampDown = stream->pitchBuffer + position*numChannels;
 
754
            rampUp = stream->pitchBuffer + position*numChannels;
 
755
            separation = newPeriod - period;
 
756
            overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
 
757
        }
 
758
        stream->numOutputSamples += newPeriod;
 
759
        position += period;
 
760
    }
 
761
    removePitchSamples(stream, position);
 
762
    return 1;
 
763
}
 
764
 
 
765
/* Skip over a pitch period, and copy period/speed samples to the output */
 
766
static int skipPitchPeriod(
 
767
    sonicStream stream,
 
768
    short *samples,
 
769
    float speed,
 
770
    int period)
 
771
{
 
772
    long newSamples;
 
773
    int numChannels = stream->numChannels;
 
774
 
 
775
    if(speed >= 2.0f) {
 
776
        newSamples = period/(speed - 1.0f);
 
777
    } else if(speed > 1.0f) {
 
778
        newSamples = period;
 
779
        stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
 
780
    }
 
781
    if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
 
782
        return 0;
 
783
    }
 
784
    overlapAdd(newSamples, numChannels, stream->outputBuffer +
 
785
        stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
 
786
    stream->numOutputSamples += newSamples;
 
787
    return newSamples;
 
788
}
 
789
 
 
790
/* Insert a pitch period, and determine how much input to copy directly. */
 
791
static int insertPitchPeriod(
 
792
    sonicStream stream,
 
793
    short *samples,
 
794
    float speed,
 
795
    int period)
 
796
{
 
797
    long newSamples;
 
798
    short *out;
 
799
    int numChannels = stream->numChannels;
 
800
 
 
801
    if(speed < 0.5f) {
 
802
        newSamples = period*speed/(1.0f - speed);
 
803
    } else {
 
804
        newSamples = period;
 
805
        stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
 
806
    }
 
807
    if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
 
808
        return 0;
 
809
    }
 
810
    out = stream->outputBuffer + stream->numOutputSamples*numChannels;
 
811
    memcpy(out, samples, period*sizeof(short)*numChannels);
 
812
    out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
 
813
    overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
 
814
    stream->numOutputSamples += period + newSamples;
 
815
    return newSamples;
 
816
}
 
817
 
 
818
/* Resample as many pitch periods as we have buffered on the input.  Return 0 if
 
819
   we fail to resize an input or output buffer.  Also scale the output by the volume. */
 
820
static int changeSpeed(
 
821
    sonicStream stream,
 
822
    float speed)
 
823
{
 
824
    short *samples;
 
825
    int numSamples = stream->numInputSamples;
 
826
    int position = 0, period, newSamples;
 
827
    int maxRequired = stream->maxRequired;
 
828
 
 
829
    if(stream->numInputSamples < maxRequired) {
 
830
        return 1;
 
831
    }
 
832
    do {
 
833
        if(stream->remainingInputToCopy > 0) {
 
834
            newSamples = copyInputToOutput(stream, position);
 
835
            position += newSamples;
 
836
        } else {
 
837
            samples = stream->inputBuffer + position*stream->numChannels;
 
838
            period = findPitchPeriod(stream, samples);
 
839
            if(speed > 1.0) {
 
840
                newSamples = skipPitchPeriod(stream, samples, speed, period);
 
841
                position += period + newSamples;
 
842
            } else {
 
843
                newSamples = insertPitchPeriod(stream, samples, speed, period);
 
844
                position += newSamples;
 
845
            }
 
846
        }
 
847
        if(newSamples == 0) {
 
848
            return 0; /* Failed to resize output buffer */
 
849
        }
 
850
    } while(position + maxRequired <= numSamples);
 
851
    removeInputSamples(stream, position);
 
852
    return 1;
 
853
}
 
854
 
 
855
/* Resample as many pitch periods as we have buffered on the input.  Return 0 if
 
856
   we fail to resize an input or output buffer.  Also scale the output by the volume. */
 
857
static int processStreamInput(
 
858
    sonicStream stream)
 
859
{
 
860
    int originalNumOutputSamples = stream->numOutputSamples;
 
861
    float speed = stream->speed/stream->pitch;
 
862
 
 
863
    if(speed > 1.00001 || speed < 0.99999) {
 
864
        changeSpeed(stream, speed);
 
865
    } else {
 
866
        if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
 
867
            return 0;
 
868
        }
 
869
        stream->numInputSamples = 0;
 
870
    }
 
871
    if(stream->pitch != 1.0f) {
 
872
        if(!adjustPitch(stream, originalNumOutputSamples)) {
 
873
            return 0;
 
874
        }
 
875
    }
 
876
    if(stream->volume != 1.0f) {
 
877
        /* Adjust output volume. */
 
878
        scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
 
879
            (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
 
880
            stream->volume);
 
881
    }
 
882
    return 1;
 
883
}
 
884
 
 
885
/* Write floating point data to the input buffer and process it. */
 
886
int sonicWriteFloatToStream(
 
887
    sonicStream stream,
 
888
    float *samples,
 
889
    int numSamples)
 
890
{
 
891
    if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
 
892
        return 0;
 
893
    }
 
894
    return processStreamInput(stream);
 
895
}
 
896
 
 
897
/* Simple wrapper around sonicWriteFloatToStream that does the short to float
 
898
   conversion for you. */
 
899
int sonicWriteShortToStream(
 
900
    sonicStream stream,
 
901
    short *samples,
 
902
    int numSamples)
 
903
{
 
904
    if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
 
905
        return 0;
 
906
    }
 
907
    return processStreamInput(stream);
 
908
}
 
909
 
 
910
/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
 
911
   conversion for you. */
 
912
int sonicWriteUnsignedCharToStream(
 
913
    sonicStream stream,
 
914
    unsigned char *samples,
 
915
    int numSamples)
 
916
{
 
917
    if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
 
918
        return 0;
 
919
    }
 
920
    return processStreamInput(stream);
 
921
}
 
922
 
 
923
/* This is a non-stream oriented interface to just change the speed of a sound sample */
 
924
int sonicChangeFloatSpeed(
 
925
    float *samples,
 
926
    int numSamples,
 
927
    float speed,
 
928
    float pitch,
 
929
    float volume,
 
930
    int sampleRate,
 
931
    int numChannels)
 
932
{
 
933
    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
 
934
 
 
935
    sonicSetSpeed(stream, speed);
 
936
    sonicSetPitch(stream, pitch);
 
937
    sonicSetVolume(stream, volume);
 
938
    sonicWriteFloatToStream(stream, samples, numSamples);
 
939
    sonicFlushStream(stream);
 
940
    numSamples = sonicSamplesAvailable(stream);
 
941
    sonicReadFloatFromStream(stream, samples, numSamples);
 
942
    sonicDestroyStream(stream);
 
943
    return numSamples;
 
944
}
 
945
 
 
946
/* This is a non-stream oriented interface to just change the speed of a sound sample */
 
947
int sonicChangeShortSpeed(
 
948
    short *samples,
 
949
    int numSamples,
 
950
    float speed,
 
951
    float pitch,
 
952
    float volume,
 
953
    int sampleRate,
 
954
    int numChannels)
 
955
{
 
956
    sonicStream stream = sonicCreateStream(sampleRate, numChannels);
 
957
 
 
958
    sonicSetSpeed(stream, speed);
 
959
    sonicSetPitch(stream, pitch);
 
960
    sonicSetVolume(stream, volume);
 
961
    sonicWriteShortToStream(stream, samples, numSamples);
 
962
    sonicFlushStream(stream);
 
963
    numSamples = sonicSamplesAvailable(stream);
 
964
    sonicReadShortFromStream(stream, samples, numSamples);
 
965
    sonicDestroyStream(stream);
 
966
    return numSamples;
 
967
}