Unmanaged VSTi does not sound

Jan 5, 2010 at 1:40 PM

I'm new in the VST world, so I need your help.

I have loaded an unmanaged VSTi (ColossusVST.dll) in my application and I have opened its editor from wich I could load a patch (instrument) for the VSTi pluggin, after that when I use the VSTi keyboard pressing some notes, no sound is produced.

I'm sure there are some methods to invoke for enabling the sound for the VSTi, but ¿which method or methods do I have to use?

Please help me, your comments will be very grateful.

Coordinator
Jan 6, 2010 at 5:57 AM

A VST host is responsible for the audio routing. A plugin just processes that audio data and outputs the processed audio. So you need something like ASIO to drive your soundcard (input and output) and connect plugins to the audio busses (usually stereo but could also be 5.1). As a Host you need to call ProcessReplacing on each of the plugins that need to process the audio, feeding the output of the previous plugin to the input of the next plugin (chain).

There are managed .NET ASIO libraries, although I have no experience with them.

NAudio (playback only)
http://naudio.codeplex.com/

Codeproject
http://www.codeproject.com/KB/audio-video/Asio_Net.aspx

Hope it helps. 

Jan 7, 2010 at 12:25 AM

Thank you for the explanation, its very helpful for me, I will try your suggestions and then I will comment you about that... thanks again.

Jan 7, 2010 at 5:29 PM

Hello obiwanjacobi, I spent a long time and I had no success trying to connect my plugin to the audio buses :(

I have loaded the plugin (VSTi) and I have loaded the AudioDriver (from ASIO.NET project) but I don't know how to use de ProcessReplacing method. The ASIO.NET library looks easy to use but I don't understand how to use it with VST.NET

I analyzed the noise button's code in your sample project but I think that code works only with effect plugins but doesn't with intrument plugins...

Please could you send me some snipped with the necessary code to connect the plugin instrument with the audio buses?

(Forgive me for ask something that can be too basic to many developers.)

Coordinator
Jan 8, 2010 at 6:39 AM

For a VST instrument (Synth) you feed MIDI to the plugin and the plugin will output audio. There are usually two threads involved: a sequencer thread that processes (and supplies) the MIDI events and an audio thread that processes all audio signal paths (passing buffers from the output of one plugin to the input of the next - or the soundcard). Besides that there is also usually a main or UI thread but that does not process any MIDI or audio (it calls the plugin editor for instance).

There is an open source VST host in C++ that you can use to find some inspiration ;-)

http://www.hermannseib.com/english/vsthost.htm (down at the bottom of the page)

A good forum for asking these question (with guys that know much more about VST than I) is the KVR Audio forum.

http://www.kvraudio.com/forum/viewforum.php?f=33

BTW: you could create a fast plugin that just fills the output buffers with noise and accepts MIDI. That way you can easily test if your audio path is working. Then you can move to the MIDI and change the plugin that it only outputs noise when an event is received. That way you can test your MIDI routing.

If you need a managed MIDI library take a look at the C# MIDI Toolkit by Leslie Sanford.

http://www.codeproject.com/KB/audio-video/MIDIToolkit.aspx
http://www.lesliesanford.com/Programming/MIDIToolkit.shtml

Hope it helps. 

Jan 10, 2010 at 1:08 PM

Ok man, thanks for the explanation and links, I'll deepen on these links and I'll ask you about other issues :)

Thanks.

Jan 10, 2010 at 10:46 PM

Hello again, thanks to your last post I FINALLY got my application to load an unmanaged VSTi and connect it to ASIO.NET for hearing the sound!!!

I'm really happy with this small step.... thank you very much.

Jul 19, 2010 at 2:01 PM
Edited Jul 19, 2010 at 2:56 PM

Hi,

 

Just picked up this thread as I'm trying to do exactly the same thing

Could you tell me how & when you take the output from the VST plugin and put it into the ASIO driver buffer  (from ASIO.NET project)?

Is is sequential after calling ProcessReplacing for the VST, or do you somehow wait for the ASIO drover to fire driver.BufferUpdate, and take the plugin output then?

