summaryrefslogtreecommitdiff
path: root/navit/support/espeak/sonic.c
diff options
context:
space:
mode:
Diffstat (limited to 'navit/support/espeak/sonic.c')
-rw-r--r--navit/support/espeak/sonic.c974
1 files changed, 974 insertions, 0 deletions
diff --git a/navit/support/espeak/sonic.c b/navit/support/espeak/sonic.c
new file mode 100644
index 000000000..0627dee2c
--- /dev/null
+++ b/navit/support/espeak/sonic.c
@@ -0,0 +1,974 @@
+/* Sonic library
+ Copyright 2010
+ Bill Cox
+ This file is part of the Sonic Library.
+
+ The Sonic 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.
+
+ The GNU C 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include "StdAfx.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "speech.h"
+#include "sonic.h"
+#ifdef INCLUDE_SONIC
+
+struct sonicStreamStruct {
+ short *inputBuffer;
+ short *outputBuffer;
+ short *pitchBuffer;
+ short *downSampleBuffer;
+ float speed;
+ float volume;
+ float pitch;
+ int numChannels;
+ int inputBufferSize;
+ int pitchBufferSize;
+ int outputBufferSize;
+ int numInputSamples;
+ int numOutputSamples;
+ int numPitchSamples;
+ int minPeriod;
+ int maxPeriod;
+ int maxRequired;
+ int remainingInputToCopy;
+ int sampleRate;
+ int prevPeriod;
+ int prevMaxDiff;
+ int prevMinDiff;
+};
+
+#if 0
+/* Just used for debugging */
+static void sonicMSG(char *format, ...)
+{
+ char buffer[4096];
+ va_list ap;
+ FILE *file;
+
+ va_start(ap, format);
+ vsprintf((char *)buffer, (char *)format, ap);
+ va_end(ap);
+ file=fopen("/tmp/sonic.log", "a");
+ fprintf(file, "%s", buffer);
+ fclose(file);
+}
+#endif
+
+/* Scale the samples by the factor. */
+static void scaleSamples(
+ short *samples,
+ int numSamples,
+ float volume)
+{
+ int fixedPointVolume = volume*4096.0f;
+ int value;
+
+ while(numSamples--) {
+ value = (*samples*fixedPointVolume) >> 12;
+ if(value > 32767) {
+ value = 32767;
+ } else if(value < -32767) {
+ value = -32767;
+ }
+ *samples++ = value;
+ }
+}
+
+/* Get the speed of the stream. */
+float sonicGetSpeed(
+ sonicStream stream)
+{
+ return stream->speed;
+}
+
+/* Set the speed of the stream. */
+void sonicSetSpeed(
+ sonicStream stream,
+ float speed)
+{
+ stream->speed = speed;
+}
+
+/* Get the pitch of the stream. */
+float sonicGetPitch(
+ sonicStream stream)
+{
+ return stream->pitch;
+}
+
+/* Set the pitch of the stream. */
+void sonicSetPitch(
+ sonicStream stream,
+ float pitch)
+{
+ stream->pitch = pitch;
+}
+
+/* Get the scaling factor of the stream. */
+float sonicGetVolume(
+ sonicStream stream)
+{
+ return stream->volume;
+}
+
+/* Set the scaling factor of the stream. */
+void sonicSetVolume(
+ sonicStream stream,
+ float volume)
+{
+ stream->volume = volume;
+}
+
+/* Get the sample rate of the stream. */
+int sonicGetSampleRate(
+ sonicStream stream)
+{
+ return stream->sampleRate;
+}
+
+/* Get the number of channels. */
+int sonicGetNumChannels(
+ sonicStream stream)
+{
+ return stream->numChannels;
+}
+
+/* Destroy the sonic stream. */
+void sonicDestroyStream(
+ sonicStream stream)
+{
+ if(stream->inputBuffer != NULL) {
+ free(stream->inputBuffer);
+ }
+ if(stream->outputBuffer != NULL) {
+ free(stream->outputBuffer);
+ }
+ if(stream->pitchBuffer != NULL) {
+ free(stream->pitchBuffer);
+ }
+ if(stream->downSampleBuffer != NULL) {
+ free(stream->downSampleBuffer);
+ }
+ free(stream);
+}
+
+/* Create a sonic stream. Return NULL only if we are out of memory and cannot
+ allocate the stream. */
+sonicStream sonicCreateStream(
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
+ int minPeriod = sampleRate/SONIC_MAX_PITCH;
+ int maxPeriod = sampleRate/SONIC_MIN_PITCH;
+ int maxRequired = 2*maxPeriod;
+
+ if(stream == NULL) {
+ return NULL;
+ }
+ stream->inputBufferSize = maxRequired;
+ stream->inputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->inputBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->outputBufferSize = maxRequired;
+ stream->outputBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->outputBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->pitchBufferSize = maxRequired;
+ stream->pitchBuffer = (short *)calloc(maxRequired, sizeof(short)*numChannels);
+ if(stream->pitchBuffer == NULL) {
+ sonicDestroyStream(stream);
+ return NULL;
+ }
+ stream->downSampleBuffer = (short *)calloc(maxRequired, sizeof(short));
+ stream->speed = 1.0f;
+ stream->pitch = 1.0f;
+ stream->volume = 1.0f;
+ stream->sampleRate = sampleRate;
+ stream->numChannels = numChannels;
+ stream->minPeriod = minPeriod;
+ stream->maxPeriod = maxPeriod;
+ stream->maxRequired = maxRequired;
+ return stream;
+}
+
+/* Enlarge the output buffer if needed. */
+static int enlargeOutputBufferIfNeeded(
+ sonicStream stream,
+ int numSamples)
+{
+ if(stream->numOutputSamples + numSamples > stream->outputBufferSize) {
+ stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
+ stream->outputBuffer = (short *)realloc(stream->outputBuffer,
+ stream->outputBufferSize*sizeof(short)*stream->numChannels);
+ if(stream->outputBuffer == NULL) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Enlarge the input buffer if needed. */
+static int enlargeInputBufferIfNeeded(
+ sonicStream stream,
+ int numSamples)
+{
+ if(stream->numInputSamples + numSamples > stream->inputBufferSize) {
+ stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
+ stream->inputBuffer = (short *)realloc(stream->inputBuffer,
+ stream->inputBufferSize*sizeof(short)*stream->numChannels);
+ if(stream->inputBuffer == NULL) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addFloatSamplesToInputBuffer(
+ sonicStream stream,
+ float *samples,
+ int numSamples)
+{
+ short *buffer;
+ int count = numSamples*stream->numChannels;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+ while(count--) {
+ *buffer++ = (*samples++)*32767.0f;
+ }
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addShortSamplesToInputBuffer(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ memcpy(stream->inputBuffer + stream->numInputSamples*stream->numChannels, samples,
+ numSamples*sizeof(short)*stream->numChannels);
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Add the input samples to the input buffer. */
+static int addUnsignedCharSamplesToInputBuffer(
+ sonicStream stream,
+ unsigned char *samples,
+ int numSamples)
+{
+ short *buffer;
+ int count = numSamples*stream->numChannels;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(!enlargeInputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ buffer = stream->inputBuffer + stream->numInputSamples*stream->numChannels;
+ while(count--) {
+ *buffer++ = (*samples++ - 128) << 8;
+ }
+ stream->numInputSamples += numSamples;
+ return 1;
+}
+
+/* Remove input samples that we have already processed. */
+static void removeInputSamples(
+ sonicStream stream,
+ int position)
+{
+ int remainingSamples = stream->numInputSamples - position;
+
+ if(remainingSamples > 0) {
+ memmove(stream->inputBuffer, stream->inputBuffer + position*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numInputSamples = remainingSamples;
+}
+
+/* Just copy from the array to the output buffer */
+static int copyToOutput(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(!enlargeOutputBufferIfNeeded(stream, numSamples)) {
+ return 0;
+ }
+ memcpy(stream->outputBuffer + stream->numOutputSamples*stream->numChannels,
+ samples, numSamples*sizeof(short)*stream->numChannels);
+ stream->numOutputSamples += numSamples;
+ return numSamples;
+}
+
+/* Just copy from the input buffer to the output buffer. Return 0 if we fail to
+ resize the output buffer. Otherwise, return numSamples */
+static int copyInputToOutput(
+ sonicStream stream,
+ int position)
+{
+ int numSamples = stream->remainingInputToCopy;
+
+ if(numSamples > stream->maxRequired) {
+ numSamples = stream->maxRequired;
+ }
+ if(!copyToOutput(stream, stream->inputBuffer + position*stream->numChannels,
+ numSamples)) {
+ return 0;
+ }
+ stream->remainingInputToCopy -= numSamples;
+ return numSamples;
+}
+
+/* Read data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadFloatFromStream(
+ sonicStream stream,
+ float *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+ short *buffer;
+ int count;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ buffer = stream->outputBuffer;
+ count = numSamples*stream->numChannels;
+ while(count--) {
+ *samples++ = (*buffer++)/32767.0f;
+ }
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Read short data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadShortFromStream(
+ sonicStream stream,
+ short *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ memcpy(samples, stream->outputBuffer, numSamples*sizeof(short)*stream->numChannels);
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Read unsigned char data out of the stream. Sometimes no data will be available, and zero
+ is returned, which is not an error condition. */
+int sonicReadUnsignedCharFromStream(
+ sonicStream stream,
+ unsigned char *samples,
+ int maxSamples)
+{
+ int numSamples = stream->numOutputSamples;
+ int remainingSamples = 0;
+ short *buffer;
+ int count;
+
+ if(numSamples == 0) {
+ return 0;
+ }
+ if(numSamples > maxSamples) {
+ remainingSamples = numSamples - maxSamples;
+ numSamples = maxSamples;
+ }
+ buffer = stream->outputBuffer;
+ count = numSamples*stream->numChannels;
+ while(count--) {
+ *samples++ = (char)((*buffer++) >> 8) + 128;
+ }
+ if(remainingSamples > 0) {
+ memmove(stream->outputBuffer, stream->outputBuffer + numSamples*stream->numChannels,
+ remainingSamples*sizeof(short)*stream->numChannels);
+ }
+ stream->numOutputSamples = remainingSamples;
+ return numSamples;
+}
+
+/* Force the sonic stream to generate output using whatever data it currently
+ has. No extra delay will be added to the output, but flushing in the middle of
+ words could introduce distortion. */
+int sonicFlushStream(
+ sonicStream stream)
+{
+ int maxRequired = stream->maxRequired;
+ int numSamples = stream->numInputSamples;
+ int remainingSpace, numOutputSamples, expectedSamples;
+
+ if(numSamples == 0) {
+ return 1;
+ }
+ if(numSamples >= maxRequired && !sonicWriteShortToStream(stream, NULL, 0)) {
+ return 0;
+ }
+ numSamples = stream->numInputSamples; /* Now numSamples < maxRequired */
+ if(numSamples == 0) {
+ return 1;
+ }
+ remainingSpace = maxRequired - numSamples;
+ memset(stream->inputBuffer + numSamples*stream->numChannels, 0,
+ remainingSpace*sizeof(short)*stream->numChannels);
+ stream->numInputSamples = maxRequired;
+ numOutputSamples = stream->numOutputSamples;
+ if(!sonicWriteShortToStream(stream, NULL, 0)) {
+ return 0;
+ }
+ /* Throw away any extra samples we generated due to the silence we added */
+ expectedSamples = (int)(numSamples*stream->speed + 0.5);
+ if(stream->numOutputSamples > numOutputSamples + expectedSamples) {
+ stream->numOutputSamples = numOutputSamples + expectedSamples;
+ }
+ return 1;
+}
+
+/* Return the number of samples in the output buffer */
+int sonicSamplesAvailable(
+ sonicStream stream)
+{
+ return stream->numOutputSamples;
+}
+
+/* If skip is greater than one, average skip samples togther and write them to
+ the down-sample buffer. If numChannels is greater than one, mix the channels
+ together as we down sample. */
+static void downSampleInput(
+ sonicStream stream,
+ short *samples,
+ int skip)
+{
+ int numSamples = stream->maxRequired/skip;
+ int samplesPerValue = stream->numChannels*skip;
+ int i, j;
+ int value;
+ short *downSamples = stream->downSampleBuffer;
+
+ for(i = 0; i < numSamples; i++) {
+ value = 0;
+ for(j = 0; j < samplesPerValue; j++) {
+ value += *samples++;
+ }
+ value /= samplesPerValue;
+ *downSamples++ = value;
+ }
+}
+
+/* Find the best frequency match in the range, and given a sample skip multiple.
+ For now, just find the pitch of the first channel. */
+static int findPitchPeriodInRange(
+ short *samples,
+ int minPeriod,
+ int maxPeriod,
+ int *retMinDiff,
+ int *retMaxDiff)
+{
+ int period, bestPeriod = 0;
+ short *s, *p, sVal, pVal;
+ unsigned long diff, minDiff = 1, maxDiff = 0;
+ int i;
+
+ for(period = minPeriod; period <= maxPeriod; period++) {
+ diff = 0;
+ s = samples;
+ p = samples + period;
+ for(i = 0; i < period; i++) {
+ sVal = *s++;
+ pVal = *p++;
+ diff += sVal >= pVal? (unsigned short)(sVal - pVal) :
+ (unsigned short)(pVal - sVal);
+ }
+ /* Note that the highest number of samples we add into diff will be less
+ than 256, since we skip samples. Thus, diff is a 24 bit number, and
+ we can safely multiply by numSamples without overflow */
+ if(diff*bestPeriod < minDiff*period) {
+ minDiff = diff;
+ bestPeriod = period;
+ }
+ if(diff*bestPeriod > maxDiff*period) {
+ maxDiff = diff;
+ }
+ }
+ *retMinDiff = minDiff;
+ *retMaxDiff = maxDiff;
+ return bestPeriod;
+}
+
+/* At abrupt ends of voiced words, we can have pitch periods that are better
+ aproximated by the previous pitch period estimate. Try to detect this case. */
+static int prevPeriodBetter(
+ sonicStream stream,
+ int period,
+ int minDiff,
+ int maxDiff)
+{
+ if(maxDiff*3/2 < stream->prevMaxDiff && (maxDiff*3.0f)*stream->prevMinDiff <
+ (float)stream->prevMaxDiff*minDiff*2) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Find the pitch period. This is a critical step, and we may have to try
+ multiple ways to get a good answer. This version uses AMDF. To improve
+ speed, we down sample by an integer factor get in the 11KHz range, and then
+ do it again with a narrower frequency range without down sampling */
+static int findPitchPeriod(
+ sonicStream stream,
+ short *samples)
+{
+ int minPeriod = stream->minPeriod;
+ int maxPeriod = stream->maxPeriod;
+ int sampleRate = stream->sampleRate;
+ int minDiff, maxDiff, retPeriod;
+ int skip = 1;
+ int period;
+
+ if(sampleRate > SONIC_AMDF_FREQ) {
+ skip = sampleRate/SONIC_AMDF_FREQ;
+ }
+ if(stream->numChannels == 1 && skip == 1) {
+ period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff, &maxDiff);
+ } else {
+ downSampleInput(stream, samples, skip);
+ period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod/skip,
+ maxPeriod/skip, &minDiff, &maxDiff);
+ if(skip != 1) {
+ period *= skip;
+ minPeriod = period - (skip << 2);
+ maxPeriod = period + (skip << 2);
+ if(minPeriod < stream->minPeriod) {
+ minPeriod = stream->minPeriod;
+ }
+ if(maxPeriod > stream->maxPeriod) {
+ maxPeriod = stream->maxPeriod;
+ }
+ if(stream->numChannels == 1) {
+ period = findPitchPeriodInRange(samples, minPeriod, maxPeriod,
+ &minDiff, &maxDiff);
+ } else {
+ downSampleInput(stream, samples, 1);
+ period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
+ maxPeriod, &minDiff, &maxDiff);
+ }
+ }
+ }
+ if(prevPeriodBetter(stream, period, minDiff, maxDiff)) {
+ retPeriod = stream->prevPeriod;
+ } else {
+ retPeriod = period;
+ }
+ stream->prevMinDiff = minDiff;
+ stream->prevMaxDiff = maxDiff;
+ stream->prevPeriod = period;
+ return retPeriod;
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+ other one from zero up, and add them, storing the result at the output. */
+static void overlapAdd(
+ int numSamples,
+ int numChannels,
+ short *out,
+ short *rampDown,
+ short *rampUp)
+{
+ short *o, *u, *d;
+ int i, t;
+
+ for(i = 0; i < numChannels; i++) {
+ o = out + i;
+ u = rampUp + i;
+ d = rampDown + i;
+ for(t = 0; t < numSamples; t++) {
+ *o = (*d*(numSamples - t) + *u*t)/numSamples;
+ o += numChannels;
+ d += numChannels;
+ u += numChannels;
+ }
+ }
+}
+
+/* Overlap two sound segments, ramp the volume of one down, while ramping the
+ other one from zero up, and add them, storing the result at the output. */
+static void overlapAddWithSeparation(
+ int numSamples,
+ int numChannels,
+ int separation,
+ short *out,
+ short *rampDown,
+ short *rampUp)
+{
+ short *o, *u, *d;
+ int i, t;
+
+ for(i = 0; i < numChannels; i++) {
+ o = out + i;
+ u = rampUp + i;
+ d = rampDown + i;
+ for(t = 0; t < numSamples + separation; t++) {
+ if(t < separation) {
+ *o = *d*(numSamples - t)/numSamples;
+ d += numChannels;
+ } else if(t < numSamples) {
+ *o = (*d*(numSamples - t) + *u*(t - separation))/numSamples;
+ d += numChannels;
+ u += numChannels;
+ } else {
+ *o = *u*(t - separation)/numSamples;
+ u += numChannels;
+ }
+ o += numChannels;
+ }
+ }
+}
+
+/* Just move the new samples in the output buffer to the pitch bufer */
+static int moveNewSamplesToPitchBuffer(
+ sonicStream stream,
+ int originalNumOutputSamples)
+{
+ int numSamples = stream->numOutputSamples - originalNumOutputSamples;
+ int numChannels = stream->numChannels;
+
+ if(stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
+ stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
+ stream->pitchBuffer = (short *)realloc(stream->pitchBuffer,
+ stream->pitchBufferSize*sizeof(short)*numChannels);
+ if(stream->pitchBuffer == NULL) {
+ return 0;
+ }
+ }
+ memcpy(stream->pitchBuffer + stream->numPitchSamples*numChannels,
+ stream->outputBuffer + originalNumOutputSamples*numChannels,
+ numSamples*sizeof(short)*numChannels);
+ stream->numOutputSamples = originalNumOutputSamples;
+ stream->numPitchSamples += numSamples;
+ return 1;
+}
+
+/* Remove processed samples from the pitch buffer. */
+static void removePitchSamples(
+ sonicStream stream,
+ int numSamples)
+{
+ int numChannels = stream->numChannels;
+ short *source = stream->pitchBuffer + numSamples*numChannels;
+
+ if(numSamples == 0) {
+ return;
+ }
+ if(numSamples != stream->numPitchSamples) {
+ memmove(stream->pitchBuffer, source, (stream->numPitchSamples -
+ numSamples)*sizeof(short)*numChannels);
+ }
+ stream->numPitchSamples -= numSamples;
+}
+
+/* Change the pitch. The latency this introduces could be reduced by looking at
+ past samples to determine pitch, rather than future. */
+static int adjustPitch(
+ sonicStream stream,
+ int originalNumOutputSamples)
+{
+ float pitch = stream->pitch;
+ int numChannels = stream->numChannels;
+ int period, newPeriod, separation;
+ int position = 0;
+ short *out, *rampDown, *rampUp;
+
+ if(stream->numOutputSamples == originalNumOutputSamples) {
+ return 1;
+ }
+ if(!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
+ return 0;
+ }
+ while(stream->numPitchSamples - position >= stream->maxRequired) {
+ period = findPitchPeriod(stream, stream->pitchBuffer + position*numChannels);
+ newPeriod = period/pitch;
+ if(!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
+ return 0;
+ }
+ out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+ if(pitch >= 1.0f) {
+ rampDown = stream->pitchBuffer + position*numChannels;
+ rampUp = stream->pitchBuffer + (position + period - newPeriod)*numChannels;
+ overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
+ } else {
+ rampDown = stream->pitchBuffer + position*numChannels;
+ rampUp = stream->pitchBuffer + position*numChannels;
+ separation = newPeriod - period;
+ overlapAddWithSeparation(period, numChannels, separation, out, rampDown, rampUp);
+ }
+ stream->numOutputSamples += newPeriod;
+ position += period;
+ }
+ removePitchSamples(stream, position);
+ return 1;
+}
+
+/* Skip over a pitch period, and copy period/speed samples to the output */
+static int skipPitchPeriod(
+ sonicStream stream,
+ short *samples,
+ float speed,
+ int period)
+{
+ long newSamples;
+ int numChannels = stream->numChannels;
+
+ if(speed >= 2.0f) {
+ newSamples = period/(speed - 1.0f);
+ } else if(speed > 1.0f) {
+ newSamples = period;
+ stream->remainingInputToCopy = period*(2.0f - speed)/(speed - 1.0f);
+ }
+ if(!enlargeOutputBufferIfNeeded(stream, newSamples)) {
+ return 0;
+ }
+ overlapAdd(newSamples, numChannels, stream->outputBuffer +
+ stream->numOutputSamples*numChannels, samples, samples + period*numChannels);
+ stream->numOutputSamples += newSamples;
+ return newSamples;
+}
+
+/* Insert a pitch period, and determine how much input to copy directly. */
+static int insertPitchPeriod(
+ sonicStream stream,
+ short *samples,
+ float speed,
+ int period)
+{
+ long newSamples;
+ short *out;
+ int numChannels = stream->numChannels;
+
+ if(speed < 0.5f) {
+ newSamples = period*speed/(1.0f - speed);
+ } else {
+ newSamples = period;
+ stream->remainingInputToCopy = period*(2.0f*speed - 1.0f)/(1.0f - speed);
+ }
+ if(!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
+ return 0;
+ }
+ out = stream->outputBuffer + stream->numOutputSamples*numChannels;
+ memcpy(out, samples, period*sizeof(short)*numChannels);
+ out = stream->outputBuffer + (stream->numOutputSamples + period)*numChannels;
+ overlapAdd(newSamples, numChannels, out, samples + period*numChannels, samples);
+ stream->numOutputSamples += period + newSamples;
+ return newSamples;
+}
+
+/* Resample as many pitch periods as we have buffered on the input. Return 0 if
+ we fail to resize an input or output buffer. Also scale the output by the volume. */
+static int changeSpeed(
+ sonicStream stream,
+ float speed)
+{
+ short *samples;
+ int numSamples = stream->numInputSamples;
+ int position = 0, period, newSamples;
+ int maxRequired = stream->maxRequired;
+
+ if(stream->numInputSamples < maxRequired) {
+ return 1;
+ }
+ do {
+ if(stream->remainingInputToCopy > 0) {
+ newSamples = copyInputToOutput(stream, position);
+ position += newSamples;
+ } else {
+ samples = stream->inputBuffer + position*stream->numChannels;
+ period = findPitchPeriod(stream, samples);
+ if(speed > 1.0) {
+ newSamples = skipPitchPeriod(stream, samples, speed, period);
+ position += period + newSamples;
+ } else {
+ newSamples = insertPitchPeriod(stream, samples, speed, period);
+ position += newSamples;
+ }
+ }
+ if(newSamples == 0) {
+ return 0; /* Failed to resize output buffer */
+ }
+ } while(position + maxRequired <= numSamples);
+ removeInputSamples(stream, position);
+ return 1;
+}
+
+/* Resample as many pitch periods as we have buffered on the input. Return 0 if
+ we fail to resize an input or output buffer. Also scale the output by the volume. */
+static int processStreamInput(
+ sonicStream stream)
+{
+ int originalNumOutputSamples = stream->numOutputSamples;
+ float speed = stream->speed/stream->pitch;
+
+ if(speed > 1.00001 || speed < 0.99999) {
+ changeSpeed(stream, speed);
+ } else {
+ if(!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
+ return 0;
+ }
+ stream->numInputSamples = 0;
+ }
+ if(stream->pitch != 1.0f) {
+ if(!adjustPitch(stream, originalNumOutputSamples)) {
+ return 0;
+ }
+ }
+ if(stream->volume != 1.0f) {
+ /* Adjust output volume. */
+ scaleSamples(stream->outputBuffer + originalNumOutputSamples*stream->numChannels,
+ (stream->numOutputSamples - originalNumOutputSamples)*stream->numChannels,
+ stream->volume);
+ }
+ return 1;
+}
+
+/* Write floating point data to the input buffer and process it. */
+int sonicWriteFloatToStream(
+ sonicStream stream,
+ float *samples,
+ int numSamples)
+{
+ if(!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the short to float
+ conversion for you. */
+int sonicWriteShortToStream(
+ sonicStream stream,
+ short *samples,
+ int numSamples)
+{
+ if(!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to float
+ conversion for you. */
+int sonicWriteUnsignedCharToStream(
+ sonicStream stream,
+ unsigned char *samples,
+ int numSamples)
+{
+ if(!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
+ return 0;
+ }
+ return processStreamInput(stream);
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeFloatSpeed(
+ float *samples,
+ int numSamples,
+ float speed,
+ float pitch,
+ float volume,
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+ sonicSetSpeed(stream, speed);
+ sonicSetPitch(stream, pitch);
+ sonicSetVolume(stream, volume);
+ sonicWriteFloatToStream(stream, samples, numSamples);
+ sonicFlushStream(stream);
+ numSamples = sonicSamplesAvailable(stream);
+ sonicReadFloatFromStream(stream, samples, numSamples);
+ sonicDestroyStream(stream);
+ return numSamples;
+}
+
+/* This is a non-stream oriented interface to just change the speed of a sound sample */
+int sonicChangeShortSpeed(
+ short *samples,
+ int numSamples,
+ float speed,
+ float pitch,
+ float volume,
+ int sampleRate,
+ int numChannels)
+{
+ sonicStream stream = sonicCreateStream(sampleRate, numChannels);
+
+ sonicSetSpeed(stream, speed);
+ sonicSetPitch(stream, pitch);
+ sonicSetVolume(stream, volume);
+ sonicWriteShortToStream(stream, samples, numSamples);
+ sonicFlushStream(stream);
+ numSamples = sonicSamplesAvailable(stream);
+ sonicReadShortFromStream(stream, samples, numSamples);
+ sonicDestroyStream(stream);
+ return numSamples;
+}
+#endif // INCLUDE_SONIC