Alright here's a little experiment:
I documented my efforts this evening in making a very simple sine synth plug, using the Projucer plugin template and code from the 'audio plugin demo' example from the 4.0.1 release I downloaded from Github. Here it is in all its glory; I haven't got it completely working yet but I'm getting there it seems. I'm curious whether it makes any sense to share a coding/debugging session like this, might be a long/tedious read, might also be helpful in terms of conversational-style tutorials. Anyways here goes:
create Audio Plugin with Projucer 4.0.2 named ’SineSynth’
set Plugin Channel Configurations to {0, 2}
make sure ‘Plugin is a Synth’ is ticked
make sure ‘Plugin wants midi input’ is ticked
Save project and open in IDE
Build Settings: Deployment: set OS X Deployment Target to OS X 10.10
(I’m on Xcode 7.1.1 in Yosemite, somehow this defaults to 10.11 as deployment target which makes for non-loading plugins)
Build
Open in Juce Plug-In Host (scan for new or updated plugins)
check build effectiveness by changing Hello World into Hallo World - reopen in Plug-In Host, works
load fresh version of ‘audio plugin demo’ from JUCE-4.0.1.zip into examples folder
open JuceDemoPlugin.jucer in Projucer 4.0.2
save and open in IDE
build settings - deployment target 10.10
build - works
change Troughput level into Throughput Level - build - works.
now to copy the sine generator from the JuceDemoPlugin to the newly made SineSynth:
compare the PluginProcessor.h files from both open .jucer projects
hmm processBlock() from the JuceDemoPlugin (JDP) is slightly more defined:
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override;
versus in SineSynth (SS):
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
JDP has a reset function:
void reset() override;
JDP’s hasEditor’s function is immediately defined:
bool hasEditor() const override { return true; }
whereas SS’s isn’t:
bool hasEditor() const override;
also JDP:
const String getName() const override { return JucePlugin_Name; }
SS:
const String getName() const override;
some other differences similar to these, not gonna bother with those now
let’s see if I can copy the sine synth functionality from JDP to SS
copy this from the private parts of the class declaration:
// the synth!
Synthesiser synth;
switch to viewing both PluginProcessor.cpp files side by side and compare
why is there on JDP’s line 14:
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
when SS doesn’t have this? does this call really need to be made here?
assuming for now that it doesn’t
copying all the SineWave stuff from JDP line 17 to 179 to the top of SS, after the #include statements
now for the constructor, copying lines 198 to 202 from JDP to SS:
// Initialise the synth...
for (int i = 4; --i >= 0;)
synth.addVoice (new SineWaveVoice()); // These voices will play our custom sine-wave sounds..
synth.addSound (new SineWaveSound());
now for prepareToPlay(), copy line 214:
synth.setCurrentPlaybackSampleRate (newSampleRate);
to SS’s prepareToPlay() method, changing newSampleRate to sampleRate
now for processBlock(): first clear out all the code from SS because we’re dealing with a zero input, two output synth here,
all the standard code there deals with input audio processing
copy from JDP:
const int numSamples = buffer.getNumSamples();
synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
save and build SS - build succesful
open in Juce Plug-In Host: Couldn’t create filter, This plug-in failed to load correctly
errrmmm
maybe add this at the top of SS’s PluginProcessor.cpp then?
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
VST again fails to load in plugin host
AU does load but doesn’t output anything
let’s set up that debugging toolchain I read about earlier between Xcode and Juce Plug-in Host
follow instructions for that on http://www.juce.com/doc/tutorial_create_projucer_basic_plugin
build and run SS from Xcode, which now opens Plug-in Host automatically
within that open SineSynth.vst, again error dialog pops up;
in all output in Xcode the following is visible:
JUCE v4.0.1
Attempting to load VST: /Users/crosswick/Library/Audio/Plug-Ins/VST/SineSynth.vst
2015-11-27 22:17:46.533 Plugin Host[5063:945279] Error loading /Users/crosswick/Library/Audio/Plug-Ins/VST/SineSynth.vst/Contents/MacOS/SineSynth: dlopen(/Users/crosswick/Library/Audio/Plug-Ins/VST/SineSynth.vst/Contents/MacOS/SineSynth, 262): Library not loaded: /System/Library/Frameworks/CoreImage.framework/Versions/A/CoreImage
Referenced from: /Users/crosswick/Library/Audio/Plug-Ins/VST/SineSynth.vst/Contents/MacOS/SineSynth
Reason: image not found
CoreImage, excuse me? what the
why did it compile and run before? did I change anything graphics wise, or should I?
let’s do a Project - Clean in Xcode and check again
wait is this the deployment target stuff again?
argh seems so (but also yay) - how would one specify this build setting in Projucer…
right SS runs now but with some kind of crazy fast delay feedback happening
would this be down to that piece of code again? let’s remove that again at the top:
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
nope, still the same. now let’s check it in concordance with the JDP in action
with JDP as MIDI insert between MIDI input and SineSynth, the weird delay/feedback from SS is gone?!
how is this possible? aren’t JDP and SS supposed to be in their own separate memory spaces?
perhaps there’s some explicitly hacky code in the SineWaveSound code I copied?
lemme read through that code and see if anything sticks out
you know what, let’s empty the audio buffer at first in SS’s processBlock() just to be sure.
it’s not done in JDP of course seen as that’s also an effect with audio input
oops deleted the code that did that already, let’s make another dummy plugin to get it back and paste it
it was
for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, buffer.getNumSamples());
aight save all, build in Xcode
yes! now we’ve got normal sines sounding; note off doesn’t work though, let’s investigate
JDP doesn’t show the same behavior within the same host & MIDI setup
perhaps JDP’s reset() method? nah that only deals with the delay thing which I’m not copying in SS
how does a note off message get processed in JDP?
wait let’s delete the FloatParameter stuff, I don’t think I need that, that might be interfering
nope still the same, but good riddance anyway I guess
any other methods I should copy from JDP to SS? in the Editor even perhaps?
wait, perhaps the MidiKeyboardState stuff (which I haven’t copied to SS yet) is somehow responsible for properly managing MIDI info?
let’s copy that stuff in starting with
// this is kept up to date with the midi messages that arrive, and the UI component
// registers with it so it can represent the incoming messages
MidiKeyboardState keyboardState;
in PluginProcessor.h
and the following to SS’s processBlock() right before the synth.renderBlock call:
// Now pass any incoming midi messages to our keyboard state object, and let it
// add messages to the buffer if the user is clicking on the on-screen keys
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
I wonder whether this is enough or I should actually add the corresponding GUi object too… let’s see if this builds
it does build and run, still no note off though…
well then… errrrm