PaError CloseAudioStream( PABLIO_Stream *aStream ) { PaError err; int bytesEmpty; int byteSize = aStream->outFIFO.bufferSize; /* If we are writing data, make sure we play everything written. */ if( byteSize > 0 ) { bytesEmpty = sys_ringbuf_GetWriteAvailable( &aStream->outFIFO ); while( bytesEmpty < byteSize ) { NPa_Sleep( 10 ); /* MSP */ bytesEmpty = sys_ringbuf_GetWriteAvailable( &aStream->outFIFO ); } } err = Pa_StopStream( aStream->stream ); if( err != paNoError ) goto error; err = Pa_CloseStream( aStream->stream ); if( err != paNoError ) goto error; Pa_Terminate(); error: PABLIO_TermFIFO( &aStream->inFIFO ); PABLIO_TermFIFO( &aStream->outFIFO ); free( aStream ); return err; }
/* callback for "non-callback" case where we communicate with the main thread via FIFO. Here we first read the sudio output FIFO (which we sync on, not waiting for it but supplying zeros to the audio output if there aren't enough samples in the FIFO when we are called), then write to the audio input FIFO. The main thread will wait for the input fifo. We can either throw it a pthreads condition or just allow the main thread to poll for us; so far polling seems to work better. */ static int pa_fifo_callback(const void *inputBuffer, void *outputBuffer, unsigned long nframes, const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData) { /* callback routine for non-callback client... throw samples into and read them out of a FIFO */ int ch; long fiforoom; float *fbuf; #if CHECKFIFOS if (pa_inchans * sys_ringbuf_GetReadAvailable(&pa_outring) != pa_outchans * sys_ringbuf_GetWriteAvailable(&pa_inring)) fprintf(stderr, "warning: in and out rings unequal (%d, %d)\n", sys_ringbuf_GetReadAvailable(&pa_outring), sys_ringbuf_GetWriteAvailable(&pa_inring)); #endif fiforoom = sys_ringbuf_GetReadAvailable(&pa_outring); if ((unsigned)fiforoom >= nframes*pa_outchans*sizeof(float)) { if (outputBuffer) sys_ringbuf_Read(&pa_outring, outputBuffer, nframes*pa_outchans*sizeof(float)); else if (pa_outchans) fprintf(stderr, "no outputBuffer but output channels\n"); if (inputBuffer) sys_ringbuf_Write(&pa_inring, inputBuffer, nframes*pa_inchans*sizeof(float)); else if (pa_inchans) fprintf(stderr, "no inputBuffer but input channels\n"); } else { /* PD could not keep up; generate zeros */ if (pa_started) pa_dio_error = 1; if (outputBuffer) { for (ch = 0; ch < pa_outchans; ch++) { unsigned long frame; fbuf = ((float *)outputBuffer) + ch; for (frame = 0; frame < nframes; frame++, fbuf += pa_outchans) *fbuf = 0; } } } #ifdef THREADSIGNAL pthread_mutex_lock(&pa_mutex); pthread_cond_signal(&pa_sem); pthread_mutex_unlock(&pa_mutex); #endif return 0; }
int pa_send_dacs(void) { t_sample *fp; float *fp2, *fp3; float *conversionbuf; int j, k; int rtnval = SENDDACS_YES; int timenow; int timeref = sys_getrealtime(); if (!sys_inchannels && !sys_outchannels) return (SENDDACS_NO); #if CHECKFIFOS if (sys_outchannels * sys_ringbuf_GetReadAvailable(&pa_inring) != sys_inchannels * sys_ringbuf_GetWriteAvailable(&pa_outring)) fprintf(stderr, "warning (2): in and out rings unequal (%d, %d)\n", sys_ringbuf_GetReadAvailable(&pa_inring), sys_ringbuf_GetWriteAvailable(&pa_outring)); #endif conversionbuf = (float *)alloca((sys_inchannels > sys_outchannels? sys_inchannels:sys_outchannels) * DEFDACBLKSIZE * sizeof(float)); if (pa_dio_error) { sys_log_error(ERR_RESYNC); pa_dio_error = 0; } if (!sys_inchannels) /* if no input channels sync on output */ { #ifdef THREADSIGNAL pthread_mutex_lock(&pa_mutex); #endif while (sys_ringbuf_GetWriteAvailable(&pa_outring) < (long)(sys_outchannels * DEFDACBLKSIZE * sizeof(float))) #ifdef THREADSIGNAL pthread_cond_wait(&pa_sem, &pa_mutex); #else #ifdef _WIN32 Sleep(1); #else usleep(1000); #endif /* _WIN32 */ #endif /* THREADSIGNAL */ #ifdef THREADSIGNAL pthread_mutex_unlock(&pa_mutex); #endif } /* write output */ if (sys_outchannels) { for (j = 0, fp = sys_soundout, fp2 = conversionbuf; j < sys_outchannels; j++, fp2++) for (k = 0, fp3 = fp2; k < DEFDACBLKSIZE; k++, fp++, fp3 += sys_outchannels) *fp3 = *fp; sys_ringbuf_Write(&pa_outring, conversionbuf, sys_outchannels*(DEFDACBLKSIZE*sizeof(float))); } if (sys_inchannels) /* if there is input sync on it */ { #ifdef THREADSIGNAL pthread_mutex_lock(&pa_mutex); #endif while (sys_ringbuf_GetReadAvailable(&pa_inring) < (long)(sys_inchannels * DEFDACBLKSIZE * sizeof(float))) #ifdef THREADSIGNAL pthread_cond_wait(&pa_sem, &pa_mutex); #else #ifdef _WIN32 Sleep(1); #else usleep(1000); #endif /* _WIN32 */ #endif /* THREADSIGNAL */ #ifdef THREADSIGNAL pthread_mutex_unlock(&pa_mutex); #endif } pa_started = 1; if (sys_inchannels) { sys_ringbuf_Read(&pa_inring, conversionbuf, sys_inchannels*(DEFDACBLKSIZE*sizeof(float))); for (j = 0, fp = sys_soundin, fp2 = conversionbuf; j < sys_inchannels; j++, fp2++) for (k = 0, fp3 = fp2; k < DEFDACBLKSIZE; k++, fp++, fp3 += sys_inchannels) *fp = *fp3; } if ((timenow = sys_getrealtime()) - timeref > 0.002) { rtnval = SENDDACS_SLEPT; } memset(sys_soundout, 0, DEFDACBLKSIZE*sizeof(t_sample)*sys_outchannels); return rtnval; }
/************************************************************ * Opens a PortAudio stream with default characteristics. * Allocates PABLIO_Stream structure. * * flags parameter can be an ORed combination of: * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE */ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, PaSampleFormat format, int inchannels, int outchannels, int framesperbuf, int nbuffers, int indeviceno, int outdeviceno) /* MSP */ { long bytesPerSample; long doRead = 0; long doWrite = 0; PaError err; PABLIO_Stream *aStream; long numFrames; PaStreamParameters instreamparams, outstreamparams; /* MSP */ /* fprintf(stderr, "open %lf fmt %d flags %d ch: %d fperbuf: %d nbuf: %d devs: %d %d\n", sampleRate, format, flags, nchannels, framesperbuf, nbuffers, indeviceno, outdeviceno); */ if (indeviceno < 0) /* MSP... */ { indeviceno = Pa_GetDefaultInputDevice(); fprintf(stderr, "using default input device number: %d\n", indeviceno); } if (outdeviceno < 0) { outdeviceno = Pa_GetDefaultOutputDevice(); fprintf(stderr, "using default output device number: %d\n", outdeviceno); } /* fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", nchannels, flags, nbuffers, framesperbuf); */ /* ...MSP */ /* Allocate PABLIO_Stream structure for caller. */ aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) ); if( aStream == NULL ) return paInsufficientMemory; memset( aStream, 0, sizeof(PABLIO_Stream) ); /* Determine size of a sample. */ bytesPerSample = Pa_GetSampleSize( format ); if( bytesPerSample < 0 ) { err = (PaError) bytesPerSample; goto error; } aStream->insamplesPerFrame = inchannels; /* MSP */ aStream->inbytesPerFrame = bytesPerSample * aStream->insamplesPerFrame; aStream->outsamplesPerFrame = outchannels; aStream->outbytesPerFrame = bytesPerSample * aStream->outsamplesPerFrame; /* Initialize PortAudio */ err = Pa_Initialize(); if( err != paNoError ) goto error; numFrames = nbuffers * framesperbuf; /* ...MSP */ instreamparams.device = indeviceno; /* MSP... */ instreamparams.channelCount = inchannels; instreamparams.sampleFormat = format; instreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; instreamparams.hostApiSpecificStreamInfo = 0; outstreamparams.device = outdeviceno; outstreamparams.channelCount = outchannels; outstreamparams.sampleFormat = format; outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; outstreamparams.hostApiSpecificStreamInfo = 0; /* ... MSP */ numFrames = nbuffers * framesperbuf; /* fprintf(stderr, "numFrames %d\n", numFrames); */ /* Initialize Ring Buffers */ doRead = (inchannels != 0); doWrite = (outchannels != 0); if(doRead) { err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->inbytesPerFrame ); if( err != paNoError ) goto error; } if(doWrite) { long numBytes; err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->outbytesPerFrame ); if( err != paNoError ) goto error; /* Make Write FIFO appear full initially. */ numBytes = sys_ringbuf_GetWriteAvailable( &aStream->outFIFO ); sys_ringbuf_AdvanceWriteIndex( &aStream->outFIFO, numBytes ); } /* Open a PortAudio stream that we will use to communicate with the underlying * audio drivers. */ err = Pa_OpenStream( &aStream->stream, (doRead ? &instreamparams : 0), /* MSP */ (doWrite ? &outstreamparams : 0), /* MSP */ sampleRate, framesperbuf, /* MSP */ paNoFlag, /* MSP -- portaudio will clip for us */ blockingIOCallback, aStream ); if( err != paNoError ) goto error; err = Pa_StartStream( aStream->stream ); if( err != paNoError ) /* MSP */ { fprintf(stderr, "Pa_StartStream failed; closing audio stream...\n"); CloseAudioStream( aStream ); goto error; } *rwblPtr = aStream; return paNoError; error: *rwblPtr = NULL; return err; }
/************************************************************ * Return the number of frames that could be written to the stream without * having to wait. */ long GetAudioStreamWriteable( PABLIO_Stream *aStream ) { int bytesEmpty = sys_ringbuf_GetWriteAvailable( &aStream->outFIFO ); return bytesEmpty / aStream->outbytesPerFrame; }