sonic音频变速变调

sonic调用:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sonic.h"
#include <time.h>
#include<Foundation/Foundation.h>

int main()
{
	
	FILE* fp_in = fopen("/pcm-1.data", "rb+");
	FILE* fp_out = fopen("/speedz.pcm", "wb+");
	sonicStream tempoStream_ = sonicCreateStream(44100, 1);

	sonicSetSpeed(tempoStream_, 4.0);
	sonicSetPitch(tempoStream_, 1.0);
	sonicSetRate(tempoStream_, 1.0);

	
	unsigned char *pbuffer = new  unsigned char[8192];

	while (!feof(fp_in))
	{
        
		int readsize= fread(pbuffer, 1,3528, fp_in);
		if (readsize == 0 || readsize < 3528)
		{
           
			break;
		}
       
		int ret = sonicWriteShortToStream(tempoStream_, (short*)pbuffer, 1764);
		if (ret) {
			int sample = sonicSamplesAvailable(tempoStream_);

			
			int out_size = sonicReadShortFromStream(tempoStream_, (short*)pbuffer, sample);
         
            int outlen =out_size * 2;
			fwrite(pbuffer, 1,outlen, fp_out);
			fflush(fp_out);
		}

	}
	if (fp_out)
	{
		fclose(fp_out);
		fp_out = NULL;
	}
	if (fp_in)
	{
		fclose(fp_in);
		fp_in = NULL;
	}

	delete pbuffer;
	pbuffer = NULL;

	sonicDestroyStream(tempoStream_);

}

 

工程引入头文件和源文件

sonic.hpp

  1 #pragma once
  2 /* Sonic library
  3 Copyright 2010
  4 Bill Cox
  5 This file is part of the Sonic Library.
  6 This file is licensed under the Apache 2.0 license.
  7 */
  8 
  9 /*
 10 The Sonic Library implements a new algorithm invented by Bill Cox for the
 11 specific purpose of speeding up speech by high factors at high quality.  It
 12 generates smooth speech at speed up factors as high as 6X, possibly more.  It is
 13 also capable of slowing down speech, and generates high quality results
 14 regardless of the speed up or slow down factor.  For speeding up speech by 2X or
 15 more, the following equation is used:
 16 newSamples = period/(speed - 1.0)
 17 scale = 1.0/newSamples;
 18 where period is the current pitch period, determined using AMDF or any other
 19 pitch estimator, and speed is the speedup factor.  If the current position in
 20 the input stream is pointed to by "samples", and the current output stream
 21 position is pointed to by "out", then newSamples number of samples can be
 22 generated with:
 23 out[t] = (samples[t]*(newSamples - t) + samples[t + period]*t)/newSamples;
 24 where t = 0 to newSamples - 1.
 25 For speed factors < 2X, the PICOLA algorithm is used.  The above
 26 algorithm is first used to double the speed of one pitch period.  Then, enough
 27 input is directly copied from the input to the output to achieve the desired
 28 speed up factor, where 1.0 < speed < 2.0.  The amount of data copied is derived:
 29 speed = (2*period + length)/(period + length)
 30 speed*length + speed*period = 2*period + length
 31 length(speed - 1) = 2*period - speed*period
 32 length = period*(2 - speed)/(speed - 1)
 33 For slowing down speech where 0.5 < speed < 1.0, a pitch period is inserted into
 34 the output twice, and length of input is copied from the input to the output
 35 until the output desired speed is reached.  The length of data copied is:
 36 length = period*(speed - 0.5)/(1 - speed)
 37 For slow down factors below 0.5, no data is copied, and an algorithm
 38 similar to high speed factors is used.
 39 */
 40 
 41 /* Uncomment this to use sin-wav based overlap add which in theory can improve
 42 sound quality slightly, at the expense of lots of floating point math. */
 43 /* #define SONIC_USE_SIN */
 44 
 45 #ifdef __cplusplus
 46 extern "C" {
 47 #endif
 48 
 49     /* This specifies the range of voice pitches we try to match.
 50     Note that if we go lower than 65, we could overflow in findPitchInRange */
 51 #define SONIC_MIN_PITCH 65
 52 #define SONIC_MAX_PITCH 400
 53 
 54     /* These are used to down-sample some inputs to improve speed */
 55 #define SONIC_AMDF_FREQ 4000
 56 
 57     struct sonicStreamStruct;
 58     typedef struct sonicStreamStruct* sonicStream;
 59 
 60     /* For all of the following functions, numChannels is multiplied by numSamples
 61     to determine the actual number of values read or returned. */
 62 
 63     /* Create a sonic stream.  Return NULL only if we are out of memory and cannot
 64     allocate the stream. Set numChannels to 1 for mono, and 2 for stereo. */
 65     sonicStream sonicCreateStream(int sampleRate, int numChannels);
 66     /* Destroy the sonic stream. */
 67     void sonicDestroyStream(sonicStream stream);
 68     /* Use this to write floating point data to be speed up or down into the stream.
 69     Values must be between -1 and 1.  Return 0 if memory realloc failed,
 70     otherwise 1 */
 71     int sonicWriteFloatToStream(sonicStream stream, float* samples, int numSamples);
 72     /* Use this to write 16-bit data to be speed up or down into the stream.
 73     Return 0 if memory realloc failed, otherwise 1 */
 74     int sonicWriteShortToStream(sonicStream stream, short* samples, int numSamples);
 75     /* Use this to write 8-bit unsigned data to be speed up or down into the stream.
 76     Return 0 if memory realloc failed, otherwise 1 */
 77     int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char* samples,
 78         int numSamples);
 79     /* Use this to read floating point data out of the stream.  Sometimes no data
 80     will be available, and zero is returned, which is not an error condition. */
 81     int sonicReadFloatFromStream(sonicStream stream, float* samples,
 82         int maxSamples);
 83     /* Use this to read 16-bit data out of the stream.  Sometimes no data will
 84     be available, and zero is returned, which is not an error condition. */
 85     int sonicReadShortFromStream(sonicStream stream, short* samples,
 86         int maxSamples);
 87     /* Use this to read 8-bit unsigned data out of the stream.  Sometimes no data
 88     will be available, and zero is returned, which is not an error condition. */
 89     int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char* samples,
 90         int maxSamples);
 91     /* Force the sonic stream to generate output using whatever data it currently
 92     has.  No extra delay will be added to the output, but flushing in the middle
 93     of words could introduce distortion. */
 94     int sonicFlushStream(sonicStream stream);
 95     /* Return the number of samples in the output buffer */
 96     int sonicSamplesAvailable(sonicStream stream);
 97     /* Get the speed of the stream. */
 98     float sonicGetSpeed(sonicStream stream);
 99     /* Set the speed of the stream. */
