Author Topic: MIDI play finish with VSTi doesn't complete - sometimes  (Read 365 times)

Wayne McHugh

  • Posts: 91
Every time I ask for help I hope it will be the last time, but here is another one that has me stumped.

The issue I'm having is in the ending of playing a MIDI file. The song:
  • always plays to completion
  • Always hits my sync for the end of the song, which performs BASS_ChannelStop(chan); BASS_ChannelSetPosition(chan, 0, BASS_POS_BYTE); and changes the play/pause icon to play.
  • Always ends properly if there is no VSTi active in the song - the time slider returns to start and the pause/play button changes to play.

However, if I have a VSTi active in the song, it MAY complete properly, or it MAY complete the music including decay, change the icon from pause to play, but not return the slider to the start position - and the UI becomes unresponsive.

I cannot debug beyond what I've reported here.

My "Stop" executes exactly the same code as the end song sync, and "Stop" works in every case when clicked during the playing of a song, regardless of VSTi. The problem only occurs at the natural ending of a song.

Studying the MIDI files that work properly vs those that don't in MIDI-DUMP, the only meaningful difference I can find is that the files that don't end properly have a timebase of 120, whereas working files have 96, 384 or 960 (from those I've studied). Also the failing ones are type 1 MIDI, and working ones type 0 MIDI, but converting them to type 0 does not resolve the problem.

It has to be remembered that when no VSTi is active, they ALL end correctly, so I can't imagine it's a fault in the MIDI files - yet the problem has to be triggered by some difference in the files.

Does this suggest a VST related thing I need to do, or could try?

I've attached two that fail.  The working ones are from a collection I'm not free to expose publicly, but I could sent privately.

Ian @ un4seen

  • Administrator
  • Posts: 26077
Are those BASS_ChannelStop and BASS_ChannelSetPosition calls within a SYNCPROC, and if so, is it a "mixtime" (BASS_SYNC_MIXTIME) sync? Those functions behave a bit differently within a mixtime SYNCPROC to allow custom looping/ending. So if the sync is mixtime, try changing that. Also, the BASS_ChannelStop call seems unnecessary, given that it's already at the end? If the problem persists, please clarify how you're using the VSTi with the MIDI stream.

Wayne McHugh

  • Posts: 91
Thanks Ian, your in-depth understanding of how your library holds together almost always takes you straight to the issue.  I had BASS_SYNC_MIXTIME on all my BASS_ChannelSetSync calls, probably only because I copied the lines from sample code, not because I understood the purpose (even with the help explanation).  Again, I know I lack the conceptual understanding of how the library operates to make those judgements myself.  I've removed mixtime from all those calls, and everything seems to be working just as well - probably better, but maybe in practice for lots of them no outward difference would be observed (eg exactly when a reverb or chorus level was altered).

Removing mixtime did the job, but I also removed the BASS_ChannelStop call if at end of song just because it was redundant.

I'm now doing extensive testing of the midi files that were failing.  Only one has failed to end properly, and on retesting it is ending properly.  That makes me nervous about the robustness, but I will keep testing it for a while and see what comes.

Ian @ un4seen

  • Administrator
  • Posts: 26077
Good to hear that removing BASS_SYNC_MIXTIME did the trick. Calling BASS_ChannelSetPosition in a "mixtime" END sync callback will make the stream continue from the new position rather than stay ended (useful for custom looping). I'm not sure why/how the VSTi would have changed things in your case, but I'm also not sure exactly how you're using the VSTi :)

Wayne McHugh

  • Posts: 91
Unfortunately this isn't quite finished, because there is a second reason why songs sometimes don't finish.  It involves changing the play position slider to alter where the song it up to.

I am finding, when I call BASS_ChannelSetPosition, that VST string notes are carrying through to the new play position.  It only happens with VST instruments - all SF2 notes are stopped. Strings carried through in this way will persist until a note off is encountered, and if not, it will persist to the end of the song and prevent it from ending properly.  I think strings stand out because their notes don't decay like many other instruments.  I suspect it is happening on other instruments, but all the other non-decay instrument are handled by SF2, not VST, so strings are different for my case.

Is it possible there is an issue with VST instruments not being reliably stopped during BASS_ChannelSetPosition?

I'm changing position by calling ...

Code: [Select]
BASS_ChannelSetPosition(hStream, pos, BASS_POS_MIDI_TICK | BASS_POS_FLUSH | BASS_MUSIC_POSRESET);

It is a MIDI stream, created with ...

Code: [Select]
BASS_MIDI_StreamCreateFile(FALSE, this->getFilename(), 0, 0, BASS_MIDI_ASYNC | BASS_MIDI_DECAYEND | BASS_SAMPLE_FLOAT, 44100)

Apart from BASS_POS_FLUSH and BASS_MUSIC_POSRESET on the BASS_ChannelSetPosition,I've tried calling BASS_VST_Resume(hVSTStream) on each VST channel, and even most recently tried ...

Code: [Select]
BASS_ChannelRemoveDSP(m_hVST_channel, m_hVSTDSP);
m_hVSTDSP = BASS_ChannelSetDSP(m_hVST_channel, DSP_Callback, (VOID*)this, 0);

... on each VST channel, renewing the DSP, in order to try and force all VST data to be killed.  But still the problem persists.

I don't have any more thoughts on how to stop those VST strings from persisting through a stream position change.

Wayne McHugh

  • Posts: 91
I've continued searching for a way to stop VST notes continuing after BASS_ChannelSetPosition, including trying to stop them with MIDI_EVENT_NOTESOFF and SOUNDOFF, all to no avail.  I can even hear all the sounds stop, but they return.  Everything except string decay, but strings continue until they are turned off by chance or by a MIDI_EVENT_NOTESOFF in the MIDI stream.

I am calling BASS_VST_Resume(hStream) for each of my 5 channels running VST, and in the course of those calls, my debugging output window trace gives:

plug called audioMasterWantMidi
plug called opcode #23
plug called audioMasterWantMidi
plug called opcode #23
plug called audioMasterWantMidi
plug called audioMasterWantMidi
plug called audioMasterWantMidi
plug called opcode #23
plug called opcode #23

Looking into BASS_VST, these are generated by audioMasterCallbackImpl() in bass_vst_impl.cpp, for opcodes not handled - opcodes 6 and 23.  Are we making calls that are no longer supported?

It doesn't make sense to me that I can't even stop these strings using MIDI_EVENT_NOTESOFF in my filterproc, yet when it encounters a 123 event in the MIDI stream they stop.  Yet I don't think I should be doing anything like that, because the docs suggest they should stop using the proper BASS calls.  Thoughts on things to try?

Ian @ un4seen

  • Administrator
  • Posts: 26077
I haven't tried this myself, but it seems best to reset the VSTi just before seeking. If seeking while playing, you should also stop/pause playback first. For example, like this:

Code: [Select]
BASS_ChannePause(midistream); // pause the MIDI stream
BASS_VST_Resume(vsti); // reset the VST
BASS_ChannelSetPosition(midistream, ...); // seek
BASS_ChannelStart(midistream); // resume

If BASS_VST_Resume doesn't do what's wanted, then how did you send the MIDI_EVENT_SOUNDOFF messages when you tried that? It should be via BASS_VST_ProcessEvent rather than BASS_MIDI_StreamEvent (note it doesn't trigger a MIDIFILTERPROC).

Wayne McHugh

  • Posts: 91
Thanks Ian.  I wouldn't have thought of pausing and playing around the BASS_ChannelSetPosition call.  Thankfully I have achieved almost complete success, however the means and the "almost" are of concern.  The steps you provided did not work, however sending NOTESOFF and SOUNDOFF to each of the VST streams along with BASS_VST_Resume(vsti) did stop the notes playing on.

However, there is a momentary echo out (how best to describe it?) of strings after the reposition, and sometimes a piano note that decays out.  Thinking this was exactly what BASS_VST_Resume was designed to prevent, I removed the BASS_VST_Resume call, and the result is identical.  So while BASS_VST_Resume is not returning an error, it appears it might not be doing its job.

The comments in bass_vst.h say that BASS_VST_Resume should go after the reposition, but I put it back there and it made no difference - still the echo out after the reposition.

Ian @ un4seen

  • Administrator
  • Posts: 26077
I wouldn't have thought of pausing and playing around the BASS_ChannelSetPosition call.

Stopping/pausing playback ensures that neither the MIDI stream or VSTi is processed until after both have seeked/reset, ie. you don't want any processing between the BASS_VST_Resume and BASS_ChannelSetPosition calls.

Thankfully I have achieved almost complete success, however the means and the "almost" are of concern.  The steps you provided did not work, however sending NOTESOFF and SOUNDOFF to each of the VST streams along with BASS_VST_Resume(vsti) did stop the notes playing on.

However, there is a momentary echo out (how best to describe it?) of strings after the reposition, and sometimes a piano note that decays out.  Thinking this was exactly what BASS_VST_Resume was designed to prevent, I removed the BASS_VST_Resume call, and the result is identical.  So while BASS_VST_Resume is not returning an error, it appears it might not be doing its job.

The comments in bass_vst.h say that BASS_VST_Resume should go after the reposition, but I put it back there and it made no difference - still the echo out after the reposition.

It looks like BASS_VST_Resume just suspends and immediately resumes the VST. I'm not familiar enough with VST to know whether that should achieve what you want.

NOTESOFF would usually result in decaying notes, while SOUNDOFF should immediately stop them (make sure it's sent while playback is still paused). Are you having the problem with all VSTi or only some? To confirm that the VSTi supports SOUNDOFF, does sending it mid-playback have effect? If SOUNDOFF is working, does the VSTi have its own reverb/echo/etc effects, and if so, does disabling that prevent the problem?

saga

  • Posts: 2748
The VST SDK says that plugins should clear their buffers (including delays and reverbs) when they are resumed. In practice, a whole lot of plugins don't do that, so stopping and resuming a plugin will just continue processing where it left off. This is a general problem with VST plugins, not BASS_VST.

Wayne McHugh

  • Posts: 91
Thanks Ian and saga.

Quote
Are you having the problem with all VSTi or only some?

All 3 of the SI VST instruments are doing it.  Strum Session 2 makes only the slightest and shortest of sounds, and Addictive Keys is silent (I once heard a note decay, but I can't guarantee the code was right at that point - right now I can't fault it).  So maybe saga is on point here, that the SI plugins don't clear their buffers and the others do, and there is nothing to be done about it.

If that is the case, then perhaps I need to be more liberal with NOTESOFF and SOUNDOFF when stopping and restarting, in order for this issue not to show elsewhere.  And these comments might be helpful to others who get involved in the VSTi pathway.

Thanks Ian, as always, and thank saga for jumping in.

Ian @ un4seen

  • Administrator
  • Posts: 26077
So long as the VSTi supports SOUNDOFF, I don't think you need to bother with NOTESOFF. NOTESOFF releases notes to decay naturally, while SOUNDOFF stops them immediately.

One other thing you could try is sending a reset message (0xFF) to the VSTi via BASS_VST_ProcessEventRaw, like this:

Code: [Select]
BASS_VST_ProcessEventRaw(vsti, (void*)0xFF0000, 0);

Wayne McHugh

  • Posts: 91
Great thought, it should kill it like a sledge hammer on an egg - but it made no difference.

In case it was being interfered with, I removed NOTESOFF, SOUNDOFF and BASS_VST_Resume(), so only the reset is sent, but then the strings don't stop at all.  The BASS_VST_ProcessEventRaw() call isn't doing anything.  I can't imagine a VSTi not supporting the reset code.  It is the SI-Strings plugin I'm testing, because it is the one most clearly misbehaving.

saga

  • Posts: 2748
While I'm here reporting on non-standard plugin behaviour, some plugins also behave really strangely when sending All Sounds Off (SOUNDOFF) messages. Sometimes it causes the entire plugin to be muted from that point onwards. Unfortunately there really isn't any one-size-fits-all approach to get plugins back to complete silence, and you may have to offer several options for users to make this work with as many plugins as possible. One thing that may be useful is let the plugin process several seconds of silence, to let them finish processing their reverb tails.

Wayne McHugh

  • Posts: 91
Thank saga. Because I'm writing for a very low-tech audience, I'm actually supporting a known and limited set of VST instruments. Therefore one blessing is I only have to contend with the behaviour of those few.  This misbehaviour is minor, and I can live with it.  It was just worth asking in case there was a known fix - and instead perhaps it's just a known problem.

I tried a 2 second Sleep after killing sounds, but it just waited 2 seconds before the same tailing sound.

The functionality is there and I'm not stressed about it.  Perfection when possible, otherwise the nearest possible.  If I come up with anything I'll post it here for sake of others.

Ian @ un4seen

  • Administrator
  • Posts: 26077
I tried a 2 second Sleep after killing sounds, but it just waited 2 seconds before the same tailing sound.

As your VSTi is a decoding channel, you will need to call BASS_ChannelGetData (rather than Sleep) to have it do some processing. Another option is to use BASS_ChannelGetLevelEx, which avoids the need to provide a buffer, something like this:

Code: [Select]
float level;
BASS_ChannelGetLevelEx(vsti, &level, 2, BASS_LEVEL_MONO); // process 2 seconds and get its level

In fact, you could repeat that call (but perhaps with a lower "length" parameter) until "level" is below some silence threshold.

Wayne McHugh

  • Posts: 91
Thanks Ian, but it made no difference.  Level comes back 0, whether repositioning within the song or to the end. however the decay is just as it has been.