#include "pa_blocking.h" #if defined(WIN32) || defined(__MACH__) /* VL: ASIO full-duplex implementation */ #define INS 0 #define OUTS 1 int openOnce = 0; int paBlockingReadWriteOpen(ENVIRON *csound, PA_BLOCKING_STREAM **pabs_in, PA_BLOCKING_STREAM **pabs_out, PaStreamParameters *paParameters) { PaError paError = -1; unsigned long maxLag_ = 0; /* input & output streams */ PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *) mcalloc(sizeof(PA_BLOCKING_STREAM)*2); pabs[OUTS].csound = pabs[INS].csound = csound; pabs[OUTS].actualBufferSampleCount = pabs[INS].actualBufferSampleCount = csound->GetKsmps(csound) * csound->GetNchnls(csound); /* allocate input */ pabs[INS].actualBuffer = (float *) mcalloc(pabs->actualBufferSampleCount * sizeof(float)); /* allocate output */ pabs[OUTS].actualBuffer = (float *) mcalloc(pabs->actualBufferSampleCount * sizeof(float)); /* locks */ pabs[INS].paLock = csoundCreateThreadLock(csound); pabs[OUTS].paLock = csoundCreateThreadLock(csound); pabs[INS].clientLock = csoundCreateThreadLock(csound); pabs[OUTS].clientLock = csoundCreateThreadLock(csound); maxLag_ = O.oMaxLag <= 0 ? IODACSAMPS : O.oMaxLag; memcpy(&pabs[INS].paParameters, paParameters, sizeof(PaStreamParameters)); memcpy(&pabs[OUTS].paParameters, paParameters, sizeof(PaStreamParameters)); csound->Message(csound, "paBlockingReadWriteOpen: nchnls %d sr %f maxLag %u\n", paParameters->channelCount, csound->GetSr(csound), maxLag_); paError = Pa_OpenStream(&pabs[INS].paStream, &pabs[INS].paParameters, &pabs[OUTS].paParameters, (double) csound->GetSr(csound), maxLag_, paNoFlag, paBlockingReadWriteStreamCallback, pabs); if (paError == paNoError) { paError = Pa_StartStream(pabs[INS].paStream); pabs[OUTS].paStream = pabs[INS].paStream; } if (paError != paNoError) { mfree(pabs[INS].actualBuffer); mfree(pabs[OUTS].actualBuffer); mfree(pabs); *pabs_in = 0; *pabs_out = 0; } else { *pabs_in = &pabs[INS]; *pabs_out = &pabs[OUTS]; openOnce = 1; } return paError; } int paBlockingReadOpen(ENVIRON *csound, PA_BLOCKING_STREAM **pabs_, PaStreamParameters *paParameters) { PaError paError = -1; unsigned long maxLag_ = 0; PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *) mcalloc(sizeof(PA_BLOCKING_STREAM)); pabs->csound = csound; pabs->actualBufferSampleCount = csound->GetKsmps(csound) * csound->GetNchnls(csound); pabs->actualBuffer = (float *) mcalloc(pabs->actualBufferSampleCount * sizeof(float)); pabs->paLock = csoundCreateThreadLock(csound); pabs->clientLock = csoundCreateThreadLock(csound); maxLag_ = O.oMaxLag <= 0 ? IODACSAMPS : O.oMaxLag; memcpy(&pabs->paParameters, paParameters, sizeof(PaStreamParameters)); csound->Message(csound, "paBlockingReadOpen: nchnls %d sr %f maxLag %u\n", pabs->paParameters.channelCount, csound->GetSr(csound), maxLag_); paError = Pa_OpenStream(&pabs->paStream, &pabs->paParameters, 0, (double) csound->GetSr(csound), maxLag_, paNoFlag, paBlockingReadStreamCallback, /* VL fixed: was paBlockingWriteStreamCallback */ pabs); if (paError == paNoError) { paError = Pa_StartStream(pabs->paStream); } if (paError != paNoError) { mfree(pabs->actualBuffer); mfree(pabs); *pabs_ = 0; } else { *pabs_ = pabs; } return paError; } /* VL ASIO full-duplex implementation */ int paBlockingReadWriteStreamCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { size_t i; size_t n; PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *)userData; float *paInput = (float *)input; float *paOutput = (float *) output; csoundNotifyThreadLock(pabs[INS].csound, pabs[INS].clientLock); csoundWaitThreadLock(pabs[INS].csound, pabs[INS].paLock, 100); for (i = 0, n = pabs[INS].actualBufferSampleCount; i < n; i++) { pabs[INS].actualBuffer[i] = paInput[i]; } csoundNotifyThreadLock(pabs[OUTS].csound, pabs[OUTS].clientLock); csoundWaitThreadLock(pabs[OUTS].csound, pabs[OUTS].paLock, 100); for (i = 0, n = pabs[OUTS].actualBufferSampleCount; i < n; i++) { paOutput[i] = pabs[OUTS].actualBuffer[i]; } return paContinue; } /* VL fixed was:PaBlockingReadStreamCallback */ int paBlockingReadStreamCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { size_t i; size_t n; PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *)userData; float *paInput = (float *)input; csoundNotifyThreadLock(pabs->csound, pabs->clientLock); csoundWaitThreadLock(pabs->csound, pabs->paLock, 100); for (i = 0, n = pabs->actualBufferSampleCount; i < n; i++) { pabs->actualBuffer[i] = paInput[i]; } return paContinue; } void paBlockingRead(PA_BLOCKING_STREAM *pabs, MYFLT *buffer) { size_t i; size_t n; csoundWaitThreadLock(pabs->csound, pabs->clientLock, 100); for (i = 0, n = pabs->actualBufferSampleCount; i < n; i++) { buffer[i] = pabs->actualBuffer[i]; } csoundNotifyThreadLock(pabs->csound, pabs->paLock); } int paBlockingWriteOpen(ENVIRON *csound, PA_BLOCKING_STREAM **pabs_, PaStreamParameters *paParameters) { PaError paError = -1; unsigned long maxLag_ = 0; PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *) mcalloc(sizeof(PA_BLOCKING_STREAM)); pabs->csound = csound; pabs->actualBufferSampleCount = csound->GetKsmps(csound) * csound->GetNchnls(csound); pabs->actualBuffer = (float *) mcalloc(pabs->actualBufferSampleCount * sizeof(float)); pabs->paLock = csoundCreateThreadLock(csound); pabs->clientLock = csoundCreateThreadLock(csound); maxLag_ = O.oMaxLag <= 0 ? IODACSAMPS : O.oMaxLag; memcpy(&pabs->paParameters, paParameters, sizeof(PaStreamParameters)); csound->Message(csound, "paBlockingWriteOpen: nchnls %d sr %f maxLag %u device %d\n", pabs->paParameters.channelCount, csound->esr_, maxLag_, pabs->paParameters.device); paError = Pa_OpenStream(&pabs->paStream, 0, &pabs->paParameters, (double) csound->esr_, maxLag_, paNoFlag, paBlockingWriteStreamCallback, pabs); if (paError == paNoError) { paError = Pa_StartStream(pabs->paStream); } csound->Message(csound, "paBlockingWriteOpen returned %d.\n", paError); if (paError != paNoError) { mfree(pabs->actualBuffer); mfree(pabs); *pabs_ = 0; } else { *pabs_ = pabs; } return paError; } int paBlockingWriteStreamCallback(const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { /* size_t i; size_t n; */ PA_BLOCKING_STREAM *pabs = (PA_BLOCKING_STREAM *)userData; float *paOutput = (float *)output; csoundNotifyThreadLock(pabs->csound, pabs->clientLock); csoundWaitThreadLock(pabs->csound, pabs->paLock, 100); /* for (i = 0, n = pabs->actualBufferSampleCount; i < n; i++) { */ /* paOutput[i] = pabs->actualBuffer[i]; */ /* } */ memcpy(paOutput, pabs->actualBuffer, pabs->actualBufferSampleCount*sizeof(float)); return paContinue; } void paBlockingWrite(PA_BLOCKING_STREAM *pabs, MYFLT *buffer) { size_t i; size_t n; for (i = 0, n = pabs->actualBufferSampleCount; i < n; i++) { pabs->actualBuffer[i] = buffer[i]; } csoundNotifyThreadLock(pabs->csound, pabs->paLock); csoundWaitThreadLock(pabs->csound, pabs->clientLock, 100); } void paBlockingClose(PA_BLOCKING_STREAM *pabs) { if (pabs) { if (pabs->paStream) { /* OS X portaudio hangs here, so until it's fixed I have commented the stream closing function */ #ifdef WIN32 Pa_AbortStream(pabs[INS].paStream); #endif mfree(pabs[INS].actualBuffer); if(openOnce) mfree(pabs[OUTS].actualBuffer); mfree(pabs); pabs->paStream = 0; } } } #endif