|
|
|
01-10-2013, 02:19 PM
|
#1
|
Human being with feelings
Join Date: Feb 2007
Posts: 3,221
|
Cap'n Caveman's JS MIDI library.
Captain Caveman was nice enough to do a port of Arduino's MIDI library to JS. i thought it deserves its own thread. this helps to simplify MIDI processing and may be preferable to more casual programmers.
Quote:
Originally Posted by Captain Caveman
I've done a mini port of the Arduino MIDI library so that the hex/bitshifting mumbi jumbi is hidden (I've added a couple of doo dahs too) and the format for accessing stuff should be more familiar..
Also added:
sendAllNotesOff(channel) - to send all notes off on all or specific channel
isNote(message) - to check for note ons or offs in one call
It'd be cool if someone could check it for any more clangers, bugs or misdirection. I've mostly tested it and it seems to be working fine now at least as a mini/diverged port/thing.
|
Code:
@init
true = 1;
false = 0;
omni = -1;
all = -1;
function MIDIThruFilterMode()
(
this.Off = 0; //< Thru disabled (nothing passes through).
this.Full = 1; //< Fully enabled Thru (every incoming message is sent back).
this.SameChannel = 2; //< Only the messages on the Input Channel will be sent back.
this.DifferentChannel = 3; //< All the messages but the ones on the Input Channel will be sent back.
);
kThruFilterMode.MIDIThruFilterMode();
function begin(inputChannel, thruFilterMode)
(
this.inputChannel = inputChannel; //-1 for omni, 1 to 16 for MIDI channels
this.thruFilterMode = thruFilterMode;
);
function MIDIType()
(
this.NoteOff = $x80;
this.NoteOn = $x90;
this.AfterTouchPoly = $xA0;
this.ControlChange = $xB0;
this.ProgramChange = $xC0;
this.AfterTouchChannel = $xD0;
this.PitchBend = $xE0;
// this.SystemExclusive = $xF0;
);
kMIDIType.MIDIType(); //now we can use (eg) type = kMIDIType.NoteOff
//MIDI_SYSEX_ARRAY_SIZE = 256;
function midimsg()
(
this.offset;
this.offsetPos=0; //set these position offsets for midiArray later
this.channel;
this.channelPos=1;
this.type; //use kMIDIType for this
this.typePos=2;
this.data1;
this.data1Pos=3;
this.data2;
this.data2Pos=4;
//this.sysex_array[MIDI_SYSEX_ARRAY_SIZE];
this.info; //for storing other stuff per event
this.infoPos=5;
this.valid;
this.validPos=6;
);
message.midimsg(); //set global variable/struct/whatever to access instance variables
//isNoteOn and isNoteOff added because simple message.type check isn't good enough
//to check for pesky zero velocity NoteOns masquerading as NoteOns.
function isNoteOn(message)
(
message.type == kMIDIType.NoteOn && message.data2 > 0 ? (
True; //return true
) : (
False;
);
);
function isNoteOff(message)
(
(message.type == kMIDIType.NoteOff ||
(message.type == kMIDIType.NoteOn && message.data2 == 0)) ? (
True; //return true
) : (
False;
);
);
function isNote(message)
(
(isNoteOn(message) || isNoteOff(message)) ? (
true;
) : (
false;
);
);
function sendNoteOn(noteNumber, velocity, channel)
(
midisend(offset, kMIDIType.NoteOn + channel, (noteNumber)|velocity<<8);
);
function sendNoteOff(noteNumber, velocity, channel)
(
midisend(offset, kMIDIType.NoteOff + channel-1, (noteNumber)|velocity<<8);
);
function sendProgramChange(ProgramNumber, channel-1)
(
midisend(offset, kMIDIType.ProgramChange + channel-1, ProgramNumber);
);
function sendControlChange(ControlNumber, ControlValue, channel)
(
midisend(offset, kMIDIType.ControlChange + channel-1, ControlNumber|(ControlValue<<8));
);
//channel is omni or 1 to 16
function sendAllNotesOff(channel)
(
channel ? (
sendControlChange(123, 0, channel);
) : (
i=1;
loop(16,
sendControlChange(123, 0, i);
i+=1;
);
);
);
function sendPitchBend(PitchValue, channel)
(
//TODO check this works
midisend(offset, kMIDIType.PitchBend + channel-1, PitchValue);
);
function send(type, param1, param2, channel)
(
midisend(offset, type+channel-1, param1|(param2<<8));
);
function sendMessage(message)
(
midisend(message.offset, message.type+message.channel-1,
message.data1 | (message.data2 << 8));
);
function isListenedChannel(message)
(
(filter.inputChannel == omni) || (filter.inputChannel == message.channel) ? (
true;
):(
false;
);
);
// this function updates global variable 'message' if a message is received
// and sends message on based on the thru filter conditions set in filter.begin
// 'message' contains all the (function) midimsg parameters above.
// eg message.type message.data1 etc
function getNextEvent()
(
midirecv(offset,ms1,ms23) ? (
message.offset = offset;
message.type = ms1&$xF0; //type
message.channel = (ms1&$x0F)+1; //channel
message.data1 = ms23&$x7F; //data1
message.data2 = ms23>>8; //data2
message.info = 0; //reserved for any extra info you want to store
(filter.thruFilterMode!=kThruFilterMode.Off) ? (
(filter.thruFilterMode==kThruFilterMode.Full) ? (midisend(offset,ms1,ms23));
(filter.thruFilterMode==kThruFilterMode.SameChannel
&& message.channel == filter.inputChannel) ? (midisend(offset,ms1,ms23));
(filter.thruFilterMode==kThruFilterMode.DifferentChannel
&& message.channel != filter.inputChannel) ? (midisend(offset,ms1,ms23));
);
true;
);
);
MESSAGESIZE = 6; //number of params in the message object
midiArray=1000; //start pos in memory for the 'array' that stores midi
eventsInBlock=0;
eventsIterator=0;
//here we add messages to the midiArray if they match the input/listening channel(s)
//set in filter.begin and returns the number of messages received to use
//as the number of loop iterations in the @block loop
function getBlockMIDI()
(
eventsInBlock = 0;
while
(
getNextEvent() ? (
isListenedChannel() ? (
startPos = eventsInBlock*MESSAGESIZE;
midiArray[startPos+message.offsetPos] = message.offset;
midiArray[startPos+message.typePos] = message.type;
//TEST=midiArray[startPos+message.typePos];
midiArray[startPos+message.channelPos]= message.channel;
midiArray[startPos+message.data1Pos] = message.data1;
midiArray[startPos+message.data2Pos] = message.data2;
midiArray[startPos+message.infoPos] = message.info;
eventsInBlock += 1;
);
true; //don't exit while loop if not on listenedChannel
);
);
eventsIterator=0;
eventsInBlock; //return number of events for loop() in @block
);
//this is the main read function we use in @block that reads the next MIDI message
//received, based on filter.begin input/listening channel. Updates global 'message'
//doo-dah and returns true until no more events in array for current block
function read()
(
eventsIterator < eventsInBlock ? (
startPos = eventsIterator*MESSAGESIZE;
message.offset=midiArray[startPos+message.offsetPos];
message.type=midiArray[startPos+message.typePos];
message.channel=midiArray[startPos+message.channelPos];
message.data1=midiArray[startPos+message.data1Pos];
message.data2=midiArray[startPos+message.data2Pos];
message.info=midiArray[startPos+message.infoPos];
eventsIterator+=1;
true;
) : (
eventsIterator=0;
false;
);
);
/////////////////////////////////////////////////////////////////////////////////////////
// Create filter.begin in @init to set listening channel(s) and ThruFilterMode
// Use 'omni' for all channels or 1 to 16
// kThruFilterMode.Off = 0; //< Thru disabled (nothing passes through).
// kThruFilterMode.Full = 1; //< Fully enabled Thru (every incoming message is sent back).
// kThruFilterMode.SameChannel = 2; //< Only the messages on the Input Channel will be sent back.
// kThruFilterMode.DifferentChannel = 3; //< All the messages but the ones on the
// Input Channel will be sent back
filter.begin(1, kThruFilterMode.DifferentChannel); //listen channel = 1, midi thru for others
@block
loop
(getBlockMidi(),
read() ? ( //get any/first/next message in block
isNote(message) ? ( //is it a note (on or off)?
message.channel = 15;
sendMessage(message);
) : ( //else not a note, send message on
sendMessage(message);
);
);
);
/////////////////////////////////////////////////////////////////////////////////////////
Last edited by plamuk; 01-10-2013 at 06:52 PM.
|
|
|
01-11-2013, 05:35 PM
|
#2
|
Human being with feelings
Join Date: Dec 2011
Posts: 999
|
Thanks nym, I haven't forgotten about your plugin btw. I want to implement a note/event stack with search etc functions for the midi library which would be more useful than just doing the plugin. I've ran out of gumption at the moment but will get on it asapinecbaftfftna/otgothr*.
* as soon as possible if nobody else can be arsed flying the flag for the nymmeister and/or the good of the human race
|
|
|
01-12-2013, 11:09 AM
|
#3
|
Human being with feelings
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,889
|
Quote:
Originally Posted by captain_caveman
I want to implement a note/event stack with search etc functions for the midi library which would be more useful than just doing the plugin. I've ran out of gumption at the moment but will get on it asapinecbaftfftna/otgothr*.
|
I'm building up a general purpose list at the moment if you're interested. It's designed to be useful and safe rather than fast but it might fit the bill. When I've finished the version I'm working on I'll do a faster one with less safety checking.
|
|
|
01-13-2013, 04:20 PM
|
#4
|
Human being with feelings
Join Date: Dec 2011
Posts: 999
|
Absolutely, general list functionality would be perfect. I don't think speed would matter all that much with simple midi plugins since there are not many events per block to check anyway.
Nice one.
|
|
|
01-14-2013, 06:17 AM
|
#5
|
Human being with feelings
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,889
|
Quote:
Originally Posted by captain_caveman
Absolutely, general list functionality would be perfect. I don't think speed would matter all that much with simple midi plugins since there are not many events per block to check anyway.
Nice one.
|
Okeydokey: http://forum.cockos.com/showthread.p...23#post1104623
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 06:40 AM.
|