123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970 |
- /* 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"
-
- 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;
- };
-
- /* Just used for debugging */
- 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);
- }
-
- /* 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;
- }
|