C++ Core API: How to get PCM data after dsp processing?

I want to get the raw PCM output data in memory after all the dsp effects rendered, which is used for other processing. Currently, I can only write audios into wav files and read them back as pcm into memory. Due to some network issues in my environment, it costs a lot to write files, read and finally delete them. Is there any method to directly read them into memory?

I found a demo written in Unity but it only defines one callback CaptureDSPReadCallback. In the dsp_custom.cpp provided in linux examples, it does not have some CaptureDSPReadCallback while requires several other callback definitions. How to write a plugin like the one in Unity?

Thanks!

All of the callbacks shown in the dsp_custom example are optional, you can set it up simply like the example you found for Unity. For dsp_custom, you can simply use just myDSPCallback.

Thanks!

I’ve tried to figured out a scratch in c++, but it doesn’t work. I got FMOD_ERR_INVALID_PARAM while calling GetParameterDataCallback.

First, I add CreateCallback and ReleaseCallback while the Unity sample does not have. I believe it’s necessary in c++ because I need to manage the memory manually.

Second, I add one GetParameterDataCallback to get the plugindata like the dsp_custom.cpp example. It is probably where the program failed. I call the GetParameterDataCallback in the do-while render loop but get FMOD_ERR_INVALID_PARAM. The process also doesn’t enter the GetParameterDataCallback function in my debug process. However, I am not aware of which param is wrong.

I am using latest version: 2.01.08.

Here is the c++ scratch I am using now. This sample intends to capture the raw mono pcm data after dsp processing and writes them as a file. I hope it can be useful for others after the problem is solved.

#include <fstream>
#include "fmod.hpp"
#include "common.h"

typedef struct {
    float *buffer;
    int length_samples;
} mono_pcm;

// This dsp does nothing on audio but copy raw data into data buffer
FMOD_RESULT F_CALLBACK
CaptureDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels,
                   int *outchannels) {
    mono_pcm *data = (mono_pcm *) dsp_state->plugindata;
    for (size_t i = 0; i < length; ++i) {
        outbuffer[i] = inbuffer[i];
        data->buffer[i] = outbuffer[i];
    }
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK CaptureDSPCreateCallback(FMOD_DSP_STATE *dsp_state) {
    unsigned int blocksize;
    FMOD_RESULT result;
    result = dsp_state->functions->getblocksize(dsp_state, &blocksize);

    // allocate mem for mono_pcm struct
    mono_pcm *data = (mono_pcm *) calloc(sizeof(mono_pcm), 1);
    if (!data) return FMOD_ERR_MEMORY;

    // allocate mem for buffer in mono_pcm
    dsp_state->plugindata = data;
    data->length_samples = blocksize;
    data->buffer = (float *) malloc(blocksize * sizeof(float));
    if (!data->buffer) return FMOD_ERR_MEMORY;
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK CaptureDSPReleaseCallback(FMOD_DSP_STATE *dsp_state) {
    if (dsp_state->plugindata) {
        mono_pcm *data = (mono_pcm *) dsp_state->plugindata;
        if (data->buffer) free(data->buffer);
        free(data);
    }
    return FMOD_OK;
}

// This api should give access to mono_pcm struct
FMOD_RESULT F_CALLBACK
captureDSPGetParameterDataCallback(FMOD_DSP_STATE *dsp_state, int index, void **data, unsigned int *length, char *) {
    printf("Entered captureDSPGetParameterDataCallback!\n");
    if (index == 0) {
        unsigned int blocksize;
        FMOD_RESULT result;
        mono_pcm *mydata = (mono_pcm *) dsp_state->plugindata;

        result = dsp_state->functions->getblocksize(dsp_state, &blocksize);
        ERRCHECK(result);

        *data = (void *) mydata;
        return FMOD_OK;
    }

    return FMOD_ERR_INVALID_PARAM;
}


int main() {
    FMOD::System *system;
    FMOD::Sound *sound1;
    FMOD::Channel *channel = 0;
    FMOD::DSP *channelhead, *shiftedVocalFader, *captureDSP;
    FMOD::ChannelGroup *masterGroup;
    FMOD_RESULT result;

    FMOD::System_Create(&system);
    system->init(32, FMOD_INIT_NORMAL, 0);

    // open sound
    system->createSound("media/drumloop.wav", FMOD_DEFAULT, 0, &sound1);
    system->playSound(sound1, 0, false, &channel);

    // prepare capture dsp
    FMOD_DSP_DESCRIPTION dspdesc;
    memset(&dspdesc, 0, sizeof(dspdesc));
    strncpy(dspdesc.name, "Capture DSP", sizeof(dspdesc.name));
    dspdesc.numinputbuffers = 1;
    dspdesc.numoutputbuffers = 1;
    dspdesc.read = CaptureDSPCallback;
    dspdesc.create = CaptureDSPCreateCallback;
    dspdesc.release = CaptureDSPReleaseCallback;
    dspdesc.getparameterdata = captureDSPGetParameterDataCallback;
    result = system->createDSP(&dspdesc, &captureDSP);
    ERRCHECK(result);

    // add dsp
    channel->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &channelhead);
    system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &shiftedVocalFader);
    shiftedVocalFader->setActive(true);
    shiftedVocalFader->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 1.8f);

    system->getMasterChannelGroup(&masterGroup);
    result = masterGroup->addDSP(0, captureDSP);  // add capture
    ERRCHECK(result);
    masterGroup->addDSP(0, shiftedVocalFader);

    bool isplaying = true;
    mono_pcm *captureData;
    std::ofstream outf("out/captured.pcm", std::ios::binary);
    unsigned int length;
    char valuestr[512];
    do {
        result = system->update();
        ERRCHECK(result);
        channel->isPlaying(&isplaying);
        // cannot get the mono_pcm into captureData: FMOD_ERR_INVALID_PARAM
        result = captureDSP->getParameterData(0, (void **) &captureData, &length, valuestr, 0);
        ERRCHECK(result);
        // failed to write pcm file because captureData == NULL
        outf.write((char *) captureData->buffer, captureData->length_samples * sizeof(float));
    } while (isplaying);

    outf.close();

    result = sound1->release();
    ERRCHECK(result);

    result = masterGroup->removeDSP(captureDSP);
    ERRCHECK(result);
    result = captureDSP->release();
    ERRCHECK(result);

    result = system->close();
    ERRCHECK(result);
    result = system->release();
    ERRCHECK(result);
}

