I Want to Call From a Class Inside my Processing Block but Having Issues Trying to Do So


#1

So I’ve used HEAVY to export C++ code from a pure data patch and have included these files inside my Juce Session. I’m currently trying to call the methods from these files in my processing block inside the juice session but can’t seem to get it to work. Here is some code to demonstrate.

This is the method I’m trying to call from the other file:

void HeavyVst2_creatureSynthHL::processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames) {
  hv_creatureSynthHL_process(_context, inputs, outputs, sampleFrames);
}

and I want to put it in here:

void Animal_generator_krotosAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        float* channelData = buffer.getWritePointer (channel);

        // ..do something to the data...
    }
}

This is just the auto-generated code for the process block from Projucer.

Any help would be greatly appreciated!


#2

It’s going to be impossible to help you unless you post the full code (or a minimal working example that repeats the error), and a list of the errors. Are they compile errors or run time errors?


#3

Yeah sorry, here is the full code. I want to call the “ProcessReplacing” function in the “processingBlock” of the JUCE plugin.

This is the other .cpp file generated by HEAVY

/**
 * Copyright (c) 2014,2015,2016 Enzien Audio Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, and/or
 * sublicense copies of the Software, strictly on a non-commercial basis,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 *
 * DO NOT MODIFY. THIS CODE IS MACHINE GENERATED BY THE ENZIEN AUDIO HEAVY COMPILER.
 */

#include "HeavyVst2_creatureSynthHL.h"
#include "HvUtils.h"

#define HV_VST2_NUM_PARAMETERS 5

extern "C" {
  HV_EXPORT AEffect *VSTPluginMain(audioMasterCallback audioMaster) {
    // Get VST Version of the Host, return NULL if old version
    if (!audioMaster(0, audioMasterVersion, 0, 0, 0, 0)) return NULL;

    // Create the AudioEffect
    AudioEffect* effect = new HeavyVst2_creatureSynthHL(audioMaster);
    if (effect == NULL) return NULL;

    // Return the VST AEffect structure
    return effect->getAeffect();
  }
}

HeavyVst2_creatureSynthHL::HeavyVst2_creatureSynthHL(audioMasterCallback amCallback) :
    AudioEffectX(amCallback, 0, HV_VST2_NUM_PARAMETERS) {
  setUniqueID(0x2FEC904B);
  setNumInputs(0);
  setNumOutputs(2);
  isSynth(true);
  canProcessReplacing(true);
  canDoubleReplacing(false);
  // initialise parameters with defaults
  _parameters[0] = 0.5f; // aggression
  _parameters[1] = 0.0f; // excitation
  _parameters[2] = 0.5f; // mouthScale
  _parameters[3] = 1.0f; // rippleScale
  _parameters[4] = 0.5f; // size
  _context = NULL;
  this->sampleRate = 0.0f; // initialise sample rate
  setSampleRate(44100.0f); // set sample rate to some default
}

HeavyVst2_creatureSynthHL::~HeavyVst2_creatureSynthHL() {
  if (_context != NULL) hv_creatureSynthHL_free(_context);
}

void HeavyVst2_creatureSynthHL::setSampleRate(float sampleRate) {
  if (this->sampleRate != sampleRate) {
    this->sampleRate = sampleRate;
    if (_context != NULL) hv_creatureSynthHL_free(_context);
    _context = hv_creatureSynthHL_new(sampleRate);
    // ensure that the new context has the current parameters
    for (int i = 0; i < HV_VST2_NUM_PARAMETERS; ++i) {
      setParameter(i, _parameters[i]);
    }
  }
}

void HeavyVst2_creatureSynthHL::processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames) {
  hv_creatureSynthHL_process(_context, inputs, outputs, sampleFrames);
    
}

