15

这将是我有史以来最糟糕的问题......

在一台旧上网本上,我安装了一个更旧版本的 Debian,并玩弄了一下。其中一个相当令人满意的结果是一个非常基本的 MP3 播放器(使用 libmpg123),它集成用于将背景音乐添加到一个小应用程序中,做一些完全不同的事情。我越来越喜欢这个小解决方案。

在那个程序中,我mpg123_decode()通过/dev/audio一个简单的fwrite().

这很好用 - 在上网本上。

现在,我开始明白这/dev/audio是 OSS 所做的事情,并且不再支持较新的 (ALSA) 机器。果然,我的笔记本电脑(运行当前的 Linux Mint)没有这个设备。

所以显然我必须改用ALSA。在网上搜索,我找到了一些教程,它们几乎让我大吃一惊。模式、参数、功能、访问类型、采样格式、采样率、通道数、周期数、周期大小……我知道 ALSA 是一个功能强大的 API,适用于雄心勃勃的人,但这不是我想要的(或有时间去摸索)。我正在寻找的只是如何播放mpg123_decode(我什至不知道的格式,而不是远距离的音频极客)的输出。

任何人都可以给我一些关于需要做什么的提示吗?

tl;博士

如何让 ALSA 播放原始音频数据?

4

4 回答 4

4

在 alsa-oss 包中有一个 ALSA 的 OSS 兼容层。安装它并在“aoss”程序中运行您的程序。或者,modprobe 此处列出的模块:

http://wiki.debian.org/SoundFAQ/#line-105

然后,您需要更改您的程序以使用“/dev/dsp”或“/dev/dsp0”而不是“/dev/audio”。它应该按照你的记忆方式工作......但你可能想交叉手指以防万一。

于 2012-07-06T18:39:54.780 回答
2

直接使用 ALSA 过于复杂,所以我希望 Gstreamer 解决方案也适合您。Gstreamer 为 ALSA/OSS/Pulseaudio/您命名它提供了一个很好的抽象——并且在 Linux 世界中无处不在。

我写了一个小库,它将打开一个FILE对象,您可以在其中将 PCM 数据写入: Gstreamer 文件。实际代码不到 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 示例

这是整个文件(以防 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;
}
于 2012-07-07T05:38:52.113 回答
2

您可以安装soxplay并使用正确的采样率和样本大小参数打开命令的管道。

于 2012-07-06T18:45:54.577 回答
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;
}

我希望这可以帮助其他有类似问题的人,作为一个模板。

于 2012-07-07T10:39:47.867 回答