Rev 2097 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* midi_in.cc
* DIN Is Noise MIDI Input is copyright (c) 2006-2025 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 <fstream>
#include <string>
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<unsigned char>& 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<unsigned char> 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();
}
}
}