Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ALSA equivalent to /dev/audio dump?

Tags:

c

alsa

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?

like image 664
DevSolar Avatar asked Jul 05 '12 15:07

DevSolar


3 Answers

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.

like image 105
Mike Andrews Avatar answered Nov 20 '22 06:11

Mike Andrews


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

like image 2
Matt K Avatar answered Nov 20 '22 07:11

Matt K


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;
}
like image 2
kay Avatar answered Nov 20 '22 07:11

kay