I'm pretty new to real-time programming and am struggling with the order of events, so any help would be most aprpreciated

Jul 28, 2010 at 1:17 AM
Edited Jul 29, 2010 at 4:38 AM

Seems like we're all trying to achieve the same thing :D I think the first steps after opening the editor (and before hearing sound) is starting the plugin, send a midi note on with ProcessEvents, initialize an empty buffer and pass it to ProcessReplacing then check if it is returning something in the output buffer.

Right now I'm trying to send the note like this:

byte[] midiData = new byte[4]; 
midiData[0] = 144; // Send note on midi channel 1 
midiData[1] = 72;   // Midi note 
midiData[2] = 127; // Note strike velocity 
midiData[3] = 0;    // Reserved, unused 

VstMidiEvent vse = new VstMidiEvent(/*DeltaFrames*/ 0,
                                               /*NoteLength*/ 0,
                                               /*NoteOffset*/  0,
                                                               midiData,
                                               /*Detune*/        0,
                                          /*NoteOffVelocity*/ 127);

VstEvent[] ve = new VstEvent[1];
ve[0] = vse;

PluginContext.PluginCommandStub.ProcessEvents(ve);

________________
Not sure what is wrong, the output buffer is still empty but I'm monitoring the calls to the plugin with PluginConsultant.dll from Hermann Seib and it seems the calls to the plugin are ok. I'll keep on debugging and report back.

Jul 28, 2010 at 1:37 AM
Edited Jul 29, 2010 at 4:39 AM

Yeah Success!!! Something is in the output buffer!!! XD So here is how I did it in the code :

