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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章