portaudio回调方式实现录制任意长度的音频

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

 

 

 

 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>