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