在 C 中使用 ALSA 和 Ogg vorbis 实现声音播放器
Implementing sound player with ALSA and Ogg vorbis in C
我正在尝试实现一个非常简单的 ogg 播放器。它基本上打开一个 ogg 文件,验证它并通过 libvorbisfile 解码器运行到 ALSA PCM。问题是声音输出非常失真、滞后,需要相当多的想象力才能听到原始音乐文件。 alsa 和 ogg 文件均以 44100Hz 采样率、两个通道和 16 位样本大小进行编码。
这里是源代码:
#include <stdio.h>
#include <stdlib.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
int main(int argc, char **argv)
{
buffer = (char *) malloc(4096);
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0))
< 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1, 500000))
< 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen("zedd.ogg", &vf);
if(err != 0) {
perror("Error opening file");
} else {
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n",
vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n",
ov_comment(&vf, -1)->vendor);
while(!eof) {
long ret = ov_read(&vf, buffer, 4096, 0, 2, 1,
¤t_section);
if(ret == 0) {
/* EOF */
eof = 1;
} else if(ret < 0) {
/* error in the stream. */
} else {
frames = snd_pcm_writei(handle, buffer, ret);
snd_pcm_wait(handle, 20000);
if(frames < 0)
frames = snd_pcm_recover(handle, frames,
0);
if(frames < 0) {
printf("snd_pcm_writei failed: %s\n",
snd_strerror(err));
break;
}
if(frames > 0 && frames < 4096)
printf("Short write (expected %li, wrote %li)\n",
ret, frames);
}
}
ov_clear(&vf);
}
snd_pcm_close(handle);
return (0);
}
任何帮助都会很有帮助。
答案其实很简单。在函数 snd_pcm_writei
中,我传递的不是帧数,而是缓冲区中的字节数。对于 16 位 2 通道音频是一帧四个字节(每个样本两个字节,我们有两个通道)。更改对 frames = snd_pcm_writei(handle, buffer, ret/4);
的调用并删除 snd_pcm_wait(阻塞模式下的冗余)可解决所有问题。在 irc.freenode.net.
上,#xiph 的小伙伴们非常感谢
我正在尝试实现一个非常简单的 ogg 播放器。它基本上打开一个 ogg 文件,验证它并通过 libvorbisfile 解码器运行到 ALSA PCM。问题是声音输出非常失真、滞后,需要相当多的想象力才能听到原始音乐文件。 alsa 和 ogg 文件均以 44100Hz 采样率、两个通道和 16 位样本大小进行编码。
这里是源代码:
#include <stdio.h>
#include <stdlib.h>
#include <vorbis/vorbisfile.h>
#include <alsa/asoundlib.h>
char *buffer;
static char *device = "default";
int main(int argc, char **argv)
{
buffer = (char *) malloc(4096);
int err;
snd_pcm_t *handle;
snd_pcm_sframes_t frames;
if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0))
< 0) {
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
if((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1, 500000))
< 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
OggVorbis_File vf;
int eof = 0;
int current_section;
err = ov_fopen("zedd.ogg", &vf);
if(err != 0) {
perror("Error opening file");
} else {
vorbis_info *vi = ov_info(&vf, -1);
fprintf(stderr, "Bitstream is %d channel, %ldHz\n",
vi->channels, vi->rate);
fprintf(stderr, "Encoded by: %s\n\n",
ov_comment(&vf, -1)->vendor);
while(!eof) {
long ret = ov_read(&vf, buffer, 4096, 0, 2, 1,
¤t_section);
if(ret == 0) {
/* EOF */
eof = 1;
} else if(ret < 0) {
/* error in the stream. */
} else {
frames = snd_pcm_writei(handle, buffer, ret);
snd_pcm_wait(handle, 20000);
if(frames < 0)
frames = snd_pcm_recover(handle, frames,
0);
if(frames < 0) {
printf("snd_pcm_writei failed: %s\n",
snd_strerror(err));
break;
}
if(frames > 0 && frames < 4096)
printf("Short write (expected %li, wrote %li)\n",
ret, frames);
}
}
ov_clear(&vf);
}
snd_pcm_close(handle);
return (0);
}
任何帮助都会很有帮助。
答案其实很简单。在函数 snd_pcm_writei
中,我传递的不是帧数,而是缓冲区中的字节数。对于 16 位 2 通道音频是一帧四个字节(每个样本两个字节,我们有两个通道)。更改对 frames = snd_pcm_writei(handle, buffer, ret/4);
的调用并删除 snd_pcm_wait(阻塞模式下的冗余)可解决所有问题。在 irc.freenode.net.