100     void sonicSetSpeed(sonicStream stream, float speed);
101     /* Get the pitch of the stream. */
102     float sonicGetPitch(sonicStream stream);
103     /* Set the pitch of the stream. */
104     void sonicSetPitch(sonicStream stream, float pitch);
105     /* Get the rate of the stream. */
106     float sonicGetRate(sonicStream stream);
107     /* Set the rate of the stream. */
108     void sonicSetRate(sonicStream stream, float rate);
109     /* Get the scaling factor of the stream. */
110     float sonicGetVolume(sonicStream stream);
111     /* Set the scaling factor of the stream. */
112     void sonicSetVolume(sonicStream stream, float volume);
113     /* Get the chord pitch setting. */
114     int sonicGetChordPitch(sonicStream stream);
115     /* Set chord pitch mode on or off.  Default is off.  See the documentation
116     page for a description of this feature. */
117     void sonicSetChordPitch(sonicStream stream, int useChordPitch);
118     /* Get the quality setting. */
119     int sonicGetQuality(sonicStream stream);
120     /* Set the "quality".  Default 0 is virtually as good as 1, but very much
121     * faster. */
122     void sonicSetQuality(sonicStream stream, int quality);
123     /* Get the sample rate of the stream. */
124     int sonicGetSampleRate(sonicStream stream);
125     /* Set the sample rate of the stream.  This will drop any samples that have not
126     * been read. */
127     void sonicSetSampleRate(sonicStream stream, int sampleRate);
128     /* Get the number of channels. */
129     int sonicGetNumChannels(sonicStream stream);
130     /* Set the number of channels.  This will drop any samples that have not been
131     * read. */
132     void sonicSetNumChannels(sonicStream stream, int numChannels);
133     /* This is a non-stream oriented interface to just change the speed of a sound
134     sample.  It works in-place on the sample array, so there must be at least
135     speed*numSamples available space in the array. Returns the new number of
136     samples. */
137     int sonicChangeFloatSpeed(float* samples, int numSamples, float speed,
138         float pitch, float rate, float volume,
139         int useChordPitch, int sampleRate, int numChannels);
140     /* This is a non-stream oriented interface to just change the speed of a sound
141     sample.  It works in-place on the sample array, so there must be at least
142     speed*numSamples available space in the array. Returns the new number of
143     samples. */
144     int sonicChangeShortSpeed(short* samples, int numSamples, float speed,
145         float pitch, float rate, float volume,
146         int useChordPitch, int sampleRate, int numChannels);
147 
148 #ifdef SONIC_SPECTROGRAM
149     /*
150     This code generates high quality spectrograms from sound samples, using
151     Time-Aliased-FFTs as described at:
152     https://github.com/waywardgeek/spectrogram
153     Basically, two adjacent pitch periods are overlap-added to create a sound
154     sample that accurately represents the speech sound at that moment in time.
155     This set of samples is converted to a spetral line using an FFT, and the result
156     is saved as a single spectral line at that moment in time.  The resulting
157     spectral lines vary in resolution (it is equal to the number of samples in the
158     pitch period), and the spacing of spectral lines also varies (proportional to
159     the numver of samples in the pitch period).
160     To generate a bitmap, linear interpolation is used to render the grayscale
161     value at any particular point in time and frequency.
162     */
163 
164 #define SONIC_MAX_SPECTRUM_FREQ 5000
165 
166     struct sonicSpectrogramStruct;
167     struct sonicBitmapStruct;
168     typedef struct sonicSpectrogramStruct* sonicSpectrogram;
169     typedef struct sonicBitmapStruct* sonicBitmap;
170 
171     /* sonicBitmap objects represent spectrograms as grayscale bitmaps where each
172     pixel is from 0 (black) to 255 (white).  Bitmaps are rows*cols in size.
173     Rows are indexed top to bottom and columns are indexed left to right */
174     struct sonicBitmapStruct {
175         unsigned char* data;
176         int numRows;
177         int numCols;
178     };
179 
180     typedef struct sonicBitmapStruct* sonicBitmap;
181 
182     /* Enable coomputation of a spectrogram on the fly. */
183     void sonicComputeSpectrogram(sonicStream stream);
184 
185     /* Get the spectrogram. */
186     sonicSpectrogram sonicGetSpectrogram(sonicStream stream);
187 
188     /* Create an empty spectrogram. Called automatically if sonicComputeSpectrogram
189     has been called. */
190     sonicSpectrogram sonicCreateSpectrogram(int sampleRate);
191 
192     /* Destroy the spectrotram.  This is called automatically when calling
193     sonicDestroyStream. */
194     void sonicDestroySpectrogram(sonicSpectrogram spectrogram);
195 
196     /* Convert the spectrogram to a bitmap. Caller must destroy bitmap when done. */
197     sonicBitmap sonicConvertSpectrogramToBitmap(sonicSpectrogram spectrogram,
198         int numRows, int numCols);
199 
200     /* Destroy a bitmap returned by sonicConvertSpectrogramToBitmap. */
201     void sonicDestroyBitmap(sonicBitmap bitmap);
202 
203     int sonicWritePGM(sonicBitmap bitmap, char* fileName);
204 
205     /* Add two pitch periods worth of samples to the spectrogram.  There must be
206     2*period samples.  Time should advance one pitch period for each call to
207     this function. */
208     void sonicAddPitchPeriodToSpectrogram(sonicSpectrogram spectrogram,
209         short* samples, int period,
210         int numChannels);
211 #endif  /* SONIC_SPECTROGRAM */
212 
213 #ifdef __cplusplus
214 }
215 #endif

