2
* $Id: pablio.c,v 1.1.1.1.16.2 2003/09/13 09:03:08 xovo Exp $
4
* Portable Audio Blocking Input/Output utility.
6
* Author: Phil Burk, http://www.softsynth.com
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
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:
20
* The above copyright notice and this permission notice shall be
21
* included in all copies or substantial portions of the Software.
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.
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.
38
* PLB021214 - check for valid stream in CloseAudioStream() to prevent hang.
39
* add timeOutMSec to CloseAudioStream() to prevent hang.
44
#include "portaudio.h"
45
#include "ringbuffer.h"
49
/************************************************************************/
50
/******** Constants *****************************************************/
51
/************************************************************************/
53
#define FRAMES_PER_BUFFER (256)
55
/************************************************************************/
56
/******** Prototypes ****************************************************/
57
/************************************************************************/
59
static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
60
unsigned long framesPerBuffer,
61
PaTimestamp outTime, void *userData );
62
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame );
63
static PaError PABLIO_TermFIFO( RingBuffer *rbuf );
65
/************************************************************************/
66
/******** Functions *****************************************************/
67
/************************************************************************/
69
/* Called from PortAudio.
70
* Read and write data only if there is room in FIFOs.
72
static int blockingIOCallback( void *inputBuffer, void *outputBuffer,
73
unsigned long framesPerBuffer,
74
PaTimestamp outTime, void *userData )
76
PABLIO_Stream *data = (PABLIO_Stream*)userData;
77
long numBytes = data->bytesPerFrame * framesPerBuffer;
80
/* This may get called with NULL inputBuffer during initial setup. */
81
if( inputBuffer != NULL )
83
RingBuffer_Write( &data->inFIFO, inputBuffer, numBytes );
85
if( outputBuffer != NULL )
88
int numRead = RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes );
89
/* Zero out remainder of buffer if we run out of data. */
90
for( i=numRead; i<numBytes; i++ )
92
((char *)outputBuffer)[i] = 0;
99
/* Allocate buffer. */
100
static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame )
102
long numBytes = numFrames * bytesPerFrame;
103
char *buffer = (char *) malloc( numBytes );
104
if( buffer == NULL ) return paInsufficientMemory;
105
memset( buffer, 0, numBytes );
106
return (PaError) RingBuffer_Init( rbuf, numBytes, buffer );
110
static PaError PABLIO_TermFIFO( RingBuffer *rbuf )
112
if( rbuf->buffer ) free( rbuf->buffer );
117
/************************************************************
118
* Write data to ring buffer.
119
* Will not return until all the data has been written.
121
long WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
124
char *p = (char *) data;
125
long numBytes = aStream->bytesPerFrame * numFrames;
128
bytesWritten = RingBuffer_Write( &aStream->outFIFO, p, numBytes );
129
numBytes -= bytesWritten;
131
if( numBytes > 0) Pa_Sleep(10);
136
/************************************************************
137
* Read data from ring buffer.
138
* Will not return until all the data has been read.
140
long ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames )
143
char *p = (char *) data;
144
long numBytes = aStream->bytesPerFrame * numFrames;
147
bytesRead = RingBuffer_Read( &aStream->inFIFO, p, numBytes );
148
numBytes -= bytesRead;
150
if( numBytes > 0) Pa_Sleep(10);
155
/************************************************************
156
* Return the number of frames that could be written to the stream without
159
long GetAudioStreamWriteable( PABLIO_Stream *aStream )
161
int bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
162
return bytesEmpty / aStream->bytesPerFrame;
165
/************************************************************
166
* Return the number of frames that are available to be read from the
167
* stream without having to wait.
169
long GetAudioStreamReadable( PABLIO_Stream *aStream )
171
int bytesFull = RingBuffer_GetReadAvailable( &aStream->inFIFO );
172
return bytesFull / aStream->bytesPerFrame;
175
/************************************************************/
176
static unsigned long RoundUpToNextPowerOf2( unsigned long n )
179
if( ((n-1) & n) == 0) return n; /* Already Power of two. */
188
/************************************************************
189
* Opens a PortAudio stream with default characteristics.
190
* Allocates PABLIO_Stream structure.
192
* flags parameter can be an ORed combination of:
193
* PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE,
194
* and either PABLIO_MONO or PABLIO_STEREO
196
PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate,
197
PaSampleFormat format, long flags )
203
PABLIO_Stream *aStream;
207
/* Allocate PABLIO_Stream structure for caller. */
208
aStream = (PABLIO_Stream *) malloc( sizeof(PABLIO_Stream) );
209
if( aStream == NULL ) return paInsufficientMemory;
210
memset( aStream, 0, sizeof(PABLIO_Stream) );
212
/* Determine size of a sample. */
213
bytesPerSample = Pa_GetSampleSize( format );
214
if( bytesPerSample < 0 )
216
err = (PaError) bytesPerSample;
219
aStream->samplesPerFrame = ((flags&PABLIO_MONO) != 0) ? 1 : 2;
220
aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame;
222
/* Initialize PortAudio */
223
err = Pa_Initialize();
224
if( err != paNoError ) goto error;
226
/* Warning: numFrames must be larger than amount of data processed per interrupt
227
* inside PA to prevent glitches. Just to be safe, adjust size upwards.
229
minNumBuffers = 2 * Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
230
numFrames = minNumBuffers * FRAMES_PER_BUFFER;
231
/* The PortAudio callback runs in a high priority thread. But PABLIO
232
* runs in a normal foreground thread. So we may have much worse
233
* latency in PABLIO. So adjust latency to a safe level.
236
const int safeLatencyMSec = 200;
237
int minLatencyMSec = (int) ((1000 * numFrames) / sampleRate);
238
if( minLatencyMSec < safeLatencyMSec )
240
numFrames = (int) ((safeLatencyMSec * sampleRate) / 1000);
243
numFrames = RoundUpToNextPowerOf2( numFrames );
245
/* Initialize Ring Buffers */
246
doRead = ((flags & PABLIO_READ) != 0);
247
doWrite = ((flags & PABLIO_WRITE) != 0);
250
err = PABLIO_InitFIFO( &aStream->inFIFO, numFrames, aStream->bytesPerFrame );
251
if( err != paNoError ) goto error;
256
err = PABLIO_InitFIFO( &aStream->outFIFO, numFrames, aStream->bytesPerFrame );
257
if( err != paNoError ) goto error;
258
/* Make Write FIFO appear full initially. */
259
numBytes = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
260
RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes );
263
/* Open a PortAudio stream that we will use to communicate with the underlying
267
(doRead ? Pa_GetDefaultInputDeviceID() : paNoDevice),
268
(doRead ? aStream->samplesPerFrame : 0 ),
271
(doWrite ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
272
(doWrite ? aStream->samplesPerFrame : 0 ),
278
paClipOff, /* we won't output out of range samples so don't bother clipping them */
281
if( err != paNoError ) goto error;
283
err = Pa_StartStream( aStream->stream );
284
if( err != paNoError ) goto error;
290
CloseAudioStream( aStream );
295
/************************************************************/
296
PaError CloseAudioStream( PABLIO_Stream *aStream )
298
PaError err = paNoError;
300
int byteSize = aStream->outFIFO.bufferSize;
302
if( aStream->stream != NULL ) /* Make sure stream was opened. PLB021214 */
304
/* If we are writing data, make sure we play everything written. */
307
int timeOutMSec = 2000;
308
bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
309
while( (bytesEmpty < byteSize) && (timeOutMSec > 0) )
313
bytesEmpty = RingBuffer_GetWriteAvailable( &aStream->outFIFO );
316
err = Pa_StopStream( aStream->stream );
317
if( err != paNoError ) goto error;
318
err = Pa_CloseStream( aStream->stream );
323
PABLIO_TermFIFO( &aStream->inFIFO );
324
PABLIO_TermFIFO( &aStream->outFIFO );