More about Controlling Patterns with MIDI

Hi all -

I am building off a recent discussion of how to use incoming MIDI to use in Pattern language: Control Patterns with MIDI

I’ve added some scaffolding to the initial code - to make a button that “records” incoming MIDI to an array - and then plays it back, as a pattern, when clicked again.

I’ll add the code below.

My questions, though: How might one record rhythms in a similar manner? Also, is it possible to record incoming MIDI as chords?

(
var ko, butt1, mfun;

ko = Window("clerk", Rect(0, 450, 250, 300));

MIDIClient.initialized.isNil.or(MIDIClient.initialized.not).if {
         MIDIClient.init;
         MIDIIn.connectAll
        };

	butt1 = Button(ki, Rect(10, 30, 40, 40));
	butt1.states = [["", Color.green, Color.green], ["", Color.red, Color.red]];
	butt1.action = ({|valo, arri, mid, new|
	valo.value.postln;
		case
	{valo.value == 0} { ~arri = []; 
		~baz.stop;
		mfun = MIDIFunc.noteOn({
		|vel, mnote|
		~arri= ~arri.add(mnote);
		~arri.postln;
	})}
		
	{valo.value == 1} {mfun.free;
		
	~baz = Pbind(
			\midinote, Plazy({Pseq(~arri, 1) })
        ).asEventStreamPlayer;
        ~baz.play;
	};
	
});
	
ko.background = Color.black;
ko.front;

);

My questions, though: How might one record rhythms in a similar manner?

The easiest way is to use MIDIRecSocket from my ddwMIDI quark.

I haven’t used this in ages, but it should work something like this:

// midiChannel also includes device info:
// chan_num (simple integer): this number is the channel number; the port will be assumed 0
// \omni: respond to any channel on port 0
// nil: assume port 0, channel 0
// [port_num, chan_num]: specify a port as well as channel. port_num can be the uid belonging to the port (see MIDIClient and MIDIEndPoint), or an integer index to the sources initialized by MIDIClient.
// [port_num, \omni]: respond to any channel on this port 
k = MIDIRecSocket(midiChannel);

k.initRecord;

... play notes...

b = k.stopRecord;

Now ‘b’ is a MIDIRecBuf containing an array of SequenceNote objects, each of which has a note number, time, duration and velocity. If you need to convert them into some other format, that’s up to you, but…

Also, is it possible to record incoming MIDI as chords?

If you keep your MIDIRecBuf data in the original format, you can do:

c = b.parse(deltaThresh, overlapThresh, allowShortNotes);

If you set the thresholds to a reasonable value (default = 0.1 s), then it should clump chord notes together into a single object, e.g.

(
// make two triads and a couple melody notes
b = MIDIRecBuf(\demo, [
	[48, 55, 60, 64,  67, 64,  50, 60, 62, 65,  69],
	[0.03, 0.02, 0.02, 1,  1, 0.93,  0.03, 0.02, 0.02, 1, 2],  // deltas
	[3, 3, 3, 1,  1, 1,  3, 3, 3, 1,  2],  // lengths
	0.5 ! 11
].asNotes);
)

b.notes;  // 11 sequence notes

c = b.parse;

c.notes;  // 5 items, including 2 SeqChordNotes

All of the note data are still there, and Pseq(c.notes, 1) will actually spit out all the 11 original notes (should be with the original timing, but I wrote this a long time ago and I wasn’t as solid a programmer then, so there might be some bugs). But you can manipulate the chords as atomic entities.

I would suggest very strongly against trying to record the incoming notes as chords. Recording is complex enough by itself, and interpreting the notes into chords is also complex enough by itself. Trying to do them both at the same time is a really nice way to get confused and make a lot of mistakes.

hjh

Hi James -
This seems like an unbelievably useful set of tools - I’m not sure why these aren’t included in SC outright. I am, however, having a little trouble accessing the documentation.

Thanks to your example, I can record an array of Sequence Notes. But taking that buffer and putting it into a Pattern is still a little unclear to me.

The secondary example, where you have a prewritten sequence - uses a method called “asNotes”, which I don’t seem to have… Changing to “asNote” brings up a bunch of deprecation messages.

If you have a few more moments to fill out the rest of the example (specifically, how I might be able to access the recorded buffer as a Pattern) - I’d be greatly appreciative.

Thanks!

I’m sorry that documentation is insufficient but I’m afraid that is not going to change anytime soon. Awhile back I made a list of all of my undocumented classes and found that, if I could actually finish one help file a week (which is already impossible under my current workload), it would take over a year to get through them all (as several of the classes are quite large, a more realistic estimate would be two or three years)… it’s really irresponsible of me as a developer but I simply don’t have time. I’m tempted sometimes to withdraw my libraries because of this. In principle it’s good to share, but I can’t always provide proper support, and I feel a bit bad about that.

In the quark folders, there are Help/ directories with old-style HTML help that you can open in a web browser.

asNotes

Oh dear, I thought that was part of the quark but maybe it isn’t.

But you don’t need that if you’re recording MIDI input. It’s not really important.

But taking that buffer and putting it into a Pattern is still a little unclear to me.

I think a translator would be something like this.

Pbind(
    \seqnote, Pseq(midiBuffer.notes, 1),
    [\midinote, \dur, \sustain, \amp], Pfunc { |ev|
        var n = ev[\seqnote];
        [n.freq, n.dur, n.length, n.gate]
    }
);

hjh

This is it! Thank you!