|
|
@@ -36,11 +36,6 @@ struct HostAudio : TerminalModule { |
|
|
|
dsp::RCFilter dcFilters[numIO]; |
|
|
|
bool dcFilterEnabled = (numIO == 2); |
|
|
|
|
|
|
|
// for stereo meter |
|
|
|
volatile bool resetMeters = true; |
|
|
|
float gainMeterL = 0.0f; |
|
|
|
float gainMeterR = 0.0f; |
|
|
|
|
|
|
|
HostAudio() |
|
|
|
: pcontext(static_cast<CardinalPluginContext*>(APP)), |
|
|
|
numParams(numIO == 2 ? 1 : 0), |
|
|
@@ -63,13 +58,10 @@ struct HostAudio : TerminalModule { |
|
|
|
void onReset() override |
|
|
|
{ |
|
|
|
dcFilterEnabled = (numIO == 2); |
|
|
|
resetMeters = true; |
|
|
|
} |
|
|
|
|
|
|
|
void onSampleRateChange(const SampleRateChangeEvent& e) override |
|
|
|
{ |
|
|
|
resetMeters = true; |
|
|
|
|
|
|
|
for (int i=0; i<numIO; ++i) |
|
|
|
dcFilters[i].setCutoffFreq(10.f * e.sampleTime); |
|
|
|
} |
|
|
@@ -103,7 +95,51 @@ struct HostAudio : TerminalModule { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void processTerminalOutput(const ProcessArgs&) override |
|
|
|
json_t* dataToJson() override |
|
|
|
{ |
|
|
|
json_t* const rootJ = json_object(); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr); |
|
|
|
|
|
|
|
json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled)); |
|
|
|
return rootJ; |
|
|
|
} |
|
|
|
|
|
|
|
void dataFromJson(json_t* const rootJ) override |
|
|
|
{ |
|
|
|
json_t* const dcFilterJ = json_object_get(rootJ, "dcFilter"); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(dcFilterJ != nullptr,); |
|
|
|
|
|
|
|
dcFilterEnabled = json_boolean_value(dcFilterJ); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
struct HostAudio2 : HostAudio<2> { |
|
|
|
// for stereo meter |
|
|
|
int internalDataFrame = 0; |
|
|
|
float internalDataBuffer[2][128]; |
|
|
|
volatile bool resetMeters = true; |
|
|
|
float gainMeterL = 0.0f; |
|
|
|
float gainMeterR = 0.0f; |
|
|
|
|
|
|
|
HostAudio2() |
|
|
|
: HostAudio<2>() |
|
|
|
{ |
|
|
|
std::memset(internalDataBuffer, 0, sizeof(internalDataBuffer)); |
|
|
|
} |
|
|
|
|
|
|
|
void onReset() override |
|
|
|
{ |
|
|
|
HostAudio<2>::onReset(); |
|
|
|
resetMeters = true; |
|
|
|
} |
|
|
|
|
|
|
|
void onSampleRateChange(const SampleRateChangeEvent& e) override |
|
|
|
{ |
|
|
|
HostAudio<2>::onSampleRateChange(e); |
|
|
|
resetMeters = true; |
|
|
|
} |
|
|
|
|
|
|
|
void processTerminalOutput(const ProcessArgs&) |
|
|
|
{ |
|
|
|
const int blockFrames = pcontext->engine->getBlockFrames(); |
|
|
|
|
|
|
@@ -116,11 +152,15 @@ struct HostAudio : TerminalModule { |
|
|
|
|
|
|
|
float** const dataOuts = pcontext->dataOuts; |
|
|
|
|
|
|
|
// stereo version gain |
|
|
|
const float gain = numParams != 0 ? std::pow(params[0].getValue(), 2.f) : 1.0f; |
|
|
|
// gain (stereo variant only) |
|
|
|
const float gain = std::pow(params[0].getValue(), 2.f); |
|
|
|
|
|
|
|
// left/mono check |
|
|
|
const bool in2connected = inputs[1].isConnected(); |
|
|
|
|
|
|
|
// read first value, special case for mono mode |
|
|
|
// read stereo values |
|
|
|
float valueL = inputs[0].getVoltageSum() * 0.1f; |
|
|
|
float valueR = inputs[1].getVoltageSum() * 0.1f; |
|
|
|
|
|
|
|
// Apply DC filter |
|
|
|
if (dcFilterEnabled) |
|
|
@@ -132,68 +172,111 @@ struct HostAudio : TerminalModule { |
|
|
|
valueL = clamp(valueL * gain, -1.0f, 1.0f); |
|
|
|
dataOuts[0][k] += valueL; |
|
|
|
|
|
|
|
// read everything else |
|
|
|
for (int i=1; i<numInputs; ++i) |
|
|
|
if (in2connected) |
|
|
|
{ |
|
|
|
float v = inputs[i].getVoltageSum() * 0.1f; |
|
|
|
|
|
|
|
// Apply DC filter |
|
|
|
if (dcFilterEnabled) |
|
|
|
{ |
|
|
|
dcFilters[i].process(v); |
|
|
|
v = dcFilters[i].highpass(); |
|
|
|
dcFilters[1].process(valueR); |
|
|
|
valueR = dcFilters[1].highpass(); |
|
|
|
} |
|
|
|
|
|
|
|
dataOuts[i][k] += clamp(v * gain, -1.0f, 1.0f); |
|
|
|
valueR = clamp(valueR * gain, -1.0f, 1.0f); |
|
|
|
dataOuts[1][k] += valueR; |
|
|
|
} |
|
|
|
|
|
|
|
if (numInputs == 2) |
|
|
|
else |
|
|
|
{ |
|
|
|
const bool connected = inputs[1].isConnected(); |
|
|
|
valueR = valueL; |
|
|
|
dataOuts[1][k] += valueL; |
|
|
|
} |
|
|
|
|
|
|
|
if (! connected) |
|
|
|
dataOuts[1][k] += valueL; |
|
|
|
const int j = internalDataFrame++; |
|
|
|
internalDataBuffer[0][j] = valueL; |
|
|
|
internalDataBuffer[1][j] = valueR; |
|
|
|
|
|
|
|
if (dataFrame == blockFrames) |
|
|
|
{ |
|
|
|
if (resetMeters) |
|
|
|
gainMeterL = gainMeterR = 0.0f; |
|
|
|
if (internalDataFrame == 128) |
|
|
|
{ |
|
|
|
internalDataFrame = 0; |
|
|
|
|
|
|
|
gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat(dataOuts[0], blockFrames)); |
|
|
|
if (resetMeters) |
|
|
|
gainMeterL = gainMeterR = 0.0f; |
|
|
|
|
|
|
|
if (connected) |
|
|
|
gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat(dataOuts[1], blockFrames)); |
|
|
|
else |
|
|
|
gainMeterR = gainMeterL; |
|
|
|
gainMeterL = std::max(gainMeterL, d_findMaxNormalizedFloat(internalDataBuffer[0], 128)); |
|
|
|
|
|
|
|
resetMeters = false; |
|
|
|
} |
|
|
|
if (in2connected) |
|
|
|
gainMeterR = std::max(gainMeterR, d_findMaxNormalizedFloat(internalDataBuffer[1], 128)); |
|
|
|
else |
|
|
|
gainMeterR = gainMeterL; |
|
|
|
|
|
|
|
resetMeters = false; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
json_t* dataToJson() override |
|
|
|
struct HostAudio8 : HostAudio<8> { |
|
|
|
// no meters in this variant |
|
|
|
|
|
|
|
void processTerminalOutput(const ProcessArgs&) override |
|
|
|
{ |
|
|
|
json_t* const rootJ = json_object(); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(rootJ != nullptr, nullptr); |
|
|
|
const int blockFrames = pcontext->engine->getBlockFrames(); |
|
|
|
|
|
|
|
json_object_set_new(rootJ, "dcFilter", json_boolean(dcFilterEnabled)); |
|
|
|
return rootJ; |
|
|
|
// only incremented on output |
|
|
|
const int k = dataFrame++; |
|
|
|
DISTRHO_SAFE_ASSERT_INT2_RETURN(k < blockFrames, k, blockFrames,); |
|
|
|
|
|
|
|
if (isBypassed()) |
|
|
|
return; |
|
|
|
|
|
|
|
float** const dataOuts = pcontext->dataOuts; |
|
|
|
|
|
|
|
for (int i=0; i<numInputs; ++i) |
|
|
|
{ |
|
|
|
float v = inputs[i].getVoltageSum() * 0.1f; |
|
|
|
|
|
|
|
if (dcFilterEnabled) |
|
|
|
{ |
|
|
|
dcFilters[i].process(v); |
|
|
|
v = dcFilters[i].highpass(); |
|
|
|
} |
|
|
|
|
|
|
|
dataOuts[i][k] += clamp(v, -1.0f, 1.0f); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void dataFromJson(json_t* const rootJ) override |
|
|
|
}; |
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
template<int numIO> |
|
|
|
struct HostAudioWidget : ModuleWidgetWith8HP { |
|
|
|
HostAudio<numIO>* const module; |
|
|
|
|
|
|
|
HostAudioWidget(HostAudio<numIO>* const m) |
|
|
|
: module(m) |
|
|
|
{ |
|
|
|
json_t* const dcFilterJ = json_object_get(rootJ, "dcFilter"); |
|
|
|
DISTRHO_SAFE_ASSERT_RETURN(dcFilterJ != nullptr,); |
|
|
|
setModule(m); |
|
|
|
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg"))); |
|
|
|
|
|
|
|
dcFilterEnabled = json_boolean_value(dcFilterJ); |
|
|
|
createAndAddScrews(); |
|
|
|
|
|
|
|
for (uint i=0; i<numIO; ++i) |
|
|
|
{ |
|
|
|
createAndAddInput(i); |
|
|
|
createAndAddOutput(i); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void appendContextMenu(Menu* const menu) override { |
|
|
|
menu->addChild(new MenuSeparator); |
|
|
|
menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled)); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
template<int numIO> |
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
struct HostAudioNanoMeter : NanoMeter { |
|
|
|
HostAudio<numIO>* const module; |
|
|
|
HostAudio2* const module; |
|
|
|
|
|
|
|
HostAudioNanoMeter(HostAudio<numIO>* const m) |
|
|
|
HostAudioNanoMeter(HostAudio2* const m) |
|
|
|
: module(m) |
|
|
|
{ |
|
|
|
hasGainKnob = true; |
|
|
@@ -211,69 +294,58 @@ struct HostAudioNanoMeter : NanoMeter { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
template<int numIO> |
|
|
|
struct HostAudioWidget : ModuleWidgetWith8HP { |
|
|
|
HostAudio<numIO>* const module; |
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
HostAudioWidget(HostAudio<numIO>* const m) |
|
|
|
: module(m) |
|
|
|
struct HostAudioWidget2 : HostAudioWidget<2> { |
|
|
|
HostAudioWidget2(HostAudio2* const m) |
|
|
|
: HostAudioWidget<2>(m) |
|
|
|
{ |
|
|
|
setModule(m); |
|
|
|
setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/HostAudio.svg"))); |
|
|
|
// FIXME |
|
|
|
const float middleX = box.size.x * 0.5f; |
|
|
|
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0)); |
|
|
|
|
|
|
|
HostAudioNanoMeter* const meter = new HostAudioNanoMeter(m); |
|
|
|
meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2); |
|
|
|
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f); |
|
|
|
addChild(meter); |
|
|
|
} |
|
|
|
|
|
|
|
createAndAddScrews(); |
|
|
|
void draw(const DrawArgs& args) override |
|
|
|
{ |
|
|
|
drawBackground(args.vg); |
|
|
|
drawOutputJacksArea(args.vg, 2); |
|
|
|
setupTextLines(args.vg); |
|
|
|
|
|
|
|
for (uint i=0; i<numIO; ++i) |
|
|
|
{ |
|
|
|
createAndAddInput(i); |
|
|
|
createAndAddOutput(i); |
|
|
|
} |
|
|
|
drawTextLine(args.vg, 0, "Left/M"); |
|
|
|
drawTextLine(args.vg, 1, "Right"); |
|
|
|
|
|
|
|
if (numIO == 2) |
|
|
|
{ |
|
|
|
// FIXME |
|
|
|
const float middleX = box.size.x * 0.5f; |
|
|
|
addParam(createParamCentered<NanoKnob>(Vec(middleX, 310.0f), m, 0)); |
|
|
|
|
|
|
|
HostAudioNanoMeter<numIO>* const meter = new HostAudioNanoMeter<numIO>(m); |
|
|
|
meter->box.pos = Vec(middleX - padding + 2.75f, startY + padding * 2); |
|
|
|
meter->box.size = Vec(padding * 2.0f - 4.0f, 136.0f); |
|
|
|
addChild(meter); |
|
|
|
} |
|
|
|
ModuleWidgetWith8HP::draw(args); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
struct HostAudioWidget8 : HostAudioWidget<8> { |
|
|
|
HostAudioWidget8(HostAudio8* const m) |
|
|
|
: HostAudioWidget<8>(m) {} |
|
|
|
|
|
|
|
void draw(const DrawArgs& args) override |
|
|
|
{ |
|
|
|
drawBackground(args.vg); |
|
|
|
drawOutputJacksArea(args.vg, numIO); |
|
|
|
drawOutputJacksArea(args.vg, 8); |
|
|
|
setupTextLines(args.vg); |
|
|
|
|
|
|
|
if (numIO == 2) |
|
|
|
{ |
|
|
|
drawTextLine(args.vg, 0, "Left/M"); |
|
|
|
drawTextLine(args.vg, 1, "Right"); |
|
|
|
} |
|
|
|
else |
|
|
|
for (int i=0; i<8; ++i) |
|
|
|
{ |
|
|
|
for (int i=0; i<numIO; ++i) |
|
|
|
{ |
|
|
|
char text[] = {'A','u','d','i','o',' ',static_cast<char>('0'+i+1),'\0'}; |
|
|
|
drawTextLine(args.vg, i, text); |
|
|
|
} |
|
|
|
char text[] = {'A','u','d','i','o',' ',static_cast<char>('0'+i+1),'\0'}; |
|
|
|
drawTextLine(args.vg, i, text); |
|
|
|
} |
|
|
|
|
|
|
|
ModuleWidgetWith8HP::draw(args); |
|
|
|
} |
|
|
|
|
|
|
|
void appendContextMenu(Menu* const menu) override { |
|
|
|
menu->addChild(new MenuSeparator); |
|
|
|
menu->addChild(createBoolPtrMenuItem("DC blocker", "", &module->dcFilterEnabled)); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
Model* modelHostAudio2 = createModel<HostAudio<2>, HostAudioWidget<2>>("HostAudio2"); |
|
|
|
Model* modelHostAudio8 = createModel<HostAudio<8>, HostAudioWidget<8>>("HostAudio8"); |
|
|
|
Model* modelHostAudio2 = createModel<HostAudio2, HostAudioWidget2>("HostAudio2"); |
|
|
|
Model* modelHostAudio8 = createModel<HostAudio8, HostAudioWidget8>("HostAudio8"); |
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------------------------------------------- |