Freq (22) [Avatar] Offline
#1
Hello,
I'm working on the second version my loop based sequencer and I have a question that might save me time here:

Can I open several MIDI outputs (using OpenMidiOut()) with same device ID? Will the out buffers be independent or do I have to create an out class that merges tracks before putting them to the output device as you've done with CMaxMidiOut? (I'm not using the MFC classes)

Thanks much for any help.
Freq (22) [Avatar] Offline
#2
Re: Merging MIDI output tracks
This list doesn't seem to be very active...

Well I'm a bit further along. Oddly enough, I can open multiple midi outs for a single device except for my MidiYoke connections - which I only really need for debugging but still need nonetheless. I noticed this in the OpenMidiOut function:

// *** V1.53 ***
// allow multiple opens of any device or devices
// is there a sync device specified?
/* if(lpSync != NULL)
{
// yes, is this device ID already open?
i = 0;
while(*(lpSync->lpMidiOutList + i) != NULL)
{
if( ((LPMIDIOUT)*(lpSync->lpMidiOutList + i))->wDeviceID == wDeviceID)
{
// it's already open, don't open it again
lpMO->hMidiOut = ((LPMIDIOUT)*(lpSync->lpMidiOutList + i))->hMidiOut;
}
}
}
*/

The code as you can see has been commented out. If I re-enable this code it still seems to crash with MIDI Yoke (don't worry, I've added a check on the open call to see if lpMO->hMidiOut was found first). Give me another few weeks and I'll probably find some kind of solution but I was hoping (again) that someone on this list might save me some time and tell me if I should be able to open a device more than once.

a.
Freq (22) [Avatar] Offline
#3
Re: Merging MIDI output tracks
Hi... Me again. So I started searching through the archives (silly me... should have done that in the first place) and found a thread on this topic, including posts by paul about removing the offending code prior to v.1.53. My problem is that I really need something like it. I'm not using the CMaxMidiTrack (no MFC) and would like to have many "virtual" outputs for a single physical device. Since all my outs map to the same sync device, I figured I could restore (and fix) the code - because the code uses the sync's output list to check for an already opened output. So now I can open the device. But it crashes shortly thereafter. Has anyone had success getting the .dll to work with multiple "virtual" outputs on the same physical out?
KUMA (56) [Avatar] Offline
#4
Re: Merging MIDI output tracks
When V1.53 was released, Paul said:
This version (V1.53) fixes a bug that prevented opening multiple output devices.

Concerning multiple opening see:
http://www.manning-sandbox.com/thread.jspa?threadID=5516&tstart=810
Freq (22) [Avatar] Offline
#5
Re: Merging MIDI output tracks
Hello Kuma,
Thank you for your response. I found that thread and a few others that deal with this issue. I don't think however that it was ever fixed - just removed. I think I understand why. The problem is deep - it has to do with the way the drivers work. The openMidiOut() uses a callback function with data you provide (in this case, a pointer to the midiout structure). You can't call the open a second time so you can't provide a second structure with the second open's buffer. So there is no way of knowing which output you're dealing with when MidiOutCallback() is called. Anyways, I really tried to get around it tweaking the maxmidi code but to no avail. I've created a port object instead with it's own buffers (virtual ports) and a merge function to output to a single midi out. A lot more work and no where near optimal but I guess we don't really have a choice. I can't ever expect drivers to change.

a.
KUMA (56) [Avatar] Offline
#6
Re: Merging MIDI output tracks
I think what Paul meant is this:
We can open as many output devices as available and connect them with single sync structure, and even when only one device is available, we can open the device more than once if the device driver admits of open call multiple times.
(Incidentally, if the above code is activated and OpenMidiOut() is called twice, then the game is over, for the code lacks the increment.)

