Subversion Repositories DIN Is Noise

Rev

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();
    }
  }
}