MIDI file variations || Process Based Music

Hi everyone,
I’m trying to automate a process to compose through the use of MIDI files.

The general Idea is just to:

  1. write something in a DAW
  2. export a midi file
  3. Import the file in sc
  4. uses sc to “compose” all the variations
  5. Import the new materials in the DAW etc …

A general question, do you know any tool or library in sc or other languages specifically dedicated to MIDI files manipulation like a way to compose music?

I’m trying to do something quite simple, but in the end is not that much …

The main souce is just a single bar of a MIDI file.
I would love to generate new MIDI files based on the first one.

The piece I took like referement is Clapping Music by Steve Reich
Clapping Music - Scrolling Score - YouTube

So the rule is just to shift/rotate the notes.

What I would love it could be something like:

  1. read the file
  2. press the magic button
  3. receive on your desktop all the variations with the “correct” name
  4. arrange everything in a DAW

For now I’m here but I encountered a problem, if I have a pause/silence at the beginning or at the end of the phrase, happens that those won’t be interpreteted maybe some of you could pointing me out.

// Read the file
~midi = SimpleMIDIFile.read("C:/Users/../Desktop/Bar0.mid");
// how long is it? it considers only the notes and not the rests... if those are at the end..  
~totalDurSrc = ~midi.endOfTrack[0][1]; 
// let me see the events noteNo/noteOff 

~events = ~midi.noteEvents;

/* in this example I'm using a rythm in 7/4 at 178 BPM  on a single note.
[
[ 0, 0, noteOn, 0, 62, 80 ], [ 0, 96, noteOff, 0, 62, 0 ],
[ 0, 96, noteOn, 0, 62, 80 ], [ 0, 192, noteOff, 0, 62, 0 ], 
[ 0, 288, noteOn, 0, 62, 80 ], [ 0, 384, noteOff, 0, 62, 0 ],
[ 0, 480, noteOn, 0, 62, 80 ], [ 0, 576, noteOff, 0, 62, 0 ]
 ]*/

// reorganize and split 

(
~pair = Array.fill((~events.size/2), {arg index;
	index = index * 2;
	[~events[index], ~events[index+1]];
});

~pairNoteOn = Array.fill((~events.size/2), {arg index;
	index = index * 2;
	~events[index];
});

~pairNoteOff = Array.fill((~events.size/2), {arg index;
	index = index * 2;
	~events[index+1];
})
)

; // <<< shifting
// deep copy
~shiftSeq = Array.fill2D(~simpleSeq.size, ~simpleSeq[0].size, {arg row, col;
	~simpleSeq[row][col]
});
// shifting
~shiftSeq = ~shiftSeq.rotate(~shift);
// get the first start time
~startTime = ~shiftSeq[0][2];
// time remapping
~shiftSeq.collect({ arg elem, index;
	elem[2] = (elem[2] - ~startTime).mod(~totalDurSrc);
})
)
// output -> shift seq -> [[midinote, velocity, start_time, note_duration], etc...]

//5 -> Genera file MIDI

( // init
~newMidiFile = SimpleMIDIFile("C:/Users/../Desktop/Bar0_Variation1.mid");
~newMidiFile.init1(2, 178, "7/4"); // args ->  n. tracks, BPM, time signature
~newMidiFile.division = ~midi.division; // adjust the subdivision with the original file, default = 1024 (!important!)

~shiftSeq.do{arg evt;
	var midinote = evt[0];
	var vel = evt[1];
	var start_time = evt[2];
	var dur = evt[3];

	// noteNumber - velocity - startTime - dur
	[midinote, vel, start_time, dur].postln;
	~newMidiFile.addNote(midinote, vel, start_time, dur, 0, 0, 1)
};
)

// close the file MIDI
~newMidiFile.adjustEndOfTrack;
// check it
~newMidiFile.midiEvents.dopostln;
// Render MIDI file 
~newMidiFile.write

Thank you very much indeed!

I tried another approach using Patterns but I encontered a similar problem as before, I can’t read correctly the .mid the last rest is missed …

b = SimpleMIDIFile.read("C:/Users/../Bar0.mid");
x = b.generatePatternSeqs.flatten(1);//.flatten(1).postln;
x.postcs;

/*
can't read the rest at the end of the phrase
[ [ 62, 1.0 ], [ 62, 1.0 ], [ 'rest', 1.0 ], [ 62, 1.0 ], [ 'rest', 1.0 ], [ 62, 1.0 ] /*????*/ ]
*/

x.rotate(1).debug("rotate: ");

/*
rotate: : [ [ 62, 1.0 ], /*????*/ [ 62, 1.0 ], [ 62, 1.0 ], [ rest, 1.0 ], [ 62, 1.0 ], [ rest, 1.0 ] ]
*/


// create your variation .. 

(
p = Pbind(
	[\midinote, \dur], Pseq(x.pyramid(1),1),
);


m = SimpleMIDIFile( "~/Desktop/simple_pyramid(1).mid" );
m.init1( 0, 60, "4/4" );
m.fromPattern( p );
)

m.plot;

m.p.play; // note numbers are not rounded
p.play; // compare

m.write; // when writing to file note numbers are rounded (MIDI file format doesn't allow floats)
1 Like