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 / examples / paex_record_file.c @ 164:9fa11135915a

History | View | Annotate | Download (15.4 KB)

1
/** @file paex_record_file.c
2
        @ingroup examples_src
3
        @brief Record input into a file, then playback recorded data from file (Windows only at the moment) 
4
        @author Robert Bielik
5
*/
6
/*
7
 * $Id: paex_record_file.c 1752 2011-09-08 03:21:55Z philburk $
8
 *
9
 * This program uses the PortAudio Portable Audio Library.
10
 * For more information see: http://www.portaudio.com
11
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
12
 *
13
 * Permission is hereby granted, free of charge, to any person obtaining
14
 * a copy of this software and associated documentation files
15
 * (the "Software"), to deal in the Software without restriction,
16
 * including without limitation the rights to use, copy, modify, merge,
17
 * publish, distribute, sublicense, and/or sell copies of the Software,
18
 * and to permit persons to whom the Software is furnished to do so,
19
 * subject to the following conditions:
20
 *
21
 * The above copyright notice and this permission notice shall be
22
 * included in all copies or substantial portions of the Software.
23
 *
24
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
28
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
 */
32

    
33
/*
34
 * The text above constitutes the entire PortAudio license; however, 
35
 * the PortAudio community also makes the following non-binding requests:
36
 *
37
 * Any person wishing to distribute modifications to the Software is
38
 * requested to send the modifications to the original developer so that
39
 * they can be incorporated into the canonical version. It is also 
40
 * requested that these non-binding requests be included along with the 
41
 * license above.
42
 */
