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 воспроизводить исходные аудиоданные?
Ответы
Ответ 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;
}