PortAudio
2.0
|
00001 00006 /* 00007 * $Id: paex_record.c 1752 2011-09-08 03:21:55Z philburk $ 00008 * 00009 * This program uses the PortAudio Portable Audio Library. 00010 * For more information see: http://www.portaudio.com 00011 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 00012 * 00013 * Permission is hereby granted, free of charge, to any person obtaining 00014 * a copy of this software and associated documentation files 00015 * (the "Software"), to deal in the Software without restriction, 00016 * including without limitation the rights to use, copy, modify, merge, 00017 * publish, distribute, sublicense, and/or sell copies of the Software, 00018 * and to permit persons to whom the Software is furnished to do so, 00019 * subject to the following conditions: 00020 * 00021 * The above copyright notice and this permission notice shall be 00022 * included in all copies or substantial portions of the Software. 00023 * 00024 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00025 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00026 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00027 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 00028 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 00029 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00030 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00031 */ 00032 00033 /* 00034 * The text above constitutes the entire PortAudio license; however, 00035 * the PortAudio community also makes the following non-binding requests: 00036 * 00037 * Any person wishing to distribute modifications to the Software is 00038 * requested to send the modifications to the original developer so that 00039 * they can be incorporated into the canonical version. It is also 00040 * requested that these non-binding requests be included along with the 00041 * license above. 00042 */ 00043 00044 #include <stdio.h> 00045 #include <stdlib.h> 00046 #include "portaudio.h" 00047 00048 /* #define SAMPLE_RATE (17932) // Test failure to open with this value. */ 00049 #define SAMPLE_RATE (44100) 00050 #define FRAMES_PER_BUFFER (512) 00051 #define NUM_SECONDS (5) 00052 #define NUM_CHANNELS (2) 00053 /* #define DITHER_FLAG (paDitherOff) */ 00054 #define DITHER_FLAG (0) 00055 00056 #define WRITE_TO_FILE (0) 00057 00058 /* Select sample format. */ 00059 #if 1 00060 #define PA_SAMPLE_TYPE paFloat32 00061 typedef float SAMPLE; 00062 #define SAMPLE_SILENCE (0.0f) 00063 #define PRINTF_S_FORMAT "%.8f" 00064 #elif 1 00065 #define PA_SAMPLE_TYPE paInt16 00066 typedef short SAMPLE; 00067 #define SAMPLE_SILENCE (0) 00068 #define PRINTF_S_FORMAT "%d" 00069 #elif 0 00070 #define PA_SAMPLE_TYPE paInt8 00071 typedef char SAMPLE; 00072 #define SAMPLE_SILENCE (0) 00073 #define PRINTF_S_FORMAT "%d" 00074 #else 00075 #define PA_SAMPLE_TYPE paUInt8 00076 typedef unsigned char SAMPLE; 00077 #define SAMPLE_SILENCE (128) 00078 #define PRINTF_S_FORMAT "%d" 00079 #endif 00080 00081 typedef struct 00082 { 00083 int frameIndex; /* Index into sample array. */ 00084 int maxFrameIndex; 00085 SAMPLE *recordedSamples; 00086 } 00087 paTestData; 00088 00089 /* This routine will be called by the PortAudio engine when audio is needed. 00090 ** It may be called at interrupt level on some machines so don't do anything 00091 ** that could mess up the system like calling malloc() or free(). 00092 */ 00093 static int recordCallback( const void *inputBuffer, void *outputBuffer, 00094 unsigned long framesPerBuffer, 00095 const PaStreamCallbackTimeInfo* timeInfo, 00096 PaStreamCallbackFlags statusFlags, 00097 void *userData ) 00098 { 00099 paTestData *data = (paTestData*)userData; 00100 const SAMPLE *rptr = (const SAMPLE*)inputBuffer; 00101 SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; 00102 long framesToCalc; 00103 long i; 00104 int finished; 00105 unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; 00106 00107 (void) outputBuffer; /* Prevent unused variable warnings. */ 00108 (void) timeInfo; 00109 (void) statusFlags; 00110 (void) userData; 00111 00112 if( framesLeft < framesPerBuffer ) 00113 { 00114 framesToCalc = framesLeft; 00115 finished = paComplete; 00116 } 00117 else 00118 { 00119 framesToCalc = framesPerBuffer; 00120 finished = paContinue; 00121 } 00122 00123 if( inputBuffer == NULL ) 00124 { 00125 for( i=0; i<framesToCalc; i++ ) 00126 { 00127 *wptr++ = SAMPLE_SILENCE; /* left */ 00128 if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE; /* right */ 00129 } 00130 } 00131 else 00132 { 00133 for( i=0; i<framesToCalc; i++ ) 00134 { 00135 *wptr++ = *rptr++; /* left */ 00136 if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */ 00137 } 00138 } 00139 data->frameIndex += framesToCalc; 00140 return finished; 00141 } 00142 00143 /* This routine will be called by the PortAudio engine when audio is needed. 00144 ** It may be called at interrupt level on some machines so don't do anything 00145 ** that could mess up the system like calling malloc() or free(). 00146 */ 00147 static int playCallback( const void *inputBuffer, void *outputBuffer, 00148 unsigned long framesPerBuffer, 00149 const PaStreamCallbackTimeInfo* timeInfo, 00150 PaStreamCallbackFlags statusFlags, 00151 void *userData ) 00152 { 00153 paTestData *data = (paTestData*)userData; 00154 SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; 00155 SAMPLE *wptr = (SAMPLE*)outputBuffer; 00156 unsigned int i; 00157 int finished; 00158 unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; 00159 00160 (void) inputBuffer; /* Prevent unused variable warnings. */ 00161 (void) timeInfo; 00162 (void) statusFlags; 00163 (void) userData; 00164 00165 if( framesLeft < framesPerBuffer ) 00166 { 00167 /* final buffer... */ 00168 for( i=0; i<framesLeft; i++ ) 00169 { 00170 *wptr++ = *rptr++; /* left */ 00171 if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */ 00172 } 00173 for( ; i<framesPerBuffer; i++ ) 00174 { 00175 *wptr++ = 0; /* left */ 00176 if( NUM_CHANNELS == 2 ) *wptr++ = 0; /* right */ 00177 } 00178 data->frameIndex += framesLeft; 00179 finished = paComplete; 00180 } 00181 else 00182 { 00183 for( i=0; i<framesPerBuffer; i++ ) 00184 { 00185 *wptr++ = *rptr++; /* left */ 00186 if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */ 00187 } 00188 data->frameIndex += framesPerBuffer; 00189 finished = paContinue; 00190 } 00191 return finished; 00192 } 00193 00194 /*******************************************************************/ 00195 int main(void); 00196 int main(void) 00197 { 00198 PaStreamParameters inputParameters, 00199 outputParameters; 00200 PaStream* stream; 00201 PaError err = paNoError; 00202 paTestData data; 00203 int i; 00204 int totalFrames; 00205 int numSamples; 00206 int numBytes; 00207 SAMPLE max, val; 00208 double average; 00209 00210 printf("patest_record.c\n"); fflush(stdout); 00211 00212 data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ 00213 data.frameIndex = 0; 00214 numSamples = totalFrames * NUM_CHANNELS; 00215 numBytes = numSamples * sizeof(SAMPLE); 00216 data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */ 00217 if( data.recordedSamples == NULL ) 00218 { 00219 printf("Could not allocate record array.\n"); 00220 goto done; 00221 } 00222 for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0; 00223 00224 err = Pa_Initialize(); 00225 if( err != paNoError ) goto done; 00226 00227 inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 00228 if (inputParameters.device == paNoDevice) { 00229 fprintf(stderr,"Error: No default input device.\n"); 00230 goto done; 00231 } 00232 inputParameters.channelCount = 2; /* stereo input */ 00233 inputParameters.sampleFormat = PA_SAMPLE_TYPE; 00234 inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency; 00235 inputParameters.hostApiSpecificStreamInfo = NULL; 00236 00237 /* Record some audio. -------------------------------------------- */ 00238 err = Pa_OpenStream( 00239 &stream, 00240 &inputParameters, 00241 NULL, /* &outputParameters, */ 00242 SAMPLE_RATE, 00243 FRAMES_PER_BUFFER, 00244 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 00245 recordCallback, 00246 &data ); 00247 if( err != paNoError ) goto done; 00248 00249 err = Pa_StartStream( stream ); 00250 if( err != paNoError ) goto done; 00251 printf("\n=== Now recording!! Please speak into the microphone. ===\n"); fflush(stdout); 00252 00253 while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) 00254 { 00255 Pa_Sleep(1000); 00256 printf("index = %d\n", data.frameIndex ); fflush(stdout); 00257 } 00258 if( err < 0 ) goto done; 00259 00260 err = Pa_CloseStream( stream ); 00261 if( err != paNoError ) goto done; 00262 00263 /* Measure maximum peak amplitude. */ 00264 max = 0; 00265 average = 0.0; 00266 for( i=0; i<numSamples; i++ ) 00267 { 00268 val = data.recordedSamples[i]; 00269 if( val < 0 ) val = -val; /* ABS */ 00270 if( val > max ) 00271 { 00272 max = val; 00273 } 00274 average += val; 00275 } 00276 00277 average = average / (double)numSamples; 00278 00279 printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max ); 00280 printf("sample average = %lf\n", average ); 00281 00282 /* Write recorded data to a file. */ 00283 #if WRITE_TO_FILE 00284 { 00285 FILE *fid; 00286 fid = fopen("recorded.raw", "wb"); 00287 if( fid == NULL ) 00288 { 00289 printf("Could not open file."); 00290 } 00291 else 00292 { 00293 fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); 00294 fclose( fid ); 00295 printf("Wrote data to 'recorded.raw'\n"); 00296 } 00297 } 00298 #endif 00299 00300 /* Playback recorded data. -------------------------------------------- */ 00301 data.frameIndex = 0; 00302 00303 outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ 00304 if (outputParameters.device == paNoDevice) { 00305 fprintf(stderr,"Error: No default output device.\n"); 00306 goto done; 00307 } 00308 outputParameters.channelCount = 2; /* stereo output */ 00309 outputParameters.sampleFormat = PA_SAMPLE_TYPE; 00310 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; 00311 outputParameters.hostApiSpecificStreamInfo = NULL; 00312 00313 printf("\n=== Now playing back. ===\n"); fflush(stdout); 00314 err = Pa_OpenStream( 00315 &stream, 00316 NULL, /* no input */ 00317 &outputParameters, 00318 SAMPLE_RATE, 00319 FRAMES_PER_BUFFER, 00320 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 00321 playCallback, 00322 &data ); 00323 if( err != paNoError ) goto done; 00324 00325 if( stream ) 00326 { 00327 err = Pa_StartStream( stream ); 00328 if( err != paNoError ) goto done; 00329 00330 printf("Waiting for playback to finish.\n"); fflush(stdout); 00331 00332 while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); 00333 if( err < 0 ) goto done; 00334 00335 err = Pa_CloseStream( stream ); 00336 if( err != paNoError ) goto done; 00337 00338 printf("Done.\n"); fflush(stdout); 00339 } 00340 00341 done: 00342 Pa_Terminate(); 00343 if( data.recordedSamples ) /* Sure it is NULL or valid. */ 00344 free( data.recordedSamples ); 00345 if( err != paNoError ) 00346 { 00347 fprintf( stderr, "An error occured while using the portaudio stream\n" ); 00348 fprintf( stderr, "Error number: %d\n", err ); 00349 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); 00350 err = 1; /* Always return 0 or 1, but no other return codes. */ 00351 } 00352 return err; 00353 } 00354