portaudio是一个跨平台音频库,类似于SDL作为跨平台图像库一样,只是在系统原生音频库(alsa、oss)上封装了一层
portaudio自带的录音示例代码只有同步IO模式,没有异步IO模式,而异步IO能释放主线程,是更好的方式
为了实现异步,需要定义回调函数,在回调函数里将音频数据不断写入文件
为了实现任意长度,需要引入无限循环,但该无限循环要能根据用户的输入及时退出。
为此可选择挂接signal处理器,监听ctrl-c组合键发送的SIGINT信号,在信号里置标志位;
回调函数检查标志位,发现置位就关闭音频流,从而使无限循环退出
不说了,上代码
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#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