22 May '13 - 14:58 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: Synchronized VSTi output via MIDI  (Read 531 times)
setheen
Posts: 7


« on: 20 Aug '12 - 01:07 »
Reply with quoteQuote

Howdy folks,

I would like to load up two VSTi's as independent channels/streams (BassVst.BASS_VST_ChannelCreate) and have both streams handle their own unique MIDI events in a synchronized fashion. 

At first glance, the VST addon appears fairly sparse in event triggering (BassVst.BASS_VST_ProcessEvent in examples I've stumbled upon).  Having played with BASS a year ago (namely bass.net), I'm aware that the MIDI addon itself has more event triggering/handling operations but these don't appear compatible with the VSTi streams I've created.  I've successfully synchronized audio via two decoding streams through the mixer (bassmix addon) so I'm hoping there is a similar method for synchronizing midi events.  I'm sure the answer is out there on these forums but I've not come across the big picture (midi events -> somehow synchronized -> trigger separate VSTi's). 

User "joeduf" uploaded a good example on basic VSTi MIDI triggering in which he uses the "ProcessEvent" method.  Cannibalizing his code, I saw he created a little musical sequence:

        public void PlayTheTones()
        {
            // play note sequence ;-)
            // G# 4
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(68, 100));
            Thread.Sleep(NOTE_DURATION);
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(68, 0));
               
            // A# 4 (Up a whole step)
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(70, 100));
            Thread.Sleep(NOTE_DURATION);
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(70, 0));

            // F# 4 (Down a major third)
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(66, 100));
            Thread.Sleep(NOTE_DURATION);
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(66, 0));

            // F# 3 (Down an octave)
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(54, 100));
            Thread.Sleep(NOTE_DURATION);
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(54, 0));

            // C# 4 (Up a perfect fifth) ;-)
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(61, 100));
            Thread.Sleep(NOTE_DURATION * 4);
            BassVst.BASS_VST_ProcessEvent(m_stream1Handle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(61, 0));
        }

This works well for monophonic melodies but I'm hoping someone can point me to posts/materials informing of simultaneous MIDI notes, even across streams.  Simply triggering two unique streams at the same time with "ProcessEvent" results in random start times (un-synchronized)
Logged
Ian @ un4seen
Administrator
Posts: 15259


« Reply #1 on: 20 Aug '12 - 14:55 »
Reply with quoteQuote

Perhaps you could use BASS_SYNC_POS syncs? For example, you could define a structure to hold event information, and have an array of them to hold all of your events, and set "mixtime" BASS_SYNC_POS syncs to play them at the wanted times. For example, something like this...

typedef structure {
double time; // position in seconds
DWORD chan; // event channel
DWORD event; // event type
DWORD param; // event param
} EVENT;

EVENT events[num_events]; // array of events

...

// set a sync to trigger the 1st event
QWORD pos=BASS_ChannelSeconds2Bytes(stream, events[0].time); // convert seconds to bytes
sync=BASS_ChannelSetSync(stream, BASS_SYNC_POS|BASS_SYNC_MIXTIME|BASS_SYNC_ONETIME, pos, EventSyncProc, (void*)0); // set a mixtime POS sync there

BASS_ChannelPlay(stream, 0); // start playback

...

void CALLBACK EventSyncProc(HSYNC handle, DWORD channel, DWORD data, void *user)
{
int n=(int)user; // event number
QWORD pos=BASS_ChannelGetPosition(channel, BASS_POS_BYTE|BASS_POS_DECODE); // current position
while (1) {
BASS_VST_ProcessEvent(channel, events[n].chan, events[n].type, events[n].param); // play the event
n++; // next event
if (n==num_events) break; // reached the end
QWORD nextpos=BASS_ChannelSeconds2Bytes(channel, events[n].time); // next event position
if (nextpos>pos) { // there is a gap to the next event
sync=BASS_ChannelSetSync(channel, BASS_SYNC_POS|BASS_SYNC_MIXTIME|BASS_SYNC_ONETIME, nextpos, EventSyncProc, (void*)n); // set a mixtime POS sync there
break;
}
}
}
Logged
setheen
Posts: 7


« Reply #2 on: 20 Aug '12 - 16:21 »
Reply with quoteQuote

Thanks for the detailed response Ian.  I will look into this as soon as possible and report back findings.
Logged
setheen
Posts: 7


« Reply #3 on: 13 Oct '12 - 21:00 »
Reply with quoteQuote

I wrote a couple apps without success.  Sorry for the delayed response on this topic.

When I used the above code, there wasn't a midi triggering event.  Could you show me how the provided code could play two notes at once?

My simple example here never triggers the callback:

        private void Form1_Load(object sender, EventArgs e)
        {
            Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            Events = new MidiEvent[1] {new MidiEvent()};
            Events[0].Time = 1000;

            VSTStreamHandle = BassVst.BASS_VST_ChannelCreate(44100, 2, @"C:\producing\vsti\synths\Distel.dll", BASSFlag.BASS_DEFAULT);

            long pos = Bass.BASS_ChannelSeconds2Bytes(VSTStreamHandle, Events[0].Time);
            IntPtr testPtr = new IntPtr();

            Bass.BASS_ChannelSetSync(VSTStreamHandle, BASSSync.BASS_SYNC_SETPOS | BASSSync.BASS_SYNC_MIXTIME | BASSSync.BASS_SYNC_ONETIME, pos, Callback, testPtr);
            Bass.BASS_ChannelPlay(VSTStreamHandle, false);
           
            //BassVst.BASS_VST_ProcessEvent(VSTStreamHandle, 0, BASSMIDIEvent.MIDI_EVENT_NOTE, Utils.MakeWord(69, 25));
        }

        private void Callback(int handle, int channel, int data, IntPtr test)
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines