portaudio是一个跨平台音频库,类似于SDL作为跨平台图像库一样,只是在系统原生音频库(alsa、oss)上封装了一层
portaudio自带的录音示例代码只有同步IO模式,没有异步IO模式,而异步IO能释放主线程,是更好的方式
为了实现异步,需要定义回调函数,在回调函数里将音频数据不断写入文件
为了实现任意长度,需要引入无限循环,但该无限循环要能根据用户的输入及时退出。
为此可选择挂接signal处理器,监听ctrl-c组合键发送的SIGINT信号,在信号里置标志位;
回调函数检查标志位,发现置位就关闭音频流,从而使无限循环退出
不说了,上代码
-
#include
-
#include
-
#include
-
#include
-
#include "portaudio.h"
-
-
/* #define SAMPLE_RATE (17932) // Test failure to open with this value. */
-
#define SAMPLE_RATE (16000)
-
#define FRAMES_PER_BUFFER (SAMPLE_RATE/1000*200)
-
#define NUM_SECONDS (5)
-
#define NUM_CHANNELS (1)
-
/* #define DITHER_FLAG (paDitherOff) */
-
#define DITHER_FLAG (0) /**/
-
-
/* Select sample format. */
-
#if 0
-
#define PA_SAMPLE_TYPE paFloat32
-
typedef float SAMPLE;
-
#define SAMPLE_SILENCE (0.0f)
-
#define PRINTF_S_FORMAT "%.8f"
-
#elif 1
-
#define PA_SAMPLE_TYPE paInt16
-
typedef short SAMPLE;
-
#define SAMPLE_SILENCE (0)
-
#define PRINTF_S_FORMAT "%d"
-
#elif 0
-
#define PA_SAMPLE_TYPE paInt8
-
typedef char SAMPLE;
-
#define SAMPLE_SILENCE (0)
-
#define PRINTF_S_FORMAT "%d"
-
#else
-
#define PA_SAMPLE_TYPE paUInt8
-
typedef unsigned char SAMPLE;
-
#define SAMPLE_SILENCE (128)
-
#define PRINTF_S_FORMAT "%d"
-
#endif
-
-
int exiting = 0;
-
FILE *fid;
-
-
void sigroutine(int dunno) { /* 信号处理例程,其中dunno将会得到信号的值 */
-
switch (dunno) {
-
case SIGINT:
-
exiting = 1;
-
break;
-
}
-
}
-
-
int cb(
-
const void *input, void *output,
-
unsigned long frameCount,
-
const PaStreamCallbackTimeInfo* timeInfo,
-
PaStreamCallbackFlags statusFlags,
-
void *userData )
-
{
-
printf("recv %lu frames\n", frameCount);
-
-
/* Write recorded data to a file. */
-
fwrite( input, NUM_CHANNELS * sizeof(SAMPLE), frameCount, fid );
-
-
-
-
if (exiting)
-
{
-
printf("exiting loop\n");
-
return paComplete;
-
}
-
else
-
return paContinue;
-
}
-
-
/*******************************************************************/
-
int main(void);
-
int main(void)
-
{
-
PaStreamParameters inputParameters, outputParameters;
-
PaStream *stream;
-
PaError err;
-
int i;
-
-
printf("patest_read_record.c\n"); fflush(stdout);
-
-
signal(SIGINT, sigroutine);
-
-
fid = fopen("recorded.raw", "wb");
-
if( fid == NULL )
-
{
-
printf("Could not open file.");
-
exit(10);
-
}
-
-
err = Pa_Initialize();
-
if( err != paNoError ) goto error;
-
-
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
-
if (inputParameters.device == paNoDevice) {
-
fprintf(stderr,"Error: No default input device.\n");
-
goto error;
-
}
-
inputParameters.channelCount = NUM_CHANNELS;
-
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
-
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
-
inputParameters.hostApiSpecificStreamInfo = NULL;
-
-
/* Record some audio. -------------------------------------------- */
-
err = Pa_OpenStream(
-
&stream,
-
&inputParameters,
-
NULL, /* &outputParameters, */
-
SAMPLE_RATE,
-
FRAMES_PER_BUFFER,
-
paClipOff, /* we won't output out of range samples so don't bother clipping them */
-
cb, /* no callback, use blocking API */
-
NULL ); /* no callback, so no callback userData */
-
if( err != paNoError ) goto error;
-
-
err = Pa_StartStream( stream );
-
if( err != paNoError ) goto error;
-
printf("Now recording!!\n"); fflush(stdout);
-
-
-
while(Pa_IsStreamActive(stream))
-
{
-
usleep(100*1000);
-
}
-
-
err = Pa_CloseStream( stream );
-
if( err != paNoError ) goto error;
-
-
fclose( fid );
-
-
Pa_Terminate();
-
return 0;
-
-
error:
-
Pa_Terminate();
-
fprintf( stderr, "An error occured while using the portaudio stream\n" );
-
fprintf( stderr, "Error number: %d\n", err );
-
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
-
return -1;
-
}
注意!portaudio回调函数第三个参数frameCount是帧数,不是缓冲区字节数,对于我的例子,帧格式是signed short,所以每帧2字节,切记
编译运行效果
-
gq@gq-All-Series:~/projects/test$ gcc record_cb.c -lportaudio
-
gq@gq-All-Series:~/projects/test$ ./a.out
-
patest_read_record.c
-
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
-
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
-
ALSA lib pcm.c:2239:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
-
ALSA lib pcm_route.c:947:(find_matching_chmap) Found no matching channel map
-
ALSA lib pcm_route.c:947:(find_matching_chmap) Found no matching channel map
-
bt_audio_service_open: connect() failed: Connection refused (111)
-
bt_audio_service_open: connect() failed: Connection refused (111)
-
bt_audio_service_open: connect() failed: Connection refused (111)
-
bt_audio_service_open: connect() failed: Connection refused (111)
-
Cannot connect to server socket err = No such file or directory
-
Cannot connect to server request channel
-
jack server is not running or cannot be started
-
Now recording!!
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
recv 3200 frames
-
^Crecv 3200 frames
-
exiting loop
-
gq@gq-All-Series:~/projects/test$ aplay -t raw -c 1 -r 16000 -f S16_LE recorded.raw
-
正在播放 原始資料 'recorded.raw' : Signed 16 bit Little Endian, 频率16000Hz, Mono