VstInt32 HeavyVst2_creatureSynthHL::processEvents(VstEvents* events) {
  for (int i = 0; i < events->numEvents; ++i) {
    VstEvent *vste = events->events[i];
    switch (vste->type) {
      case kVstMidiType: {
        VstMidiEvent *vstme = (VstMidiEvent *) vste;

        const unsigned char command = vstme->midiData[0] & 0xF0;
        const unsigned char channel = vstme->midiData[0] & 0x0F;
        const unsigned char data0   = vstme->midiData[1] & 0x7F;
        const unsigned char data1   = vstme->midiData[2] & 0x7F;

        hv_uint32_t receiverHash = 0;
        switch (command) {
          case 0x80:   // note off
          case 0x90: { // note on
            receiverHash = 0x67E37CA3; // __hv_notein
            break;
          }
          case 0xB0: { // control change
            receiverHash = 0x41BE0F9C; // __hv_ctlin
            break;
          }
          default: continue;
        }

        hv_vscheduleMessageForReceiver(_context, receiverHash, 1000.0*vste->deltaFrames/sampleRate, "fffff",
            (float) data1,
            (float) data0,
            (float) channel,
            (float) command,
            0.0f // port
        );
        break;
      }
      case kVstSysExType: {
        // not handling this case at the moment, VstMidiSysexEvent *vstmse;
        break;
      }
      default: break;
    }
  }
  return 1;
}

static float scaleParameterForIndex(VstInt32 index, float value) {
  switch (index) {
    case 0: return (1.0f*value) + 0.0f; // aggression
    case 1: return (1.0f*value) + 0.0f; // excitation
    case 2: return (1.0f*value) + 0.0f; // mouthScale
    case 3: return (1.0f*value) + 0.0f; // rippleScale
    case 4: return (1.0f*value) + 0.0f; // size
    default: return 0.0f;
  }
}

void HeavyVst2_creatureSynthHL::setParameter(VstInt32 index, float value) {
  switch (index) {
    case 0: {
      hv_sendFloatToReceiver(_context, HV_CREATURESYNTHHL_PARAM_AGGRESSION, scaleParameterForIndex(index, value));
      break;
    }
    case 1: {
      hv_sendFloatToReceiver(_context, HV_CREATURESYNTHHL_PARAM_EXCITATION, scaleParameterForIndex(index, value));
      break;
    }
    case 2: {
      hv_sendFloatToReceiver(_context, HV_CREATURESYNTHHL_PARAM_MOUTHSCALE, scaleParameterForIndex(index, value));
      break;
    }
    case 3: {
      hv_sendFloatToReceiver(_context, HV_CREATURESYNTHHL_PARAM_RIPPLESCALE, scaleParameterForIndex(index, value));
      break;
    }
    case 4: {
      hv_sendFloatToReceiver(_context, HV_CREATURESYNTHHL_PARAM_SIZE, scaleParameterForIndex(index, value));
      break;
    }
    default: return;
  }
  _parameters[index] = value;
}

float HeavyVst2_creatureSynthHL::getParameter(VstInt32 index) {
  return _parameters[index];
}

void HeavyVst2_creatureSynthHL::getParameterName(VstInt32 index, char* text) {
  switch (index) {
    case 0: strncpy(text, "aggression", kVstMaxParamStrLen); break;
    case 1: strncpy(text, "excitation", kVstMaxParamStrLen); break;
    case 2: strncpy(text, "mouthScale", kVstMaxParamStrLen); break;
    case 3: strncpy(text, "rippleScale", kVstMaxParamStrLen); break;
    case 4: strncpy(text, "size", kVstMaxParamStrLen); break;
    default: text[0] = '\0'; break;
  }
  text[kVstMaxParamStrLen-1] = '\0';
}

void HeavyVst2_creatureSynthHL::getParameterDisplay(VstInt32 index, char* text) {
  snprintf(text, kVstMaxParamStrLen, "%0.3f", scaleParameterForIndex(index, _parameters[index]));
  text[kVstMaxParamStrLen-1] = '\0';
}

bool HeavyVst2_creatureSynthHL::string2parameter(VstInt32 index, char* text) {
  setParameter(index, (float) atof(text));
  return true;
}

