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:

perfect sine wave, sending audio direct driver

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:

rendering blocks of audio, poorly

here's same file, markers every 512 samples (i.e. every block of audio):

with markers

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

Popular posts from this blog

angular - Ionic slides - dynamically add slides before and after -

minify - Minimizing css files -

Add a dynamic header in angular 2 http provider -