alsa框架编写应用层,实现边播放边录音

时间:2025-01-31 07:18:43
#include <> #include <> #include <alsa/> snd_pcm_t *open_sound_dev(snd_pcm_stream_t type) { int err; snd_pcm_t *handle; snd_pcm_hw_params_t *hw_params; unsigned int rate = 44100; /* int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode) pcmp 打开的pcm句柄 name 要打开的pcm设备名字,默认default,或者从或者asoundrc里面选择所要打开的设备 stream SND_PCM_STREAM_PLAYBACK 或 SND_PCM_STREAM_CAPTURE,分别表示播放和录音的PCM流 mode 打开pcm句柄时的一些附加参数 SND_PCM_NONBLOCK 非阻塞打开(默认阻塞打开), SND_PCM_ASYNC 异步模式打开 返回值 0 表示打开成功,负数表示失败,对应错误码 */ if ((err = snd_pcm_open (&handle, "default", type, 0)) < 0) { return NULL; } /* snd_pcm_hw_params_malloc( ) 在栈中分配 snd_pcm_hw_params_t 结构的空间,然后使用 snd_pcm_hw_params_any( ) 函数用声卡的全配置空间参数初始化已经分配的 snd_pcm_hw_params_t 结构。snd_pcm_hw_params_set_access ( ) 设置访问类型,常用访问类型的宏定义有: SND_PCM_ACCESS_RW_INTERLEAVED 交错访问。在缓冲区的每个 PCM 帧都包含所有设置的声道的连续的采样数据。比如声卡要播放采样长度是 16-bit 的 PCM 立体声数据,表示每个 PCM 帧中有 16-bit 的左声道数据,然后是 16-bit 右声道数据。 SND_PCM_ACCESS_RW_NONINTERLEAVED 非交错访问。每个 PCM 帧只是一个声道需要的数据,如果使用多个声道,那么第一帧是第一个声道的数据,第二帧是第二个声道的数据,依此类推。 函数 snd_pcm_hw_params_set_format() 设置数据格式,主要控制输入的音频数据的类型、无符号还是有符号、是 little-endian 还是 bit-endian。比如对于 16-bit 长度的采样数据可以设置为: */ if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); return NULL; } if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); return NULL; } if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {// 交错访问 fprintf (stderr, "cannot set access type (%s)\n", snd_strerror (err)); return NULL; } if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { // SND_PCM_FORMAT_S16_LE 有符号16 bit Little Endian fprintf (stderr, "cannot set sample format (%s)\n", snd_strerror (err)); return NULL; } if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) { //snd_pcm_hw_params_set_rate_near () 函数设置音频数据的最接近目标的采样率 fprintf (stderr, "cannot set sample rate (%s)\n", snd_strerror (err)); return NULL; } if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 2)) < 0) { fprintf (stderr, "cannot set channel count (%s)\n", snd_strerror (err)); return NULL; } //snd_pcm_hw_params( ) 从设备配置空间选择一个配置,让函数 snd_pcm_prepare() 准备好 PCM 设备,以便写入 PCM 数据。 if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)\n", snd_strerror (err)); return NULL; } snd_pcm_hw_params_free (hw_params); return handle; } void close_sound_dev(snd_pcm_t *handle) { snd_pcm_close (handle); } snd_pcm_t *open_playback(void) { return open_sound_dev(SND_PCM_STREAM_PLAYBACK); } snd_pcm_t *open_capture(void) { return open_sound_dev(SND_PCM_STREAM_CAPTURE); } int main (int argc, char *argv[]) { int err; char buf[512]; snd_pcm_t *playback_handle; snd_pcm_t *capture_handle; playback_handle = open_playback(); if (!playback_handle) { fprintf (stderr, "cannot open for playback\n"); return -1; } capture_handle = open_capture(); if (!capture_handle) { fprintf (stderr, "cannot open for capture\n"); return -1; } if ((err = snd_pcm_prepare (playback_handle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); return -1; } if ((err = snd_pcm_prepare (capture_handle)) < 0) { fprintf (stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror (err)); return -1; } while (1) { if ((err = snd_pcm_readi (capture_handle, buf, 128)) != 128) { fprintf (stderr, "read from audio interface failed (%s)\n", snd_strerror (err)); return -1; } if ((err = snd_pcm_writei (playback_handle, buf, 128)) != 128) { //snd_pcm_writei() 用来把交错的音频数据写入到音频设备。 fprintf (stderr, "write to audio interface failed (%s)\n", snd_strerror (err)); return -1; } } snd_pcm_close (playback_handle); snd_pcm_close (capture_handle); return 0; }