void *SoundTouch_init(void) { SoundTouch *soundTouch = new SoundTouch(); soundTouch->setSetting(SETTING_USE_QUICKSEEK, false); soundTouch->setSetting(SETTING_USE_AA_FILTER, true); return (void *)soundTouch; }
AVSStereoSoundTouch(PClip _child, float _tempo, float _rate, float _pitch, const AVSValue* args, IScriptEnvironment* env) : GenericVideoFilter(_child), tempo(_tempo/100.0f), rate(_rate/100.0f), pitch(_pitch/100.0f) { // last_nch = vi.AudioChannels(); dstbuffer = new SFLOAT[BUFFERSIZE * vi.AudioChannels()]; sample_multiplier = tempo / pitch; // Do it the same way the library does it! sample_multiplier *= pitch * rate; sampler = new SoundTouch(); sampler->setRate(rate); sampler->setTempo(tempo); sampler->setPitch(pitch); sampler->setChannels(2); sampler->setSampleRate(vi.audio_samples_per_second); AVSsoundtouch::setSettings(sampler, args, env); vi.num_audio_samples = (__int64)((long double)vi.num_audio_samples / sample_multiplier); next_sample = 0; // Next output sample inputReadOffset = 0; // Next input sample dst_samples_filled = 0; }
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { if (start != next_sample) { // Reset on seek sampler->clear(); next_sample = start; inputReadOffset = (__int64)(sample_multiplier * (long double)start); // Reset at new read position (NOT sample exact :( ). dst_samples_filled=0; } bool buffer_full = false; int samples_filled = 0; do { // Empty buffer if something is still left. if (dst_samples_filled) { int copysamples = min((int)count-samples_filled, dst_samples_filled); // Copy finished samples if (copysamples) { memcpy((BYTE*)buf+vi.BytesFromAudioSamples(samples_filled), (BYTE*)dstbuffer, (size_t)vi.BytesFromAudioSamples(copysamples)); samples_filled += copysamples; dst_samples_filled -= copysamples; // Move non-used samples memcpy(dstbuffer, &dstbuffer[copysamples*2], (size_t)vi.BytesFromAudioSamples(dst_samples_filled)); } if (samples_filled >= count) buffer_full = true; } // If buffer empty - refill if (dst_samples_filled==0) { // Read back samples from filter int samples_out = 0; int gotsamples = 0; do { gotsamples = sampler->receiveSamples(&dstbuffer[vi.BytesFromAudioSamples(samples_out)], BUFFERSIZE - samples_out); samples_out += gotsamples; } while (gotsamples > 0); dst_samples_filled = samples_out; if (!dst_samples_filled) { // We didn't get any samples // Feed new samples to filter child->GetAudio(dstbuffer, inputReadOffset, BUFFERSIZE, env); inputReadOffset += BUFFERSIZE; sampler->putSamples(dstbuffer, BUFFERSIZE); } // End if no samples } // end if empty buffer } while (!buffer_full); next_sample += count; }
void *vc_soundtouch_create(int rate, float pitch) { SoundTouch *st; st = new SoundTouch(); if (st) { st->setChannels(1); st->setSampleRate(rate); st->setPitchSemiTones(pitch); st->setSetting(SETTING_USE_QUICKSEEK, 1); st->setSetting(SETTING_USE_AA_FILTER, 1); } return st; }
void vc_voice_change(void *st_, float *fbuf, int16_t *data, int samples, int datalen) { SoundTouch *st = (SoundTouch *)st_; #if defined(INTEGER_SAMPLES) || defined(SOUNDTOUCH_INTEGER_SAMPLES) st->putSamples(data, samples); if (st->numSamples() >= samples) { st->receiveSamplesEx(data, samples); } else { memset(data, 0, datalen); } #elif defined(FLOAT_SAMPLES) || defined(SOUNDTOUCH_FLOAT_SAMPLES) slin_to_flin(fbuf, data, samples); st->putSamples(fbuf, samples); if ((int)st->numSamples() >= samples) { st->receiveSamples(fbuf, samples); flin_to_slin(data, fbuf, samples); } else { memset(data, 0, datalen); } #else # error "unknown soundtouch sample type" #endif }
void* audioConsumerNoStretch(void* param) { soundTouch.setSampleRate(GameFreq); soundTouch.setChannels(2); soundTouch.setSetting( SETTING_USE_QUICKSEEK, 1 ); soundTouch.setSetting( SETTING_USE_AA_FILTER, 1 ); double speedFactor = static_cast<double>(speed_factor)/100.0; soundTouch.setTempo(speedFactor); soundTouch.setRate((double)GameFreq/(double)OutputFreq); queueData* currQueueData = NULL; struct timespec prevTime; int lastSpeedFactor = speed_factor; //How long to wait for some data struct timespec waitTime; waitTime.tv_sec = 1; waitTime.tv_nsec = 0; while(!shutdown) { struct threadmsg msg; clock_gettime(CLOCK_MONOTONIC_RAW, &prevTime); int result = thread_queue_get(&audioConsumerQueue, &waitTime, &msg); if( result != ETIMEDOUT ) { currQueueData = (queueData*)msg.data; int dataLength = currQueueData->lenght; if(lastSpeedFactor != speed_factor) { lastSpeedFactor = speed_factor; double speedFactor = static_cast<double>(speed_factor)/100.0; soundTouch.setTempo(speedFactor); } processAudio(currQueueData->data, dataLength); free(currQueueData->data); free(currQueueData); } } return 0; }
unsigned int SoundTouch_receiveSamples(void *stouch, float *samples, unsigned int maxSamples) { SoundTouch *soundTouch = (SoundTouch *)stouch; return soundTouch->receiveSamples(samples, maxSamples); }
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setTempo(JNIEnv *env, jobject thiz, jlong handle, jfloat tempo) { SoundTouch *ptr = (SoundTouch*)handle; ptr->setTempo(tempo); }
void SoundTouch_setTempo(void *stouch, float tempo) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setTempo(tempo); }
void SoundTouch_setTempoChange(void *stouch, float percentTempo) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setTempoChange(percentTempo); }
void SoundTouch_setChannels(void *stouch, unsigned int channels) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setChannels(channels); }
void SoundTouch_setSampleRate(void *stouch, unsigned int sampleRate) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setSampleRate(sampleRate); }
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setSpeed(JNIEnv *env, jobject thiz, jlong handle, jfloat speed) { SoundTouch *ptr = (SoundTouch*)handle; ptr->setRate(speed); }
extern "C" DLL_PUBLIC void Java_net_surina_soundtouch_SoundTouch_setPitchSemiTones(JNIEnv *env, jobject thiz, jlong handle, jfloat pitch) { SoundTouch *ptr = (SoundTouch*)handle; ptr->setPitchSemiTones(pitch); }
void processAudio(const unsigned char* buffer, unsigned int length) { if (length < primaryBufferBytes) { unsigned int i; for ( i = 0 ; i < length ; i += 4 ) { if(SwapChannels == 0) { /* Left channel */ primaryBuffer[ i ] = buffer[ i + 2 ]; primaryBuffer[ i + 1 ] = buffer[ i + 3 ]; /* Right channel */ primaryBuffer[ i + 2 ] = buffer[ i ]; primaryBuffer[ i + 3 ] = buffer[ i + 1 ]; } else { /* Left channel */ primaryBuffer[ i ] = buffer[ i ]; primaryBuffer[ i + 1 ] = buffer[ i + 1 ]; /* Right channel */ primaryBuffer[ i + 2 ] = buffer[ i + 2 ]; primaryBuffer[ i + 3 ] = buffer[ i + 3 ]; } } } else DebugMessage(M64MSG_WARNING, "processAudio(): Audio primary buffer overflow."); #ifdef FP_ENABLED int numSamples = length/sizeof(short); short* primaryBufferShort = (short*)primaryBuffer; float primaryBufferFloat[numSamples]; for(int index = 0; index < numSamples; ++index) { primaryBufferFloat[index] = static_cast<float>(primaryBufferShort[index])/32767.0; } soundTouch.putSamples((SAMPLETYPE*)primaryBufferFloat, length/N64_SAMPLE_BYTES); #else soundTouch.putSamples((SAMPLETYPE*)primaryBuffer, length/N64_SAMPLE_BYTES); #endif int outSamples = 0; do { outSamples = soundTouch.receiveSamples((SAMPLETYPE*)secondaryBuffers[secondaryBufferIndex], SecondaryBufferSize); if(outSamples != 0 && lock.value > 0) { SLresult result = (*bufferQueue)->Enqueue(bufferQueue, secondaryBuffers[secondaryBufferIndex], outSamples*SLES_SAMPLE_BYTES); if(result != SL_RESULT_SUCCESS) { lock.errors++; } else { --lock.value; ++lock.count; } secondaryBufferIndex++; if(secondaryBufferIndex > (SecondaryBufferNbr-1)) secondaryBufferIndex = 0; } } while (outSamples != 0); }
void* audioConsumerStretch(void* param) { /* static int sequenceLenMS = 63; static int seekWindowMS = 16; static int overlapMS = 7;*/ soundTouch.setSampleRate((uint)GameFreq); soundTouch.setChannels(2); soundTouch.setSetting( SETTING_USE_QUICKSEEK, 1 ); soundTouch.setSetting( SETTING_USE_AA_FILTER, 1 ); //soundTouch.setSetting( SETTING_SEQUENCE_MS, sequenceLenMS ); //soundTouch.setSetting( SETTING_SEEKWINDOW_MS, seekWindowMS ); //soundTouch.setSetting( SETTING_OVERLAP_MS, overlapMS ); soundTouch.setRate((double)GameFreq/(double)OutputFreq); double speedFactor = static_cast<double>(speed_factor)/100.0; soundTouch.setTempo(speedFactor); double bufferMultiplier = (double)OutputFreq/DEFAULT_FREQUENCY; int maxQueueSize = (int)(TargetSecondaryBuffers + 30.0*bufferMultiplier); int minQueueSize = (int)(TargetSecondaryBuffers*bufferMultiplier); bool drainQueue = false; //Sound queue ran dry, device is running slow int ranDry = 0; //adjustment used when a device running too slow double slowAdjustment = 1.0; double currAdjustment = 1.0; //how quickly to return to original speed const double returnSpeed = 0.10; const double minSlowValue = 0.2; const double maxSlowValue = 3.0; const float maxSpeedUpRate = 0.5; const float slowRate = 0.05; queueData* currQueueData = NULL; struct timespec prevTime; struct timespec currTime; //How long to wait for some data struct timespec waitTime; waitTime.tv_sec = 1; waitTime.tv_nsec = 0; int feedTimeWindowSize = 50; int feedTimeIndex = 0; bool feedTimesSet = false; float feedTimes[feedTimeWindowSize]; float gameTimes[feedTimeWindowSize]; float averageGameTime = 0.01666; float averageFeedTime = 0.01666; while(!shutdown) { int slesQueueLength = lock.limit - lock.value; ranDry = slesQueueLength < minQueueSize; struct threadmsg msg; clock_gettime(CLOCK_MONOTONIC_RAW, &prevTime); int result = thread_queue_get(&audioConsumerQueue, &waitTime, &msg); if( result != ETIMEDOUT ) { int threadQueueLength = thread_queue_length(&audioConsumerQueue); currQueueData = (queueData*)msg.data; int dataLength = currQueueData->lenght; float temp = averageGameTime/averageFeedTime; // Only start running algorithm once enough samples are built if(lock.count <= lock.limit) { double speedFactor = static_cast<double>(speed_factor)/100.0; soundTouch.setTempo(speedFactor); processAudio(currQueueData->data, dataLength); free(currQueueData->data); free(currQueueData); //Useful logging //if(slesQueueLength == 0) //{ //DebugMessage(M64MSG_ERROR, "sles_length=%d, min_size=%d, count=%d", // slesQueueLength, minQueueSize, lock.count); //} } else{ //Game is running too fast speed up audio if ((slesQueueLength > maxQueueSize || drainQueue) && !ranDry) { drainQueue = true; currAdjustment = temp + (float) (slesQueueLength - minQueueSize) / (float) (lock.limit - minQueueSize) * maxSpeedUpRate; } //Device can't keep up with the game or we have too much in the queue after slowing it down else if (ranDry) { drainQueue = false; currAdjustment = temp - slowRate; } else if (!ranDry && slesQueueLength < maxQueueSize) { currAdjustment = temp; } //Allow the tempo to slow quickly with no minimum value change, but restore original tempo more slowly. if (currAdjustment > minSlowValue && currAdjustment < maxSlowValue) { slowAdjustment = currAdjustment; static const int increments = 4; //Adjust tempo in x% increments so it's more steady int temp2 = ((int) (slowAdjustment * 100)) / increments; temp2 *= increments; slowAdjustment = ((double) temp2) / 100; soundTouch.setTempo(slowAdjustment); } processAudio(currQueueData->data, dataLength); free(currQueueData->data); free(currQueueData); //Useful logging //if(slesQueueLength == 0) //{ //DebugMessage(M64MSG_ERROR, "sles_length=%d, thread_length=%d, dry=%d, slow_adj=%f, curr_adj=%f, temp=%f, feed_time=%f, game_time=%f, min_size=%d, count=%d", // slesQueueLength, threadQueueLength, ranDry, slowAdjustment, currAdjustment, temp, averageFeedTime, averageGameTime, minQueueSize, lock.count); //} //We don't want to calculate the average until we give everything a time to settle. //Calculate rates clock_gettime(CLOCK_MONOTONIC_RAW, &currTime); //Figure out how much to slow down by float timeDiff = TimeDiff(&currTime, &prevTime); //sometimes this ends up as less than 0, not sure how if (timeDiff > 0) { feedTimes[feedTimeIndex] = timeDiff; } averageFeedTime = GetAverageTime(feedTimes, feedTimesSet ? feedTimeWindowSize : (feedTimeIndex + 1)); gameTimes[feedTimeIndex] = (float) dataLength / (float) N64_SAMPLE_BYTES / (float) GameFreq; averageGameTime = GetAverageTime(gameTimes, feedTimesSet ? feedTimeWindowSize : (feedTimeIndex + 1)); ++feedTimeIndex; if (feedTimeIndex == feedTimeWindowSize) { feedTimeIndex = 0; feedTimesSet = true; } } } } return 0; }
void SoundTouch_setPitchSemiTones(void *stouch, float semiTones) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setPitchSemiTones(semiTones); }
void SoundTouch_putSamples(void *stouch, float *samples, unsigned int numSamples) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->putSamples(samples, numSamples); }
static void ChangePcmTone(FILE *inFile, FILE *outFile, float TempoChange, float PitchSemiTones, float RateChange) { SoundTouch soundTouch; // Setup the 'SoundTouch' object for processing the sound soundTouch.setSampleRate(8000); soundTouch.setChannels(1); soundTouch.setTempoChange(TempoChange); soundTouch.setPitchSemiTones(PitchSemiTones); //’˝œÚ «x÷·¿≠≥§£¨4±»Ωœ¿ÌœÎ soundTouch.setRateChange(RateChange); //’˝œÚ «x÷·¿≠≥§£¨50 //soundTouch.setTempoChange(20); //soundTouch.setPitchSemiTones(6.0f); //’˝œÚ «x÷·¿≠≥§£¨4±»Ωœ¿ÌœÎ //soundTouch.setRateChange(0); //’˝œÚ «x÷·¿≠≥§£¨50 //soundTouch.setTempoChange(1.0); //soundTouch.setPitchSemiTones(20); //soundTouch.setRateChange(-2.0); soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0); soundTouch.setSetting(SETTING_USE_AA_FILTER, 1); soundTouch.setSetting(SETTING_SEQUENCE_MS, 40); soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15); soundTouch.setSetting(SETTING_OVERLAP_MS, 8); #if 0 if (params->speech) { // use settings for speech processing soundTouch.setSetting(SETTING_SEQUENCE_MS, 40); soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15); soundTouch.setSetting(SETTING_OVERLAP_MS, 8); fprintf(stderr, "Tune processing parameters for speech processing.\n"); } #endif // Process the sound process(&soundTouch, inFile, outFile); fflush(outFile); }