43

    
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include "portaudio.h"
47
#include "pa_ringbuffer.h"
48
#include "pa_util.h"
49

    
50
#ifdef _WIN32
51
#include <windows.h>
52
#include <process.h>
53
#endif
54

    
55
static ring_buffer_size_t rbs_min(ring_buffer_size_t a, ring_buffer_size_t b)
56
{
57
    return (a < b) ? a : b;
58
}
59

    
60
/* #define SAMPLE_RATE  (17932) // Test failure to open with this value. */
61
#define FILE_NAME       "audio_data.raw"
62
#define SAMPLE_RATE  (44100)
63
#define FRAMES_PER_BUFFER (512)
64
#define NUM_SECONDS     (10)
65
#define NUM_CHANNELS    (2)
66
#define NUM_WRITES_PER_BUFFER   (4)
67
/* #define DITHER_FLAG     (paDitherOff) */
68
#define DITHER_FLAG     (0) /**/
69

    
70

    
71
/* Select sample format. */
72
#if 1
73
#define PA_SAMPLE_TYPE  paFloat32
74
typedef float SAMPLE;
75
#define SAMPLE_SILENCE  (0.0f)
76
#define PRINTF_S_FORMAT "%.8f"
77
#elif 1
78
#define PA_SAMPLE_TYPE  paInt16
79
typedef short SAMPLE;
80
#define SAMPLE_SILENCE  (0)
81
#define PRINTF_S_FORMAT "%d"
82
#elif 0
83
#define PA_SAMPLE_TYPE  paInt8
84
typedef char SAMPLE;
85
#define SAMPLE_SILENCE  (0)
86
#define PRINTF_S_FORMAT "%d"
87
#else
88
#define PA_SAMPLE_TYPE  paUInt8
89
typedef unsigned char SAMPLE;
90
#define SAMPLE_SILENCE  (128)
91
#define PRINTF_S_FORMAT "%d"
92
#endif
93

    
94
typedef struct
95
{
96
    unsigned            frameIndex;
97
    int                 threadSyncFlag;
98
    SAMPLE             *ringBufferData;
99
    PaUtilRingBuffer    ringBuffer;
100
    FILE               *file;
101
    void               *threadHandle;
102
}
103
paTestData;
104

    
105
/* This routine is run in a separate thread to write data from the ring buffer into a file (during Recording) */
106
static int threadFunctionWriteToRawFile(void* ptr)
107
{
108
    paTestData* pData = (paTestData*)ptr;
109

    
110
    /* Mark thread started */
111
    pData->threadSyncFlag = 0;
112

    
113
    while (1)
114
    {
115
        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferReadAvailable(&pData->ringBuffer);
116
        if ( (elementsInBuffer >= pData->ringBuffer.bufferSize / NUM_WRITES_PER_BUFFER) ||
117
             pData->threadSyncFlag )
118
        {
119
            void* ptr[2] = {0};
120
            ring_buffer_size_t sizes[2] = {0};
121

    
122
            /* By using PaUtil_GetRingBufferReadRegions, we can read directly from the ring buffer */
123
            ring_buffer_size_t elementsRead = PaUtil_GetRingBufferReadRegions(&pData->ringBuffer, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
124
            if (elementsRead > 0)
125
            {
126
                int i;
127
                for (i = 0; i < 2 && ptr[i] != NULL; ++i)
128
                {
129
                    fwrite(ptr[i], pData->ringBuffer.elementSizeBytes, sizes[i], pData->file);
130
                }
131
                PaUtil_AdvanceRingBufferReadIndex(&pData->ringBuffer, elementsRead);
132
            }
133

    
134
            if (pData->threadSyncFlag)
135
            {
136
                break;
137
            }
138
        }
139

    
140
        /* Sleep a little while... */
141
        Pa_Sleep(20);
142
    }
143

    
144
    pData->threadSyncFlag = 0;
145

    
146
    return 0;
147
}
148

    
149
/* This routine is run in a separate thread to read data from file into the ring buffer (during Playback). When the file
150
   has reached EOF, a flag is set so that the play PA callback can return paComplete */
151
static int threadFunctionReadFromRawFile(void* ptr)
152
{
153
    paTestData* pData = (paTestData*)ptr;
154

    
155
    while (1)
156
    {
157
        ring_buffer_size_t elementsInBuffer = PaUtil_GetRingBufferWriteAvailable(&pData->ringBuffer);
158

    
159
        if (elementsInBuffer >= pData->ringBuffer.bufferSize / NUM_WRITES_PER_BUFFER)
160
        {
161
            void* ptr[2] = {0};
162
            ring_buffer_size_t sizes[2] = {0};
163

    
164
            /* By using PaUtil_GetRingBufferWriteRegions, we can write directly into the ring buffer */
165
            PaUtil_GetRingBufferWriteRegions(&pData->ringBuffer, elementsInBuffer, ptr + 0, sizes + 0, ptr + 1, sizes + 1);
166

    
167
            if (!feof(pData->file))
168
            {
169
                ring_buffer_size_t itemsReadFromFile = 0;
170
                int i;
171
                for (i = 0; i < 2 && ptr[i] != NULL; ++i)
172
                {
173
                    itemsReadFromFile += (ring_buffer_size_t)fread(ptr[i], pData->ringBuffer.elementSizeBytes, sizes[i], pData->file);
174
                }
175
                PaUtil_AdvanceRingBufferWriteIndex(&pData->ringBuffer, itemsReadFromFile);
176

    
177
                /* Mark thread started here, that way we "prime" the ring buffer before playback */
178
                pData->threadSyncFlag = 0;
179
            }
180
            else
181
            {
182
                /* No more data to read */
183
                pData->threadSyncFlag = 1;
184
                break;
185
            }
186
        }
187

    
188
        /* Sleep a little while... */
189
        Pa_Sleep(20);
190
    }
191

    
192
    return 0;
193
}
194

    
195
typedef int (*ThreadFunctionType)(void*);
196

    
197
/* Start up a new thread in the given function, at the moment only Windows, but should be very easy to extend
198
   to posix type OSs (Linux/Mac) */
199
static PaError startThread( paTestData* pData, ThreadFunctionType fn )
200
{
201
#ifdef _WIN32
202
    typedef unsigned (__stdcall* WinThreadFunctionType)(void*);
203
    pData->threadHandle = (void*)_beginthreadex(NULL, 0, (WinThreadFunctionType)fn, pData, CREATE_SUSPENDED, NULL);
204
    if (pData->threadHandle == NULL) return paUnanticipatedHostError;
205

    
206
    /* Set file thread to a little higher prio than normal */
207
    SetThreadPriority(pData->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
208

    
209
    /* Start it up */
210
    pData->threadSyncFlag = 1;
211
    ResumeThread(pData->threadHandle);
212

    
213
#endif
214

    
215
    /* Wait for thread to startup */
216
    while (pData->threadSyncFlag) {
217
        Pa_Sleep(10);
218
    }
219

    
220
    return paNoError;
221
}
222

    
223
static int stopThread( paTestData* pData )
224
{
225
    pData->threadSyncFlag = 1;
226
    /* Wait for thread to stop */
227
    while (pData->threadSyncFlag) {
228
        Pa_Sleep(10);
229
    }
230
#ifdef _WIN32
231
    CloseHandle(pData->threadHandle);
232
    pData->threadHandle = 0;
233
#endif
234

    
235
    return paNoError;
236
}
237

    
238

    
239
/* This routine will be called by the PortAudio engine when audio is needed.
240
** It may be called at interrupt level on some machines so don't do anything
241
** that could mess up the system like calling malloc() or free().
242
*/
243
static int recordCallback( const void *inputBuffer, void *outputBuffer,
244
                           unsigned long framesPerBuffer,
245
                           const PaStreamCallbackTimeInfo* timeInfo,
246
                           PaStreamCallbackFlags statusFlags,
247
                           void *userData )
248
{
249
    paTestData *data = (paTestData*)userData;
250
    ring_buffer_size_t elementsWriteable = PaUtil_GetRingBufferWriteAvailable(&data->ringBuffer);
251
    ring_buffer_size_t elementsToWrite = rbs_min(elementsWriteable, (ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS));
252
    const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
253

    
254
    (void) outputBuffer; /* Prevent unused variable warnings. */
255
    (void) timeInfo;
256
    (void) statusFlags;
257
    (void) userData;
258

    
259
    data->frameIndex += PaUtil_WriteRingBuffer(&data->ringBuffer, rptr, elementsToWrite);
260

    
261
    return paContinue;
262
}
263

    
264
/* This routine will be called by the PortAudio engine when audio is needed.
265
** It may be called at interrupt level on some machines so don't do anything
266
** that could mess up the system like calling malloc() or free().
267
*/
268
static int playCallback( const void *inputBuffer, void *outputBuffer,
269
                         unsigned long framesPerBuffer,
270
                         const PaStreamCallbackTimeInfo* timeInfo,
271
                         PaStreamCallbackFlags statusFlags,
272
                         void *userData )
273
{
274
    paTestData *data = (paTestData*)userData;
275
    ring_buffer_size_t elementsToPlay = PaUtil_GetRingBufferReadAvailable(&data->ringBuffer);
276
    ring_buffer_size_t elementsToRead = rbs_min(elementsToPlay, (ring_buffer_size_t)(framesPerBuffer * NUM_CHANNELS));
277
    SAMPLE* wptr = (SAMPLE*)outputBuffer;
278

    
279
    (void) inputBuffer; /* Prevent unused variable warnings. */
280
    (void) timeInfo;
281
    (void) statusFlags;
282
    (void) userData;
283

    
284
    data->frameIndex += PaUtil_ReadRingBuffer(&data->ringBuffer, wptr, elementsToRead);
285

    
286
    return data->threadSyncFlag ? paComplete : paContinue;
287
}
288

    
289
static unsigned NextPowerOf2(unsigned val)
290
{
291
    val--;
292
    val = (val >> 1) | val;
293
    val = (val >> 2) | val;
294
    val = (val >> 4) | val;
295
    val = (val >> 8) | val;
296
    val = (val >> 16) | val;
297
    return ++val;
298
}
299

    
300
/*******************************************************************/
301
int main(void);
302
int main(void)
303
{
304
    PaStreamParameters  inputParameters,
305
                        outputParameters;
306
    PaStream*           stream;
307
    PaError             err = paNoError;
308
    paTestData          data = {0};
309
    unsigned            delayCntr;
310
    unsigned            numSamples;
311
    unsigned            numBytes;
312

    
313
    printf("patest_record.c\n"); fflush(stdout);
314

    
315
    /* We set the ring buffer size to about 500 ms */
316
    numSamples = NextPowerOf2((unsigned)(SAMPLE_RATE * 0.5 * NUM_CHANNELS));
317
    numBytes = numSamples * sizeof(SAMPLE);
318
    data.ringBufferData = (SAMPLE *) PaUtil_AllocateMemory( numBytes );
319
    if( data.ringBufferData == NULL )
320
    {
321
        printf("Could not allocate ring buffer data.\n");
322
        goto done;
323
    }
324

    
325
    if (PaUtil_InitializeRingBuffer(&data.ringBuffer, sizeof(SAMPLE), numSamples, data.ringBufferData) < 0)
326
    {
327
        printf("Failed to initialize ring buffer. Size is not power of 2 ??\n");
328
        goto done;
329
    }
330

    
331
    err = Pa_Initialize();
332
    if( err != paNoError ) goto done;
333

    
334
    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
335
    if (inputParameters.device == paNoDevice) {
336
        fprintf(stderr,"Error: No default input device.\n");
337
        goto done;
338
    }
339
    inputParameters.channelCount = 2;                    /* stereo input */
340
    inputParameters.sampleFormat = PA_SAMPLE_TYPE;
341
    inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
342
    inputParameters.hostApiSpecificStreamInfo = NULL;
343

    
344
    /* Record some audio. -------------------------------------------- */
345
    err = Pa_OpenStream(
346
              &stream,
347
              &inputParameters,
348
              NULL,                  /* &outputParameters, */
349
              SAMPLE_RATE,
350
              FRAMES_PER_BUFFER,
351
              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
352
              recordCallback,
353
              &data );
354
    if( err != paNoError ) goto done;
355

    
356
    /* Open the raw audio 'cache' file... */
357
    data.file = fopen(FILE_NAME, "wb");
358
    if (data.file == 0) goto done;
359

    
360
    /* Start the file writing thread */
361
    err = startThread(&data, threadFunctionWriteToRawFile);
362
    if( err != paNoError ) goto done;
363

    
364
    err = Pa_StartStream( stream );
365
    if( err != paNoError ) goto done;
366
    printf("\n=== Now recording to '" FILE_NAME "' for %d seconds!! Please speak into the microphone. ===\n", NUM_SECONDS); fflush(stdout);
367

    
368
    /* Note that the RECORDING part is limited with TIME, not size of the file and/or buffer, so you can
369
       increase NUM_SECONDS until you run out of disk */
370
    delayCntr = 0;
371
    while( delayCntr++ < NUM_SECONDS )
372
    {
373
        printf("index = %d\n", data.frameIndex ); fflush(stdout);
374
        Pa_Sleep(1000);
375
    }
376
    if( err < 0 ) goto done;
377

    
378
    err = Pa_CloseStream( stream );
379
    if( err != paNoError ) goto done;
380

    
381
    /* Stop the thread */
382
    err = stopThread(&data);
383
    if( err != paNoError ) goto done;
384

    
385
    /* Close file */
386
    fclose(data.file);
387
    data.file = 0;
388

    
389
    /* Playback recorded data.  -------------------------------------------- */
390
    data.frameIndex = 0;
391

    
392
    outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
393
    if (outputParameters.device == paNoDevice) {
394
        fprintf(stderr,"Error: No default output device.\n");
395
        goto done;
396
    }
397
    outputParameters.channelCount = 2;                     /* stereo output */
398
    outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
399
    outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
400
    outputParameters.hostApiSpecificStreamInfo = NULL;
401

    
402
    printf("\n=== Now playing back from file '" FILE_NAME "' until end-of-file is reached ===\n"); fflush(stdout);
403
    err = Pa_OpenStream(
404
              &stream,
405
              NULL, /* no input */
406
              &outputParameters,
407
              SAMPLE_RATE,
408
              FRAMES_PER_BUFFER,
409
              paClipOff,      /* we won't output out of range samples so don't bother clipping them */
410
              playCallback,
411
              &data );
412
    if( err != paNoError ) goto done;
413

    
414
    if( stream )
415
    {
416
        /* Open file again for reading */
417
        data.file = fopen(FILE_NAME, "rb");
418
        if (data.file != 0)
419
        {
420
            /* Start the file reading thread */
421
            err = startThread(&data, threadFunctionReadFromRawFile);
422
            if( err != paNoError ) goto done;
423

    
424
            err = Pa_StartStream( stream );
425
            if( err != paNoError ) goto done;
426

    
427
            printf("Waiting for playback to finish.\n"); fflush(stdout);
428

    
429
            /* The playback will end when EOF is reached */
430
            while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) {
431
                printf("index = %d\n", data.frameIndex ); fflush(stdout);
432
                Pa_Sleep(1000);
433
            }
434
            if( err < 0 ) goto done;
435
        }
436
        
437
        err = Pa_CloseStream( stream );
438
        if( err != paNoError ) goto done;
439

    
440
        fclose(data.file);
441
        
442
        printf("Done.\n"); fflush(stdout);
443
    }
444

    
445
done:
446
    Pa_Terminate();
447
    if( data.ringBufferData )       /* Sure it is NULL or valid. */
448
        PaUtil_FreeMemory( data.ringBufferData );
449
    if( err != paNoError )
450
    {
451
        fprintf( stderr, "An error occured while using the portaudio stream\n" );
452
        fprintf( stderr, "Error number: %d\n", err );
453
        fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
454
        err = 1;          /* Always return 0 or 1, but no other return codes. */
455
    }
456
    return err;
457
}
458