linux下无法得到音频流,望有人讲解清楚录音原理,分不够再加
参考了网上大部分文章, 基本都是 你抄我来我抄他。在录音着一块,基本思路都是从声卡读数据。我调试到现在也没调通。只能从声卡读到一个统一的乱码字符“?”。特来请教,代码如下
- C/C++ code
int Audio_Record(char *pathname,int nSampleRate,int nChannels,int fmt)
{
int dsp_fd,mix_fd,status,arg;
dsp_fd = open("/dev/dsp" , O_RDWR); /*open dsp*/
if(dsp_fd < 0)
{
return OPEN_DSP_FAILED;
}
// arg = nSampleRate;
//status = ioctl(dsp_fd,SOUND_PCM_READ_RATE,&arg); /*set samplerate*/
/**
if(status < 0)
{
close(dsp_fd);
return SAMPLERATE_STATUS;
}
if(arg != nSampleRate)
{
close(dsp_fd);
return SET_SAMPLERATE_FAILED;
}
**/
// arg = nChannels; /*set channels*/
// status = ioctl(dsp_fd, SOUND_PCM_READ_CHANNELS, &arg);
/**
if(status < 0)
{
close(dsp_fd);
return CHANNELS_STATUS;
}
if( arg != nChannels)
{
close(dsp_fd);
return SET_CHANNELS_FAILED;
}
**/
// arg = fmt; /*set bit fmt*/
//status = ioctl(dsp_fd, SOUND_PCM_READ_BITS, &arg);
/**
if(status < 0)
{
close(dsp_fd);
return FMT_STATUS;
}
if(arg != fmt)
{
close(dsp_fd);
return SET_FMT_FAILED;
}
**/
arg = 0;
ioctl( dsp_fd, SNDCTL_DSP_RESET, (char *)&arg );
ioctl( dsp_fd, SNDCTL_DSP_SYNC, (char *)&arg );
arg = 1;
ioctl( dsp_fd, SNDCTL_DSP_NONBLOCK, (char*)&arg );
arg = nSampleRate;
ioctl( dsp_fd, SNDCTL_DSP_SPEED, (char *)&arg );
arg = nChannels;
ioctl( dsp_fd, SNDCTL_DSP_STEREO, 0 );
ioctl( dsp_fd, SNDCTL_DSP_CHANNELS, (char *)&arg );
arg = fmt;
ioctl( dsp_fd, SNDCTL_DSP_SETFMT, (char*)&arg );
arg = 3;
ioctl( dsp_fd, SNDCTL_DSP_SETTRIGGER, (char*)&arg );
arg = 3;
ioctl( dsp_fd, SNDCTL_DSP_SETFRAGMENT, (char*)&arg );
arg = 1;
ioctl( dsp_fd, SNDCTL_DSP_PROFILE, (char*)&arg );
FILE *file_fd = fopen(pathname,"w+");
if(file_fd == NULL)
{
close(dsp_fd);
return OPEN_FILE_FAILED;
}
int num = 2 * nChannels*nSampleRate*fmt/8;
int get_num;
unsigned char buf[num];
// Record
int j = 0;
while( 1 )
{
get_num = read( dsp_fd, buf, num );
printf( "buf = %s\n", buf );
printf( "buf = %d\n", buf[0] );
fwrite( buf, 1, get_num, file_fd );
j++;
}
close(dsp_fd);
fclose(file_fd);
return 0;
}
代码中的注释部分是 另一种设置方法,都试过了,不行。
[解决办法]
up
[解决办法]
为什么不使用ALSA呢?
我现在使用ALSA可以轻松的得到声音。
但现在我只能使用固定的取样长度chunk_size(就是一次取几帧数据),不然如果每次取不如128就报-EMLINK,即连接数太多的错误。最后我找到规律就是每次取
chunk_size个样本数据就没问题。chunk_size通过 snd_pcm_hw_params_get_period_size(params, &chunk_size, 0)取得。
我录音的程序根据ALSA 例子放音程序改的:
#include "../include/asoundlib.h"
static char *device = "default";/* playback device */
snd_output_t *output = NULL;
unsigned char buffer[2352]; /* 根据打印period_size*snd_pcm_format_t*channels*/
int main(void)
{
int err;
unsigned int i;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if ((err = snd_pcm_set_params(handle,
SND_PCM_FORMAT_U8,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
48000,
1,
500000)) < 0) {/* 0.5sec */
printf("capture open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
for (int i=0; i<100; ++i)
{
frames = snd_pcm_readi(handle, buffer, 2352); //2352是自己根据snd_pcm_hw_params_get_period_size得到,正好一帧就一个字节
//如果此时不是2352而是160等就会报-EMLINK错误。
}
snd_pcm_close(handle);
return 0;
}
通过读snd_pcm_set_params这个函数,发现只要snd_pcm_readi第三个参数等于period_size就没问题。ALSA自带的录音程序也是这样做的。现在是我要改变period_size
我要怎么做?就是简单的设置snd_pcm_hw_params_set_period_size吗?我看代码还要设置什么BUFFER_TIME,BUFFER_SIZE,但不懂他们之间的关系。
有没有人懂ALSA解释下 BUFFER_TIME ,BUFFER_SIZE, PERIOD_SIZE之间的关系。下面是代码里的解释看不懂。谢谢了。
SNDRV_PCM_HW_PARAM_PERIOD_TIME,/* Approx distance between interrupts
in us */
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,/* Approx frames between interrupts */
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, /* Approx bytes between interrupts */
SNDRV_PCM_HW_PARAM_PERIODS,/* Approx interrupts per buffer */
SNDRV_PCM_HW_PARAM_BUFFER_TIME,/* Approx duration of buffer in us */
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,/* Size of buffer in frames */
SNDRV_PCM_HW_PARAM_BUFFER_BYTES, /* Size of buffer in bytes */
SNDRV_PCM_HW_PARAM_TICK_TIME,/* Approx tick duration in us */
[解决办法]
学习下。。。