bool HeavyVst2_creatureSynthHL::getEffectName(char* name) {
  strncpy(name, "creatureSynthHL", kVstMaxEffectNameLen);
  name[kVstMaxEffectNameLen-1] = '\0';
  return true;
}

bool HeavyVst2_creatureSynthHL::getVendorString(char* text) {
  strncpy(text, "Enzien Audio, Ltd.", kVstMaxVendorStrLen);
  text[kVstMaxVendorStrLen-1] = '\0';
  return true;
}

VstInt32 HeavyVst2_creatureSynthHL::canDo(char *text) {
  if (!strcmp(text, "receiveVstEvents")) return 1; // PlugCanDos::canDoReceiveVstEvents
  if (!strcmp(text, "receiveVstMidiEvent")) return 1; // PlugCanDos::canDoReceiveVstMidiEvent
  return 0;
}

VstInt32 HeavyVst2_creatureSynthHL::getChunk(void** data, bool isPreset) {
  const VstInt32 numBytes = HV_VST2_NUM_PARAMETERS * sizeof(float);
  float *chunk = (float *) malloc(numBytes);
  memcpy(chunk, _parameters, numBytes);
  *data = chunk;
  return numBytes;
}

VstInt32 HeavyVst2_creatureSynthHL::setChunk(void* data, VstInt32 byteSize, bool isPreset) {
  float *const chunk = (float *) data;
  for (int i = 0; i < HV_VST2_NUM_PARAMETERS; ++i) {
    setParameter(i, chunk[i]);
  }
  return byteSize;
}

I thought it would be something like this but I don’t know how to implement it properly.

void Animal_generator_krotosAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{
    const int totalNumInputChannels  = getTotalNumInputChannels();
    const int totalNumOutputChannels = getTotalNumOutputChannels();
    
    HeavyVst2_creatureSynthHL::HeavyVst2_creatureSynthHL processReplacing(float** inputs, float** outputs, VstInt32 sampleFrames);

    // In case we have more outputs than inputs, this code clears any output
    // channels that didn't contain input data, (because these aren't
    // guaranteed to be empty - they may contain garbage).
    // This is here to avoid people getting screaming feedback
    // when they first compile a plugin, but obviously you don't need to keep
    // this code if your algorithm always overwrites all the output channels.
    for (int i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // This is the place where you'd normally do the guts of your plugin's
    // audio processing...
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
    {
        float* channelData = buffer.getWritePointer (channel);

        // ..do something to the data...
    }
}

This “processReplacing” function is the audio generator created for the Pure Data patch using HEAVY if that helps illustrate why I want to do this. I’m simply trying to channel the audio from the Pure data patch and output it through “processingBlock” in JUCE.


#4

I think you need to learn a little bit about C++ before you dive in here because there’s a lot wrong with what you’re doing. You’re not calling the function (you need to pass in those arguments, not declare them), it doesn’t look like you create an instance of the class to call the function on, and the data for the function is not compatible with the data you get from getWritePointer. You’ll need to rearrange the buffer data in order to pass it in as an argument for that function.


#5

Yeah I know how to call methods from classes if it’s just simple arguments being passed in like ints and floats but I have no idea what this is even wanting. I just know that when I put in break points before this function the plugin doesn’t create audio and after this the plugin does create audio when I’m running the standard HEAVY generated VST. I just want to lift this generator out of the HEAVY code and into JUCE

What would you suggest are the areas I should read up on to get my head around what’s going on here?


#6

The function is looking for arrays of floats as arguments for the input/output buffer. processBlock() uses a ‘bus’ for the input/output, meaning that each channel is an array of samples and the same array functions as both input and output (IE read the sample at some index, process it, then put it back at the same index).

That function you’re trying to call is looking for 2D arrays, or an “array of arrays” where the first dimension is (usually) the channel number, and the second dimension is the sample index.

An alternative approach is to copy the functionality of the HEAVY code instead of trying to call it from within your JUCE plugin.