Search code examples
c++audiosoxlibsox

Proper way to chain several effects with libSox and read the output data


I am trying programmatically apply few effects with libSox and I currently cannot understand if I am doing it right. For example, I need to apply tempo and gain effects and read the resulting audio in a buffer for a further processing. The documentation is really scarce and googling did not work out. Here is my code:

sox_format_t* input = sox_open_read("<file.wav>", NULL, NULL, NULL);
//sox_format_t* out;

sox_format_t* output = sox_open_memstream_write(&buffer, &buffer_size,
                                             &input->signal, &input->encoding, "raw", NULL);
//assert(output = sox_open_write("/home/egor/hello_processed.wav", &input->signal, NULL, NULL, NULL, NULL));
sox_effects_chain_t* chain = sox_create_effects_chain(&input->encoding, &output->encoding);

char* sox_args[10];
//input effect

sox_effect_t* e = sox_create_effect(sox_find_effect("input"));
sox_args[0] = (char*)input;
assert(sox_effect_options(e, 1, sox_args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &input->signal, &input->signal) ==
       SOX_SUCCESS);
free(e);

e = sox_create_effect(sox_find_effect("tempo"));
std::string tempo_str = "1.01";
sox_args[0] = (char*)tempo_str.c_str();
assert(sox_effect_options(e, 1, sox_args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &input->signal,&input->signal) ==
       SOX_SUCCESS);
free(e);


e = sox_create_effect(sox_find_effect("output"));
sox_args[0] = (char*)output;
assert(sox_effect_options(e, 1, sox_args) == SOX_SUCCESS);
assert(sox_add_effect(chain, e, &input->signal, &input->signal) ==
       SOX_SUCCESS);
free(e);
sox_flow_effects(chain, NULL, NULL);


static const size_t maxSamples=4096;
sox_sample_t samples[maxSamples];

std::vector<sox_sample_t> audio_buffer;
for (size_t r; 0 != (r=sox_read(output,samples,maxSamples));)
    for(int i=0;i<r ;i++)
        audio_buffer.push_back(samples[i]);

std::cout << audio_buffer.size() << std::endl;

My questions are:

  1. Do I correctly set up the effect chain?

  2. How to read in-memory resulting audio samples?

    If I use tempo values < 1 I get the correct number of samples (in audio_buffer) from the output, but if I change it for example to 1.2 I suddenly get a very small number of sample and 0 if I use the value of 1.0. I am wondering if there is a bug in my chain configuration or data reading from the output? It is my first experience with libsox and I tried to follow examples, but I got stuck here.

Thank you in advance for the help!

Thank you!


Solution

    1. Your effect chain is ok and you can check that it gives correct output buffer by writing it to file - just replace this line in your code:

    sox_format_t* output = sox_open_memstream_write(&buffer, &buffer_size, &input->signal, &input->encoding, "raw", NULL);

    to this one:

    sox_format_t* output = sox_open_write("2.wav", &input->signal, &input->encoding, "raw", NULL, NULL);

    1. Regarding the memory output buffer issue - I studied libsox code, it seems there is a bug in its memory buffer treatment. As a workaround I would suggest you to add output->olength = 0; before reading output buffer, then it seems to work correctly.

    So, your code will look like this:

    ...
    if (std::stof(tempo_str) >= 1.0) { // use workaround only if tempo >= 1.0
        output->olength = 0;
    }
    
    std::vector<sox_sample_t> audio_buffer;
    for (size_t r; 0 != (r=sox_read(output,samples,maxSamples));)
        for(int i=0;i<r ;i++)
            audio_buffer.push_back(samples[i]);
    ...
    

    UPD: use workaround only when tempo >= 1.0