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; }
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* 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_setTempo(void *stouch, float tempo) { SoundTouch *soundTouch = (SoundTouch *)stouch; soundTouch->setTempo(tempo); }
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); }