Juceフレームワークを使用してVST/AUオーディオプラグインを構築しています。オーディオプラグインは、MIDIを受け取り、FluidSynth(サウンドフォントシンセサイザー)によって処理されるMIDIメッセージを送信することによって、そのMIDIをオーディオサンプルとしてレンダリングします。シンセサイザで処理するオーディオブロックの送信方法 - 不連続なし
これはほとんど動作しています。 MIDIメッセージはFluidSynthに正しく送信されます。確かに、オーディオプラグインは、オーディオドライバに直接MIDIメッセージをレンダリングするFluidSynthを伝えている場合 - - 正弦波のSoundFontを使用して、我々は完璧な結果を達成:
をしかし、私は直接レンダリングするFluidSynthを頼むべきではありませんオーディオドライバ。そのため、VSTホストはオーディオを受信しないためです。
適切に行うにはレンダラーを実装する必要があります。 VSTホストは、512サンプルのオーディオをレンダリングするために、毎秒44100÷512回の時間を要求します。
私はオンデマンドのオーディオサンプルのブロックをレンダリングし、VSTホストのオーディオバッファにそれらを出力しようとしたが、これは私が得た波形の一種である:
がここに同じですマーカーを持つファイルは、すべての512個のサンプル(オーディオのすなわちすべてのブロック):
それで、明らかに私は間違ったことをしています。私は連続波形を得ていません。処理中の各オーディオブロック間で不連続性が非常に目立ちます。 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
いい仕事です。 :)実際、FluidSynthはこの無国籍者であることは少し驚きです。遅延を伴うフィルタリングの多くを実行しますか? – bipll
@ bipllありがとう。 :)私はあなたの質問を理解するか分からない。 fluidsynthがバッファを満たすのに十分な速さでオーディオをレンダリングできるかどうか尋ねていますか?確かに512サンプルを毎秒44100回レンダリングすることができます。オーディオは素晴らしい音です。バッファをいっぱいにすばやくフィルタを適用できるかどうか尋ねていますか?私はまだコーラスとリバーブを試していません。またはあなたはそれが "遅延"フィルタを持っているかどうか尋ねていますか?私はそうは思わない。合成が低レイテンシであるかどうか尋ねていますか?私は、重要なレイテンシーは「どのくらい迅速にmidiNoteOnを処理できるか」だと思います。私はそのレイテンシを測定していません。 – Birchlabs
's /ステートレス/ステートフル/'> _ <。私は、fluid_synth_processにstartSample引数がなくても、wavedataをブロック単位で順番に返すだけであるのは不思議です。これはおそらく、遅延を使用する内部フィルタと関係しています。 – bipll