PluginContext.PluginCommandStub.MainsChanged(true);
PluginContext.PluginCommandStub.StartProcess();
// INSERT CODE TO SEND MIDI NOTE FROM MY LAST POST HERE
SendMidiNoteWithProcessEvent(); int inputCount = PluginContext.PluginInfo.AudioInputCount; int outputCount = PluginContext.PluginInfo.AudioOutputCount; int blockSize = 1024; VstAudioBufferManager inputMgr = new VstAudioBufferManager(inputCount, blockSize); VstAudioBufferManager outputMgr = new VstAudioBufferManager(outputCount, blockSize); foreach (VstAudioBuffer buffer in inputMgr.ToArray()) { for (int i = 0; i < blockSize; i++) { buffer[i] = 0; // Initialize to 0 } } PluginContext.PluginCommandStub.SetBlockSize(blockSize); PluginContext.PluginCommandStub.SetSampleRate(44100f); VstAudioBuffer[] inputBuffers = inputMgr.ToArray(); VstAudioBuffer[] outputBuffers = outputMgr.ToArray(); PluginContext.PluginCommandStub.ProcessReplacing(inputBuffers, outputBuffers); for (int i = 0;/* i < inputBuffers.Length &&*/ i < outputBuffers.Length; i++) { for (int j = 0; j < blockSize; j++) { if (outputBuffers[i][j] != 0.0) { // IF THIS LINE IS HIT YOU HAVE SOMETHING PLAYING } } } // Close resources PluginContext.PluginCommandStub.StopProcess(); PluginContext.PluginCommandStub.MainsChanged(false);

______

I think next step is finally hearing something out of this. I'm no guru at audio programming (I know next to nothing) but my sixth sense tells me that the Asio/DirectX/MME/WMI/whatever audio library you pick provides a callback method at regular interval that will request a buffer of sound. When you get that request you fill it with the output buffer from the Vsti and prepare the ProcessReplacing for the next audio buffer pull.

Coordinator
Jul 28, 2010 at 4:57 AM

Just a small note on the code for setting up a midi event:

You can use the VstEventCollection class (Framework) to setup one or more events and then call the ToArray() method on the collection when passing it to ProcessEvents. This will save you the hassle of dealing with arrays explicitly.

 

Jul 29, 2010 at 4:14 AM
Edited Jul 29, 2010 at 4:43 AM

Thanks for the tips, VstEventCollection is great for convenience.
I'm on a roll so I'll finish posting my code snippets for other fellows out there.

So, next step after getting something in the Vst Output buffer is feeding that
into an AudioPlayback buffer. We need to diverge from Vst.Net for this functionnality
so I'll use NAudio Library from Mark Heath for my example. PortAudioSharp managed
wrapper from R. Gerosa would also be a good choice if performance matters.

I made two classes here, one WaveStream derived class that provides the read callback
pull from the audio playback buffer and one Playback class that deals with Initialization and
Playback control. Creating an instance of the VSTPlayback class will play a sine wave generated in the
VSTStream read buffer. All you need to do is replace the Sine Wave with the Vst ouput buffer.

I'm pretty confident you'll get one Vst Instrument 'sounding' if you follow the steps in the code I posted.
From nothing to minimal Vst Host in 3 days, let's do it!!! I'm on fire baby XD

using System;
using NAudio.Wave;

namespace VSTPlaybackDemo
{
    class VSTStream : WaveStream
    {
        public override WaveFormat WaveFormat { get { return new WaveFormat(); } }
        public override long Length { get { throw new System.NotSupportedException(); } }
        public override long Position { get { throw new System.NotSupportedException(); } set { throw new System.NotSupportedException(); } }

        public override int Read(byte[] buffer, int offset, int count)
        {
            // FILL THE AUDIO BUFFER WITH THE VST BUFFER HERE

            // LET'S PLAY A SINE WAVE FOR NOW...
            int sampleRate = 44100;
            double amplitude = 0.25 * byte.MaxValue;
            double frequency = 350;

            for (int n = 0; n < buffer.Length; n++)
                buffer[n] = (byte)(amplitude * Math.Sin((2 * Math.PI * n * frequency) / sampleRate));

            offset = 0;
            count = buffer.Length;
            return buffer.Length;
        }
    }

    class VSTPlayback : IDisposable
    {
        private IWavePlayer playbackDevice = null;
        private VSTStream vstStream = null;

        public VSTPlayback()
        {
            Init();
            Play();
        }

        public void Init()
        {
            vstStream = new VSTStream();
            playbackDevice = new WaveOut();
            playbackDevice.Init(vstStream); } public void Play() { if (playbackDevice != null && playbackDevice.PlaybackState != PlaybackState.Playing) playbackDevice.Play(); } public void Stop() { if (playbackDevice != null && playbackDevice.PlaybackState != PlaybackState.Stopped) playbackDevice.Stop(); } public void Dispose() { if (playbackDevice != null) { playbackDevice.Dispose(); playbackDevice = null; } } } }
Aug 2, 2010 at 4:35 AM
Edited Aug 12, 2010 at 12:37 AM

Hi folks, Success I can hear the VSTi XD

Just a final note to tell you to derive the NAudio stream class from WaveProvider32 instead of WaveStream if you don't want to deal with converting the Vst buffer to the NAudio buffer.

public class VSTStream : WaveProvider32
{
   public override int Read(float[] buffer, int offset, int sampleCount)
   {
      // CALL VST PROCESS HERE WITH BLOCK SIZE OF sampleCount
      float[] tempBuffer = plugin.ProcessReplace(sampleCount);

      // Copying Vst buffer inside Audio buffer, no conversion needed for WaveProvider32
      for (int i = 0; i < tempBuffer.Length; i++)
         buffer[i] = tempBuffer[i];

      return sampleCount;
   }
}
Aug 10, 2010 at 4:22 PM

Hi YuryK,

 

Thanks for sharing your thinking, the two parts of your solution work well, but when I follow your last example to try and send a single Midi note but I'm running into problems.

To clarify, are you saying that you call ProcessReplace on the plugin on every callback from the audio device?

When I do this it eats up CPU to the point where I can't submit any Midi events to the plugin.

My thinking is that I need to run the audio callback in a separate thread from the plugin, with the output buffer from the VST being shared.

Aug 12, 2010 at 12:00 AM
Edited Aug 12, 2010 at 12:32 AM
Hi donc, it shouldn't eat all CPU, I achieve VST Playback at 2-10% CPU with a old P4.
I think your problem is that you are creating the plugin and audio playback in the main UI Thread (although it should work).
Try creating the plugin and the audio playback in separate threads (not the UI thread).
To comply with C# UI Guidelines, you can create the Plugin Gui in the Main UI thread or in a separate Single-Threaded Apartment thread.
One more thing, I learned that to create audio playback in a separate thread you need to call this constructor: new WaveOut(WaveCallbackInfo.FunctionCallback());
Tell me if you need more info.

To answer your question, yes I'm calling ProcessReplace in the AudioCallback.
Ideally I think the Plugin should feed a buffer independently from the Audio callback and the audio callback should pull from this buffer.
But this scenario is hard to implement because you have to synchronize the buffers very carefully or else it will introduce latency and thread locking.
Specifically, Vst buffer should be ready as soon as Audio callback needs it but it should not contain more data than the Audio callback requires because it will introduce latency
Also, Vst buffer should not be buffering when Audio callback needs it or vice-versa because it will lock both threads.
If you have any idea on how to implement independent buffers please tell me.
Sep 22, 2010 at 3:51 PM

Hi,

Many thanks for the help and suggestions on how to get a host running.

As an update, for general interest, I eventually came to the conclusion that coding a host was out of my reach and not really the core of what I was trying to acheive.

What I wanted was to build a simple interface to load a single VST and allow program selection, specifically through a small keypad and dot matrix display interfaced to a PC.

I think I'm now some way to acheiving this, by combining the excellent resources at VST.NET with a readily available host, in this case SAVIHost from Hermann Seib.

What I'm doing is using VST.NET to query the programs and parameters of my selected VST to enable selection, then setting a registry key for SAVIHost to launch with the selected program

It's clunky, but it actually does the job I want pretty smoothly, happy to share more information if anybody is interested

 

 

Feb 22, 2011 at 6:55 PM

Hi YuryK,

 

Thanks for posting this has helped me a lot. I am trying to implement a program that will load one VSTi and generate and send MIDI data to it and output the resulting audio. I'm pretty close now, I have the VSTi loading and I can generate a MIDI message and I have followed this thread and have generated a test sine wave that plays. The trouble im having is getting the VSTi's output buffer into the Audio buffer. I have looked at your code, the object plugin in the last section of code doesnt seem to be declared (unless im missing something) I'm assuming its a VstAudioBuffer, but i cant seem to find the function ProcessReplace(). Im pretty new to this so sorry if i am not clear or asking a silly question but I could really use your help.

Apr 5, 2011 at 4:53 AM

Hi YuryK,

Unfortunately, I am still having trouble getting your code to work - can you tell me what class the first part of your code is supposed to exist in? I think that my problem lies here.

Thanks,

Jake

Apr 5, 2011 at 1:00 PM

If you mean the code that sends a Midi Note Event and the one that Initializes the Vst Plugin, it is procedural code so you can place it pretty much anywhere.

The Host sample project in VstNet source has the same crude initialization routine.

Apr 5, 2011 at 6:33 PM

I actually meant the part that starts:

 

PluginContext.PluginCommandStub.MainsChanged(true);
PluginContext.PluginCommandStub.StartProcess();
// INSERT CODE TO SEND MIDI NOTE FROM MY LAST POST HERE
SendMidiNoteWithProcessEvent(); int inputCount = PluginContext.PluginInfo.AudioInputCount; int outputCount = PluginContext.PluginInfo.AudioOutputCount; int blockSize = 1024;

I am not sure where this whole section of code is supposed to go - is it procedural also?

Thanks for your help!

Jake
Apr 5, 2011 at 7:01 PM
Edited Apr 5, 2011 at 7:13 PM

Yes it is procedural, so it could go anywhere you want.

The fastest way to set things up would be opening the VstNet Host sample, you will find a form that has a button called Process Noise (or something similar). You can copy-paste the code inside the Process Noise button click event handler. If you have trouble finding the relevant function, search for MainsChanged inside the Host project.

Yury