我正在尝试使用 C 程序捕获和播放音频。为此我得到了这个教程。。这是我正在运行的程序:-
/**
* Jan Newmarch
*/
#define PERIOD_SIZE 1024
#define BUF_SIZE (PERIOD_SIZE * 2)
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
void print_pcm_state(snd_pcm_t *handle, char *name) {
switch (snd_pcm_state(handle)) {
case SND_PCM_STATE_OPEN:
printf("state open %s\n", name);
break;
case SND_PCM_STATE_SETUP:
printf("state setup %s\n", name);
break;
case SND_PCM_STATE_PREPARED:
printf("state prepare %s\n", name);
break;
case SND_PCM_STATE_RUNNING:
printf("state running %s\n", name);
break;
case SND_PCM_STATE_XRUN:
printf("state xrun %s\n", name);
break;
default:
printf("state other %s\n", name);
break;
}
}
int setparams(snd_pcm_t *handle, char *name) {
snd_pcm_hw_params_t *hw_params;
int err;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
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));
exit (1);
}
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
exit (1);
}
unsigned int rate = 48000;
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
exit (1);
}
printf("Rate for %s is %d\n", name, rate);
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 2)) < 0) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t buffersize = BUF_SIZE;
if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffersize)) < 0) {
printf("Unable to set buffer size %li: %s\n", BUF_SIZE, snd_strerror(err));
exit (1);;
}
snd_pcm_uframes_t periodsize = PERIOD_SIZE;
fprintf(stderr, "period size now %d\n", periodsize);
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &periodsize, 0)) < 0) {
printf("Unable to set period size %li: %s\n", periodsize, snd_strerror(err));
exit (1);
}
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t p_psize;
snd_pcm_hw_params_get_period_size(hw_params, &p_psize, NULL);
fprintf(stderr, "period size %d\n", p_psize);
snd_pcm_hw_params_get_buffer_size(hw_params, &p_psize);
fprintf(stderr, "buffer size %d\n", p_psize);
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (handle)) < 0) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
return 0;
}
int set_sw_params(snd_pcm_t *handle, char *name) {
snd_pcm_sw_params_t *swparams;
int err;
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Broken configuration for this PCM: no configurations available\n");
exit(1);
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Unable to set start threshold: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_avail_min(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Unable to set avail min: %s\n", snd_strerror(err));
return err;
}
if (snd_pcm_sw_params(handle, swparams) < 0) {
fprintf(stderr, "unable to install sw params:\n");
exit(1);
}
return 0;
}
/************** some code from latency.c *****************/
main (int argc, char *argv[])
{
int i;
int err;
int buf[BUF_SIZE];
snd_pcm_t *playback_handle;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
FILE *fin;
size_t nread;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
if (argc != 3) {
fprintf(stderr, "Usage: %s in-card out-card\n", argv[0]);
exit(1);
}
/**** Out card *******/
if ((err = snd_pcm_open (&playback_handle, argv[2], SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[2],
snd_strerror (err));
exit (1);
}
setparams(playback_handle, "playback");
set_sw_params(playback_handle, "playback");
/*********** In card **********/
if ((err = snd_pcm_open (&capture_handle, argv[1], SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[1],
snd_strerror (err));
exit (1);
}
setparams(capture_handle, "capture");
set_sw_params(capture_handle, "capture");
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0) {
printf("Streams link error: %s\n", snd_strerror(err));
exit(0);
}
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
fprintf (stderr, "cannot prepare playback audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
/**************** stuff something into the playback buffer ****************/
if (snd_pcm_format_set_silence(format, buf, 2*BUF_SIZE) < 0) {
fprintf(stderr, "silence error\n");
exit(1);
}
int n = 0;
while (n++ < 2) {
if (snd_pcm_writei (playback_handle, buf, BUF_SIZE) < 0) {
fprintf(stderr, "write error\n");
exit(1);
}
}
/************* COPY ************/
while (1) {
int nread;
if ((nread = snd_pcm_readi (capture_handle, buf, BUF_SIZE)) != BUF_SIZE) {
if (nread < 0) {
fprintf (stderr, "read from audio interface failed (%s)\n",
snd_strerror (nread));
} else {
fprintf (stderr, "read from audio interface failed after %d frames\n", nread);
}
snd_pcm_prepare(capture_handle);
continue;
}
if ((err = snd_pcm_writei (playback_handle, buf, nread)) != nread) {
if (err < 0) {
fprintf (stderr, "write to audio interface failed (%s)\n",
snd_strerror (err));
} else {
fprintf (stderr, "write to audio interface failed after %d frames\n", err);
}
snd_pcm_prepare(playback_handle);
}
}
snd_pcm_drain(playback_handle);
snd_pcm_close (playback_handle);
exit (0);
}
我编译它并使用以下参数运行:-
./playback-capture hw:0 hw:0
到目前为止,我的代码运行良好,但现在我决定使用 USB 声卡运行该程序。为此我编辑
/etc/modprobe.d/alsa-base.conf
以下是更改:-
我替换
options snd_usb_audio index=-2
options snd_hda_intel index=-1
和
options snd_usb_audio index=-1
options snd_hda_intel index=-2
我替换
# Keep snd-usb-audio from beeing loaded as first soundcard
options snd-usb-audio index=-1
和
# Keep snd-usb-audio from beeing loaded as first soundcard
options snd-usb-audio index=-1
现在,当我运行我的代码时,我得到了这个输出:-
Rate for playback is 48000
period size now 1024
period size 1024
buffer size 2048
Rate for capture is 48000
cannot set channel count (Invalid argument)
那么,谁能告诉我还应该做什么来使用声卡运行我的代码。
注意:-我运行命令
aplay -l
并得到这个输出:-
card 0: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: PCH [HDA Intel PCH], device 0: ALC221 Analog [ALC221 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
答案1
作为项目的一部分,我想要一个全双工 ALSA高级 Linux 声音架构例如可以:
- 通过麦克风插孔(粉色)从声卡捕获声音。
- 在其他 USB 声卡输出(线路输出绿色插孔)上播放。
通过对代码进行一些小的更改,这里是双向 ALSA 捕获播放示例。
代码
/**
* Jan Newmarch
*/
/*
* File name: rec-play-inline.c
*
* compile: gcc rec-play-inline.c -o rec-play-inline -lasound
*
* run: ./rec-play-inline "plughw:2,0" "plughw:0,0"
*
*
* */
#define PERIOD_SIZE 1024
#define BUF_SIZE (PERIOD_SIZE * 2)
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
void print_pcm_state(snd_pcm_t *handle, char *name) {
switch (snd_pcm_state(handle)) {
case SND_PCM_STATE_OPEN:
printf("state open %s\n", name);
break;
case SND_PCM_STATE_SETUP:
printf("state setup %s\n", name);
break;
case SND_PCM_STATE_PREPARED:
printf("state prepare %s\n", name);
break;
case SND_PCM_STATE_RUNNING:
printf("state running %s\n", name);
break;
case SND_PCM_STATE_XRUN:
printf("state xrun %s\n", name);
break;
default:
printf("state other %s\n", name);
break;
}
}
int setparams(snd_pcm_t *handle, char *name) {
snd_pcm_hw_params_t *hw_params;
int err;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) {
fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) {
fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
snd_strerror (err));
exit (1);
}
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));
exit (1);
}
if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) {
fprintf (stderr, "cannot set sample format (%s)\n",
snd_strerror (err));
exit (1);
}
unsigned int rate = 48000;
if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &rate, 0)) < 0) {
fprintf (stderr, "cannot set sample rate (%s)\n",
snd_strerror (err));
exit (1);
}
printf("Rate for %s is %d\n", name, rate);
if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, 2)) < 0) {
fprintf (stderr, "cannot set channel count (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t buffersize = BUF_SIZE;
if ((err = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffersize)) < 0) {
printf("Unable to set buffer size %li: %s\n", (long int)BUF_SIZE, snd_strerror(err));
exit (1);;
}
snd_pcm_uframes_t periodsize = PERIOD_SIZE;
fprintf(stderr, "period size now %d\n", (int) periodsize);
if ((err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &periodsize, 0)) < 0) {
printf("Unable to set period size %li: %s\n", periodsize, snd_strerror(err));
exit (1);
}
if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) {
fprintf (stderr, "cannot set parameters (%s)\n",
snd_strerror (err));
exit (1);
}
snd_pcm_uframes_t p_psize;
snd_pcm_hw_params_get_period_size(hw_params, &p_psize, NULL);
fprintf(stderr, "period size %d\n", (int)p_psize);
snd_pcm_hw_params_get_buffer_size(hw_params, &p_psize);
fprintf(stderr, "buffer size %d\n", (int)p_psize);
snd_pcm_hw_params_free (hw_params);
if ((err = snd_pcm_prepare (handle)) < 0) {
fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
return 0;
}
int set_sw_params(snd_pcm_t *handle, char *name) {
snd_pcm_sw_params_t *swparams;
int err;
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Broken configuration for this PCM: no configurations available\n");
exit(1);
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Unable to set start threshold: %s\n", snd_strerror(err));
return err;
}
err = snd_pcm_sw_params_set_avail_min(handle, swparams, PERIOD_SIZE);
if (err < 0) {
printf("Unable to set avail min: %s\n", snd_strerror(err));
return err;
}
if (snd_pcm_sw_params(handle, swparams) < 0) {
fprintf(stderr, "unable to install sw params:\n");
exit(1);
}
return 0;
}
/************** some code from latency.c *****************/
int main (int argc, char *argv[])
{
int i;
int err;
int buf[BUF_SIZE];
snd_pcm_t *playback_handle;
snd_pcm_t *capture_handle;
snd_pcm_hw_params_t *hw_params;
FILE *fin;
size_t nread;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
///Check for command line arguments
if (argc != 3) {
fprintf(stderr, "Usage: %s input-soundCard output-soundCard\n", argv[0]);
exit(1);
}
/**** Out card *******/
if ((err = snd_pcm_open (&playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[2],
snd_strerror (err));
exit (1);
}
setparams(playback_handle, "playback");
set_sw_params(playback_handle, "playback");
/*********** In card **********/
if ((err = snd_pcm_open (&capture_handle, argv[2], SND_PCM_STREAM_CAPTURE, 0)) < 0) {
fprintf (stderr, "cannot open audio device %s (%s)\n",
argv[1],
snd_strerror (err));
exit (1);
}
setparams(capture_handle, "capture");
set_sw_params(capture_handle, "capture");
///Comment by EE
/*
if ((err = snd_pcm_link(capture_handle, playback_handle)) < 0) {
printf("Streams link error: %s\n", snd_strerror(err));
exit(0);
}
*/
if ((err = snd_pcm_prepare (playback_handle)) < 0) {
fprintf (stderr, "cannot prepare playback audio interface for use (%s)\n",
snd_strerror (err));
exit (1);
}
/**************** stuff something into the playback buffer ****************/
if (snd_pcm_format_set_silence(format, buf, 2*BUF_SIZE) < 0) {
fprintf(stderr, "silence error\n");
exit(1);
}
///Comment by EE
/*
int n = 0;
while (n++ < 2) {
if (snd_pcm_writei (playback_handle, buf, BUF_SIZE) < 0) {
fprintf(stderr, "write error\n");
exit(1);
}
}
*/
///
/************* Capture and Play Voice ***************/
while (1) {
int nread;
if ((nread = snd_pcm_readi (capture_handle, buf, BUF_SIZE)) != BUF_SIZE) {
if (nread < 0) {
fprintf (stderr, "read from audio interface failed (%s)\n",
snd_strerror (nread));
} else {
fprintf (stderr, "read from audio interface failed after %d frames\n", nread);
}
snd_pcm_prepare(capture_handle);
continue;
}
///added by EE
snd_pcm_prepare(playback_handle);
///
if ((err = snd_pcm_writei (playback_handle, buf, nread)) != nread) {
if (err < 0) {
fprintf (stderr, "write to audio interface failed (%s)\n",
snd_strerror (err));
} else {
fprintf (stderr, "write to audio interface failed after %d frames\n", err);
}
snd_pcm_prepare(playback_handle);
}
}
snd_pcm_drain(playback_handle);
snd_pcm_close (playback_handle);
exit (0);
return 0;
}
解释
我的板载声卡是:plughw:0,0
USB 连接的外部声卡是:plughw:2,0
我通过运行以下命令找到了这些名称:
- 要获得播放设备列表:
aplay --list--devices
- 要获得捕获设备列表:
arecord --list-devices
来源
通过在调用snd_pcm_prepare(playback_handle);
之前添加writei()
解决问题
ALSA:snd_pcm_writei 调用时缓冲区不足