工程引入头文件和源文件
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 }
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,numChannels,return,stream,int,变调,音频,numSamples,samples From: https://www.cnblogs.com/8335IT/p/16708777.html