1
votes

how can I decode with C++ (Ubuntu) an audio file (wav, mp3, aiff) and store it (PCM/int) in a vector/array?

What I did so far: I used gstreamer (I'm a very beginner) to decode the file and I can play it and get data with pull-buffer, however I did't find a method to get the whole audio data at ones to store in an array.

Is there such a method in gstreamer? Or exists there an other C++ library to decode audio files and get the raw (PCM/int) data?

edit: change frequency to PCM

1
"Frequency data"? Are you sure you don't mean PCM samples?Michael
@Michael the data that I get from g_signal_emit_by_name(appsink, "pull-buffer", &buffer);, buffer->data are these PCM samples?sam
@Michael I guess you are right, changed it in the questionsam

1 Answers

1
votes

I solved the problem by myself with gstreamer. The trick is to use giostreamsink as a sink, this stores the data into a G_MEMORY_OUTPUT_STREAM.

The full code sample:

#include <string>
#include <stdio.h>
#include <gst/gst.h>
#include <gio/gio.h>
#include <boost/thread.hpp>

static void on_pad_added(GstElement *decodebin,
                         GstPad *pad,
                         gpointer data) {
    GstElement *convert = (GstElement *) data;

    GstCaps *caps;
    GstStructure *str;
    GstPad *audiopad;

    audiopad = gst_element_get_static_pad(convert, "sink");
    if (GST_PAD_IS_LINKED(audiopad)) {
        g_object_unref(audiopad);
        return;
    }

    caps = gst_pad_get_caps(pad);
    str = gst_caps_get_structure(caps, 0);
    printf("here %s\n",gst_structure_get_name(str));
    if (!g_strrstr(gst_structure_get_name(str), "audio")) {
        gst_caps_unref(caps);
        gst_object_unref(audiopad);
        return;
    }
    gst_caps_unref(caps);
    gst_pad_link(pad, audiopad);
    g_object_unref(audiopad);
}

static gboolean bus_call(GstBus *bus,
                         GstMessage *msg,
                         gpointer data) {
    GMainLoop *loop = (GMainLoop*)data;

    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_EOS:
            g_print ("End of stream\n");
            g_main_loop_quit(loop);
            break;
        case GST_MESSAGE_ERROR: {
            gchar  *debug;
            GError *error;

            gst_message_parse_error(msg, &error, &debug);
            g_free (debug);

            g_printerr("Error: %s\n", error->message);
            g_error_free(error);

            g_main_loop_quit(loop);
            break;
        }
        default:
            break;
    }
    return true;
}

int main (int argc, char **argv) {
    gst_init(&argc, &argv);

    GstElement *pipeline, *source, *decode, *sink, *convert;
    int rate = 44100;
    int channels = 1;
    int depth = 16;
    bool output_signed = true;
    GMainLoop *loop;
    GstBus *bus;
    guint bus_watch_id;
    GMemoryOutputStream *stream;
    gpointer out_data;

    // loop
    loop = g_main_loop_new(NULL, false);
    // pipeline
    pipeline = gst_pipeline_new("test_pipeline");
    // sink
    stream = G_MEMORY_OUTPUT_STREAM(g_memory_output_stream_new(NULL, 0, (GReallocFunc)g_realloc, (GDestroyNotify)g_free));
    sink = gst_element_factory_make ("giostreamsink", "sink");
    g_object_set(G_OBJECT(sink), "stream", stream, NULL);
    // source
    source = gst_element_factory_make("filesrc", "source");
    g_object_set(G_OBJECT(source), "location", "/home/sam/Desktop/audio/audio.wav", NULL);
    // convert
    convert = gst_element_factory_make("audioconvert", "convert");
    // decode
    decode = gst_element_factory_make("decodebin", "decoder");
    // link decode to convert
    g_signal_connect(decode, "pad-added", G_CALLBACK(on_pad_added), convert);

    // bus
    bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
    bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
    gst_object_unref(bus);

    // add elements into pipeline
    gst_bin_add_many(GST_BIN(pipeline), source, decode, convert, sink, NULL);
    // link source to decode
    gst_element_link(source, decode);
    // caps
    GstCaps *caps;
    caps = gst_caps_new_simple("audio/x-raw-int",
                               "rate", G_TYPE_INT, rate,
                               "channels", G_TYPE_INT, channels,
                               "width", G_TYPE_INT, depth,
                               "depth", G_TYPE_INT, depth,
                               "signed", G_TYPE_BOOLEAN, output_signed,
                               NULL);
    // link convert to sink
    gst_element_link_filtered(convert, sink, caps);
    gst_caps_unref(caps);
    // start playing
    gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING);

    // iterate
    g_print("Running...\n");
    g_main_loop_run(loop);

    // out of the main loop, clean up nicely
    g_print("Returned, stopping playback\n");
    gst_element_set_state(pipeline, GST_STATE_NULL);

    g_print("Deleting pipeline\n");
    gst_object_unref(GST_OBJECT(pipeline));
    g_source_remove (bus_watch_id);
    g_main_loop_unref(loop);

    // get data
    g_print("get data\n");
    out_data = g_memory_output_stream_get_data(G_MEMORY_OUTPUT_STREAM(stream));

    unsigned long size = g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(stream));
    unsigned long sizeData = g_memory_output_stream_get_data_size(G_MEMORY_OUTPUT_STREAM(stream));
    std::cout << "stream size: " << size << std::endl;
    std::cout << "stream data size: " << sizeData << std::endl;

    // access data and store in vector
    std::vector<int16_t> data;
    for (unsigned long i = 0; i < sizeData/2; ++i) {
        data.push_back(((gint16*)out_data)[i]);
    }
    return 0;
}