Old 01-10-2013, 02:19 PM   #1
plamuk
Human being with feelings
 
Join Date: Feb 2007
Posts: 3,221
Default 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.
plamuk is offline   Reply With Quote
Old 01-11-2013, 05:35 PM   #2
captain_caveman
Human being with feelings
 
captain_caveman's Avatar
 
Join Date: Dec 2011
Posts: 999
Default

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
captain_caveman is offline   Reply With Quote
Old 01-12-2013, 11:09 AM   #3
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,889
Default

Quote:
Originally Posted by captain_caveman View Post
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.
IXix is offline   Reply With Quote
Old 01-13-2013, 04:20 PM   #4
captain_caveman
Human being with feelings
 
captain_caveman's Avatar
 
Join Date: Dec 2011
Posts: 999
Default

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.
captain_caveman is offline   Reply With Quote
Old 01-14-2013, 06:17 AM   #5
IXix
Human being with feelings
 
Join Date: Jan 2007
Location: mcr:uk
Posts: 3,889
Default

Quote:
Originally Posted by captain_caveman View Post
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
IXix is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 06:40 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.