void World_NonRealTimeSynthesis(struct World *world, WorldOptions *inOptions) { if (inOptions->mLoadGraphDefs) { World_LoadGraphDefs(world); } int bufLength = world->mBufLength; int fileBufFrames = inOptions->mPreferredHardwareBufferFrameSize; if (fileBufFrames <= 0) fileBufFrames = 8192; int bufMultiple = (fileBufFrames + bufLength - 1) / bufLength; fileBufFrames = bufMultiple * bufLength; // batch process non real time audio if (!inOptions->mNonRealTimeOutputFilename) throw std::runtime_error("Non real time output filename is NULL.\n"); SF_INFO inputFileInfo, outputFileInfo; float *inputFileBuf = 0; float *outputFileBuf = 0; int numInputChannels = 0; int numOutputChannels; outputFileInfo.samplerate = inOptions->mPreferredSampleRate; numOutputChannels = outputFileInfo.channels = world->mNumOutputs; sndfileFormatInfoFromStrings(&outputFileInfo, inOptions->mNonRealTimeOutputHeaderFormat, inOptions->mNonRealTimeOutputSampleFormat); world->hw->mNRTOutputFile = sf_open(inOptions->mNonRealTimeOutputFilename, SFM_WRITE, &outputFileInfo); sf_command(world->hw->mNRTOutputFile, SFC_SET_CLIPPING, NULL, SF_TRUE); if (!world->hw->mNRTOutputFile) throw std::runtime_error("Couldn't open non real time output file.\n"); outputFileBuf = (float*)calloc(1, world->mNumOutputs * fileBufFrames * sizeof(float)); if (inOptions->mNonRealTimeInputFilename) { world->hw->mNRTInputFile = sf_open(inOptions->mNonRealTimeInputFilename, SFM_READ, &inputFileInfo); if (!world->hw->mNRTInputFile) throw std::runtime_error("Couldn't open non real time input file.\n"); inputFileBuf = (float*)calloc(1, inputFileInfo.channels * fileBufFrames * sizeof(float)); if (world->mNumInputs != (uint32)inputFileInfo.channels) scprintf("WARNING: input file channels didn't match number of inputs specified in options.\n"); numInputChannels = world->mNumInputs = inputFileInfo.channels; // force it. if (inputFileInfo.samplerate != (int)inOptions->mPreferredSampleRate) scprintf("WARNING: input file sample rate does not equal output sample rate.\n"); } else { world->hw->mNRTInputFile = 0; } FILE *cmdFile; if (inOptions->mNonRealTimeCmdFilename) { #ifdef _WIN32 cmdFile = fopen(inOptions->mNonRealTimeCmdFilename, "rb"); #else cmdFile = fopen(inOptions->mNonRealTimeCmdFilename, "r"); #endif } else cmdFile = stdin; if (!cmdFile) throw std::runtime_error("Couldn't open non real time command file.\n"); OSC_Packet packet; memset(&packet, 0, sizeof(packet)); packet.mData = (char *)malloc(8192); packet.mIsBundle = true; packet.mReplyAddr.mReplyFunc = null_reply_func; int64 schedTime; if (nextOSCPacket(cmdFile, &packet, schedTime)) throw std::runtime_error("command file empty.\n"); int64 prevTime = schedTime; World_SetSampleRate(world, inOptions->mPreferredSampleRate); World_Start(world); int64 oscTime = 0; double oscToSeconds = 1. / pow(2.,32.); double oscToSamples = inOptions->mPreferredSampleRate * oscToSeconds; int64 oscInc = (int64)((double)bufLength / oscToSamples); if(inOptions->mVerbosity >= 0) { printf("start time %g\n", schedTime * oscToSeconds); } bool run = true; int inBufStep = numInputChannels * bufLength; int outBufStep = numOutputChannels * bufLength; float* inputBuses = world->mAudioBus + world->mNumOutputs * bufLength; float* outputBuses = world->mAudioBus; int32* inputTouched = world->mAudioBusTouched + world->mNumOutputs; int32* outputTouched = world->mAudioBusTouched; for (; run;) { int bufFramesCalculated = 0; float* inBufPos = inputFileBuf; float* outBufPos = outputFileBuf; if (world->hw->mNRTInputFile) { int framesRead = sf_readf_float(world->hw->mNRTInputFile, inputFileBuf, fileBufFrames); if (framesRead < fileBufFrames) { memset(inputFileBuf + framesRead * numInputChannels, 0, (fileBufFrames - framesRead) * numInputChannels * sizeof(float)); } } for (int i=0; i<bufMultiple && run; ++i) { int bufCounter = world->mBufCounter; // deinterleave input to input buses if (inputFileBuf) { float *inBus = inputBuses; for (int j=0; j<numInputChannels; ++j, inBus += bufLength) { float *inFileBufPtr = inBufPos + j; for (int k=0; k<bufLength; ++k) { inBus[k] = *inFileBufPtr; inFileBufPtr += numInputChannels; } inputTouched[j] = bufCounter; } } // execute ready commands int64 nextTime = oscTime + oscInc; while (schedTime <= nextTime) { float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5; float diffTimeFloor = floor(diffTime); world->mSampleOffset = (int)diffTimeFloor; world->mSubsampleOffset = diffTime - diffTimeFloor; if (world->mSampleOffset < 0) world->mSampleOffset = 0; else if (world->mSampleOffset >= bufLength) world->mSampleOffset = bufLength-1; PerformOSCBundle(world, &packet); if (nextOSCPacket(cmdFile, &packet, schedTime)) { run = false; break; } if(inOptions->mVerbosity >= 0) { printf("nextOSCPacket %g\n", schedTime * oscToSeconds); } if (schedTime < prevTime) { scprintf("ERROR: Packet time stamps out-of-order.\n"); run = false; goto Bail; } prevTime = schedTime; } World_Run(world); // interleave output to output buffer float *outBus = outputBuses; for (int j=0; j<numOutputChannels; ++j, outBus += bufLength) { float *outFileBufPtr = outBufPos + j; if (outputTouched[j] == bufCounter) { for (int k=0; k<bufLength; ++k) { *outFileBufPtr = outBus[k]; outFileBufPtr += numOutputChannels; } } else { for (int k=0; k<bufLength; ++k) { *outFileBufPtr = 0.f; outFileBufPtr += numOutputChannels; } } } bufFramesCalculated += bufLength; inBufPos += inBufStep; outBufPos += outBufStep; world->mBufCounter++; oscTime = nextTime; } Bail: // write output sf_writef_float(world->hw->mNRTOutputFile, outputFileBuf, bufFramesCalculated); } if (cmdFile != stdin) fclose(cmdFile); sf_close(world->hw->mNRTOutputFile); world->hw->mNRTOutputFile = 0; if (world->hw->mNRTInputFile) { sf_close(world->hw->mNRTInputFile); world->hw->mNRTInputFile = 0; } free(packet.mData); World_Cleanup(world,true); }
void SC_JackDriver::Run() { jack_client_t* client = mClient; World* world = mWorld; #ifdef SC_JACK_USE_DLL mDLL.Update(secondsSinceEpoch(getTime())); #if SC_JACK_DEBUG_DLL static int tick = 0; if (++tick >= 10) { tick = 0; scprintf("DLL: t %.6f p %.9f sr %.6f e %.9f avg(e) %.9f inc %.9f\n", mDLL.PeriodTime(), mDLL.Period(), mDLL.SampleRate(), mDLL.Error(), mDLL.AvgError(), mOSCincrement * kOSCtoSecs); } #endif #else HostTime hostTime = getTime(); double hostSecs = secondsSinceEpoch(hostTime); double sampleTime = (double)(jack_frame_time(client) + jack_frames_since_cycle_start(client)); if (mStartHostSecs == 0) { mStartHostSecs = hostSecs; mStartSampleTime = sampleTime; } else { double instSampleRate = (sampleTime - mPrevSampleTime) / (hostSecs - mPrevHostSecs); double smoothSampleRate = mSmoothSampleRate; smoothSampleRate = smoothSampleRate + 0.002 * (instSampleRate - smoothSampleRate); if (fabs(smoothSampleRate - mSampleRate) > 10.) { smoothSampleRate = mSampleRate; } mOSCincrement = (int64)(mOSCincrementNumerator / smoothSampleRate); mSmoothSampleRate = smoothSampleRate; #if 0 double avgSampleRate = (sampleTime - mStartSampleTime)/(hostSecs - mStartHostSecs); double jitter = (smoothSampleRate * (hostSecs - mPrevHostSecs)) - (sampleTime - mPrevSampleTime); double drift = (smoothSampleRate - mSampleRate) * (hostSecs - mStartHostSecs); #endif } mPrevHostSecs = hostSecs; mPrevSampleTime = sampleTime; #endif try { mFromEngine.Free(); mToEngine.Perform(); mOscPacketsToEngine.Perform(); int numInputs = mInputList->mSize; int numOutputs = mOutputList->mSize; jack_port_t **inPorts = mInputList->mPorts; jack_port_t **outPorts = mOutputList->mPorts; sc_jack_sample_t **inBuffers = mInputList->mBuffers; sc_jack_sample_t **outBuffers = mOutputList->mBuffers; int numSamples = NumSamplesPerCallback(); int bufFrames = mWorld->mBufLength; int numBufs = numSamples / bufFrames; float *inBuses = mWorld->mAudioBus + mWorld->mNumOutputs * bufFrames; float *outBuses = mWorld->mAudioBus; int32 *inTouched = mWorld->mAudioBusTouched + mWorld->mNumOutputs; int32 *outTouched = mWorld->mAudioBusTouched; int minInputs = sc_min(numInputs, (int)mWorld->mNumInputs); int minOutputs = sc_min(numOutputs, (int)mWorld->mNumOutputs); int bufFramePos = 0; // cache I/O buffers for (int i = 0; i < minInputs; ++i) { inBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(inPorts[i], numSamples); } for (int i = 0; i < minOutputs; ++i) { outBuffers[i] = (sc_jack_sample_t*)jack_port_get_buffer(outPorts[i], numSamples); } // main loop #ifdef SC_JACK_USE_DLL int64 oscTime = mOSCbuftime = (int64)((mDLL.PeriodTime() - mMaxOutputLatency) * kSecondsToOSCunits + .5); // int64 oscInc = mOSCincrement = (int64)(mOSCincrementNumerator / mDLL.SampleRate()); int64 oscInc = mOSCincrement = (int64)((mDLL.Period() / numBufs) * kSecondsToOSCunits + .5); mSmoothSampleRate = mDLL.SampleRate(); double oscToSamples = mOSCtoSamples = mSmoothSampleRate * kOSCtoSecs /* 1/pow(2,32) */; #else int64 oscTime = mOSCbuftime = OSCTime(hostTime) - (int64)(mMaxOutputLatency * kSecondsToOSCunits + .5); int64 oscInc = mOSCincrement; double oscToSamples = mOSCtoSamples; #endif for (int i = 0; i < numBufs; ++i, mWorld->mBufCounter++, bufFramePos += bufFrames) { int32 bufCounter = mWorld->mBufCounter; int32 *tch; // copy+touch inputs tch = inTouched; for (int k = 0; k < minInputs; ++k) { sc_jack_sample_t *src = inBuffers[k] + bufFramePos; float *dst = inBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } *tch++ = bufCounter; } // run engine int64 schedTime; int64 nextTime = oscTime + oscInc; while ((schedTime = mScheduler.NextTime()) <= nextTime) { float diffTime = (float)(schedTime - oscTime) * oscToSamples + 0.5; float diffTimeFloor = floor(diffTime); world->mSampleOffset = (int)diffTimeFloor; world->mSubsampleOffset = diffTime - diffTimeFloor; if (world->mSampleOffset < 0) world->mSampleOffset = 0; else if (world->mSampleOffset >= world->mBufLength) world->mSampleOffset = world->mBufLength-1; SC_ScheduledEvent event = mScheduler.Remove(); event.Perform(); } world->mSampleOffset = 0; world->mSubsampleOffset = 0.f; World_Run(world); // copy touched outputs tch = outTouched; for (int k = 0; k < minOutputs; ++k) { sc_jack_sample_t *dst = outBuffers[k] + bufFramePos; if (*tch++ == bufCounter) { float *src = outBuses + k * bufFrames; for (int n = 0; n < bufFrames; ++n) { *dst++ = *src++; } } else { for (int n = 0; n < bufFrames; ++n) { *dst++ = 0.0f; } } } // advance OSC time mOSCbuftime = oscTime = nextTime; } } catch (std::exception& exc) { scprintf("%s: exception in real time: %s\n", kJackDriverIdent, exc.what()); } catch (...) { scprintf("%s: unknown exception in real time\n", kJackDriverIdent); } double cpuUsage = (double)jack_cpu_load(mClient); mAvgCPU = mAvgCPU + 0.1 * (cpuUsage - mAvgCPU); if (cpuUsage > mPeakCPU || --mPeakCounter <= 0) { mPeakCPU = cpuUsage; mPeakCounter = mMaxPeakCounter; } mAudioSync.Signal(); }