//execute:
//npx tsx main.ts
//npm install config
//npm i --save-dev @types/config
import config from 'config';
const csvList : string = config.get('Init.externalFiles.csvList');
import {MyCSV} from './MyCSV';
// let csv = new MyCSV;
// csv.printCsvToConsole(csvList, "\t", "");
//async
import async from 'async';
let q = async.queue((task: any, callback:any)=>{
console.log('taskname ' + task.name);
setTimeout(()=>{callback();}, 10);
}, 1);
import midi from 'midi';
class MidiFilter
{
public getTypeGroup(message : midi.MidiMessage) : string
{
let msgType : string = "";
let typeGroup : number = -1;
typeGroup = parseInt(message[0].toString(16).charAt(0),16);
switch (typeGroup)
{
case 0x8: msgType = "note off"; break;
case 0x9: msgType = "note on"; break;
case 0xa: msgType = "polyphonic aftertouch"; break;
case 0xb: msgType = "control / mode change"; break;
case 0xc: msgType = "program change"; break;
case 0xd: msgType = "channel aftertouch"; break;
case 0xe: msgType = "pitch bend change"; break;
default: msgType = "unknown"; break;
}
return msgType;
}
public getType(message : midi.MidiMessage): string
{
// see https://www.midi.org/specifications-old/item/table-2-expanded-messages-list-status-bytes
// and https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
let msgType : string = "";
let channel : number = -1;
let typeGroup : number = -1;
if(message[0] < 0xF0)
{
//midi status byte LEFT side:
typeGroup = parseInt(message[0].toString(16).charAt(0),16);
//midi status byte RIGHT side PLUS one:
channel = parseInt(message[0].toString(16).charAt(1),16)+1;
switch (typeGroup)
{
case 0x8: msgType = "channel " + channel + " note off"; break;
case 0x9: msgType = "channel " + channel + " note on"; break;
case 0xa: msgType = "channel " + channel + " polyphonic aftertouch"; break;
case 0xb: msgType = "channel " + channel + " control / mode change"; break;
case 0xc: msgType = "channel " + channel + " program change"; break;
case 0xd: msgType = "channel " + channel + " channel aftertouch"; break;
case 0xe: msgType = "channel " + channel + " pitch bend change"; break;
default: msgType = "unknown"; break;
}
}
else
{
switch(message[0])
{
case 0xf0 : msgType = "system exclusive"; break;
case 0xf1 : msgType = "MIDI time code qtr. frame"; break;
case 0xf2 : msgType = "song position pointer"; break;
case 0xf3 : msgType = "song select"; break;
case 0xf4 : msgType = "undefined"; break;
case 0xf5 : msgType = "undefined"; break;
case 0xf6 : msgType = "tune request"; break;
case 0xf7 : msgType = "end of sysex (EOX)"; break;
case 0xf8 : msgType = "timing clock"; break;
case 0xf9 : msgType = "undefined"; break;
case 0xfa : msgType = "start"; break;
case 0xfb : msgType = "continue"; break;
case 0xfc : msgType = "stop"; break;
case 0xfd : msgType = "undefined"; break;
case 0xfe : msgType = "active sensing"; break;
case 0xff : msgType = "system reset"; break;
default: msgType = "unknown"; break;
}
}
// console.log(msgType);
return msgType;
}
public getNote (message : midi.MidiMessage) : string | undefined
{
let note : string[] = [];
note[0] = 'C-1';
note[1] = 'C#-1';
note[2] = 'D-1';
note[3] = 'D#-1';
note[4] = 'E-1';
note[5] = 'F-1';
note[6] = 'F#-1';
note[7] = 'G-1';
note[8] = 'G#-1';
note[9] = 'A-1';
note[10] = 'A#-1';
note[11] = 'B-1';
note[12] = 'C-2';
note[13] = 'C#-2';
note[14] = 'D-2';
note[15] = 'D#-2';
note[16] = 'E-2';
note[17] = 'F-2';
note[18] = 'F#-2';
note[19] = 'G-2';
note[20] = 'G#-2';
note[21] = 'A-2';
note[22] = 'A#-22';
note[23] = 'B-2';
note[24] = 'C-3';
note[25] = 'C#-3';
note[26] = 'D-3';
note[27] = 'D#-3';
note[28] = 'E-3';
note[29] = 'F-3';
note[30] = 'F#-3';
note[31] = 'G-3';
note[32] = 'G#-3';
note[33] = 'A-3';
note[34] = 'A#-3';
note[35] = 'B-3';
note[36] = 'C-4';
note[37] = 'C#-4';
note[38] = 'D-4';
note[39] = 'D#-4';
note[40] = 'E-4';
note[41] = 'F-4';
note[42] = 'F#-4';
note[43] = 'G-4';
note[44] = 'G#-4';
note[45] = 'A-4';
note[46] = 'A#-4';
note[47] = 'B-4';
note[48] = 'C-5';
note[49] = 'C#-5';
note[50] = 'D-5';
note[51] = 'D#-5';
note[52] = 'E-5';
note[53] = 'F-5';
note[54] = 'F#-5';
note[55] = 'G-5';
note[56] = 'G#-5';
note[57] = 'A-5';
note[58] = 'A#-5';
note[59] = 'B-5';
note[60] = 'C-6';
note[61] = 'C#-6';
note[62] = 'D-6';
note[63] = 'D#-6';
note[64] = 'E-6';
note[65] = 'F-6';
note[66] = 'F#-6';
note[67] = 'G-6';
note[68] = 'G#-6';
note[69] = 'A-6';
note[70] = 'A#-6';
note[71] = 'B-6';
note[72] = 'C-7';
note[73] = 'C#-7';
note[74] = 'D-7';
note[75] = 'D#-7';
note[76] = 'E-7';
note[77] = 'F-7';
note[78] = 'F#-7';
note[79] = 'G-7';
note[80] = 'G#-7';
note[81] = 'A-7';
note[82] = 'A#-7';
note[83] = 'B-7';
note[84] = 'C-8';
note[85] = 'C#-8';
note[86] = 'D-8';
note[87] = 'D#-8';
note[88] = 'E-8';
note[89] = 'F-8';
note[90] = 'F#-8';
note[91] = 'G-8';
note[92] = 'G#-8';
note[93] = 'A-8';
note[94] = 'A#-8';
note[95] = 'B-8';
note[96] = 'C-9';
note[97] = 'C#-9';
note[98] = 'D-9';
note[99] = 'D#-9';
note[100] = 'E-9';
note[101] = 'F-9';
note[102] = 'F#-9';
note[103] = 'G-9';
note[104] = 'G#-9';
note[105] = 'A-9';
note[106] = 'A#-9';
note[107] = 'B-9';
if(this.getTypeGroup(message) == "note on" || this.getTypeGroup(message) == "note off")
{
return note[message[1]];
}
else
{
return undefined;
}
}
}
class MidiComm
{
private input = new midi.Input();
private output = new midi.Output();
private filter = new MidiFilter();
constructor()
{
}
public inputMonitorOn(deviceName : string) : void
{
this.input.openPort(this.getInputPortByName(deviceName));
this.input.on('message', (deltaTime : number, message : midi.MidiMessage) =>
{
console.log(`t: ${this.filter.getType(message)}\tm: ${message}\td: ${deltaTime}`);
}
);
}
private getInputPortByName(deviceName : string): number
{
let portNumber : number = -1;
for(let i : number = 0; i < this.input.getPortCount(); i++ )
{
if(this.input.getPortName(i) == deviceName)
{
portNumber = i;
}
}
return portNumber;
}
private chord : number [] = new Array();
private interval : number [] = new Array();
private intercecptedInterval : number[] = [36,48];
private setInterval()
{
this.chord = this.chord.sort((a,b) => a-b);
this.interval = this.chord;
let tempInterval : number[] = new Array();
for(let i: number=0;i<this.interval.length;i++)
{
// console.log(this.interval[i] - this.chord[0]);
tempInterval.push(this.interval[i] - this.chord[0]);
}
// console.log(tempInterval);
this.interval = tempInterval;
}
private intercept(msg: midi.MidiMessage) : void
{
if(msg[1] >= this.intercecptedInterval[0] && msg[1] <= this.intercecptedInterval[1])
{
if(this.filter.getTypeGroup(msg) == "note on")
{
this.chord.push(msg[1]);
}
if(this.filter.getTypeGroup(msg) == "note off")
{
this.chord = this.chord.filter(function(e) { return e !== msg[1] });
}
this.setInterval()
console.log(this.interval);
}
else
{
for(let i : number = 0; i < this.interval.length; i++)
{
// this.output.send([msg[0], msg[1] + this.interval[i] ,msg[2]]);
q.push({name: this.filter.getNote([msg[0], msg[1] + this.interval[i] ,msg[2]])}, () => this.output.send([msg[0], msg[1] + this.interval[i] ,msg[2]]));
}
this.setInterval()
console.log(this.interval);
}
// return msg;
}
public sendMidi ():void
{
this.input.openPort(this.getInputPortByName('The Laboratory'));
this.output.openPort(this.getOutputPortByName('fromNode'));
this.input.on('message', (deltaTime : number, message : midi.MidiMessage) =>
{
console.log(`t: ${this.filter.getType(message)}\tm: ${message}\td: ${deltaTime}`);
this.intercept(message);
// this.output.send(this.intercept(message));
// this.output.closePort();
}
);
}
private getOutputPortByName(deviceName : string): number
{
let portNumber : number = -1;
for(let i : number = 0; i < this.output.getPortCount(); i++ )
{
if(this.output.getPortName(i) == deviceName)
{
portNumber = i;
}
}
return portNumber;
}
}
// "toNode" & "fromNode" are virtual midi devices, managed by the windows-app "loopMIDI" written by Tobias Erichsen
// new MidiComm().inputMonitorOn('The Laboratory');
new MidiComm().sendMidi();