I’ve got a slider that represents the position in a MIDI file. When the user changes the position, I chase from the beginning of the song looking for and setting controllers like patch changes, volume, pan, modulation, etc. (no notes played) so that playback resumes in the new position with the correct settings. A click somewhere in the slider changes its value and ValueChanged calls my “DoPosition” routine which chases and resumes playback. This works very quickly and has no noticeable delay.
But when I drag the slider, the value changes mount up quickly so that there may be several stacked up false starts before the playback resumes from the final location. I’d like to have the slider “scrub” so you can hear the movement as you drag, but I don’t want to hear a bunch of garbage for a quarter or half second after a quick drag.
So my question is, is there any way for my DoPosition to detect that there are numerous ValueChanged calls stacked up so it could skip some or omit the chase? Or alternatively, is there a way to have a mouse drag call ValueChanged fewer times?
I’ve done something similar by making a container control that contains a slider and timer, plus an event definition for a new ValueChanged event.
In the slider’s ValueChanged event I have this code:
timer1.Mode = timer.ModeMultiple
In the timer’s action event I have this code:
ValueChanged 'Raise the new ValueChanged event
if not System.mousedown then
You can change the behaviour by changing the code in the timer action event.
Robert Weaver, thanks. That looks promising. I’ll give it a try when I get a chance.
Andrew Lambert, thanks for this tip. I haven’t updated Xojo that far yet (updates are expensive when I’m not making any money from Xojo). That looks like a nice added feature, but wouldn’t give me the “scrub” that I seek if I turn off live scrolling.
Slider.AllowLiveScrolling is known as Slider.LiveScroll in older versions.
I forgot to mention that you need to enable live scrolling for my method to work. Live scrolling means that you get a ValueChanged event for every incremental move of the slider. (It sounds like you already have this enabled.) The code that I posted will intercept these and redirect to the timer action event which only triggers at whatever time interval the timer is set to.
Thanks, Robert. Before I try that, I’ve come up with what might be a better idea for my situation. Besides my array of MIDI events (myEvent class), I’m going to create another array of just chase-worthy events (controllers, patch changes). Then instead of chasing through thousands of events looking for the right stuff, I’ll only have to skim through a few dozen. I think this will be more efficient. If that doesn’t work, I’ll try your very elegant solution. Thanks for taking the time to present it.
I’m working on another major change right now and will get back to this problem later. Thanks everyone for your help.
BTW, if anyone is wondering, I’m rewriting software I wrote 30 years ago in 68000 assembly language for the Atari ST. I called it “MIDI Spy” back then (from CodeHead Software).
The concept is the same. It’s always live, capturing anything played on a MIDI keyboard. As a pianist, I often just start playing, never knowing what I might come up with and this gives me a mechanism to never wish I had hit the “Record” button on a sequencer. Whenever no data is received for a given “Song Gap” (say 5 seconds), the next data received starts a new “song” (track). There’s a lot more to it, but that’s the main idea. Not only does this capture my playing at my computer, I also have a Mac mini inside my Yamaha Disklavier grand piano to capture my playing there.
Since someone else has an app called “MIDI Spy” now (a MIDI monitoring app), I’m calling it “Noodle Jar”. If I ever reach a stable point where I think it’s ready, I’ll seek beta testers and hope to get it in the App Store.
I finally returned to this issue and found a much easier solution. The time-consuming routine was the examining of numerous MIDI events (the chase of perhaps thousands of MIDI events). I simply save the slider value before looping. In the loop, if the value has changed, I simply abort the chase and it begins again with the next slider ValueChanged event. Only when the slider stays at the destination does the chase loop complete its search. This gives me the desired effect of scrubbing through the music and immediately playing when the slider stops.
Thanks for the suggestions.