Now, I realize what you want.
You want to connect several midiout structures to the previously opened output device that doesn't admit of multiple open calls.
I think it is possible if sysex is not used. (I assume you don't rest on the thunk.)
The messages which MidiOutCallback() posts are MOM_DONE and MOM_CLOSE, the former concerns sysex data and the latter closing process of the output device; MOM_CLOSE is posted to the hidden window pointed at by hMWnd of the midiout structure. On the other hand, OUTBUFFER_READY is posted by the sync engine to the main window, and it has as lparam the pointer held in lpMidiOutList of the sync structure.
So think about a function like below:
The function is like OpenMidiOut() but has no midiOutOpen() call nor lines concerning sysex nor CreateMidiOutWindow() call; instead, it contains such lines as copying hWnd, wDeviceID and hMidiOut of the midiout structure connected to the output device previously opened into the newly instantiated midiout structure.
I think such a function (and a corresponding close function) will work for your purpose.

By the way, I'm interested in loop based sequencer.
Could you tell me how to repeat short patterns with MidiOut and Syc functions?
Freq (22) [Avatar] Offline
#7
Re: Merging MIDI output tracks
>> The messages which MidiOutCallback() posts are MOM_DONE and MOM_CLOSE

You're right. I don't know why I didn't see this before. So I basically don't need to give a function unless I want sysex. Wow... Man. I've gone so far the other way, I wish I had seen this before. OK, I'm going to try to use an older version. Thanks KUMA.

>> By the way, I'm interested in loop based sequencer. Could you tell me how to repeat short patterns with MidiOut and Syc functions?

Hehe. That's a big topic. Loops will be activated/disactivated/changed a lot while the sequencer is playing. The idea for me is that I never want to have to press stop to record or whatever. I use it live on stage and it plays for hours at a time without ever stopping (this is my second version - the first was built with directX).

So how you do that with midiout and sync? I'm still figuring it all out but so far this is what I've got.

Each loop (track) remembers its start position. You send events to the buffer and, knowing the loop length, when you reach the end, you go back to the first event taking the delta between the last and first events. I made things a bit easier by using absolute ticks for midi events but I might end up going back to delta to optimize - should be faster, especially with loops. Each loop should remember the last event that was sent so that an OUTBUFFER_READY can continue sending from where it last left off. And it basically keeps going and going and going until the user stops the loop.

Thanks again for the reply.

a.

(I updated this after I realized what you were saying KUMA)
Freq (22) [Avatar] Offline
#8
Re: Merging MIDI output tracks
Woohoo! It works! Thank you so much for the help KUMA. This makes things so much easier for me. If I had noticed the fact that the callback is only used for sysex stuff, I could have finished my sequencer a few weeks ago. It runs really smoothly. I'm actually a bit surprised. I can't wait to start using it in the studio.

There is one very annoying thing you might be able to help me with. If my program crahes (or I stop it in debug without proper exit), the next time I try to start the app, I get an invalid handle bug (right after the midiOutOpen() call). Is there any way to properly close a midiOut after a crash? I'm worried that if this happens on stage, I'll have to reboot my laptop in order to start the app again.
KUMA (56) [Avatar] Offline
#9
Re: Merging MIDI output tracks
Thanks Freq for the answer. What I have in mind is not so live-oriented; I want to input data numerically and specify repeat times.(The good old Roland's style, do you know?)

As for missing handle, I have no idea for now, since I don't know much about programming Windows. You should refer to MSDN.
Freq (22) [Avatar] Offline
#10
Re: Merging MIDI output tracks
I have an MC505 that I use quite a bit and an old MC-50 that I don't use anymore cause I have my own software. smilie I think I know what you mean by Roland style - more like old school Atari style? So what kind of problems are you having specifically?
KUMA (56) [Avatar] Offline
#11
Re: Merging MIDI output tracks
I'm thinking about MC-8 etc. Ealy Roland style was not so bounded to the Western modern music style; it was simple but flexible.
Anyway, though I'm intereseted in a pattern sequencer, I don't set out to write it yet. So I have no specific problems, but one thing on my mind is how to cope with buffered events. It seems that changing events in a loop on the fly is not so easy.
Thank you.
Freq (22) [Avatar] Offline
#12
Re: Merging MIDI output tracks
Hmm... MC-8 is pre-MIDI. What non-western feature are you looking for? Timing or pitch? Do you mean step sequencing? As in, note-rest-rest-note etc?

As for buffering, it isn't that hard. Whenever a note changes, flush the buffer and re-output. Now that I've fixed the toolkit so that I don't even have to worry about merging, I assign an out per pattern (track) and just flush it and rebuffer when the user changes a note (onmouseup).

a.
KUMA (56) [Avatar] Offline
#13
Re: Merging MIDI output tracks
Thanks Freq.
> Whenever a note changes, flush the buffer and re-output.
Yes, but as the flushing initializes the buffer pointers, it needs to memorize the event outputed just before the flushing and to buffer the events succeeding that one. OK, I'll try it later.

> As in, note-rest-rest-note etc?
Basically, so. The post-StandardMIDI standard style is, I think, the Performer style. In that style, any note needs to be located within a measure and beat. This is uncomfortable for me.
Freq (22) [Avatar] Offline
#14
Re: Merging MIDI output tracks
>> Yes, but as the flushing initializes the buffer pointers, it needs to memorize the event outputed just before the flushing and to buffer the events succeeding that one.

I guess you could do it that way but I think you'll be making it harder on yourself.

You just have two cases to worry about:

First is the non-flush "OUTBUFFER_READY" case. Since you're dealing with loops, you probably manage your own buffer per track so just remember the index of the last midi message you successfully put out to the buffer so that you can continue from there.

The second case is the the flush which is a bit harder. You need to "roll up" to "now". Each track needs to remember a start position and a loop length (duration). After the flush, your loop start might be way behind when you try to rebuffer. Keep adding duration to the loop start until loop start + duration > now (make sure loop start < now). Then run through the events in your loop buffer until the current event + loop start > now (or equal to). And start sending puts from there.

Hope that isn't too confusing. Maybe I should write this in pseudo code for you...

>> In that style, any note needs to be located within a measure and beat.

Hmm. K, I think I understand. You want to trigger events in a loop at any time (not necessarily on a beat or measure). I think that's a GUI thing rather than a MIDI thing. Just let the user specify events, start times, and durations in ticks. With 960 ticks per beat, I think you can effectively get away from western time signatures.