annotate src/portaudio_20140130/pablio/pablio.c @ 83:ae30d91d2ffe

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