vst - How to send blocks of audio to be processed by synthesizer -- without discontinuities -
i using juce framework build vst/au audio plugin. audio plugin accepts midi, , renders midi audio samples — sending midi messages processed fluidsynth (a soundfont synthesizer).
this working. midi messages sent fluidsynth correctly. indeed, if audio plugin tells fluidsynth render midi messages directly audio driver — using sine wave soundfont — achieve perfect result:
but shouldn't ask fluidsynth render directly audio driver. because vst host won't receive audio.
to properly: need implement renderer. vst host ask me (44100÷512) times per second render 512 samples of audio.
i tried rendering blocks of audio samples on-demand, , outputting vst host's audio buffer, kind of waveform got:
here's same file, markers every 512 samples (i.e. every block of audio):
so, i'm doing wrong. not getting continuous waveform. discontinuities visible between each blocks of audio process.
here's important part of code: implementation of juce's synthesiservoice
.
#include "soundfontsynthvoice.h" #include "soundfontsynthsound.h" soundfontsynthvoice::soundfontsynthvoice(const shared_ptr<fluid_synth_t> synth) : midinotenumber(0), synth(synth) {} bool soundfontsynthvoice::canplaysound(synthesisersound* sound) { return dynamic_cast<soundfontsynthsound*> (sound) != nullptr; } void soundfontsynthvoice::startnote(int midinotenumber, float velocity, synthesisersound* /*sound*/, int /*currentpitchwheelposition*/) { this->midinotenumber = midinotenumber; fluid_synth_noteon(synth.get(), 0, midinotenumber, static_cast<int>(velocity * 127)); } void soundfontsynthvoice::stopnote (float /*velocity*/, bool /*allowtailoff*/) { clearcurrentnote(); fluid_synth_noteoff(synth.get(), 0, this->midinotenumber); } void soundfontsynthvoice::rendernextblock ( audiobuffer<float>& outputbuffer, int startsample, int numsamples ) { fluid_synth_process( synth.get(), // fluid_synth_t *synth //fluidsynth instance numsamples, // int len //count of audio frames synthesize 1, // int nin //ignored nullptr, // float **in //ignored outputbuffer.getnumchannels(), // int nout //count of arrays in 'out' outputbuffer.getarrayofwritepointers() // float **out //array of arrays store audio ); }
this each voice of synthesizer asked produce block of 512 audio samples.
the important function here synthesiservoice::rendernextblock()
, wherein ask fluid_synth_process()
produce block of audio samples.
and here's code tells every voice rendernextblock()
: implementation of audioprocessor
.
audioprocessor::processblock()
main loop of audio plugin. within it, synthesiser::rendernextblock()
invokes every voice's synthesiservoice::rendernextblock()
:
void lazarusaudioprocessor::processblock ( audiobuffer<float>& buffer, midibuffer& midimessages ) { jassert (!isusingdoubleprecision()); const int numsamples = buffer.getnumsamples(); // pass incoming midi messages our keyboard state object, , let // add messages buffer if user clicking on on-screen keys keyboardstate.processnextmidibuffer (midimessages, 0, numsamples, true); // , our synth process these midi events , generate output. synth.rendernextblock ( buffer, // audiobuffer<float> &outputaudio midimessages, // const midibuffer &inputmidi 0, // int startsample numsamples // int numsamples ); // in case have more outputs inputs, we'll clear output // channels didn't contain input data, (because these aren't // guaranteed empty - may contain garbage). (int = gettotalnuminputchannels(); < gettotalnumoutputchannels(); ++i) buffer.clear (i, 0, numsamples); }
is there i'm misunderstanding here? there timing subtlety required make fluidsynth give me samples back-to-back previous block of samples? maybe offset need pass in?
maybe fluidsynth stateful, , has own clock need gain control of?
is waveform symptomatic of well-known problem?
source code here, in case i've omitted important. question posted @ time of commit 95605
.
as wrote final paragraph, realised:
fluid_synth_process()
provides no mechanism specifying timing information or sample offset. yet observe time advances nevertheless (each block different), simplest explanation is: fluidsynth instance begins @ time 0, , advances numsamples*samplerate seconds every time fluid_synth_process()
invoked.
this leads revelation: since fluid_synth_process()
has side-effects upon fluidsynth instance's timing: it dangerous multiple voices run upon same synth instance.
i tried reducing const int numvoices = 8;
const int numvoices = 1;
. 1 agent invoke fluid_synth_process()
per block.
this fixed problem; produced perfect waveform, , revealed source of discontinuity.
so, i'm left easier problem of "what's best way synthesize plurality of voices in fluidsynth". nicer problem have. that's outside of scope of question, , i'll investigate separately. time!
edit: fixed multiple voices. did making synthesiservoice::rendernextblock()
no-op, , moving fluid_synth_process()
audioprocessor::processblock()
instead — because should invoked once per block (not once per voice per block).
Comments
Post a Comment