2017-09-11 13 views
1

Juceフレームワークを使用してVST/AUオーディオプラグインを構築しています。オーディオプラグインは、MIDIを受け取り、FluidSynth(サウンドフォントシンセサイザー)によって処理されるMIDIメッセージを送信することによって、そのMIDIをオーディオサンプルとしてレンダリングします。シンセサイザで処理するオーディオブロックの送信方法 - 不連続なし

これはほとんど動作しています。 MIDIメッセージはFluidSynthに正しく送信されます。確かに、オーディオプラグインは、オーディオドライバに直接MIDIメッセージをレンダリングするFluidSynthを伝えている場合 - - 正弦波のSoundFontを使用して、我々は完璧な結果を達成:

Perfect sine wave, by sending audio direct to driver

をしかし、私は直接レンダリングするFluidSynthを頼むべきではありませんオーディオドライバ。そのため、VSTホストはオーディオを受信しないためです。

適切に行うにはレンダラーを実装する必要があります。 VSTホストは、512サンプルのオーディオをレンダリングするために、毎秒44100÷512回の時間を要求します。


私はオンデマンドのオーディオサンプルのブロックをレンダリングし、VSTホストのオーディオバッファにそれらを出力しようとしたが、これは私が得た波形の一種である:

Rendering blocks of audio, poorly

がここに同じですマーカーを持つファイルは、すべての512個のサンプル(オーディオのすなわちすべてのブロック):

with markers

それで、明らかに私は間違ったことをしています。私は連続波形を得ていません。処理中の各オーディオブロック間で不連続性が非常に目立ちます。 JUCEの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 to 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 to store audio to 
     ); 
} 

これはシンセサイザーの各音声が512オーディオサンプルのブロックを生成することを要求する場所です。

ここで重要な機能はSynthesiserVoice::renderNextBlock()です。ここで私はfluid_synth_process()にオーディオサンプルのブロックを生成するように頼んでいます。


そしてここrenderNextBlock()にすべての音声を伝えるコードです:AudioProcessorの私の実装は。

AudioProcessor::processBlock()は、オーディオプラグインのメインループです。その中に、Synthesiser::renderNextBlock()は、すべての音声のSynthesiserVoice::renderNextBlock()を呼び出す:

void LazarusAudioProcessor::processBlock (
    AudioBuffer<float>& buffer, 
    MidiBuffer& midiMessages 
    ) { 
    jassert (!isUsingDoublePrecision()); 
    const int numSamples = buffer.getNumSamples(); 

    // 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); 

    // and now get our synth to process these midi events and generate its output. 
    synth.renderNextBlock (
     buffer,  // AudioBuffer<float> &outputAudio 
     midiMessages, // const MidiBuffer &inputMidi 
     0,   // int startSample 
     numSamples // int numSamples 
     ); 

    // In case we have more outputs than inputs, we'll clear any output 
    // channels that didn't contain input data, (because these aren't 
    // guaranteed to be empty - they may contain garbage). 
    for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i) 
     buffer.clear (i, 0, numSamples); 
} 

は、私がここで誤解してる何かはありますか? FluidSynthがサンプルの前のブロックと連続しているサンプルを私に与えるためには、タイミングの微妙さが必要ですか?私は渡す必要があるオフセットでしょうか?

多分、FluidSynthはステートフルであり、私がコントロールする必要がある独自のクロックがありますか?

私の波形には、よく知られているいくつかの問題の症状がありますか?私は重要な何かを省略してきた場合には

ソースコードは、hereです。コミット時に投稿された質問95605

答えて

1

私は最後の段落に書いたように、私は実現:

fluid_synth_process()は、オフセットタイミング情報やサンプルを指定するためのメカニズムを提供していません。 FluidSynthインスタンスは時刻0で始まり、fluid_synth_process()が呼び出されるたびにnumSamples * sampleRate秒だけ進むことがわかります(各ブロックは異なります)。

これは啓示につながる:fluid_synth_process()はFluidSynthインスタンスのタイミングに副作用を持っているので:複数の声は同じシンセ・インスタンス時にこれを実行するのは危険です。

私はconst int numVoices = 1;からconst int numVoices = 8;を削減しようとしました。したがって、1つのエージェントがブロックごとにfluid_synth_process()を呼び出します。

は、これが問題を修正しました。それは完全な波形を生成し、不連続の原因を明らかにした。

だから、私は「FluidSynthに複数の音声を合成するための最良の方法何」の方がはるかに簡単な問題で、今残っています。これははるかに良い問題です。それはこの質問の範囲外です。私はそれを別々に調査します。御時間ありがとうございます!

EDIT:は、複数のボイスを修正しました。私はSynthesiserVoice::renderNextBlock()無操作を行うと、代わりにそのfluid_synth_process()AudioProcessor::processBlock()にを移動することでこれをしなかった - それは(ない一度ブロックごとにボイスあたり)ブロックごとに一度起動する必要がありますので。

+0

いい仕事です。 :)実際、FluidSynthはこの無国籍者であることは少し驚きです。遅延を伴うフィルタリングの多くを実行しますか? – bipll

+0

@ bipllありがとう。 :)私はあなたの質問を理解するか分からない。 fluidsynthがバッファを満たすのに十分な速さでオーディオをレンダリングできるかどうか尋ねていますか?確かに512サンプルを毎秒44100回レンダリングすることができます。オーディオは素晴らしい音です。バッファをいっぱいにすばやくフィルタを適用できるかどうか尋ねていますか?私はまだコーラスとリバーブを試していません。またはあなたはそれが "遅延"フィルタを持っているかどうか尋ねていますか?私はそうは思わない。合成が低レイテンシであるかどうか尋ねていますか?私は、重要なレイテンシーは「どのくらい迅速にmidiNoteOnを処理できるか」だと思います。私はそのレイテンシを測定していません。 – Birchlabs

+0

's /ステートレス/ステートフル/'> _ <。私は、fluid_synth_processにstartSample引数がなくても、wavedataをブロック単位で順番に返すだけであるのは不思議です。これはおそらく、遅延を使用する内部フィルタと関係しています。 – bipll

関連する問題