opll->adr = opll_buf.adr;\r
}\r
\r
+#include "mkbd.h"\r
+static int mkbd_fd = -1;\r
+\r
+static int midi_init(void);\r
+\r
PICO_INTERNAL void PsndInit(void)\r
{\r
opll = OPLL_new(OSC_NTSC/15, OSC_NTSC/15/72);\r
OPLL_setChipType(opll,0);\r
OPLL_reset(opll);\r
+\r
+ if (mkbd_fd == -1)\r
+ mkbd_fd = midi_init();\r
+ if (mkbd_fd == -1)\r
+ exit(1);\r
+ printf("mkbd_fd: %d\n", mkbd_fd);\r
}\r
\r
PICO_INTERNAL void PsndExit(void)\r
memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r
}\r
\r
+#include <stdint.h>\r
+#include <fcntl.h>\r
+#include <poll.h>\r
+#include <unistd.h>\r
+#include <errno.h>\r
+#include <alsa/asoundef.h>\r
+#define ESC_RED "\033[1;31m"\r
+#define ESC_NORM "\033[0m"\r
+\r
+static void cfail(const char *name, const char *arg)\r
+{\r
+ fprintf(stderr, "%s(%s): ", name, arg ? arg : "");\r
+ perror(NULL);\r
+ exit(1);\r
+}\r
+\r
+static int midi_init(void)\r
+{\r
+ char buf[16];\r
+ int i, fd;\r
+\r
+ for (i = 0; i < 10; i++) {\r
+ // /dev/snd/midiC%dD0 ?\r
+ snprintf(buf, sizeof(buf), "/dev/midi%d", i);\r
+ fd = open(buf, O_RDONLY);\r
+ if (fd != -1)\r
+ break;\r
+ if (errno != ENOENT)\r
+ perror(buf);\r
+ }\r
+ if (fd == -1) {\r
+ fprintf(stderr, ESC_RED "no midi" ESC_NORM "\n");\r
+ cfail("open", NULL);\r
+ }\r
+ return fd;\r
+}\r
+\r
+static void do_note(uint8_t note, int is_down)\r
+{\r
+ static uint32_t state[96/8/4];\r
+ int i, v = 0, release_all = 0;\r
+ if (note >= 96)\r
+ return;\r
+ if (is_down) {\r
+ state[note / 8 / 4] |= 1u << (note % 32u);\r
+ ym2612_inject(4, 0, 0, 0);\r
+ ym2612_inject(4, note / 12, note % 12, 1);\r
+ }\r
+ else {\r
+ state[note / 8 / 4] &= ~(1u << (note % 32u));\r
+ for (i = 0; i < ARRAY_SIZE(state); i++)\r
+ v |= state[i];\r
+ release_all = !v;\r
+ if (release_all)\r
+ ym2612_inject(4, 0, 0, 0);\r
+ }\r
+}\r
+\r
+static void midi_read(void)\r
+{\r
+ struct pollfd pfds[1] = {{ mkbd_fd, POLLIN, 0 }};\r
+ uint8_t status = 0;\r
+ uint8_t byte;\r
+ struct\r
+ {\r
+ uint8_t note;\r
+ uint8_t velocity;\r
+ } note;\r
+ int ret;\r
+\r
+ for (;;)\r
+ {\r
+ ret = poll(pfds, ARRAY_SIZE(pfds), 0);\r
+ if (ret < 0)\r
+ cfail("poll", NULL);\r
+ if (ret == 0)\r
+ return;\r
+ if (pfds[0].revents & ~POLLIN)\r
+ cfail("poll revents", NULL);\r
+\r
+ errno = 0;\r
+ if (read(mkbd_fd, &status, sizeof(status)) != sizeof(status))\r
+ cfail("read", NULL);\r
+ //printf("%d %02x\n", Pico.m.frame_count, status);\r
+ if (!(status & 0x80)) // not cmd\r
+ continue;\r
+ //channel = status & 0x0f;\r
+ switch (status & 0xf0) {\r
+ case MIDI_CMD_NOTE_ON: // 0x90\r
+ if (read(mkbd_fd, ¬e, sizeof(note)) != sizeof(note))\r
+ cfail("read", "note");\r
+ if (!note.velocity)\r
+ goto do_off;\r
+ do_note(note.note, 1);\r
+ break;\r
+ case MIDI_CMD_NOTE_OFF: // 0x80\r
+ if (read(mkbd_fd, ¬e, sizeof(note)) != sizeof(note))\r
+ cfail("read", NULL);\r
+ do_off:\r
+ do_note(note.note, 0);\r
+ break;\r
+ case 0xf0:\r
+ switch (status) {\r
+ case MIDI_CMD_COMMON_SYSEX:\r
+ do {\r
+ if (read(mkbd_fd, &byte, sizeof(byte)) != sizeof(byte))\r
+ cfail("read", NULL);\r
+ } while (byte != MIDI_CMD_COMMON_SYSEX_END); // 0xf7\r
+ break;\r
+ default:\r
+ goto unhandled;\r
+ }\r
+ break;\r
+ default:\r
+ unhandled:\r
+ break;\r
+ } // switch\r
+ }\r
+}\r
\r
static int PsndRender(int offset, int length)\r
{\r
buf32 = PsndBuffer+(offset<<stereo);\r
\r
pprof_start(sound);\r
+ // mkbd\r
+ if (offset == 0)\r
+ midi_read();\r
\r
// Add in parts of the PSG output not yet done\r
if (length-psglen > 0 && PicoIn.sndOut) {\r
}\r
}\r
\r
+#undef NDEBUG\r
+#include <assert.h>\r
+#include "mkbd.h"\r
+\r
+static\r
+const char *keys[] = { "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" };\r
+// 643.8 682.1 722.6 765.6 811.1 859.3 910.4 964.6 1021.9 1082.7 1147.1 1215.3\r
+static int fns[] = { 644, 682, 723, 766, 813, 860, 910, 965, 1023, 1084, 1148, 1216 };\r
+\r
+int mkbd_stolen_channels = (1<<3) | (1<<4);\r
+int mkbd_blk_fn_overide[6+4];\r
+int mkbd_fn_adj[6+4];\r
+\r
+// (144 * fnote * 2^20 / φM) / 2^(B-1)\r
+// x *144*2097152/7670443 / 16\r
+static void handle_blk_fn(int c, UINT8 *blk, UINT32 *fn, int do_log)\r
+{\r
+ int min_abs = 1<<11;\r
+ int min_di = 0;\r
+ int min_i = 0;\r
+ char buf[16];\r
+ int i;\r
+\r
+ for (i = 0; i < ARRAY_SIZE(fns); i++) {\r
+ int di = *fn - fns[i];\r
+ if (abs(di) < min_abs) {\r
+ min_abs = abs(di);\r
+ min_i = i;\r
+ min_di = di;\r
+ }\r
+ }\r
+ mkbd_fn_adj[c] = min_di;\r
+\r
+ if (do_log) {\r
+ i = snprintf(buf, sizeof(buf), "%s%d", keys[min_i], *blk);\r
+ if (min_di != 0)\r
+ snprintf(buf + i, sizeof(buf) - i, "%+d", min_di);\r
+ printf("%-8s", buf);\r
+ }\r
+\r
+ if (!do_log && mkbd_blk_fn_overide[c]) {\r
+ *blk = mkbd_blk_fn_overide[c] >> 11;\r
+ *fn = (mkbd_blk_fn_overide[c] & 0x7ff) + min_di;\r
+ }\r
+}\r
+\r
+static int is_stolen(int c)\r
+{\r
+ return mkbd_stolen_channels & (1 << c);\r
+}\r
+\r
+static void do_log_note(int c)\r
+{\r
+#if 0\r
+ int i, chans = (1<<0);// | (1<<4);\r
+ if (chans & (1 << c)) {\r
+ printf("%-5d ", Pico.m.frame_count);\r
+ for (i = 0; i < 6; i++) {\r
+ UINT8 blk = ym2612.CH[i].block_fnum >> 11;\r
+ UINT32 fn = ym2612.CH[i].block_fnum & 0x7ff;\r
+ if (chans & (1 << i))\r
+ handle_blk_fn(i, &blk, &fn, 1);\r
+ }\r
+ puts("");\r
+ }\r
+#endif\r
+}\r
+\r
+static void do_key_onoff(int c, int v)\r
+{\r
+ if(v&0x10) FM_KEYON(c,SLOT1); else FM_KEYOFF(c,SLOT1);\r
+ if(v&0x20) FM_KEYON(c,SLOT2); else FM_KEYOFF(c,SLOT2);\r
+ if(v&0x40) FM_KEYON(c,SLOT3); else FM_KEYOFF(c,SLOT3);\r
+ if(v&0x80) FM_KEYON(c,SLOT4); else FM_KEYOFF(c,SLOT4);\r
+}\r
+\r
+static int OPNWriteReg(int r, int v, int injecting);\r
+\r
+void ym2612_inject(int c, unsigned int octave, unsigned int key, int is_on)\r
+{\r
+ UINT8 fn_h_saved = ym2612.OPN.ST.fn_h;\r
+ UINT32 r = c < 3 ? c : (0x100 + (c - 3));\r
+ UINT32 val;\r
+\r
+ //printf("%s %d %d,%d %d\n", __func__, c, octave, key, is_on);\r
+ assert(octave < 8);\r
+ assert(key < ARRAY_SIZE(fns));\r
+ if (is_on) {\r
+ val = fns[key] + mkbd_fn_adj[c];\r
+ assert(val < (1u << 11));\r
+ val |= octave << 11;\r
+ mkbd_blk_fn_overide[c] = val;\r
+ ym2612.OPN.ST.fn_h = val >> 8;\r
+ OPNWriteReg(r | 0xa0, val & 0xff, 1);\r
+ ym2612.OPN.ST.fn_h = fn_h_saved;\r
+ }\r
+ else\r
+ mkbd_blk_fn_overide[c] = 0;\r
+\r
+ do_key_onoff(c, is_on ? 0xf0 : 0);\r
+}\r
\r
/* write a OPN register (0x30-0xff) */\r
-static int OPNWriteReg(int r, int v)\r
+static int OPNWriteReg(int r, int v, int injecting)\r
{\r
int ret = 1;\r
FM_CH *CH;\r
{\r
UINT32 fn = ((UINT32)(ym2612.OPN.ST.fn_h & 7) << 8) | v;\r
UINT8 blk = ym2612.OPN.ST.fn_h >> 3;\r
+\r
+ if (!injecting) {\r
+ handle_blk_fn(c, &blk, &fn, 0);\r
+ //if (is_stolen(c)) break;\r
+ }\r
+\r
/* keyscale code */\r
CH->kcode = (blk<<2) | opn_fktable[fn >> 7];\r
/* phase increment counter */\r
CH->fc = fn_table[fn*2]>>(7-blk);\r
\r
+ if (!injecting && CH->block_fnum != ((blk<<11) | fn))\r
+ do_log_note(c);\r
+\r
/* store fnum in clear form for LFO PM calculations */\r
CH->block_fnum = (blk<<11) | fn;\r
\r
reset_channels( &ym2612.CH[0] );\r
for(i = 0xb6 ; i >= 0xb4 ; i-- )\r
{\r
- OPNWriteReg(i ,0xc0);\r
- OPNWriteReg(i|0x100,0xc0);\r
+ OPNWriteReg(i ,0xc0,0);\r
+ OPNWriteReg(i|0x100,0xc0,0);\r
ym2612.REGS[i ] = 0xc0;\r
ym2612.REGS[i|0x100] = 0xc0;\r
}\r
for(i = 0xb2 ; i >= 0x30 ; i-- )\r
{\r
- OPNWriteReg(i ,0);\r
- OPNWriteReg(i|0x100,0);\r
+ OPNWriteReg(i ,0,0);\r
+ OPNWriteReg(i|0x100,0,0);\r
}\r
- for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(i,0);\r
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(i,0,0);\r
/* DAC mode clear */\r
ym2612.dacen = 0;\r
ym2612.dacout = 0;\r
c = v & 0x03;\r
if( c == 3 ) { ret=0; break; }\r
if( v&0x04 ) c+=3;\r
+ if (is_stolen(c)) { ret = 0; break; }\r
+ //if (c == 4) printf("konoff %02x\n", v);\r
if(v&0x10) FM_KEYON(c,SLOT1); else FM_KEYOFF(c,SLOT1);\r
if(v&0x20) FM_KEYON(c,SLOT2); else FM_KEYOFF(c,SLOT2);\r
if(v&0x40) FM_KEYON(c,SLOT3); else FM_KEYOFF(c,SLOT3);\r
break;\r
default: /* 0x30-0xff OPN section */\r
/* write register */\r
- ret = OPNWriteReg(addr,v);\r
+ ret = OPNWriteReg(addr,v,0);\r
}\r
break;\r
}\r