mackron / miniaudio

Audio playback and capture library written in C, in a single source file.
https://miniaud.io
Other
4.07k stars 361 forks source link

How to attach ma_sound to ma_node_graph #566

Closed mincequi closed 2 years ago

mincequi commented 2 years ago

I am trying to define a nodegraph wich can be attached inbetween a _masound object an a _maengine. The nodegraph shall carry multiple instances of filters. However, your API is apparently not supposed to be used like this. Attaching a _masound to a _ma_nodegraph fails with MA_INVALID_OPERATION. Let me express with a code snippet what i am trying to achieve:

#include <assert.h>
#include <stdio.h>
#include "miniaudio.h"

int main(int argc, char** argv)
{
    ma_result result;
    ma_engine engine;

    if (argc < 2) {
        printf("No input file.");
        return -1;
    }

    result = ma_engine_init(NULL, &engine);
    if (result != MA_SUCCESS) {
        printf("Failed to initialize audio engine.");
        return -1;
    }

    ma_sound sound;
    result = ma_sound_init_from_file(&engine, argv[1], 0, NULL, NULL, &sound);
    if (result != MA_SUCCESS) {
        printf("Failed to init sound from file.");
        return -1;
    }

    // Init node graph
    ma_node_graph_config graphConfig = ma_node_graph_config_init(2);
    ma_node_graph nodeGraph;
    ma_node_graph_init(&graphConfig, NULL, &nodeGraph);

    // Init filter
    ma_lpf_node_config filerConfig = ma_lpf_node_config_init(2, 48000.0, 500.0, 2);
    ma_lpf_node filterNode;
    ma_lpf_node_init(&nodeGraph, &filerConfig, NULL, &filterNode);

    // Wiring things together:
    // Sound -> NodeGraph -> Engine
    // THIS FAILS:
    assert(ma_node_attach_output_bus(&sound, 0, &nodeGraph, 0) == MA_SUCCESS);
    assert(ma_node_attach_output_bus(&nodeGraph , 0, &filterNode, 0) == MA_SUCCESS);
    assert(ma_node_attach_output_bus(&filterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0) == MA_SUCCESS);
    assert(ma_node_attach_output_bus(ma_node_graph_get_endpoint(&nodeGraph), 0, ma_engine_get_endpoint(&engine), 0) == MA_SUCCESS);

    // THIS SUCCEEDS:
    //ma_node_attach_output_bus(&sound, 0, &filterNode, 0);
    //ma_node_attach_output_bus(&filterNode, 0, ma_engine_get_endpoint(&engine), 0);

    ma_sound_start(&sound);
    printf("Press Enter to quit...");
    getchar();

    ma_engine_uninit(&engine);

    return 0;
}

The line _assert(ma_node_attach_output_bus(&sound, 0, &nodeGraph, 0) == MASUCCESS); fails here, while attaching a single filter inbetween _masound and _maengine works.

How could I realise and/or group a set of filters together?

Thanks in advance.

mackron commented 2 years ago

A few issues here. First of all, the ma_engine object is a node graph, so there's no need to create a separate ma_node_graph object. Just use ma_engine_get_node_graph(&engine) to get a pointer to the node graph.

Regarding your assertion, when you attach a node to the graph, you attach it to another node. You're passing in &nodeGraph which is not a node. You need to attach it to the endpoint of the graph:

ma_node_attach_output_bus(&sound, 0, ma_node_graph_get_endpoint(&nodeGraph), 0)

You didn't get a compilation error because the ma_node type is actually void* and the compiler just implicitly casts it. miniaudio does not hold your hand, so you need to be careful.

So just to summarise, when you attach a node to the graph, you need to attach it to another node, not directly to the ma_node_graph object. The graph has an endpoint node which is retrieved with ma_node_graph_get_endpoint(). Secondly, the engine itself is a node graph and there's no need for a separate ma_node_graph object for what you're trying to do.

(Moving this to the discussion section.)