Ok first step is to make it work. This seems to work. Curious if I am on the right track and coding feedback is welcome.
It works with the Launchpad MK3 mini. One step of scrolling as test is possible.
(
~lpNoteOffset = 11; // First midi notenumber of the LP in programmer mode.
~lpStep = 10; // New sample is 10 steps
~ledCol = 87; // active note color
~collapseCol = 84; //104; // when the playhead meets a set led.
~playCol = 13; // playhead color
~scrollUp = ( 0 * ~lpStep );
~upCol = 119; // scrollUp/Down buttons init color
)
(
// MIDI init
MIDIClient.list;
~midiOut = MIDIOut.newByName("Launchpad Mini MK3", "Launchpad Mini MK3 LPMiniMK3 MI"); //
//~midiOut = MIDIOut.newByName("Launchpad X", "Launchpad X LPX MIDI In");
m = ~midiOut;
MIDIIn.connectAll;
MIDIFunc.trace(true);
)
(
// mini programmer / live mode select, 0 live, 1 programmer mode.
~programmerMode = 1;
m.sysex(Int8Array[240, 0, 32, 41, 2, 13, 14, ~programmerMode, 247]);
)
(
// Synthdef to play the samples
SynthDef(\playBuf, {
var sig;
var buf = \buf.kr(4);
var rate = \rate.kr(1) * BufRateScale.kr(buf);
sig = PlayBuf.ar(2, buf, rate, \loop.kr(0), doneAction: 2);
Out.ar(\out.kr(0), sig!2);
}).add;
// load the samples
~samplePath = PathName( thisProcess.nowExecutingPath ).pathOnly;
b = (
\none: nil, // TODO?
\0: Buffer.read( s, ~samplePath ++ "/KICK75.wav" ),
\1: Buffer.read( s, ~samplePath ++ "/SNARE0.wav" ),
\2: Buffer.read( s, ~samplePath ++ "/002.wav" ),
\3: Buffer.read( s, ~samplePath ++ "/CLAP62.wav" ),
\4: Buffer.read( s, ~samplePath ++ "/hihat1.wav" ),
\5: Buffer.read( s, ~samplePath ++ "/bass1.wav" ),
\6: Buffer.read( s, ~samplePath ++ "/bell.wav" ),
\7: Buffer.read( s, ~samplePath ++ "/snareroll.wav" ),
\8: Buffer.read( s, ~samplePath ++ "/RS.WAV" ), // TODO buffer has 1 channel.
);
// Make Grid
// 8 * 10 = 80; 1x pageup = 90;
~grid = Array.fill(90, { Dictionary.newFrom([\num, 0, \chan, 0, \vel, 0, \sample, b[\0]]) }; );
// assign samples
// TODO switch or case
~grid.do{ | val , i |
//val.postln;
i.postln;
case (
{ ( i <= 9 ) }, { ~grid[i][\sample] = b[\0] },
{ ( i > 9 ) && ( i <= 18 ) } ,{ ~grid[i][\sample] = b[\1] },
{ ( i > 18 ) && ( i <= 28 ) }, { ~grid[i][\sample] = b[\2] },
{ ( i > 28 ) && ( i <= 38 ) }, { ~grid[i][\sample] = b[\3] },
{ ( i > 38 ) && ( i <= 48 ) }, { ~grid[i][\sample] = b[\4] },
{ ( i > 48 ) && ( i <= 58 ) }, { ~grid[i][\sample] = b[\5] },
{ ( i > 58 ) && ( i <= 68 ) }, { ~grid[i][\sample] = b[\6] },
{ ( i > 68 ) && ( i <= 78 ) }, { ~grid[i][\sample] = b[\7] },
{ ( i > 78 ) && ( i <= 88 ) }, { ~grid[i][\sample] = b[\8] };
);
};
// clear all note leds
// TODO which function names are allowed?
c = {
"clear grid".postln;
~grid.do{ | v, i |
//m.noteOn(0, i, 0);
m.sysex(Int8Array[144, i, 0]);
};
};
g = {
"redraw grid".postln;
~grid.do{ | v, i |
var upBoundary = ~scrollUp + 80; // 80 is amount of midi note numbers (8 * 10);
var index = ( i - ~scrollUp ) + ~lpNoteOffset; // 11 -0; 21 - 10 = 11
if ( ( i >= ~scrollUp ) && ( i <= upBoundary ) && ( v[\vel] > 0 ),
{ m.sysex(Int8Array[144, index, ~ledCol]); }
);
};
}
)
// MIDIdef
// set velocity value and / or change color led.
(
~srcID = nil; //1572865; // src id of the LP mini NOTE is not consistent?
MIDIdef.noteOn(\b, { | vel, num, chan |
var index = (num - ~lpNoteOffset) + ~scrollUp;
num.postln;
if ( ~grid[index][\vel] > 0,
{ ~grid[index][\vel] = 0; m.sysex(Int8Array[144, num, 0]) }, // LED off
{ ~grid[index][\vel] = vel; m.sysex(Int8Array[144, num, ~ledCol]); } // LED on
);
},
noteNum: ( ~lpNoteOffset..88 ), // TODO
chan: nil,
srcID: ~srcID, //nil,
).permanent_(true);
// scrollUp
MIDIdef.cc(\sUp, { | val, num, chan|
if ( ( val > 0 ) && ( ~scrollUp < ~lpStep ), // only when page up is 0;
{
~scrollUp = ( ~scrollUp + ~lpStep );
~upCol = ( ~upCol + 1 );
c.(); // clear grid
g.(); // draw grid
m.control(0, 91, ~upCol); // scroll up button
});
},
ccNum: 91,
chan: nil,
srcID: ~srcID, //nil,
).permanent_(true);
// scrollDown
MIDIdef.cc(\sDown, { | val, num, chan |
val.postln;
if ( ( val > 0 ) && ( ~scrollUp >= ~lpStep ),
{
~scrollUp = ( ~scrollUp - ~lpStep );
~upCol = ( ~upCol - 1 );
c.(); // clear grid
g.(); // draw grid
m.control( 0, 91, ~upCol );
});
},
ccNum: 92,
chan: nil,
srcID: ~srcID, //nil,
).permanent_(true);
)
/// Tempoclock
(
t = TempoClock(120/60).permanent_(true);
)
// clear velocities
(
~grid.do{ | v, i |
v[\vel] = 0;
};
c.() // clear screen
)
// Playhead
(
p = {
Routine{
loop{
~timeline.do{ | v, i |
var led = i + ~lpNoteOffset;
var index = i + ~scrollUp; // always on bottom line
[v, i].postln;
if ( ~grid[index][\vel] == 0,
{
m.sysex(Int8Array[144, led, ~playCol]);
~dur.wait;
m.sysex(Int8Array[144, led, 0]);
},
{
m.sysex(Int8Array[144, led, ~collapseCol]);
~dur.wait;
m.sysex(Int8Array[144, led, ~ledCol]);
});
};
};
}.play(t);
};
)
// Sequences
(
r = { | seqNum |
var num = seqNum;
Routine{
loop{
~timeline.do{ | v, i |
var index = i + ( num * ~lpStep);
[v, i].postln;
if ( ~grid[index][\vel] > 0,
{ Synth(\playBuf, [\buf, ~grid[index][\sample].bufnum])}
);
~dur.wait;
};
};
}.play(t);
};
)
// Play
(
~timeline = 0!8;
~dur = 0.5;
~scrollUp = 0;
~upCol = 119;
m.control( 0, 91, ~upCol );
m.control( 0, 92, ~upCol ); // scroll button default static col
c.(); // clear grid
g.(); // draw grid
// playhead
p.();
// play one routine per sample sequence
(0..8).do{ | i |
r.(i);
};
)
t.tempo_(80/60);