ALSA equivalent to /dev/audio dump?
Asked Answered
W

4

15

This will be my poorest question ever...

On an old netbook, I installed an even older version of Debian, and toyed around a bit. One of the rather pleasing results was a very basic MP3 player (using libmpg123), integrated for adding background music to a little application doing something completely different. I grew rather fond of this little solution.

In that program, I dumped the decoded audio (from mpg123_decode()) to /dev/audio via a simple fwrite().

This worked fine - on the netbook.

Now, I came to understand that /dev/audio was something done by OSS, and is no longer supported on newer (ALSA) machines. Sure enough, my laptop (running a current Linux Mint) does not have this device.

So apparently I have to use ALSA instead. Searching the web, I've found a couple of tutorials, and they pretty much blow my mind. Modes, parameters, capabilities, access type, sample format, sample rate, number of channels, number of periods, period size... I understand that ALSA is a powerful API for the ambitious, but that's not what I am looking for (or have the time to grok). All I am looking for is how to play the output of mpg123_decode (the format of which I don't even know, not being an audio geek by a long shot).

Can anybody give me some hints on what needs to be done?

tl;dr

How do I get ALSA to play raw audio data?

Wilder answered 5/7, 2012 at 15:16 Comment(2)
have you looked at these sample programs? alumnos.elo.utfsm.cl/~yanez/alsa-sample-programsJanuary
@0A0D: Indeed I have not. Ah... those are simple examples? 885 lines, that's a bit more than I was hoping for... 8-OWilder
W
0

And ten years later, the "actual" answer is found: That's the wrong way to do it in the first place.

libmpg123 comes with a companion library, libout123, which abstracts the underlying audio system for you. Based on libmpg123 example code:

#include <stdlib.h>
#include "mpg123.h"
#include "out123.h"

int main()
{
    mpg123_handle * _mpg_handle;
    out123_handle * _out_handle;
    double rate, channels, encoding;
    size_t position, buffer_size;
    unsigned char * buffer;
    char filename[] = "Example.mp3";

    mpg123_open( _mpg_handle, filename );
    mpg123_getformat( _mpg_handle, &rate, &channels, &encoding );
    out123_open( _out_handle, NULL, NULL );
    mpg123_format_none( _mpg_handle );
    mpg123_format( _mpg_handle, rate, channels, encoding );
    out123_start( _out_handle, rate, channels, encoding );

    buffer_size = mpg123_outblock( _mpg_handle );
    buffer = malloc( buffer_size );

    do
    {
        mpg123_read( _mpg_handle, buffer.get(), buffer_size, &position );
        out123_play( _out_handle, buffer.get(), position );
    } while ( position );

    out123_close( _out_handle );
    mpg123_close( _mpg_handle );
    free( buffer );
}
Wilder answered 18/6, 2022 at 16:54 Comment(0)
P
4

There's an OSS compatibility layer for ALSA in the alsa-oss package. Install it and run your program inside the "aoss" program. Or, modprobe the modules listed here:

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

Then, you'll need to change your program to use "/dev/dsp" or "/dev/dsp0" instead of "/dev/audio". It should work how you remembered... but you might want to cross your fingers just in case.

Provenance answered 6/7, 2012 at 18:39 Comment(2)
Oh how I would have loved that one to work... but it didn't. On the positive side, it made me learn a couple of new tricks (see my answer), and made my decoding code much simpler because I took the time to read the API docs again. ;-)Wilder
Ah, bummer! Your answer is a much, much better way of doing this though!Provenance
R
2

You could install sox and open a pipe to the play command with the correct samplerate and sample size arguments.

Rob answered 6/7, 2012 at 18:45 Comment(1)
I don't think sox is necessary, just feed the samples to aplay and fiddle around with the sample rates/endianness.Hocker
D
2

Using ALSA directly is overly complicated, so I hope a Gstreamer solution is fine to you too. Gstreamer gives a nice abstraction to ALSA/OSS/Pulseaudio/you name it -- and is ubiquitous in the Linux world.

I wrote a little library that will open a FILE object where you can fwrite PCM data into: Gstreamer file. The actual code is less than 100 lines.

Use use it like that:

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

I added an mpg123 example, too.

Here is the whole file (in case Github get's out of business ;-) ):

/**
 * 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;
}
Demott answered 7/7, 2012 at 5:38 Comment(3)
I did only a few tests, yet. Comments and contributions are welcome!Demott
Sorry, I was already knee-deep in another solution angle (see my answer), and did not get around to test this one.Wilder
No problem. The real magic is fopencookie. (There even is a related funopen call for *BSD.)Demott
W
0

And ten years later, the "actual" answer is found: That's the wrong way to do it in the first place.

libmpg123 comes with a companion library, libout123, which abstracts the underlying audio system for you. Based on libmpg123 example code:

#include <stdlib.h>
#include "mpg123.h"
#include "out123.h"

int main()
{
    mpg123_handle * _mpg_handle;
    out123_handle * _out_handle;
    double rate, channels, encoding;
    size_t position, buffer_size;
    unsigned char * buffer;
    char filename[] = "Example.mp3";

    mpg123_open( _mpg_handle, filename );
    mpg123_getformat( _mpg_handle, &rate, &channels, &encoding );
    out123_open( _out_handle, NULL, NULL );
    mpg123_format_none( _mpg_handle );
    mpg123_format( _mpg_handle, rate, channels, encoding );
    out123_start( _out_handle, rate, channels, encoding );

    buffer_size = mpg123_outblock( _mpg_handle );
    buffer = malloc( buffer_size );

    do
    {
        mpg123_read( _mpg_handle, buffer.get(), buffer_size, &position );
        out123_play( _out_handle, buffer.get(), position );
    } while ( position );

    out123_close( _out_handle );
    mpg123_close( _mpg_handle );
    free( buffer );
}
Wilder answered 18/6, 2022 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.