/* * Copyright (C) 2017 The Contributors * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * A copy of the GNU General Public License can be found in the file * "gpl.txt" in the top directory of this repository. */ #include #include #define ver(...) _ver(mid_verbose, 0, __VA_ARGS__) #define ver2(...) _ver(mid_verbose, 1, __VA_ARGS__) #define ver3(...) _ver(mid_verbose, 2, __VA_ARGS__) #define REG_IER_ISR 0 #define REG_CFR_SR 1 #define REG_CDR_TBR 2 #define REG_TDR_RDR 3 #define BUF_SZ 256 typedef struct { int32_t buf_hd; int32_t buf_tl; uint8_t buf[BUF_SZ]; bool irq_r; bool irq_t; bool rdr_ok; uint8_t rdr; } state_t; static state_t state[] = { { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 }, { .buf_hd = 0, .buf_tl = 0, .irq_r = false, .irq_t = false, .rdr_ok = false, .rdr = 0x00 } }; int32_t mid_verbose = 0; struct RtMidiWrapper* mid_in; static void xmit(int32_t un) { int32_t i = state[un].buf_tl; ver2("mid xmit %d %d", i, state[un].buf_hd); if (i >= state[un].buf_hd) { return; } uint8_t byte = state[un].buf[i % BUF_SZ]; ver2("mid xmit 0x%02x", byte); state[un].rdr = byte; state[un].rdr_ok = true; state[un].irq_r = true; state[un].buf_tl = i + 1; if (state[un].buf_tl >= BUF_SZ) { state[un].buf_hd -= BUF_SZ; state[un].buf_tl -= BUF_SZ; ver2("mid adj %d %d", state[un].buf_tl, state[un].buf_hd); } } static void out_lk(int32_t un, uint8_t c) { int32_t i = state[un].buf_hd; ver2("mid out %d %d 0x%02x", state[un].buf_tl, i, c); if (i >= state[un].buf_tl + BUF_SZ) { err("midi port %d losing data", un); return; } state[un].buf[i % BUF_SZ] = c; state[un].buf_hd = i + 1; if (!state[un].irq_r && !state[un].rdr_ok) { xmit(un); } } static void out(int32_t un, uint8_t c) { if (SDL_LockMutex(cpu_mutex) < 0) { fail("SDL_LockMutex() failed: %s", SDL_GetError()); } out_lk(un, c); if (SDL_UnlockMutex(cpu_mutex) < 0) { fail("SDL_UnlockMutex() failed: %s", SDL_GetError()); } } static void mid_callback(double timeStamp, const unsigned char* message, size_t size, void *userData) { (void) size; (void) userData; ver2("Timestamp %f\n", timeStamp); for (uint8_t i = 0; i < sizeof(message); i++) { ver2("Message %i %u", i, (uint8_t) message[i]); out(0, message[i]); } } void mid_init(void) { ver("mid init"); mid_in = rtmidi_in_create_default(); ver2("%p", mid_in->ptr); // check if null uint32_t portcount = rtmidi_get_port_count(mid_in); if (portcount == 0) { mid_in = NULL; ver2("No MIDI ports\n"); return; } if (mid_port > portcount) { mid_in = NULL; ver2("Selected MIDI port larger than number of midi ports"); return; } rtmidi_open_port(mid_in, mid_port, rtmidi_get_port_name(mid_in, mid_port)); if(mid_in->ok == 0) { fail("Failed to open MIDI port"); } ver2("Using MIDI port %u", mid_port); rtmidi_in_set_callback(mid_in, mid_callback, mid_in->data); if(mid_in->ok == 0) { fail("Failed to set MIDI Callback"); } } void mid_quit(void) { if(mid_in) { rtmidi_close_port(mid_in); rtmidi_in_free(mid_in); } ver("mid quit"); } bool mid_exec(void) { ver3("mid exec"); return state[0].irq_r || state[0].irq_t || state[1].irq_r || state[1].irq_t; } uint32_t mid_read(uint32_t off, int32_t sz) { ver2("mid rd %u:%d", off, sz * 8); if (sz != 1 || off > 7) { fail("invalid mid rd %u:%d", off, sz * 8); } int32_t rg = (int32_t)(off % 4); int32_t un = (int32_t)(off / 4); uint32_t rv; switch (rg) { case REG_IER_ISR: rv = (uint32_t)(0xc0 | (state[un].rdr_ok ? 0x01 : 0x00)); state[un].irq_r = false; state[un].irq_t = false; ver2("ISR[%d] 0x%02x", un, rv); break; case REG_TDR_RDR: rv = state[un].rdr; state[un].rdr_ok = false; ver2("RDR[%d] 0x%02x", un, rv); break; default: rv = 0x00; break; } if (!state[un].irq_r && !state[un].rdr_ok) { xmit(un); } return rv; } void mid_write(uint32_t off, int32_t sz, uint32_t val) { ver2("mid wr %u:%d 0x%0*x", off, sz * 8, sz * 2, val); if (sz != 1 || off > 7) { fail("invalid mid wr %u:%d", off, sz * 8); } int32_t rg = (int32_t)(off % 4); int32_t un = (int32_t)(off / 4); switch (rg) { case REG_CFR_SR: ver2("CFR[%d] 0x%02x", un, val); if (un == 1) { fdd_set_side((int32_t)val & 0x01); } else { fdd_set_sel((int32_t)val & 0x01); } break; default: break; } } void mid_list(void) { mid_in = rtmidi_in_create_default(); uint32_t portcount = rtmidi_get_port_count(mid_in); if (portcount == 0) { mid_in = NULL; ver2("no MIDI ports\n"); return; } fprintf(stdout, "the following MIDI ports are available:\n"); for (uint32_t i = 0; i < portcount; i++) { fprintf(stdout, "port %d: %s\n", i, rtmidi_get_port_name(mid_in, i)); } rtmidi_in_free(mid_in); }