这将是我有史以来最贫穷的问题......
在一台旧的上网本上,我安装了一个更旧的Debian版本,并玩弄了一下。其中一个相当令人满意的结果是一个非常基本的MP3播放器(使用libmpg123),它集成了为一个小应用程序添加完全不同的背景音乐。我非常喜欢这个小小的解决方案。
在该程序中,我通过一个简单的mpg123_decode()
将解码后的音频(从/dev/audio
)转储到fwrite()
。
这在上网本上运行良好。
现在,我开始明白/dev/audio
是由OSS完成的,并且在新的(ALSA)机器上不再受支持。果然,我的笔记本电脑(运行当前的Linux Mint)没有这个设备。
显然我必须使用ALSA。在网上搜索,我发现了几个教程,他们几乎让我大吃一惊。模式,参数,功能,访问类型,样本格式,采样率,通道数,周期数,周期大小......我理解ALSA是雄心勃勃的强大API,但这不是我想要的(或者有时间去grok)。我正在寻找的是如何播放mpg123_decode
的输出(我甚至不知道的格式,远远不是一个音频极客)。
有人可以给我一些关于需要做什么的提示吗?
TL;博士
如何让ALSA播放原始音频数据?
答案 0 :(得分:3)
alsa-oss包中有ALSA的OSS兼容层。安装它并在“aoss”程序中运行程序。或者,modprobe这里列出的模块:
http://wiki.debian.org/SoundFAQ/#line-105
然后,您需要将程序更改为使用“/ dev / dsp”或“/ dev / dsp0”而不是“/ dev / audio”。这应该与你记忆中的方式有关......但你可能想要用手指以防万一。
答案 1 :(得分:2)
您可以安装sox并使用正确的采样率和样本大小参数打开play
命令的管道。
答案 2 :(得分:2)
直接使用ALSA过于复杂,所以我希望Gstreamer解决方案也适合您。 Gstreamer为ALSA / OSS / Pulseaudio /您提供了一个很好的抽象概念 - 并且在Linux世界中无处不在。
我写了一个小库,它将打开一个FILE
对象,你可以将PCM数据写入:
的 Gstreamer file 即可。实际代码少于100行。
使用它:
FILE *output = fopen_gst(rate, channels, bit_depth); // open audio output file
while (have_more_data) fwrite(data, amount, 1, output); // output audio data
fclose(output); // close the output file
我也添加了mpg123 example。
这是整个文件(如果Github失业;-)):
/**
* gstreamer_file.c
* Copyright 2012 René Kijewski <rene.SURNAME@fu-berlin.de>
* License: LGPL 3.0 (http://www.gnu.org/licenses/lgpl-3.0)
*/
#include "gstreamer_file.h"
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <gst/gst.h>
#ifndef _GNU_SOURCE
# error "You need to add -D_GNU_SOURCE to the GCC parameters!"
#endif
/**
* Cookie passed to the callbacks.
*/
typedef struct {
/** { file descriptor to read from, fd to write to } */
int pipefd[2];
/** Gstreamer pipeline */
GstElement *pipeline;
} cookie_t;
static ssize_t write_gst(void *cookie_, const char *buf, size_t size) {
cookie_t *cookie = cookie_;
return write(cookie->pipefd[1], buf, size);
}
static int close_gst(void *cookie_) {
cookie_t *cookie = cookie_;
gst_element_set_state(cookie->pipeline, GST_STATE_NULL); /* we are finished */
gst_object_unref(GST_OBJECT(cookie->pipeline)); /* we won't access the pipeline anymore */
close(cookie->pipefd[0]); /* we won't write anymore */
close(cookie->pipefd[1]); /* we won't read anymore */
free(cookie); /* dispose the cookie */
return 0;
}
FILE *fopen_gst(long rate, int channels, int depth) {
/* initialize Gstreamer */
if (!gst_is_initialized()) {
GError *error;
if (!gst_init_check(NULL, NULL, &error)) {
g_error_free(error);
return NULL;
}
}
/* get a cookie */
cookie_t *cookie = malloc(sizeof(*cookie));
if (!cookie) {
return NULL;
}
/* open a pipe to be used between the caller and the Gstreamer pipeline */
if (pipe(cookie->pipefd) != 0) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* set up the pipeline */
char description[256];
snprintf(description, sizeof(description),
"fdsrc fd=%d ! " /* read from a file descriptor */
"audio/x-raw-int, rate=%ld, channels=%d, " /* get PCM data */
"endianness=1234, width=%d, depth=%d, signed=true ! "
"audioconvert ! audioresample ! " /* convert/resample if needed */
"autoaudiosink", /* output to speakers (using ALSA, OSS, Pulseaudio ...) */
cookie->pipefd[0], rate, channels, depth, depth);
cookie->pipeline = gst_parse_launch_full(description, NULL,
GST_PARSE_FLAG_FATAL_ERRORS, NULL);
if (!cookie->pipeline) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* open a FILE with specialized write and close functions */
cookie_io_functions_t io_funcs = { NULL, write_gst, NULL, close_gst };
FILE *result = fopencookie(cookie, "w", io_funcs);
if (!result) {
close_gst(cookie);
return NULL;
}
/* start the pipeline (of cause it will wait for some data first) */
gst_element_set_state(cookie->pipeline, GST_STATE_PLAYING);
return result;
}
答案 3 :(得分:1)
结合Artefact2的评论(使用aplay
输出)和kay的答案(使用pipe()
,我之前没有碰过),我想出了这个“最小”的例子。对于ALSA版本,它会创建一个管道,使用适当的参数分叉aplay
进程,并将解码后的音频提供给它。
使用大量assert()
来显示与各个函数调用关联的错误代码。当然,下一步不是添加-DNDEBUG
(这会使这个程序真的快速真的无用),但是替换了断言并进行适当的错误处理,包括人类可读的错误消息。
// A small example program showing how to decode an MP3 file.
#include <assert.h>
#include <stdlib.h>
#include <mpg123.h>
#include <unistd.h>
#include <fcntl.h>
int main( int argc, char * argv[] )
{
// buffer and counter for decoded audio data
size_t OUTSIZE = 65536;
unsigned char outmem[OUTSIZE];
size_t outbytes;
// output file descriptor
int outfile;
// handle, return code for mpg123
mpg123_handle * handle;
int rc;
// one command line parameter, being the MP3 filename
assert( argc == 2 );
#ifdef OSS
assert( ( outfile = open( "/dev/audio", O_WRONLY ) ) != -1 );
#else // ALSA
// pipe file descriptors
int piped[2];
assert( pipe( piped ) != -1 );
// fork into decoder (parent) and player (child)
if ( fork() )
{
// player (child)
assert( dup2( piped[0], 0 ) != -1 ); // make pipe-in the new stdin
assert( close( piped[1] ) == 0 ); // pipe-out, not needed
assert( execlp( "aplay", "aplay", "-q", "-r44100", "-c2", "-fS16_LE", "-traw", NULL ) != -1 ); // should not return
}
else
{
// decoder (parent)
close( piped[0] ); // pipe-in, not needed
outfile = piped[1];
}
#endif
// initializing
assert( mpg123_init() == MPG123_OK );
assert( atexit( mpg123_exit ) == 0 );
// setting up handle
assert( ( handle = mpg123_new( NULL, NULL ) ) != NULL );
// clearing the format list, and setting the one preferred format
assert( mpg123_format_none( handle ) == MPG123_OK );
assert( mpg123_format( handle, 44100, MPG123_STEREO, MPG123_ENC_SIGNED_16 ) == MPG123_OK );
// open input MP3 file
assert( mpg123_open( handle, argv[1] ) == MPG123_OK );
// loop over input
while ( rc != MPG123_DONE )
{
rc = mpg123_read( handle, outmem, OUTSIZE, &outbytes );
assert( rc != MPG123_ERR && rc != MPG123_NEED_MORE );
assert( write( outfile, outmem, outbytes ) != -1 );
}
// cleanup
assert( close( outfile ) == 0 );
mpg123_delete( handle );
return EXIT_SUCCESS;
}
我希望这可以帮助其他有类似问题的人作为模板。