pablio.c (10835B)
1 /* 2 * $Id$ 3 * pablio.c 4 * Portable Audio Blocking Input/Output utility. 5 * 6 * Author: Phil Burk, http://www.softsynth.com 7 * 8 * This program uses the PortAudio Portable Audio Library. 9 * For more information see: http://www.portaudio.com 10 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining 13 * a copy of this software and associated documentation files 14 * (the "Software"), to deal in the Software without restriction, 15 * including without limitation the rights to use, copy, modify, merge, 16 * publish, distribute, sublicense, and/or sell copies of the Software, 17 * and to permit persons to whom the Software is furnished to do so, 18 * subject to the following conditions: 19 * 20 * The above copyright notice and this permission notice shall be 21 * included in all copies or substantial portions of the Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 26 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 27 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 28 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 */ 31 32 /* 33 * The text above constitutes the entire PortAudio license; however, 34 * the PortAudio community also makes the following non-binding requests: 35 * 36 * Any person wishing to distribute modifications to the Software is 37 * requested to send the modifications to the original developer so that 38 * they can be incorporated into the canonical version. It is also 39 * requested that these non-binding requests be included along with the 40 * license above. 41 */ 42 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <math.h> 46 #include "portaudio.h" 47 #include "pa_ringbuffer.h" 48 #include "pablio.h" 49 #include <string.h> 50 51 /************************************************************************/ 52 /******** Constants *****************************************************/ 53 /************************************************************************/ 54 55 #define FRAMES_PER_BUFFER (256) 56 57 /************************************************************************/ 58 /******** Prototypes ****************************************************/ 59 /************************************************************************/ 60 61 static int blockingIOCallback( void *inputBuffer, void *outputBuffer, 62 unsigned long framesPerBuffer, 63 PaTimestamp outTime, void *userData ); 64 static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); 65 static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); 66 67 /************************************************************************/ 68 /******** Functions *****************************************************/ 69 /************************************************************************/ 70 71 /* Called from PortAudio. 72 * Read and write data only if there is room in FIFOs. 73 */ 74 static int blockingIOCallback( void *inputBuffer, void *outputBuffer, 75 unsigned long framesPerBuffer, 76 PaTimestamp outTime, void *userData ) 77 { 78 PABLIO_Stream *data = (PABLIO_Stream*)userData; 79 long numBytes = data->bytesPerFrame * framesPerBuffer; 80 (void) outTime; 81 82 /* This may get called with NULL inputBuffer during initial setup. */ 83 if( inputBuffer != NULL ) 84 { 85 PaUtil_WriteRingBuffer( &data->inFIFO, inputBuffer, numBytes ); 86 } 87 if( outputBuffer != NULL ) 88 { 89 int i; 90 int numRead = PaUtil_ReadRingBuffer( &data->outFIFO, outputBuffer, numBytes ); 91 /* Zero out remainder of buffer if we run out of data. */ 92 for( i=numRead; i<numBytes; i++ ) 93 { 94 ((char *)outputBuffer)[i] = 0; 95 } 96 } 97 98 return 0; 99 } 100 101 /* Allocate buffer. */ 102 static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ) 103 { 104 long numBytes = numFrames * bytesPerFrame; 105 char *buffer = (char *) malloc( numBytes ); 106 if( buffer == NULL ) return paInsufficientMemory; 107 memset( buffer, 0, numBytes ); 108 return (PaError) PaUtil_InitializeRingBuffer( rbuf, numBytes, buffer ); 109 } 110 111 /* Free buffer. */ 112 static PaError PABLIO_TermFIFO( RingBuffer *rbuf ) 113 { 114 if( rbuf->buffer ) free( rbuf->buffer ); 115 rbuf->buffer = NULL; 116 return paNoError; 117 } 118 119 /************************************************************ 120 * Write data to ring buffer. 121 * Will not return until all the data has been written. 122 */ 123 long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) 124 { 125 long bytesWritten; 126 char *p = (char *) data; 127 long numBytes = aStream->bytesPerFrame * numFrames; 128 while( numBytes > 0) 129 { 130 bytesWritten = PaUtil_WriteRingBuffer( &aStream->outFIFO, p, numBytes ); 131 numBytes -= bytesWritten; 132 p += bytesWritten; 133 if( numBytes > 0) Pa_Sleep(10); 134 } 135 return numFrames; 136 } 137 138 /************************************************************ 139 * Read data from ring buffer. 140 * Will not return until all the data has been read. 141 */ 142 long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ) 143 { 144 long bytesRead; 145 char *p = (char *) data; 146 long numBytes = aStream->bytesPerFrame * numFrames; 147 while( numBytes > 0) 148 { 149 bytesRead = PaUtil_ReadRingBuffer( &aStream->inFIFO, p, numBytes ); 150 numBytes -= bytesRead; 151 p += bytesRead; 152 if( numBytes > 0) Pa_Sleep(10); 153 } 154 return numFrames; 155 } 156 157 /************************************************************ 158 * Return the number of frames that could be written to the stream without 159 * having to wait. 160 */ 161 long GetAudioStreamWriteable( PABLIO_Stream *aStream ) 162 { 163 int bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO ); 164 return bytesEmpty / aStream->bytesPerFrame; 165 } 166 167 /************************************************************ 168 * Return the number of frames that are available to be read from the 169 * stream without having to wait. 170 */ 171 long GetAudioStreamReadable( PABLIO_Stream *aStream ) 172 { 173 int bytesFull = PaUtil_GetRingBufferReadAvailable( &aStream->inFIFO ); 174 return bytesFull / aStream->bytesPerFrame; 175 } 176 177 /************************************************************/ 178 static unsigned long RoundUpToNextPowerOf2( unsigned long n ) 179 { 180 long numBits = 0; 181 if( ((n-1) & n) == 0) return n; /* Already Power of two. */ 182 while( n > 0 ) 183 { 184 n= n>>1; 185 numBits++; 186 } 187 return (1<<numBits); 188 } 189 190 /************************************************************ 191 * Opens a PortAudio stream with default characteristics. 192 * Allocates PABLIO_Stream structure. 193 * 194 * flags parameter can be an ORed combination of: 195 * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, 196 * and either PABLIO_MONO or PABLIO_STEREO 197 */ 198 PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, 199 PaSampleFormat format, long flags ) 200 { 201 long bytesPerSample; 202 long doRead = 0; 203 long doWrite = 0; 204 PaError err; 205 PABLIO_Stream *aStream; 206 long minNumBuffers; 207 long numFrames; 208 209 /* Allocate PABLIO_Stream structure for caller. */ 210 aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) ); 211 if( aStream == NULL ) return paInsufficientMemory; 212 memset( aStream, 0, sizeof(PABLIO_Stream) ); 213 214 /* Determine size of a sample. */ 215 bytesPerSample = Pa_GetSampleSize( format ); 216 if( bytesPerSample < 0 ) 217 { 218 err = (PaError) bytesPerSample; 219 goto error; 220 } 221 aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2; 222 aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame; 223 224 /* Initialize PortAudio */ 225 err = Pa_Initialize(); 226 if( err != paNoError ) goto error; 227 228 /* Warning: numFrames must be larger than amount of data processed per interrupt 229 * inside PA to prevent glitches. Just to be safe, adjust size upwards. 230 */ 231 minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); 232 numFrames = minNumBuffers * FRAMES_PER_BUFFER; 233 numFrames = RoundUpToNextPowerOf2( numFrames ); 234 235 /* Initialize Ring Buffers */ 236 doRead = ((flags & PABLIO_READ) != 0); 237 doWrite = ((flags & PABLIO_WRITE) != 0); 238 if(doRead) 239 { 240 err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame ); 241 if( err != paNoError ) goto error; 242 } 243 if(doWrite) 244 { 245 long numBytes; 246 err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame ); 247 if( err != paNoError ) goto error; 248 /* Make Write FIFO appear full initially. */ 249 numBytes = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO ); 250 PaUtil_AdvanceRingBufferWriteIndex( &aStream->outFIFO, numBytes ); 251 } 252 253 /* Open a PortAudio stream that we will use to communicate with the underlying 254 * audio drivers. */ 255 err = Pa_OpenStream( 256 &aStream->stream, 257 (doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice), 258 (doRead ? aStream->samplesPerFrame : 0 ), 259 format, 260 NULL, 261 (doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice), 262 (doWrite ? aStream->samplesPerFrame : 0 ), 263 format, 264 NULL, 265 sampleRate, 266 FRAMES_PER_BUFFER, 267 minNumBuffers, 268 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 269 blockingIOCallback, 270 aStream ); 271 if( err != paNoError ) goto error; 272 273 err = Pa_StartStream( aStream->stream ); 274 if( err != paNoError ) goto error; 275 276 *rwblPtr = aStream; 277 return paNoError; 278 279 error: 280 CloseAudioStream( aStream ); 281 *rwblPtr = NULL; 282 return err; 283 } 284 285 /************************************************************/ 286 PaError CloseAudioStream( PABLIO_Stream *aStream ) 287 { 288 PaError err; 289 int bytesEmpty; 290 int byteSize = aStream->outFIFO.bufferSize; 291 292 /* If we are writing data, make sure we play everything written. */ 293 if( byteSize > 0 ) 294 { 295 bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO ); 296 while( bytesEmpty < byteSize ) 297 { 298 Pa_Sleep( 10 ); 299 bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &aStream->outFIFO ); 300 } 301 } 302 303 err = Pa_StopStream( aStream->stream ); 304 if( err != paNoError ) goto error; 305 err = Pa_CloseStream( aStream->stream ); 306 if( err != paNoError ) goto error; 307 Pa_Terminate(); 308 309 error: 310 PABLIO_TermFIFO( &aStream->inFIFO ); 311 PABLIO_TermFIFO( &aStream->outFIFO ); 312 free( aStream ); 313 return err; 314 }