play

Plays a standard midi file. Can only start playback from the beginning of the midi file.
Author: Johannes Taelman
License: BSD
Github: jt/midi/play.axo

Inlets

bool32 start on risign edge, stop on falling edge

Outlets

None

Attributes

file MIDI file

combo device

Declaration
tml_message *TinyMidiLoader = NULL;
tml_message *g_MidiMessage = NULL;
tml_message *g_FirstMidiMessage = NULL;

int ktime = 0;

bool pplay = 0;
Init
FIL fp;
FRESULT res;
const char *fn = "attr_file";

res = f_open(&fp, fn, FA_READ | FA_OPEN_EXISTING);

if (res != FR_OK) {
  report_fatfs_error(res, fn);
}

UINT file_size = f_size(&fp);

if (file_size > (4 * 1024 * 1024)) {
  LogTextMessage("MIDI file is very large, likely to fail\n");
}

void *file_buffer;

file_buffer = sdram_malloc(file_size);

UINT bytes_read;
res = f_read(&fp, file_buffer, file_size, &bytes_read);

if (res != FR_OK) {
  report_fatfs_error(res, fn);
}

f_close(&fp);

if (bytes_read != file_size) {
  LogTextMessage("bytes_read (%d) != file_size (%d)", bytes_read, file_size);
}

TinyMidiLoader = tml_load_memory(file_buffer, file_size);

if (!TinyMidiLoader) {
  LogTextMessage("Could not load MIDI file\n");
} else {
  // LogTextMessage("MIDI file loaded succesfully\n");
}

// Set up the global MidiMessage pointer to the first MIDI message
g_FirstMidiMessage = TinyMidiLoader;
Control Rate
if (inlet_play && !pplay) {
  if (!g_MidiMessage) {
    // LogTextMessage("start");
    ktime = 0;
    g_MidiMessage = g_FirstMidiMessage;
  }
}

if (!inlet_play && pplay) {
  // stop playing
  g_MidiMessage = 0;
  // send all notes off to all channels
  int i;
  for (i = 0; i < 16; i++) {
    MidiSend3((midi_device_t)attr_device, MIDI_CONTROL_CHANGE | i, 123, 0);
  }
  // LogTextMessage("stopped");
}

if (ktime & 0x80000000) {
  // prevent timer overflow after several days
  g_MidiMessage = 0;
  ktime = 0;
}

pplay = inlet_play;

ktime++;                         // current playback time in buffers
float g_Msec = ktime * 0.33333f; // current playback time in milliseconds
// 0.3333ms = 1s/3000Hz

for (; g_MidiMessage && g_Msec >= g_MidiMessage->time;
     g_MidiMessage = g_MidiMessage->next) {
  switch (g_MidiMessage->type) {
  case TML_NOTE_ON: // play a note
    MidiSend3((midi_device_t)attr_device, MIDI_NOTE_ON | g_MidiMessage->channel,
              g_MidiMessage->key, g_MidiMessage->velocity);
    /*
LogTextMessage("note-on : %02x %02x %02x",
g_MidiMessage->channel,
g_MidiMessage->key,
g_MidiMessage->velocity);
*/
    break;
  case TML_NOTE_OFF: // stop a note
    MidiSend3((midi_device_t)attr_device,
              MIDI_NOTE_OFF | g_MidiMessage->channel, g_MidiMessage->key,
              g_MidiMessage->velocity);
    break;
  case TML_KEY_PRESSURE:
    MidiSend3((midi_device_t)attr_device,
              MIDI_POLY_PRESSURE | g_MidiMessage->channel, g_MidiMessage->key,
              g_MidiMessage->velocity);
    break;
  case TML_PITCH_BEND: // pitch wheel modification
    MidiSend3((midi_device_t)attr_device,
              MIDI_PITCH_BEND | g_MidiMessage->channel,
              g_MidiMessage->pitch_bend & 0x7F, g_MidiMessage->pitch_bend >> 7);
    break;
  case TML_CONTROL_CHANGE: // MIDI controller messages
    MidiSend3((midi_device_t)attr_device,
              MIDI_CONTROL_CHANGE | g_MidiMessage->channel,
              g_MidiMessage->control, g_MidiMessage->control_value);
    break;
  case TML_PROGRAM_CHANGE:
    MidiSend2((midi_device_t)attr_device,
              MIDI_PROGRAM_CHANGE | g_MidiMessage->channel,
              g_MidiMessage->program);
    break;
  }
}

Privacy

© 2024 Zrna Research