To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / pablio / pablio.c @ 164:9fa11135915a

History | View | Annotate | Download (10.6 KB)

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
}