sonic的源文件:

   1 /* Sonic library
   2 Copyright 2010
   3 Bill Cox
   4 This file is part of the Sonic Library.
   5 This file is licensed under the Apache 2.0 license.
   6 */
   7 
   8 #include "sonic.h"
   9 
  10 #include <limits.h>
  11 #include <math.h>
  12 #include <stdarg.h>
  13 #include <stdio.h>
  14 #include <stdlib.h>
  15 #include <string.h>
  16 
  17 /*
  18 The following code was used to generate the following sinc lookup table.
  19 #include <limits.h>
  20 #include <math.h>
  21 #include <stdio.h>
  22 double findHannWeight(int N, double x) {
  23 return 0.5*(1.0 - cos(2*M_PI*x/N));
  24 }
  25 double findSincCoefficient(int N, double x) {
  26 double hannWindowWeight = findHannWeight(N, x);
  27 double sincWeight;
  28 x -= N/2.0;
  29 if (x > 1e-9 || x < -1e-9) {
  30 sincWeight = sin(M_PI*x)/(M_PI*x);
  31 } else {
  32 sincWeight = 1.0;
  33 }
  34 return hannWindowWeight*sincWeight;
  35 }
  36 int main() {
  37 double x;
  38 int i;
  39 int N = 12;
  40 for (i = 0, x = 0.0; x <= N; x += 0.02, i++) {
  41 printf("%u %d\n", i, (int)(SHRT_MAX*findSincCoefficient(N, x)));
  42 }
  43 return 0;
  44 }
  45 */
  46 
  47 /* The number of points to use in the sinc FIR filter for resampling. */
  48 #define SINC_FILTER_POINTS \
  49   12 /* I am not able to hear improvement with higher N. */
  50 #define SINC_TABLE_SIZE 601
  51 
  52 /* Lookup table for windowed sinc function of SINC_FILTER_POINTS points. */
  53 static short sincTable[SINC_TABLE_SIZE] = {
  54     0,     0,     0,     0,     0,     0,     0,     -1,    -1,    -2,    -2,
  55     -3,    -4,    -6,    -7,    -9,    -10,   -12,   -14,   -17,   -19,   -21,
  56     -24,   -26,   -29,   -32,   -34,   -37,   -40,   -42,   -44,   -47,   -48,
  57     -50,   -51,   -52,   -53,   -53,   -53,   -52,   -50,   -48,   -46,   -43,
  58     -39,   -34,   -29,   -22,   -16,   -8,    0,     9,     19,    29,    41,
  59     53,    65,    79,    92,    107,   121,   137,   152,   168,   184,   200,
  60     215,   231,   247,   262,   276,   291,   304,   317,   328,   339,   348,
  61     357,   363,   369,   372,   374,   375,   373,   369,   363,   355,   345,
  62     332,   318,   300,   281,   259,   234,   208,   178,   147,   113,   77,
  63     39,    0,     -41,   -85,   -130,  -177,  -225,  -274,  -324,  -375,  -426,
  64     -478,  -530,  -581,  -632,  -682,  -731,  -779,  -825,  -870,  -912,  -951,
  65     -989,  -1023, -1053, -1080, -1104, -1123, -1138, -1149, -1154, -1155, -1151,
  66     -1141, -1125, -1105, -1078, -1046, -1007, -963,  -913,  -857,  -796,  -728,
  67     -655,  -576,  -492,  -403,  -309,  -210,  -107,  0,     111,   225,   342,
  68     462,   584,   708,   833,   958,   1084,  1209,  1333,  1455,  1575,  1693,
  69     1807,  1916,  2022,  2122,  2216,  2304,  2384,  2457,  2522,  2579,  2625,
  70     2663,  2689,  2706,  2711,  2705,  2687,  2657,  2614,  2559,  2491,  2411,
  71     2317,  2211,  2092,  1960,  1815,  1658,  1489,  1308,  1115,  912,   698,
  72     474,   241,   0,     -249,  -506,  -769,  -1037, -1310, -1586, -1864, -2144,
  73     -2424, -2703, -2980, -3254, -3523, -3787, -4043, -4291, -4529, -4757, -4972,
  74     -5174, -5360, -5531, -5685, -5819, -5935, -6029, -6101, -6150, -6175, -6175,
  75     -6149, -6096, -6015, -5905, -5767, -5599, -5401, -5172, -4912, -4621, -4298,
  76     -3944, -3558, -3141, -2693, -2214, -1705, -1166, -597,  0,     625,   1277,
  77     1955,  2658,  3386,  4135,  4906,  5697,  6506,  7332,  8173,  9027,  9893,
  78     10769, 11654, 12544, 13439, 14335, 15232, 16128, 17019, 17904, 18782, 19649,
  79     20504, 21345, 22170, 22977, 23763, 24527, 25268, 25982, 26669, 27327, 27953,
  80     28547, 29107, 29632, 30119, 30569, 30979, 31349, 31678, 31964, 32208, 32408,
  81     32565, 32677, 32744, 32767, 32744, 32677, 32565, 32408, 32208, 31964, 31678,
  82     31349, 30979, 30569, 30119, 29632, 29107, 28547, 27953, 27327, 26669, 25982,
  83     25268, 24527, 23763, 22977, 22170, 21345, 20504, 19649, 18782, 17904, 17019,
  84     16128, 15232, 14335, 13439, 12544, 11654, 10769, 9893,  9027,  8173,  7332,
  85     6506,  5697,  4906,  4135,  3386,  2658,  1955,  1277,  625,   0,     -597,
  86     -1166, -1705, -2214, -2693, -3141, -3558, -3944, -4298, -4621, -4912, -5172,
  87     -5401, -5599, -5767, -5905, -6015, -6096, -6149, -6175, -6175, -6150, -6101,
  88     -6029, -5935, -5819, -5685, -5531, -5360, -5174, -4972, -4757, -4529, -4291,
  89     -4043, -3787, -3523, -3254, -2980, -2703, -2424, -2144, -1864, -1586, -1310,
  90     -1037, -769,  -506,  -249,  0,     241,   474,   698,   912,   1115,  1308,
  91     1489,  1658,  1815,  1960,  2092,  2211,  2317,  2411,  2491,  2559,  2614,
  92     2657,  2687,  2705,  2711,  2706,  2689,  2663,  2625,  2579,  2522,  2457,
  93     2384,  2304,  2216,  2122,  2022,  1916,  1807,  1693,  1575,  1455,  1333,
  94     1209,  1084,  958,   833,   708,   584,   462,   342,   225,   111,   0,
  95     -107,  -210,  -309,  -403,  -492,  -576,  -655,  -728,  -796,  -857,  -913,
  96     -963,  -1007, -1046, -1078, -1105, -1125, -1141, -1151, -1155, -1154, -1149,
  97     -1138, -1123, -1104, -1080, -1053, -1023, -989,  -951,  -912,  -870,  -825,
  98     -779,  -731,  -682,  -632,  -581,  -530,  -478,  -426,  -375,  -324,  -274,
  99     -225,  -177,  -130,  -85,   -41,   0,     39,    77,    113,   147,   178,
 100     208,   234,   259,   281,   300,   318,   332,   345,   355,   363,   369,
 101     373,   375,   374,   372,   369,   363,   357,   348,   339,   328,   317,
 102     304,   291,   276,   262,   247,   231,   215,   200,   184,   168,   152,
 103     137,   121,   107,   92,    79,    65,    53,    41,    29,    19,    9,
 104     0,     -8,    -16,   -22,   -29,   -34,   -39,   -43,   -46,   -48,   -50,
 105     -52,   -53,   -53,   -53,   -52,   -51,   -50,   -48,   -47,   -44,   -42,
 106     -40,   -37,   -34,   -32,   -29,   -26,   -24,   -21,   -19,   -17,   -14,
 107     -12,   -10,   -9,    -7,    -6,    -4,    -3,    -2,    -2,    -1,    -1,
 108     0,     0,     0,     0,     0,     0,     0 };
 109 
 110 struct sonicStreamStruct {
 111 #ifdef SONIC_SPECTROGRAM
 112     sonicSpectrogram spectrogram;
 113 #endif  /* SONIC_SPECTROGRAM */
 114     short* inputBuffer;
 115     short* outputBuffer;
 116     short* pitchBuffer;
 117     short* downSampleBuffer;
 118     float speed;
 119     float volume;
 120     float pitch;
 121     float rate;
 122     int oldRatePosition;
 123     int newRatePosition;
 124     int useChordPitch;
 125     int quality;
 126     int numChannels;
 127     int inputBufferSize;
 128     int pitchBufferSize;
 129     int outputBufferSize;
 130     int numInputSamples;
 131     int numOutputSamples;
 132     int numPitchSamples;
 133     int minPeriod;
 134     int maxPeriod;
 135     int maxRequired;
 136     int remainingInputToCopy;
 137     int sampleRate;
 138     int prevPeriod;
 139     int prevMinDiff;
 140     float avePower;
 141 };
 142 
 143 #ifdef SONIC_SPECTROGRAM
 144 
 145 /* Compute a spectrogram on the fly. */
 146 void sonicComputeSpectrogram(sonicStream stream) {
 147     stream->spectrogram = sonicCreateSpectrogram(stream->sampleRate);
 148     /* Force changeSpeed to be called to compute the spectrogram. */
 149     sonicSetSpeed(stream, 2.0);
 150 }
 151 
 152 /* Get the spectrogram. */
 153 sonicSpectrogram sonicGetSpectrogram(sonicStream stream) {
 154     return stream->spectrogram;
 155 }
 156 
 157 #endif
 158 
 159 /* Scale the samples by the factor. */
 160 static void scaleSamples(short* samples, int numSamples, float volume) {
 161     int fixedPointVolume = volume * 4096.0f;
 162     int value;
 163 
 164     while (numSamples--) {
 165         value = (*samples * fixedPointVolume) >> 12;
 166         if (value > 32767) {
 167             value = 32767;
 168         }
 169         else if (value < -32767) {
 170             value = -32767;
 171         }
 172         *samples++ = value;
 173     }
 174 }
 175 
 176 /* Get the speed of the stream. */
 177 float sonicGetSpeed(sonicStream stream) { return stream->speed; }
 178 
 179 /* Set the speed of the stream. */
 180 void sonicSetSpeed(sonicStream stream, float speed) { stream->speed = speed; }
 181 
 182 /* Get the pitch of the stream. */
 183 float sonicGetPitch(sonicStream stream) { return stream->pitch; }
 184 
 185 /* Set the pitch of the stream. */
 186 void sonicSetPitch(sonicStream stream, float pitch) { stream->pitch = pitch; }
 187 
 188 /* Get the rate of the stream. */
 189 float sonicGetRate(sonicStream stream) { return stream->rate; }
 190 
 191 /* Set the playback rate of the stream. This scales pitch and speed at the same
 192 time. */
 193 void sonicSetRate(sonicStream stream, float rate) {
 194     stream->rate = rate;
 195 
 196     stream->oldRatePosition = 0;
 197     stream->newRatePosition = 0;
 198 }
 199 
 200 /* Get the vocal chord pitch setting. */
 201 int sonicGetChordPitch(sonicStream stream) { return stream->useChordPitch; }
 202 
 203 /* Set the vocal chord mode for pitch computation.  Default is off. */
 204 void sonicSetChordPitch(sonicStream stream, int useChordPitch) {
 205     stream->useChordPitch = useChordPitch;
 206 }
 207 
 208 /* Get the quality setting. */
 209 int sonicGetQuality(sonicStream stream) { return stream->quality; }
 210 
 211 /* Set the "quality".  Default 0 is virtually as good as 1, but very much
 212 faster. */
 213 void sonicSetQuality(sonicStream stream, int quality) {
 214     stream->quality = quality;
 215 }
 216 
 217 /* Get the scaling factor of the stream. */
 218 float sonicGetVolume(sonicStream stream) { return stream->volume; }
 219 
 220 /* Set the scaling factor of the stream. */
 221 void sonicSetVolume(sonicStream stream, float volume) {
 222     stream->volume = volume;
 223 }
 224 
 225 /* Free stream buffers. */
 226 static void freeStreamBuffers(sonicStream stream) {
 227     if (stream->inputBuffer != NULL) {
 228         free(stream->inputBuffer);
 229     }
 230     if (stream->outputBuffer != NULL) {
 231         free(stream->outputBuffer);
 232     }
 233     if (stream->pitchBuffer != NULL) {
 234         free(stream->pitchBuffer);
 235     }
 236     if (stream->downSampleBuffer != NULL) {
 237         free(stream->downSampleBuffer);
 238     }
 239 }
 240 
 241 /* Destroy the sonic stream. */
 242 void sonicDestroyStream(sonicStream stream) {
 243 #ifdef SONIC_SPECTROGRAM
 244     if (stream->spectrogram != NULL) {
 245         sonicDestroySpectrogram(stream->spectrogram);
 246     }
 247 #endif  /* SONIC_SPECTROGRAM */
 248     freeStreamBuffers(stream);
 249     free(stream);
 250 }
 251 
 252 /* Allocate stream buffers. */
 253 static int allocateStreamBuffers(sonicStream stream, int sampleRate,
 254     int numChannels) {
 255     int minPeriod = sampleRate / SONIC_MAX_PITCH;
 256     int maxPeriod = sampleRate / SONIC_MIN_PITCH;
 257     int maxRequired = 2 * maxPeriod;
 258 
 259     stream->inputBufferSize = maxRequired;
 260     stream->inputBuffer =
 261         (short*)calloc(maxRequired, sizeof(short) * numChannels);
 262     if (stream->inputBuffer == NULL) {
 263         sonicDestroyStream(stream);
 264         return 0;
 265     }
 266     stream->outputBufferSize = maxRequired;
 267     stream->outputBuffer =
 268         (short*)calloc(maxRequired, sizeof(short) * numChannels);
 269     if (stream->outputBuffer == NULL) {
 270         sonicDestroyStream(stream);
 271         return 0;
 272     }
 273     stream->pitchBufferSize = maxRequired;
 274     stream->pitchBuffer =
 275         (short*)calloc(maxRequired, sizeof(short) * numChannels);
 276     if (stream->pitchBuffer == NULL) {
 277         sonicDestroyStream(stream);
 278         return 0;
 279     }
 280     stream->downSampleBuffer = (short*)calloc(maxRequired, sizeof(short));
 281     if (stream->downSampleBuffer == NULL) {
 282         sonicDestroyStream(stream);
 283         return 0;
 284     }
 285     stream->sampleRate = sampleRate;
 286     stream->numChannels = numChannels;
 287     stream->oldRatePosition = 0;
 288     stream->newRatePosition = 0;
 289     stream->minPeriod = minPeriod;
 290     stream->maxPeriod = maxPeriod;
 291     stream->maxRequired = maxRequired;
 292     stream->prevPeriod = 0;
 293     return 1;
 294 }
 295 
 296 /* Create a sonic stream.  Return NULL only if we are out of memory and cannot
 297 allocate the stream. */
 298 sonicStream sonicCreateStream(int sampleRate, int numChannels) {
 299     sonicStream stream = (sonicStream)calloc(1, sizeof(struct sonicStreamStruct));
 300 
 301     if (stream == NULL) {
 302         return NULL;
 303     }
 304     if (!allocateStreamBuffers(stream, sampleRate, numChannels)) {
 305         return NULL;
 306     }
 307     stream->speed = 1.0f;
 308     stream->pitch = 1.0f;
 309     stream->volume = 1.0f;
 310     stream->rate = 1.0f;
 311     stream->oldRatePosition = 0;
 312     stream->newRatePosition = 0;
 313     stream->useChordPitch = 0;
 314     stream->quality = 0;
 315     stream->avePower = 50.0f;
 316     return stream;
 317 }
 318 
 319 /* Get the sample rate of the stream. */
 320 int sonicGetSampleRate(sonicStream stream) { return stream->sampleRate; }
 321 
 322 /* Set the sample rate of the stream.  This will cause samples buffered in the
 323 stream to be lost. */
 324 void sonicSetSampleRate(sonicStream stream, int sampleRate) {
 325     freeStreamBuffers(stream);
 326     allocateStreamBuffers(stream, sampleRate, stream->numChannels);
 327 }
 328 
 329 /* Get the number of channels. */
 330 int sonicGetNumChannels(sonicStream stream) { return stream->numChannels; }
 331 
 332 /* Set the num channels of the stream.  This will cause samples buffered in the
 333 stream to be lost. */
 334 void sonicSetNumChannels(sonicStream stream, int numChannels) {
 335     freeStreamBuffers(stream);
 336     allocateStreamBuffers(stream, stream->sampleRate, numChannels);
 337 }
 338 
 339 /* Enlarge the output buffer if needed. */
 340 static int enlargeOutputBufferIfNeeded(sonicStream stream, int numSamples) {
 341     if (stream->numOutputSamples + numSamples > stream->outputBufferSize) {
 342         stream->outputBufferSize += (stream->outputBufferSize >> 1) + numSamples;
 343         stream->outputBuffer = (short*)realloc(
 344             stream->outputBuffer,
 345             stream->outputBufferSize * sizeof(short) * stream->numChannels);
 346         if (stream->outputBuffer == NULL) {
 347             return 0;
 348         }
 349     }
 350     return 1;
 351 }
 352 
 353 /* Enlarge the input buffer if needed. */
 354 static int enlargeInputBufferIfNeeded(sonicStream stream, int numSamples) {
 355     if (stream->numInputSamples + numSamples > stream->inputBufferSize) {
 356         stream->inputBufferSize += (stream->inputBufferSize >> 1) + numSamples;
 357         stream->inputBuffer = (short*)realloc(
 358             stream->inputBuffer,
 359             stream->inputBufferSize * sizeof(short) * stream->numChannels);
 360         if (stream->inputBuffer == NULL) {
 361             return 0;
 362         }
 363     }
 364     return 1;
 365 }
 366 
 367 /* Add the input samples to the input buffer. */
 368 static int addFloatSamplesToInputBuffer(sonicStream stream, float* samples,
 369     int numSamples) {
 370     short* buffer;
 371     int count = numSamples * stream->numChannels;
 372 
 373     if (numSamples == 0) {
 374         return 1;
 375     }
 376     if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
 377         return 0;
 378     }
 379     buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
 380     while (count--) {
 381         *buffer++ = (*samples++) * 32767.0f;
 382     }
 383     stream->numInputSamples += numSamples;
 384     return 1;
 385 }
 386 
 387 /* Add the input samples to the input buffer. */
 388 static int addShortSamplesToInputBuffer(sonicStream stream, short* samples,
 389     int numSamples) {
 390     if (numSamples == 0) {
 391         return 1;
 392     }
 393     if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
 394         return 0;
 395     }
 396     memcpy(stream->inputBuffer + stream->numInputSamples * stream->numChannels,
 397         samples, numSamples * sizeof(short) * stream->numChannels);
 398     stream->numInputSamples += numSamples;
 399     return 1;
 400 }
 401 
 402 /* Add the input samples to the input buffer. */
 403 static int addUnsignedCharSamplesToInputBuffer(sonicStream stream,
 404     unsigned char* samples,
 405     int numSamples) {
 406     short* buffer;
 407     int count = numSamples * stream->numChannels;
 408 
 409     if (numSamples == 0) {
 410         return 1;
 411     }
 412     if (!enlargeInputBufferIfNeeded(stream, numSamples)) {
 413         return 0;
 414     }
 415     buffer = stream->inputBuffer + stream->numInputSamples * stream->numChannels;
 416     while (count--) {
 417         *buffer++ = (*samples++ - 128) << 8;
 418     }
 419     stream->numInputSamples += numSamples;
 420     return 1;
 421 }
 422 
 423 /* Remove input samples that we have already processed. */
 424 static void removeInputSamples(sonicStream stream, int position) {
 425     int remainingSamples = stream->numInputSamples - position;
 426 
 427     if (remainingSamples > 0) {
 428         memmove(stream->inputBuffer,
 429             stream->inputBuffer + position * stream->numChannels,
 430             remainingSamples * sizeof(short) * stream->numChannels);
 431     }
 432     stream->numInputSamples = remainingSamples;
 433 }
 434 
 435 /* Just copy from the array to the output buffer */
 436 static int copyToOutput(sonicStream stream, short* samples, int numSamples) {
 437     if (!enlargeOutputBufferIfNeeded(stream, numSamples)) {
 438         return 0;
 439     }
 440     memcpy(stream->outputBuffer + stream->numOutputSamples * stream->numChannels,
 441         samples, numSamples * sizeof(short) * stream->numChannels);
 442     stream->numOutputSamples += numSamples;
 443     return 1;
 444 }
 445 
 446 /* Just copy from the input buffer to the output buffer.  Return 0 if we fail to
 447 resize the output buffer.  Otherwise, return numSamples */
 448 static int copyInputToOutput(sonicStream stream, int position) {
 449     int numSamples = stream->remainingInputToCopy;
 450 
 451     if (numSamples > stream->maxRequired) {
 452         numSamples = stream->maxRequired;
 453     }
 454     if (!copyToOutput(stream,
 455         stream->inputBuffer + position * stream->numChannels,
 456         numSamples)) {
 457         return 0;
 458     }
 459     stream->remainingInputToCopy -= numSamples;
 460     return numSamples;
 461 }
 462 
 463 /* Read data out of the stream.  Sometimes no data will be available, and zero
 464 is returned, which is not an error condition. */
 465 int sonicReadFloatFromStream(sonicStream stream, float* samples,
 466     int maxSamples) {
 467     int numSamples = stream->numOutputSamples;
 468     int remainingSamples = 0;
 469     short* buffer;
 470     int count;
 471 
 472     if (numSamples == 0) {
 473         return 0;
 474     }
 475     if (numSamples > maxSamples) {
 476         remainingSamples = numSamples - maxSamples;
 477         numSamples = maxSamples;
 478     }
 479     buffer = stream->outputBuffer;
 480     count = numSamples * stream->numChannels;
 481     while (count--) {
 482         *samples++ = (*buffer++) / 32767.0f;
 483     }
 484     if (remainingSamples > 0) {
 485         memmove(stream->outputBuffer,
 486             stream->outputBuffer + numSamples * stream->numChannels,
 487             remainingSamples * sizeof(short) * stream->numChannels);
 488     }
 489     stream->numOutputSamples = remainingSamples;
 490     return numSamples;
 491 }
 492 
 493 /* Read short data out of the stream.  Sometimes no data will be available, and
 494 zero is returned, which is not an error condition. */
 495 int sonicReadShortFromStream(sonicStream stream, short* samples,
 496     int maxSamples) {
 497     int numSamples = stream->numOutputSamples;
 498     int remainingSamples = 0;
 499 
 500     if (numSamples == 0) {
 501         return 0;
 502     }
 503     if (numSamples > maxSamples) {
 504         remainingSamples = numSamples - maxSamples;
 505         numSamples = maxSamples;
 506     }
 507     memcpy(samples, stream->outputBuffer,
 508         numSamples * sizeof(short) * stream->numChannels);
 509     if (remainingSamples > 0) {
 510         memmove(stream->outputBuffer,
 511             stream->outputBuffer + numSamples * stream->numChannels,
 512             remainingSamples * sizeof(short) * stream->numChannels);
 513     }
 514     stream->numOutputSamples = remainingSamples;
 515     return numSamples;
 516 }
 517 
 518 /* Read unsigned char data out of the stream.  Sometimes no data will be
 519 available, and zero is returned, which is not an error condition. */
 520 int sonicReadUnsignedCharFromStream(sonicStream stream, unsigned char* samples,
 521     int maxSamples) {
 522     int numSamples = stream->numOutputSamples;
 523     int remainingSamples = 0;
 524     short* buffer;
 525     int count;
 526 
 527     if (numSamples == 0) {
 528         return 0;
 529     }
 530     if (numSamples > maxSamples) {
 531         remainingSamples = numSamples - maxSamples;
 532         numSamples = maxSamples;
 533     }
 534     buffer = stream->outputBuffer;
 535     count = numSamples * stream->numChannels;
 536     while (count--) {
 537         *samples++ = (char)((*buffer++) >> 8) + 128;
 538     }
 539     if (remainingSamples > 0) {
 540         memmove(stream->outputBuffer,
 541             stream->outputBuffer + numSamples * stream->numChannels,
 542             remainingSamples * sizeof(short) * stream->numChannels);
 543     }
 544     stream->numOutputSamples = remainingSamples;
 545     return numSamples;
 546 }
 547 
 548 /* Force the sonic stream to generate output using whatever data it currently
 549 has.  No extra delay will be added to the output, but flushing in the middle
 550 of words could introduce distortion. */
 551 int sonicFlushStream(sonicStream stream) {
 552     int maxRequired = stream->maxRequired;
 553     int remainingSamples = stream->numInputSamples;
 554     float speed = stream->speed / stream->pitch;
 555     float rate = stream->rate * stream->pitch;
 556     int expectedOutputSamples =
 557         stream->numOutputSamples +
 558         (int)((remainingSamples / speed + stream->numPitchSamples) / rate + 0.5f);
 559 
 560     /* Add enough silence to flush both input and pitch buffers. */
 561     if (!enlargeInputBufferIfNeeded(stream, remainingSamples + 2 * maxRequired)) {
 562         return 0;
 563     }
 564     memset(stream->inputBuffer + remainingSamples * stream->numChannels, 0,
 565         2 * maxRequired * sizeof(short) * stream->numChannels);
 566     stream->numInputSamples += 2 * maxRequired;
 567     if (!sonicWriteShortToStream(stream, NULL, 0)) {
 568         return 0;
 569     }
 570     /* Throw away any extra samples we generated due to the silence we added */
 571     if (stream->numOutputSamples > expectedOutputSamples) {
 572         stream->numOutputSamples = expectedOutputSamples;
 573     }
 574     /* Empty input and pitch buffers */
 575     stream->numInputSamples = 0;
 576     stream->remainingInputToCopy = 0;
 577     stream->numPitchSamples = 0;
 578     return 1;
 579 }
 580 
 581 /* Return the number of samples in the output buffer */
 582 int sonicSamplesAvailable(sonicStream stream) {
 583     return stream->numOutputSamples;
 584 }
 585 
 586 /* If skip is greater than one, average skip samples together and write them to
 587 the down-sample buffer.  If numChannels is greater than one, mix the channels
 588 together as we down sample. */
 589 static void downSampleInput(sonicStream stream, short* samples, int skip) {
 590     int numSamples = stream->maxRequired / skip;
 591     int samplesPerValue = stream->numChannels * skip;
 592     int i, j;
 593     int value;
 594     short* downSamples = stream->downSampleBuffer;
 595 
 596     for (i = 0; i < numSamples; i++) {
 597         value = 0;
 598         for (j = 0; j < samplesPerValue; j++) {
 599             value += *samples++;
 600         }
 601         value /= samplesPerValue;
 602         *downSamples++ = value;
 603     }
 604 }
 605 
 606 /* Find the best frequency match in the range, and given a sample skip multiple.
 607 For now, just find the pitch of the first channel. */
 608 static int findPitchPeriodInRange(short* samples, int minPeriod, int maxPeriod,
 609     int* retMinDiff, int* retMaxDiff) {
 610     int period, bestPeriod = 0, worstPeriod = 255;
 611     short* s;
 612     short* p;
 613     short sVal, pVal;
 614     unsigned long diff, minDiff = 1, maxDiff = 0;
 615     int i;
 616 
 617     for (period = minPeriod; period <= maxPeriod; period++) {
 618         diff = 0;
 619         s = samples;
 620         p = samples + period;
 621         for (i = 0; i < period; i++) {
 622             sVal = *s++;
 623             pVal = *p++;
 624             diff += sVal >= pVal ? (unsigned short)(sVal - pVal)
 625                 : (unsigned short)(pVal - sVal);
 626         }
 627         /* Note that the highest number of samples we add into diff will be less
 628         than 256, since we skip samples.  Thus, diff is a 24 bit number, and
 629         we can safely multiply by numSamples without overflow */
 630         /* if (bestPeriod == 0 || (bestPeriod*3/2 > period && diff*bestPeriod <
 631         minDiff*period) || diff*bestPeriod < (minDiff >> 1)*period) {*/
 632         if (bestPeriod == 0 || diff * bestPeriod < minDiff * period) {
 633             minDiff = diff;
 634             bestPeriod = period;
 635         }
 636         if (diff * worstPeriod > maxDiff * period) {
 637             maxDiff = diff;
 638             worstPeriod = period;
 639         }
 640     }
 641     *retMinDiff = minDiff / bestPeriod;
 642     *retMaxDiff = maxDiff / worstPeriod;
 643     return bestPeriod;
 644 }
 645 
 646 /* At abrupt ends of voiced words, we can have pitch periods that are better
 647 approximated by the previous pitch period estimate.  Try to detect this case.
 648 */
 649 static int prevPeriodBetter(sonicStream stream, int period, int minDiff,
 650     int maxDiff, int preferNewPeriod) {
 651     if (minDiff == 0 || stream->prevPeriod == 0) {
 652         return 0;
 653     }
 654     if (preferNewPeriod) {
 655         if (maxDiff > minDiff * 3) {
 656             /* Got a reasonable match this period */
 657             return 0;
 658         }
 659         if (minDiff * 2 <= stream->prevMinDiff * 3) {
 660             /* Mismatch is not that much greater this period */
 661             return 0;
 662         }
 663     }
 664     else {
 665         if (minDiff <= stream->prevMinDiff) {
 666             return 0;
 667         }
 668     }
 669     return 1;
 670 }
 671 
 672 /* Find the pitch period.  This is a critical step, and we may have to try
 673 multiple ways to get a good answer.  This version uses Average Magnitude
 674 Difference Function (AMDF).  To improve speed, we down sample by an integer
 675 factor get in the 11KHz range, and then do it again with a narrower
 676 frequency range without down sampling */
 677 static int findPitchPeriod(sonicStream stream, short* samples,
 678     int preferNewPeriod) {
 679     int minPeriod = stream->minPeriod;
 680     int maxPeriod = stream->maxPeriod;
 681     int sampleRate = stream->sampleRate;
 682     int minDiff, maxDiff, retPeriod;
 683     int skip = 1;
 684     int period;
 685 
 686     if (sampleRate > SONIC_AMDF_FREQ && stream->quality == 0) {
 687         skip = sampleRate / SONIC_AMDF_FREQ;
 688     }
 689     if (stream->numChannels == 1 && skip == 1) {
 690         period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
 691             &maxDiff);
 692     }
 693     else {
 694         downSampleInput(stream, samples, skip);
 695         period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod / skip,
 696             maxPeriod / skip, &minDiff, &maxDiff);
 697         if (skip != 1) {
 698             period *= skip;
 699             minPeriod = period - (skip << 2);
 700             maxPeriod = period + (skip << 2);
 701             if (minPeriod < stream->minPeriod) {
 702                 minPeriod = stream->minPeriod;
 703             }
 704             if (maxPeriod > stream->maxPeriod) {
 705                 maxPeriod = stream->maxPeriod;
 706             }
 707             if (stream->numChannels == 1) {
 708                 period = findPitchPeriodInRange(samples, minPeriod, maxPeriod, &minDiff,
 709                     &maxDiff);
 710             }
 711             else {
 712                 downSampleInput(stream, samples, 1);
 713                 period = findPitchPeriodInRange(stream->downSampleBuffer, minPeriod,
 714                     maxPeriod, &minDiff, &maxDiff);
 715             }
 716         }
 717     }
 718     if (prevPeriodBetter(stream, period, minDiff, maxDiff, preferNewPeriod)) {
 719         retPeriod = stream->prevPeriod;
 720     }
 721     else {
 722         retPeriod = period;
 723     }
 724     stream->prevMinDiff = minDiff;
 725     stream->prevPeriod = period;
 726     return retPeriod;
 727 }
 728 
 729 /* Overlap two sound segments, ramp the volume of one down, while ramping the
 730 other one from zero up, and add them, storing the result at the output. */
 731 static void overlapAdd(int numSamples, int numChannels, short* out,
 732     short* rampDown, short* rampUp) {
 733     short* o;
 734     short* u;
 735     short* d;
 736     int i, t;
 737 
 738     for (i = 0; i < numChannels; i++) {
 739         o = out + i;
 740         u = rampUp + i;
 741         d = rampDown + i;
 742         for (t = 0; t < numSamples; t++) {
 743 #ifdef SONIC_USE_SIN
 744             float ratio = sin(t * M_PI / (2 * numSamples));
 745             *o = *d * (1.0f - ratio) + *u * ratio;
 746 #else
 747             *o = (*d * (numSamples - t) + *u * t) / numSamples;
 748 #endif
 749             o += numChannels;
 750             d += numChannels;
 751             u += numChannels;
 752         }
 753     }
 754 }
 755 
 756 /* Overlap two sound segments, ramp the volume of one down, while ramping the
 757 other one from zero up, and add them, storing the result at the output. */
 758 static void overlapAddWithSeparation(int numSamples, int numChannels,
 759     int separation, short* out,
 760     short* rampDown, short* rampUp) {
 761     short *o, *u, *d;
 762     int i, t;
 763 
 764     for (i = 0; i < numChannels; i++) {
 765         o = out + i;
 766         u = rampUp + i;
 767         d = rampDown + i;
 768         for (t = 0; t < numSamples + separation; t++) {
 769             if (t < separation) {
 770                 *o = *d * (numSamples - t) / numSamples;
 771                 d += numChannels;
 772             }
 773             else if (t < numSamples) {
 774                 *o = (*d * (numSamples - t) + *u * (t - separation)) / numSamples;
 775                 d += numChannels;
 776                 u += numChannels;
 777             }
 778             else {
 779                 *o = *u * (t - separation) / numSamples;
 780                 u += numChannels;
 781             }
 782             o += numChannels;
 783         }
 784     }
 785 }
 786 
 787 /* Just move the new samples in the output buffer to the pitch buffer */
 788 static int moveNewSamplesToPitchBuffer(sonicStream stream,
 789     int originalNumOutputSamples) {
 790     int numSamples = stream->numOutputSamples - originalNumOutputSamples;
 791     int numChannels = stream->numChannels;
 792 
 793     if (stream->numPitchSamples + numSamples > stream->pitchBufferSize) {
 794         stream->pitchBufferSize += (stream->pitchBufferSize >> 1) + numSamples;
 795         stream->pitchBuffer =
 796             (short*)realloc(stream->pitchBuffer,
 797                 stream->pitchBufferSize * sizeof(short) * numChannels);
 798         if (stream->pitchBuffer == NULL) {
 799             return 0;
 800         }
 801     }
 802     memcpy(stream->pitchBuffer + stream->numPitchSamples * numChannels,
 803         stream->outputBuffer + originalNumOutputSamples * numChannels,
 804         numSamples * sizeof(short) * numChannels);
 805     stream->numOutputSamples = originalNumOutputSamples;
 806     stream->numPitchSamples += numSamples;
 807     return 1;
 808 }
 809 
 810 /* Remove processed samples from the pitch buffer. */
 811 static void removePitchSamples(sonicStream stream, int numSamples) {
 812     int numChannels = stream->numChannels;
 813     short* source = stream->pitchBuffer + numSamples * numChannels;
 814 
 815     if (numSamples == 0) {
 816         return;
 817     }
 818     if (numSamples != stream->numPitchSamples) {
 819         memmove(
 820             stream->pitchBuffer, source,
 821             (stream->numPitchSamples - numSamples) * sizeof(short) * numChannels);
 822     }
 823     stream->numPitchSamples -= numSamples;
 824 }
 825 
 826 /* Change the pitch.  The latency this introduces could be reduced by looking at
 827 past samples to determine pitch, rather than future. */
 828 static int adjustPitch(sonicStream stream, int originalNumOutputSamples) {
 829     float pitch = stream->pitch;
 830     int numChannels = stream->numChannels;
 831     int period, newPeriod, separation;
 832     int position = 0;
 833     short* out;
 834     short* rampDown;
 835     short* rampUp;
 836 
 837     if (stream->numOutputSamples == originalNumOutputSamples) {
 838         return 1;
 839     }
 840     if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
 841         return 0;
 842     }
 843     while (stream->numPitchSamples - position >= stream->maxRequired) {
 844         period = findPitchPeriod(stream,
 845             stream->pitchBuffer + position * numChannels, 0);
 846         newPeriod = period / pitch;
 847         if (!enlargeOutputBufferIfNeeded(stream, newPeriod)) {
 848             return 0;
 849         }
 850         out = stream->outputBuffer + stream->numOutputSamples * numChannels;
 851         if (pitch >= 1.0f) {
 852             rampDown = stream->pitchBuffer + position * numChannels;
 853             rampUp =
 854                 stream->pitchBuffer + (position + period - newPeriod) * numChannels;
 855             overlapAdd(newPeriod, numChannels, out, rampDown, rampUp);
 856         }
 857         else {
 858             rampDown = stream->pitchBuffer + position * numChannels;
 859             rampUp = stream->pitchBuffer + position * numChannels;
 860             separation = newPeriod - period;
 861             overlapAddWithSeparation(period, numChannels, separation, out, rampDown,
 862                 rampUp);
 863         }
 864         stream->numOutputSamples += newPeriod;
 865         position += period;
 866     }
 867     removePitchSamples(stream, position);
 868     return 1;
 869 }
 870 
 871 /* Aproximate the sinc function times a Hann window from the sinc table. */
 872 static int findSincCoefficient(int i, int ratio, int width) {
 873     int lobePoints = (SINC_TABLE_SIZE - 1) / SINC_FILTER_POINTS;
 874     int left = i * lobePoints + (ratio * lobePoints) / width;
 875     int right = left + 1;
 876     int position = i * lobePoints * width + ratio * lobePoints - left * width;
 877     int leftVal = sincTable[left];
 878     int rightVal = sincTable[right];
 879 
 880     return ((leftVal * (width - position) + rightVal * position) << 1) / width;
 881 }
 882 
 883 /* Return 1 if value >= 0, else -1.  This represents the sign of value. */
 884 static int getSign(int value) { return value >= 0 ? 1 : -1; }
 885 
 886 /* Interpolate the new output sample. */
 887 static short interpolate(sonicStream stream, short* in, int oldSampleRate,
 888     int newSampleRate) {
 889     /* Compute N-point sinc FIR-filter here.  Clip rather than overflow. */
 890     int i;
 891     int total = 0;
 892     int position = stream->newRatePosition * oldSampleRate;
 893     int leftPosition = stream->oldRatePosition * newSampleRate;
 894     int rightPosition = (stream->oldRatePosition + 1) * newSampleRate;
 895     int ratio = rightPosition - position - 1;
 896     int width = rightPosition - leftPosition;
 897     int weight, value;
 898     int oldSign;
 899     int overflowCount = 0;
 900 
 901     for (i = 0; i < SINC_FILTER_POINTS; i++) {
 902         weight = findSincCoefficient(i, ratio, width);
 903         /* printf("%u %f\n", i, weight); */
 904         value = in[i * stream->numChannels] * weight;
 905         oldSign = getSign(total);
 906         total += value;
 907         if (oldSign != getSign(total) && getSign(value) == oldSign) {
 908             /* We must have overflowed.  This can happen with a sinc filter. */
 909             overflowCount += oldSign;
 910         }
 911     }
 912     /* It is better to clip than to wrap if there was a overflow. */
 913     if (overflowCount > 0) {
 914         return SHRT_MAX;
 915     }
 916     else if (overflowCount < 0) {
 917         return SHRT_MIN;
 918     }
 919     return total >> 16;
 920 }
 921 
 922 /* Change the rate.  Interpolate with a sinc FIR filter using a Hann window. */
 923 static int adjustRate(sonicStream stream, float rate,
 924     int originalNumOutputSamples) {
 925     int newSampleRate = stream->sampleRate / rate;
 926     int oldSampleRate = stream->sampleRate;
 927     int numChannels = stream->numChannels;
 928     int position = 0;
 929     short *in, *out;
 930     int i;
 931     int N = SINC_FILTER_POINTS;
 932 
 933     /* Set these values to help with the integer math */
 934     while (newSampleRate > (1 << 14) || oldSampleRate > (1 << 14)) {
 935         newSampleRate >>= 1;
 936         oldSampleRate >>= 1;
 937     }
 938     if (stream->numOutputSamples == originalNumOutputSamples) {
 939         return 1;
 940     }
 941     if (!moveNewSamplesToPitchBuffer(stream, originalNumOutputSamples)) {
 942         return 0;
 943     }
 944     /* Leave at least N pitch sample in the buffer */
 945     for (position = 0; position < stream->numPitchSamples - N; position++) {
 946         while ((stream->oldRatePosition + 1) * newSampleRate >
 947             stream->newRatePosition * oldSampleRate) {
 948             if (!enlargeOutputBufferIfNeeded(stream, 1)) {
 949                 return 0;
 950             }
 951             out = stream->outputBuffer + stream->numOutputSamples * numChannels;
 952             in = stream->pitchBuffer + position * numChannels;
 953             for (i = 0; i < numChannels; i++) {
 954                 *out++ = interpolate(stream, in, oldSampleRate, newSampleRate);
 955                 in++;
 956             }
 957             stream->newRatePosition++;
 958             stream->numOutputSamples++;
 959         }
 960         stream->oldRatePosition++;
 961         if (stream->oldRatePosition == oldSampleRate) {
 962             stream->oldRatePosition = 0;
 963             if (stream->newRatePosition != newSampleRate) {
 964                 fprintf(stderr,
 965                     "Assertion failed: stream->newRatePosition != newSampleRate\n");
 966                 exit(1);
 967             }
 968             stream->newRatePosition = 0;
 969         }
 970     }
 971     removePitchSamples(stream, position);
 972     return 1;
 973 }
 974 
 975 /* Skip over a pitch period, and copy period/speed samples to the output */
 976 static int skipPitchPeriod(sonicStream stream, short* samples, float speed,
 977     int period) {
 978     long newSamples;
 979     int numChannels = stream->numChannels;
 980 
 981     if (speed >= 2.0f) {
 982         newSamples = period / (speed - 1.0f);
 983     }
 984     else {
 985         newSamples = period;
 986         stream->remainingInputToCopy = period * (2.0f - speed) / (speed - 1.0f);
 987     }
 988     if (!enlargeOutputBufferIfNeeded(stream, newSamples)) {
 989         return 0;
 990     }
 991     overlapAdd(newSamples, numChannels,
 992         stream->outputBuffer + stream->numOutputSamples * numChannels,
 993         samples, samples + period * numChannels);
 994     stream->numOutputSamples += newSamples;
 995     return newSamples;
 996 }
 997 
 998 /* Insert a pitch period, and determine how much input to copy directly. */
 999 static int insertPitchPeriod(sonicStream stream, short* samples, float speed,
1000     int period) {
1001     long newSamples;
1002     short* out;
1003     int numChannels = stream->numChannels;
1004 
1005     if (speed < 0.5f) {
1006         newSamples = period * speed / (1.0f - speed);
1007     }
1008     else {
1009         newSamples = period;
1010         stream->remainingInputToCopy =
1011             period * (2.0f * speed - 1.0f) / (1.0f - speed);
1012     }
1013     if (!enlargeOutputBufferIfNeeded(stream, period + newSamples)) {
1014         return 0;
1015     }
1016     out = stream->outputBuffer + stream->numOutputSamples * numChannels;
1017     memcpy(out, samples, period * sizeof(short) * numChannels);
1018     out =
1019         stream->outputBuffer + (stream->numOutputSamples + period) * numChannels;
1020     overlapAdd(newSamples, numChannels, out, samples + period * numChannels,
1021         samples);
1022     stream->numOutputSamples += period + newSamples;
1023     return newSamples;
1024 }
1025 
1026 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1027 we fail to resize an input or output buffer. */
1028 static int changeSpeed(sonicStream stream, float speed) {
1029     short* samples;
1030     int numSamples = stream->numInputSamples;
1031     int position = 0, period, newSamples;
1032     int maxRequired = stream->maxRequired;
1033 
1034     /* printf("Changing speed to %f\n", speed); */
1035     if (stream->numInputSamples < maxRequired) {
1036         return 1;
1037     }
1038     do {
1039         if (stream->remainingInputToCopy > 0) {
1040             newSamples = copyInputToOutput(stream, position);
1041             position += newSamples;
1042         }
1043         else {
1044             samples = stream->inputBuffer + position * stream->numChannels;
1045             period = findPitchPeriod(stream, samples, 1);
1046 #ifdef SONIC_SPECTROGRAM
1047             if (stream->spectrogram != NULL) {
1048                 sonicAddPitchPeriodToSpectrogram(stream->spectrogram, samples, period,
1049                     stream->numChannels);
1050                 newSamples = period;
1051                 position += period;
1052             }
1053             else
1054 #endif  /* SONIC_SPECTROGRAM */
1055                 if (speed > 1.0) {
1056                     newSamples = skipPitchPeriod(stream, samples, speed, period);
1057                     position += period + newSamples;
1058                 }
1059                 else {
1060                     newSamples = insertPitchPeriod(stream, samples, speed, period);
1061                     position += newSamples;
1062                 }
1063         }
1064         if (newSamples == 0) {
1065             return 0; /* Failed to resize output buffer */
1066         }
1067     } while (position + maxRequired <= numSamples);
1068     removeInputSamples(stream, position);
1069     return 1;
1070 }
1071 
1072 /* Resample as many pitch periods as we have buffered on the input.  Return 0 if
1073 we fail to resize an input or output buffer.  Also scale the output by the
1074 volume. */
1075 static int processStreamInput(sonicStream stream) {
1076     int originalNumOutputSamples = stream->numOutputSamples;
1077     float speed = stream->speed / stream->pitch;
1078     float rate = stream->rate;
1079 
1080     if (!stream->useChordPitch) {
1081         rate *= stream->pitch;
1082     }
1083     if (speed > 1.00001 || speed < 0.99999) {
1084         changeSpeed(stream, speed);
1085     }
1086     else {
1087         if (!copyToOutput(stream, stream->inputBuffer, stream->numInputSamples)) {
1088             return 0;
1089         }
1090         stream->numInputSamples = 0;
1091     }
1092     if (stream->useChordPitch) {
1093         if (stream->pitch != 1.0f) {
1094             if (!adjustPitch(stream, originalNumOutputSamples)) {
1095                 return 0;
1096             }
1097         }
1098     }
1099     else if (rate != 1.0f) {
1100         if (!adjustRate(stream, rate, originalNumOutputSamples)) {
1101             return 0;
1102         }
1103     }
1104     if (stream->volume != 1.0f) {
1105         /* Adjust output volume. */
1106         scaleSamples(
1107             stream->outputBuffer + originalNumOutputSamples * stream->numChannels,
1108             (stream->numOutputSamples - originalNumOutputSamples) *
1109             stream->numChannels,
1110             stream->volume);
1111     }
1112     return 1;
1113 }
1114 
1115 /* Write floating point data to the input buffer and process it. */
1116 int sonicWriteFloatToStream(sonicStream stream, float* samples,
1117     int numSamples) {
1118     if (!addFloatSamplesToInputBuffer(stream, samples, numSamples)) {
1119         return 0;
1120     }
1121     return processStreamInput(stream);
1122 }
1123 
1124 /* Simple wrapper around sonicWriteFloatToStream that does the short to float
1125 conversion for you. */
1126 int sonicWriteShortToStream(sonicStream stream, short* samples,
1127     int numSamples) {
1128     if (!addShortSamplesToInputBuffer(stream, samples, numSamples)) {
1129         return 0;
1130     }
1131     return processStreamInput(stream);
1132 }
1133 
1134 /* Simple wrapper around sonicWriteFloatToStream that does the unsigned char to
1135 float conversion for you. */
1136 int sonicWriteUnsignedCharToStream(sonicStream stream, unsigned char* samples,
1137     int numSamples) {
1138     if (!addUnsignedCharSamplesToInputBuffer(stream, samples, numSamples)) {
1139         return 0;
1140     }
1141     return processStreamInput(stream);
1142 }
1143 
1144 /* This is a non-stream oriented interface to just change the speed of a sound
1145 * sample */
1146 int sonicChangeFloatSpeed(float* samples, int numSamples, float speed,
1147     float pitch, float rate, float volume,
1148     int useChordPitch, int sampleRate, int numChannels) {
1149     sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1150 
1151     sonicSetSpeed(stream, speed);
1152     sonicSetPitch(stream, pitch);
1153     sonicSetRate(stream, rate);
1154     sonicSetVolume(stream, volume);
1155     sonicSetChordPitch(stream, useChordPitch);
1156     sonicWriteFloatToStream(stream, samples, numSamples);
1157     sonicFlushStream(stream);
1158     numSamples = sonicSamplesAvailable(stream);
1159     sonicReadFloatFromStream(stream, samples, numSamples);
1160     sonicDestroyStream(stream);
1161     return numSamples;
1162 }
1163 
1164 /* This is a non-stream oriented interface to just change the speed of a sound
1165 * sample */
1166 int sonicChangeShortSpeed(short* samples, int numSamples, float speed,
1167     float pitch, float rate, float volume,
1168     int useChordPitch, int sampleRate, int numChannels) {
1169     sonicStream stream = sonicCreateStream(sampleRate, numChannels);
1170 
1171     sonicSetSpeed(stream, speed);
1172     sonicSetPitch(stream, pitch);
1173     sonicSetRate(stream, rate);
1174     sonicSetVolume(stream, volume);
1175     sonicSetChordPitch(stream, useChordPitch);
1176     sonicWriteShortToStream(stream, samples, numSamples);
1177     sonicFlushStream(stream);
1178     numSamples = sonicSamplesAvailable(stream);
1179     sonicReadShortFromStream(stream, samples, numSamples);
1180     sonicDestroyStream(stream);
1181     return numSamples;
1182 }

 

posted on 2022-09-19 19:28  邗影  阅读(109)  评论(0编辑  收藏  举报

导航