Подтвердить что ты не робот

ALSA эквивалент /dev/audio dump?

Это будет мой самый бедный вопрос когда-либо...

На старом нетбуке я установил еще более старую версию Debian и немного поиграл. Одним из довольно приятных результатов был очень простой MP3-плеер (с использованием libmpg123), интегрированный для добавления фоновой музыки в небольшое приложение, делающее что-то совершенно другое. Я очень любил это небольшое решение.

В этой программе я сбрасывал декодированный звук (от mpg123_decode()) до /dev/audio с помощью простого fwrite().

Это отлично работало - на нетбуке.

Теперь я понял, что /dev/audio было что-то сделано OSS и больше не поддерживается на более новых (ALSA) машинах. Разумеется, мой ноутбук (работающий с текущим Монетным двором Linux) не имеет этого устройства.

Поэтому, видимо, я должен использовать ALSA. Поиск в Интернете, я нашел несколько учебников, и они в значительной степени дуют в мой разум. Режимы, параметры, возможности, тип доступа, формат выборки, частота дискретизации, количество каналов, количество периодов, размер периода... Я понимаю, что ALSA - это мощный API для амбициозных, но это не то, что я ищу (или есть время, чтобы заглянуть). Все, что я ищу, - это как воспроизвести вывод mpg123_decode (формат которого я даже не знаю, не будучи звуковым выродком с большим ударом).

Может кто-нибудь дать мне несколько советов о том, что нужно сделать?

TL;DR

Как мне заставить ALSA воспроизводить исходные аудиоданные?

4b9b3361

Ответ 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;
}

Я надеюсь, что это поможет другим с похожими проблемами, как шаблон.

Ответ 2

В пакете alsa-oss имеется совместимый уровень OSS для ALSA. Установите его и запустите программу в программе "aoss". Или, modprobe модули, перечисленные здесь:

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

Затем вам нужно будет изменить вашу программу, чтобы использовать "/dev/dsp" или "/dev/dsp0" вместо "/dev/audio". Он должен работать, как вы помнили... но вы можете на всякий случай скрестить пальцы.

Ответ 3

Вы можете установить sox и открыть трубу в команде play с правильными аргументами выборки и размера выборки.

Ответ 4

Использование 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  <[email protected]>
 * 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;
}