|  | /*
 * Carla Native Plugins
 * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
 */
#include "CarlaNative.h"
#include "CarlaMIDI.h"
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CHANNELS 2
// -----------------------------------------------------------------------
typedef struct {
    float a0, b1, z1;
} Filter;
static inline
void set_filter_sample_rate(Filter* const filter, const float sampleRate)
{
    static const float M_PIf = (float)M_PI;
    const float frequency = 30.0f / sampleRate;
    filter->b1 = expf(-2.0f * M_PIf * frequency);
    filter->a0 = 1.0f - filter->b1;
    filter->z1 = 0.0f;
}
// -----------------------------------------------------------------------
typedef enum {
    // all versions
    PARAM_GAIN = 0,
    PARAM_COUNT_MONO,
    // stereo version
    PARAM_APPLY_LEFT = PARAM_COUNT_MONO,
    PARAM_APPLY_RIGHT,
    PARAM_COUNT_STEREO
} AudioGainParams;
typedef struct {
    Filter lowpass[MAX_CHANNELS];
    float gain;
    bool isMono;
    bool applyLeft;
    bool applyRight;
} AudioGainHandle;
// -----------------------------------------------------------------------
static NativePluginHandle audiogain_instantiate(const NativeHostDescriptor* host, const bool isMono)
{
    AudioGainHandle* const handle = (AudioGainHandle*)malloc(sizeof(AudioGainHandle));
    if (handle == NULL)
        return NULL;
    handle->gain = 1.0f;
    handle->isMono = isMono;
    handle->applyLeft = true;
    handle->applyRight = true;
    const float sampleRate = (float)host->get_sample_rate(host->handle);
    for (unsigned i = 0; i < MAX_CHANNELS; ++i)
        set_filter_sample_rate(&handle->lowpass[i], sampleRate);
    return handle;
}
static NativePluginHandle audiogain_instantiate_mono(const NativeHostDescriptor* host)
{
    return audiogain_instantiate(host, true);
}
static NativePluginHandle audiogain_instantiate_stereo(const NativeHostDescriptor* host)
{
    return audiogain_instantiate(host, false);
}
#define handlePtr ((AudioGainHandle*)handle)
static void audiogain_cleanup(NativePluginHandle handle)
{
    free(handlePtr);
}
static uint32_t audiogain_get_parameter_count(NativePluginHandle handle)
{
    return handlePtr->isMono ? PARAM_COUNT_MONO : PARAM_COUNT_STEREO;
}
static const NativeParameter* audiogain_get_parameter_info(NativePluginHandle handle, uint32_t index)
{
    if (index > (handlePtr->isMono ? PARAM_COUNT_MONO : PARAM_COUNT_STEREO))
        return NULL;
    static NativeParameter param;
    param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
    param.unit  = NULL;
    param.scalePointCount = 0;
    param.scalePoints     = NULL;
    switch (index)
    {
    case PARAM_GAIN:
        param.name = "Gain";
        param.ranges.def = 1.0f;
        param.ranges.min = 0.0f;
        param.ranges.max = 4.0f;
        param.ranges.step = PARAMETER_RANGES_DEFAULT_STEP;
        param.ranges.stepSmall = PARAMETER_RANGES_DEFAULT_STEP_SMALL;
        param.ranges.stepLarge = PARAMETER_RANGES_DEFAULT_STEP_LARGE;
        break;
    case PARAM_APPLY_LEFT:
        param.name   = "Apply Left";
        param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
        param.ranges.def = 1.0f;
        param.ranges.min = 0.0f;
        param.ranges.max = 1.0f;
        param.ranges.step = 1.0f;
        param.ranges.stepSmall = 1.0f;
        param.ranges.stepLarge = 1.0f;
        break;
    case PARAM_APPLY_RIGHT:
        param.name   = "Apply Right";
        param.hints |= NATIVE_PARAMETER_IS_BOOLEAN;
        param.ranges.def = 1.0f;
        param.ranges.min = 0.0f;
        param.ranges.max = 1.0f;
        param.ranges.step = 1.0f;
        param.ranges.stepSmall = 1.0f;
        param.ranges.stepLarge = 1.0f;
        break;
    }
    return ¶m;
}
static float audiogain_get_parameter_value(NativePluginHandle handle, uint32_t index)
{
    switch (index)
    {
    case PARAM_GAIN:
        return handlePtr->gain;
    case PARAM_APPLY_LEFT:
        return handlePtr->applyLeft ? 1.0f : 0.0f;
    case PARAM_APPLY_RIGHT:
        return handlePtr->applyRight ? 1.0f : 0.0f;
    default:
        return 0.0f;
    }
}
static void audiogain_set_parameter_value(NativePluginHandle handle, uint32_t index, float value)
{
    switch (index)
    {
    case PARAM_GAIN:
        handlePtr->gain = value;
        break;
    case PARAM_APPLY_LEFT:
        handlePtr->applyLeft = (value >= 0.5f);
        break;
    case PARAM_APPLY_RIGHT:
        handlePtr->applyRight = (value >= 0.5f);
        break;
    }
}
static inline
void handle_audio_buffers(const float* inBuffer, float* outBuffer, Filter* const filter, const float gain, const uint32_t frames)
{
    const float a0 = filter->a0;
    const float b1 = filter->b1;
    float z1 = filter->z1;
    for (uint32_t i=0; i < frames; ++i) {
        z1 = gain * a0 + z1 * b1;
        *outBuffer++ = *inBuffer++ * z1;
    }
    filter->z1 = z1;
}
// FIXME for v3.0, use const for the input buffer
static void audiogain_process(NativePluginHandle handle,
                             float** inBuffer, float** outBuffer, uint32_t frames,
                             const NativeMidiEvent* midiEvents, uint32_t midiEventCount)
{
    const float gain      = handlePtr->gain;
    const bool applyLeft  = handlePtr->applyLeft;
    const bool applyRight = handlePtr->applyRight;
    const bool isMono     = handlePtr->isMono;
    handle_audio_buffers(inBuffer[0], outBuffer[0], &handlePtr->lowpass[0], (isMono || applyLeft) ? gain : 1.0f, frames);
    if (! isMono)
        handle_audio_buffers(inBuffer[1], outBuffer[1], &handlePtr->lowpass[1], applyRight ? gain : 1.0f, frames);
    return;
    // unused
    (void)midiEvents;
    (void)midiEventCount;
}
static intptr_t audiogain_dispatcher(NativePluginHandle handle, NativePluginDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
{
    switch (opcode)
    {
    case NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED:
        for (unsigned i = 0; i < MAX_CHANNELS; ++i)
            set_filter_sample_rate(&handlePtr->lowpass[i], opt);
        break;
    default:
        break;
    }
    return 0;
    // unused
    (void)index;
    (void)value;
    (void)ptr;
}
// -----------------------------------------------------------------------
static const NativePluginDescriptor audiogainMonoDesc = {
    .category  = NATIVE_PLUGIN_CATEGORY_UTILITY,
    .hints     = NATIVE_PLUGIN_IS_RTSAFE,
    .supports  = NATIVE_PLUGIN_SUPPORTS_NOTHING,
    .audioIns  = 1,
    .audioOuts = 1,
    .midiIns   = 0,
    .midiOuts  = 0,
    .paramIns  = PARAM_COUNT_MONO,
    .paramOuts = 0,
    .name      = "Audio Gain (Mono)",
    .label     = "audiogain",
    .maker     = "falkTX",
    .copyright = "GNU GPL v2+",
    .instantiate = audiogain_instantiate_mono,
    .cleanup     = audiogain_cleanup,
    .get_parameter_count = audiogain_get_parameter_count,
    .get_parameter_info  = audiogain_get_parameter_info,
    .get_parameter_value = audiogain_get_parameter_value,
    .get_midi_program_count = NULL,
    .get_midi_program_info  = NULL,
    .set_parameter_value = audiogain_set_parameter_value,
    .set_midi_program    = NULL,
    .set_custom_data     = NULL,
    .ui_show = NULL,
    .ui_idle = NULL,
    .ui_set_parameter_value = NULL,
    .ui_set_midi_program    = NULL,
    .ui_set_custom_data     = NULL,
    .activate   = NULL,
    .deactivate = NULL,
    .process    = audiogain_process,
    .get_state = NULL,
    .set_state = NULL,
    .dispatcher = audiogain_dispatcher,
    .render_inline_display = NULL
};
static const NativePluginDescriptor audiogainStereoDesc = {
    .category  = NATIVE_PLUGIN_CATEGORY_UTILITY,
    .hints     = NATIVE_PLUGIN_IS_RTSAFE,
    .supports  = NATIVE_PLUGIN_SUPPORTS_NOTHING,
    .audioIns  = 2,
    .audioOuts = 2,
    .cvIns     = 0,
    .cvOuts    = 0,
    .midiIns   = 0,
    .midiOuts  = 0,
    .paramIns  = PARAM_COUNT_STEREO,
    .paramOuts = 0,
    .name      = "Audio Gain (Stereo)",
    .label     = "audiogain_s",
    .maker     = "falkTX",
    .copyright = "GNU GPL v2+",
    .instantiate = audiogain_instantiate_stereo,
    .cleanup     = audiogain_cleanup,
    .get_parameter_count = audiogain_get_parameter_count,
    .get_parameter_info  = audiogain_get_parameter_info,
    .get_parameter_value = audiogain_get_parameter_value,
    .get_midi_program_count = NULL,
    .get_midi_program_info  = NULL,
    .set_parameter_value = audiogain_set_parameter_value,
    .set_midi_program    = NULL,
    .set_custom_data     = NULL,
    .ui_show = NULL,
    .ui_idle = NULL,
    .ui_set_parameter_value = NULL,
    .ui_set_midi_program    = NULL,
    .ui_set_custom_data     = NULL,
    .activate   = NULL,
    .deactivate = NULL,
    .process    = audiogain_process,
    .get_state = NULL,
    .set_state = NULL,
    .dispatcher = audiogain_dispatcher
};
// -----------------------------------------------------------------------
void carla_register_native_plugin_audiogain(void);
void carla_register_native_plugin_audiogain(void)
{
    carla_register_native_plugin(&audiogainMonoDesc);
    carla_register_native_plugin(&audiogainStereoDesc);
}
// -----------------------------------------------------------------------
 |