/* * midi_in.cc * DIN Is Noise MIDI Input is copyright (c) 2006-2021 Jagannathan Sampath * DIN Is Noise is released under GNU Public License 2.0 * For more information, please visit https://dinisnoise.org/ */ #include "midi_in.h" #include "console.h" #include "tcl_interp.h" #include "keyboard_keyboard.h" #include "chrono.h" #include "log.h" #include #include using namespace std; extern double MIDI_BPM; extern char BUFFER []; #define DIN_IS_NOISE_MIDI_INPUT "DIN Is Noise MIDI Input" #if defined __WINDOWS_MM__ midi_in::midi_in () : rt (RtMidi::WINDOWS_MM, DIN_IS_NOISE_MIDI_INPUT) #elif defined __MACOSX_CORE__ midi_in::midi_in () : rt (RtMidi::MACOSX_CORE, DIN_IS_NOISE_MIDI_INPUT) #elif defined __UNIX_JACK__ midi_in::midi_in () : rt (RtMidi::UNIX_JACK, DIN_IS_NOISE_MIDI_INPUT) #elif defined __LINUX_ALSA__ midi_in::midi_in () : rt (RtMidi::LINUX_ALSA, DIN_IS_NOISE_MIDI_INPUT) #endif { available = 0; input_port = 0; channel = -1; probe (); } void midi_in::probe () { num_ports = rt.getPortCount (); int api = rt.getCurrentApi (); if (api == RtMidi::WINDOWS_MM) dlog << "*** current MIDI API = Windows Multimedia ***" << endl; else if (api == RtMidi::MACOSX_CORE) dlog << "*** current MIDI API = Mac OS X Core ***" << endl; else if (api == RtMidi::UNIX_JACK) dlog << "*** current MIDI API = JACK ***" << endl; else if (api == RtMidi::LINUX_ALSA) dlog << "*** current MIDI API = ALSA ***" << endl; else dlog << "!!! MIDI API not found. No MIDI input is possible !!!" << endl; if (num_ports == 0) { dlog << "!!! found no MIDI input ports !!!" << endl; } else { dlog << "+++ found " << num_ports << " MIDI input ports +++" << endl; names.clear (); for (int i = 0; i < num_ports; ++i) { string name = rt.getPortName (i); names.push_back (name); } input_port = 0; } } void midi_in::open () { if (input_port >= num_ports) { dlog << "!!! couldnt open MIDI port " << input_port << " !!!" << endl; } else { if (available) rt.closePort (); rt.openPort (input_port); rt.ignoreTypes (0, 0, 0); // dont ignore sysex, timing or active sensing messages available = 1; dlog << "+++ opened MIDI input port " << input_port << " ["<< rt.getPortName (input_port) << "] +++" << endl; } } void midi_in::open (int port) { input_port = port; open (); } void run_midi_cmd (const string& c, std::vector& args) { string cmd (c); for (size_t i = 0, j = args.size (); i < j; ++i) { sprintf (BUFFER, " %u", args[i]); cmd += BUFFER; } cons (cmd); } void midi_in::handle_input () { if (available == 0) return; std::vector args; rt.getMessage (&args); int args_size = args.size (); static int num_clocks = 0; static double start_time = ui_clk(); channel = cc = val = 0xff; if (args_size) { unsigned char type = args[0]; unsigned char type_f0 = type & 0xf0; channel = type & 0x0f; args.push_back (channel); if (type_f0 == 0xb0) { // control change cc = args[1]; val = args[2]; run_midi_cmd ("midi-cc", args); } else if (type_f0 == 0x90) { // note on run_midi_cmd ("midi-note-on", args); keybd2.note_on (args[1], args[2]); } else if (type_f0 == 0x80) { // note off run_midi_cmd ("midi-note-off", args); keybd2.note_off (args[1]); } else if (type_f0 == 0xc0) { // program change run_midi_cmd ("midi-program-change", args); } else if (type_f0 == 0xe0) { // pitch bend char status = args[0]; char lsb = args[1]; char msb = args[2]; // normalise pitchbend range to -1.0...1.0 short value = (lsb | (msb << 7)) - 0x2000; float fvalue = (float)value / ((value < 0) ? 0x2000 : 0x1FFF); keybd2.pitch_bend (fvalue); ostringstream msg; msg << "midi-pitch-bend " << status << " " << value << " " << fvalue; cons (msg.str()); } else if (type == 0xf8) { // midi clock tick ++num_clocks; double time_now = ui_clk(); double elapsed_time = time_now - start_time; if (elapsed_time >= 1) { // from MIDI specification - see https://en.wikipedia.org/wiki/MIDI_beat_clock float quarter_notes_per_sec = num_clocks * 1.0f / (elapsed_time * 24); MIDI_BPM = quarter_notes_per_sec * 60; // a beat is a quarter note Tcl_UpdateLinkedVar (interpreter.interp, "midibpm"); // accessible as Tcl variable on DIN's command line cons ("midi-clock"); num_clocks = 0; start_time = time_now; } } else if (type == 0xfa) { // midi start cons ("midi-start"); num_clocks = 0; start_time = ui_clk(); } } }