It looks like your plugin description is missing the parameter definitions. For getparameterdata to work, you need to define that parameter. If you look at the dsp_custom example you will see dspdesc.numparameters and dspdesc.paramdesc are specified. You’ll want to copy this, specifically the wavedata_desc parameter.

The invalid parameter error you are receiving is because the first parameter to getParameterData is the parameter index and since you haven’t defined any parameters, that index is out of range.

1 Like

Thanks! That is the problem. I thought it is something optional like help message, so I skipped that part.

I fixed it and now the code works. The DSP part:

#include <fstream>
#include "fmod.hpp"
#include "common.h"

typedef struct {
    float *buffer;
    int length_samples;
} mono_pcm;

// This dsp does nothing on audio but copy raw data into data buffer
FMOD_RESULT F_CALLBACK
CaptureDSPCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels,
                   int *outchannels) {
    mono_pcm *data = (mono_pcm *) dsp_state->plugindata;
    for (size_t i = 0; i < length; ++i) {
        outbuffer[i] = inbuffer[i];
        data->buffer[i] = outbuffer[i];
    }
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK CaptureDSPCreateCallback(FMOD_DSP_STATE *dsp_state) {
    unsigned int blocksize;
    FMOD_RESULT result;
    result = dsp_state->functions->getblocksize(dsp_state, &blocksize);

    // allocate mem for mono_pcm struct
    mono_pcm *data = (mono_pcm *) calloc(sizeof(mono_pcm), 1);
    if (!data) return FMOD_ERR_MEMORY;

    // allocate mem for buffer in mono_pcm
    dsp_state->plugindata = data;
    data->length_samples = blocksize;
    data->buffer = (float *) malloc(blocksize * sizeof(float));
    if (!data->buffer) return FMOD_ERR_MEMORY;
    return FMOD_OK;
}

FMOD_RESULT F_CALLBACK CaptureDSPReleaseCallback(FMOD_DSP_STATE *dsp_state) {
    if (dsp_state->plugindata) {
        mono_pcm *data = (mono_pcm *) dsp_state->plugindata;
        if (data->buffer) free(data->buffer);
        free(data);
    }
    return FMOD_OK;
}

// This api should give access to mono_pcm struct
FMOD_RESULT F_CALLBACK
captureDSPGetParameterDataCallback(FMOD_DSP_STATE *dsp_state, int index, void **data, unsigned int *length, char *) {
    if (index == 0) {
        unsigned int blocksize;
        FMOD_RESULT result;
        mono_pcm *mydata = (mono_pcm *) dsp_state->plugindata;

        result = dsp_state->functions->getblocksize(dsp_state, &blocksize);
        ERRCHECK(result);

        *data = (void *) mydata;
        return FMOD_OK;
    }

    return FMOD_ERR_INVALID_PARAM;
}

and the main part:

int main() {
    FMOD::System *system;
    FMOD::Sound *sound1;
    FMOD::Channel *channel = 0;
    FMOD::DSP *channelhead, *shiftedVocalFader, *captureDSP;
    FMOD::ChannelGroup *masterGroup;
    FMOD_RESULT result;

    FMOD::System_Create(&system);
    system->setOutput(FMOD_OUTPUTTYPE_WAVWRITER_NRT);
    result = system->init(32, FMOD_INIT_NORMAL | FMOD_INIT_PROFILE_ENABLE, (void *) "out/fmod_pitch_shift.wav");

    // open sound
    system->createSound("media/drumloop.wav", FMOD_DEFAULT, 0, &sound1);
    system->playSound(sound1, 0, false, &channel);

    // prepare capture dsp
    FMOD_DSP_PARAMETER_DESC pcmBuffer;
    FMOD_DSP_PARAMETER_DESC *captureDSPParamDescs[1] =
            {
                    &pcmBuffer,
            };

    FMOD_DSP_INIT_PARAMDESC_DATA(pcmBuffer, "pcm data", "", "pcm data", FMOD_DSP_PARAMETER_DATA_TYPE_USER);

    FMOD_DSP_DESCRIPTION dspdesc;
    memset(&dspdesc, 0, sizeof(dspdesc));
    strncpy(dspdesc.name, "Capture DSP", sizeof(dspdesc.name));
    dspdesc.numinputbuffers = 1;
    dspdesc.numoutputbuffers = 1;
    dspdesc.read = CaptureDSPCallback;
    dspdesc.create = CaptureDSPCreateCallback;
    dspdesc.release = CaptureDSPReleaseCallback;
    dspdesc.getparameterdata = captureDSPGetParameterDataCallback;
    dspdesc.numparameters = 1;
    dspdesc.paramdesc = captureDSPParamDescs;
    result = system->createDSP(&dspdesc, &captureDSP);
    ERRCHECK(result);

    // add dsp
    channel->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &channelhead);
    system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &shiftedVocalFader);
    shiftedVocalFader->setActive(true);
    shiftedVocalFader->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 1.8f);

    system->getMasterChannelGroup(&masterGroup);
    masterGroup->addDSP(0, shiftedVocalFader);
    result = masterGroup->addDSP(0, captureDSP);  // add capture
    ERRCHECK(result);

    bool isplaying = true;
    mono_pcm *captureData;
    std::ofstream outf("out/captured.pcm", std::ios::binary);
    unsigned int length, bufferlength;
    char valuestr[512];
    system->getDSPBufferSize(&bufferlength, 0);

    do {
        result = system->update();
        ERRCHECK(result);
        channel->isPlaying(&isplaying);
        result = captureDSP->getParameterData(0, (void **) &captureData, &length, valuestr, 0);
        ERRCHECK(result);
        outf.write((char *) captureData->buffer, captureData->length_samples * sizeof(float));
    } while (isplaying);

    outf.close();

    result = sound1->release();
    ERRCHECK(result);

    result = masterGroup->removeDSP(captureDSP);
    ERRCHECK(result);
    result = captureDSP->release();
    ERRCHECK(result);

    result = system->close();
    ERRCHECK(result);
    result = system->release();
    ERRCHECK(result);
}
1 Like

how to process PCM data without playing sound.

Linking into this discussion Adjust the sound effect without playing the sound - #2 by jeff_fmod