Separate SimpleMIDIFile data into tracks/channels

Hi all, I’ve got what I think is a relatively simple question about SimpleMIDIFile (wslib extension). Let’s say I’ve got a .mid file that has multiple channels. Is there a way to isolate or separate channels from one another? For example, I might want to use a different SynthDef for each MIDI channel.

I’ve tried extracting all the channel 0 events and loading them into a new SMF instance. When I convert to pattern and play, I get silence, but lots of \default Synths on the server, and my CPU is around 25-30%. I wonder if I’m missing a step somewhere:

s.boot;

m = SimpleMIDIFile.read("/Users/eli/Desktop/bwv772.mid");

~m_ch0 = SimpleMIDIFile.new("~/Desktop/channel0.mid");

~m_ch0.init1(2, m.tempo, m.timeSignatures[0][1]);

~m_ch0.addAllMIDIEvents( m.midiChannelEvents(0) );

~m_ch0.p.play; //silence, significant CPU usage

I’ve also tried using patterns (Pselect) to handle the filtering, but the note durations get erroneously quantized to the nearest beat (or maybe rests are being ignored?), which destroys the original rhythm:

s.boot;

m = SimpleMIDIFile.read("/Users/eli/Desktop/bwv772.mid");

m.p.select({arg ev; ev[\chan]==0}).play; //malformed metric values

Basically, I want the equivalent of having multiple MIDI tracks/channels in a DAW, and e.g. muting one of them, or assigning a different instrument to play one of them. What is the easiest way to do this?

By the way, this is the test file I’m using: http://www.jsbach.net/midi/bwv772.mid

Thanks,
Eli

Hey Eli,

Not sure if this works for you, but specifying the track on addNote does the trick for me:

m = SimpleMIDIFile( "~/Desktop/flVoice.mid" );
m.init1( 2, 120, "4/4" );
m.timeMode = \seconds;

10.do{m.addNote(rrand(32,64), 64, 10.rand, track: 2.rand)};

m.write

Sam

Right, this code works fine. But then, how would I work with m to play or generate a pattern containing only track 1 or only track 2? I initially assumed the .p method would contain track or channel arguments (similar to Buffer.readChannel(channels:[0])), but there’s no such option.

I did get a bit closer by also extracting and reloading the metaEvents into the child SMFs. The channel 0 version seems to work ok, but I’m getting “ERROR: ListPattern (Ppar) requires a non-empty collection; received [ ].” for channel 1.

s.boot;

(
m = SimpleMIDIFile.read("/Users/eli/Desktop/bwv772.mid");

~m_ch0 = SimpleMIDIFile("~/Desktop/channel0.mid");
~m_ch0.init1(2, 80, "4/4");
~m_ch0.addAllMIDIEvents(m.midiChannelEvents(0));
m.metaEvents.do({ |ev|
	~m_ch0.addMetaEvent(ev);
});
~m_ch0.sortMIDIEvents;
~m_ch0.sortMetaEvents;

~m_ch1 = SimpleMIDIFile("~/Desktop/channel1.mid");
~m_ch1.init1(2, 80, "4/4");
~m_ch1.addAllMIDIEvents(m.midiChannelEvents(1));
m.metaEvents.do({ |ev|
	~m_ch1.addMetaEvent(ev);
});
~m_ch1.sortMIDIEvents;
~m_ch1.sortMetaEvents;
)

~m_ch0.p.play; //fine
~m_ch1.p.play; //apparently empty