6.1 从tinyalsa开始
这一章将对播放音频的具体内容做讲解。我的想法是按照tinyalsa中的例子作为讲解的范本,因为tinyalsa足够简单,很多时候都忽略了它的细节。趁着这个机会再整理一下tinyalsa的内容。我使用的tinyalsa从https://github.com/tinyalsa/tinyalsa下载,从examples/writei.c开始。
int main(void)
{
void *frames;
size_t size;
size = read_file(&frames);
if (size == 0) {
return EXIT_FAILURE;
}
if (write_frames(frames, size) < 0) {
return EXIT_FAILURE;
}
free(frames);
return EXIT_SUCCESS;
}
其中函数read_file从指定的文件中读取pcm数据到frames。文章来源:https://www.toymoban.com/news/detail-400658.html
static int write_frames(const void * frames, size_t byte_count){
unsigned int card = 0;
unsigned int device = 0;
int flags = PCM_OUT;
const struct pcm_config config = {
.channels = 2,
.rate = 48000,
.format = PCM_FORMAT_S32_LE,
.period_size = 1024,
.period_count = 2,
.start_threshold = 1024,
.silence_threshold = 1024 * 2,
.stop_threshold = 1024 * 2
};
struct pcm * pcm = pcm_open(card, device, flags, &config);
if (pcm == NULL) {
fprintf(stderr, "failed to allocate memory for PCM\n");
return -1;
} else if (!pcm_is_ready(pcm)){
pcm_close(pcm);
fprintf(stderr, "failed to open PCM\n");
return -1;
}
unsigned int frame_count = pcm_bytes_to_frames(pcm, byte_count);
int err = pcm_writei(pcm, frames, frame_count);
if (err < 0) {
printf("error: %s\n", pcm_get_error(pcm));
}
pcm_close(pcm);
return 0;
}
这个函数里通过pcm_open打开设备,后面通过pcm_writei去写数据。这里先注意一下pcm_config中的内容,包括声道数、采样数、数据格式等内容。period_size表示的内核中DMA块的大小,period_count表示这样的块有几个,period_count的大小应该大于2,这样才能保证音频播放过程中无卡顿。start_threshold用来表示有多少帧数据的时候,才开始播放声音。silence_threshold和stop_threshold表示静音和停止播放时候需要的帧数,帧数太少的时候进行操作,可能导致破音的产生。文章来源地址https://www.toymoban.com/news/detail-400658.html
struct pcm *pcm_open(unsigned int card, unsigned int device,
unsigned int flags, const struct pcm_config *config)
{
struct pcm *pcm;
struct snd_pcm_info info;
int rc;
pcm = calloc(1, sizeof(struct pcm));
if (!pcm) {
oops(&bad_pcm, ENOMEM, "can't allocate PCM object");
return &bad_pcm;
}
/* Default to hw_ops, attemp plugin open only if hw (/dev/snd/pcm*) open fails */
pcm->ops = &hw_ops;
pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, NULL);
#ifdef TINYALSA_USES_PLUGINS
if (pcm->fd < 0) {
int pcm_type;
pcm->snd_node = snd_utils_open_pcm(card, device);
pcm_type = snd_utils_get_node_type(pcm->snd_node);
if (!pcm->snd_node || pcm_type != SND_NODE_TYPE_PLUGIN) {
oops(&bad_pcm, ENODEV, "no device (hw/plugin) for card(%u), device(%u)",
card, device);
goto fail_close_dev_node;
}
pcm->ops = &plug_ops;
pcm->fd = pcm->ops->open(card, device, flags, &pcm->data, pcm->snd_node);
}
#endif
if (pcm->fd < 0) {
oops(&bad_pcm, errno, "cannot open device (%u) for card (%u)",
device, card);
goto fail_close_dev_node;
}
pcm->flags = flags;
if (pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_INFO, &info)) {
oops(&bad_pcm, errno, "cannot get info");
goto fail_close;
}
pcm->subdevice = info.subdevice;
if (pcm_set_config(pcm, config) != 0)
goto fail_close;
rc = pcm_hw_mmap_status(pcm);
if (rc < 0) {
oops(&bad_pcm, errno, "mmap status failed");
goto fail;
}
#ifdef SNDRV_PCM_IOCTL_TTSTAMP
if (pcm->flags & PCM_MONOTONIC) {
int arg = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC;
rc = pcm->ops->ioctl(pcm->data, SNDRV_PCM_IOCTL_TTSTAMP, &arg);
if (rc < 0) {
oops(&bad_pcm, errno, "cannot set timestamp type");
goto fail;
}
}
#endif
pcm->xruns = 0;
return pcm;
fail:
pcm_hw_munmap_status(pcm);
if (flags & PCM_MMAP)
到了这里,关于6.播放音频(第一部分)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!