if (len <= 0)\r
return;\r
\r
+ if (mkbd_stolen_channels & (1u << 5))\r
+ ;\r
+ else\r
// fill buffer, applying a rather weak order 1 bessel IIR on the way\r
// y[n] = (x[n] + x[n-1])*(1/2) (3dB cutoff at 11025 Hz, no gain)\r
// 1 sample delay for correct IIR filtering over audio frame boundaries\r
memset32(PsndBuffer, 0, PicoIn.opt & POPT_EN_STEREO ? len*2 : len);\r
}\r
\r
+static int g_tempo_kdown;\r
+static int g_tempo_increase;\r
+\r
+static int is_rom(const char *name)\r
+{\r
+ const char *rn = (const char *)Pico.rom + 0x150;\r
+ int i;\r
+ for (i = 0; name[i]; i++)\r
+ if (rn[i^1] != name[i])\r
+ return 0;\r
+ return 1;\r
+}\r
+\r
+static void smps_set_init_tempo(u32 base, u8 val)\r
+{\r
+ Pico.rom[(base + 5)^1] = val;\r
+}\r
+\r
+static void do_tempo_adj(void)\r
+{\r
+ static int step;\r
+ //u8 *mem = &Pico.rom[Pico.m.z80_bank68k << 15];\r
+ //u8 *mem = &Pico.rom[0x0EBE80];\r
+ //mem[5^1]--;\r
+ if (!g_tempo_kdown)\r
+ return;\r
+ if (step++ < 2)\r
+ return;\r
+ step = 0;\r
+ if (g_tempo_increase)\r
+ PicoMem.zram[0x1c24]--;\r
+ else\r
+ PicoMem.zram[0x1c24]++;\r
+ //printf("tempo %02x\n", PicoMem.zram[0x1c24]);\r
+ if (is_rom("SONIC & KNUCKLES")) {\r
+ smps_set_init_tempo(0x0EBE80, PicoMem.zram[0x1c24]); // ssz\r
+ }\r
+ else if (is_rom("SONIC 3D FLICKIES")) {\r
+ smps_set_init_tempo(0x0C7091, PicoMem.zram[0x1c24]); // diamond dust1\r
+ }\r
+}\r
+\r
+void mkbd_tempo_kdown(int tempo_up, int is_kdown)\r
+{\r
+ g_tempo_kdown = is_kdown;\r
+ g_tempo_increase = tempo_up;\r
+ //u16 mp = PicoMem.zram[0x1618] | (PicoMem.zram[0x1619] << 8);\r
+#if 0\r
+ int i;\r
+ for (i = 0; i < 0x2000-1; i++)\r
+ if (PicoMem.zram[i] == 0x80 && PicoMem.zram[i+1] == 0xbe)\r
+ printf("%04x\n", i);\r
+#endif\r
+}\r
+\r
#include <stdint.h>\r
#include <fcntl.h>\r
#include <poll.h>\r
\r
static void do_note(uint8_t note, int is_down)\r
{\r
- static uint32_t state[96/8/4];\r
+ static uint32_t state[128/8/4];\r
int i, v = 0, release_all = 0;\r
- if (note >= 96)\r
+ note += mkbd_transpose;\r
+ if (note >= 128u)\r
return;\r
if (is_down) {\r
state[note / 8 / 4] |= 1u << (note % 32u);\r
- //ym2612h_inject(4, 0, 0, 0, -1, 0);\r
- ym2612h_inject(4, note / 12, note % 12, 1, 3, 13);\r
+ //mkbd_snd_inject(4, 0, 0, 0, -1, 0);\r
+ if (mkbd_kbd_channel >= 0)\r
+ mkbd_snd_inject(mkbd_kbd_channel, note / 12, note % 12, 1,\r
+ mkbd_kbd_echo_channel, mkbd_echo_delay_on);\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
- ym2612h_inject(4, 0, 0, 0, 3, 21);\r
+ if (release_all && mkbd_kbd_channel >= 0)\r
+ mkbd_snd_inject(mkbd_kbd_channel, 0, 0, 0,\r
+ mkbd_kbd_echo_channel, mkbd_echo_delay_off);\r
}\r
}\r
\r
} note;\r
int ret;\r
\r
- ym2612h_adj_step();\r
- ym2612h_echo_step();\r
+ do_tempo_adj();\r
+ mkbd_snd_do_frame();\r
\r
for (;;)\r
{\r
if (read(mkbd_fd, &data, sizeof(data)) != sizeof(data))\r
cfail("read", NULL);\r
//printf("p %02x %02x\n", data[0], data[1]);\r
- ym2612h_adj_start(4, 4);\r
+ mkbd_snd_adj_start(4, 4);\r
break;\r
case 0xf0:\r
switch (status) {\r
// Fill up DAC output in case of missing samples (Q rounding errors)\r
if (length-daclen > 0 && PicoIn.sndOut) {\r
Pico.snd.dac_pos += (length-daclen) << 20;\r
+ if (mkbd_stolen_channels & (1u << 5))\r
+ ;\r
+ else\r
if (PicoIn.opt & POPT_EN_STEREO) {\r
s16 *d = PicoIn.sndOut + daclen*2;\r
int pan = ym2612.REGS[YM2612_CH6PAN];\r
\r
static int g_lfo_ampm;\r
\r
+\r
+#undef NDEBUG\r
+#include <assert.h>\r
+#include "mkbd.h"\r
+\r
+int mkbd_stolen_channels;\r
+int mkbd_kbd_channel = -1;\r
+int mkbd_kbd_echo_channel = -1;\r
+int mkbd_echo_delay_on;\r
+int mkbd_echo_delay_off;\r
+int mkbd_transpose;\r
+int mkbd_ar_min;\r
+int mkbd_stop_z80;\r
+\r
/* register number to channel number , slot offset */\r
#define OPN_CHAN(N) (N&3)\r
#define OPN_SLOT(N) ((N>>2)&3)\r
UINT8 old_KSR = SLOT->KSR;\r
int eg_sh_ar, eg_sel_ar;\r
\r
- SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;\r
+ int c = CH - ym2612.CH;\r
+ int ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;\r
+ if (c == mkbd_kbd_channel || c == mkbd_kbd_echo_channel)\r
+ if (ar < mkbd_ar_min)\r
+ ar = mkbd_ar_min;\r
+ //if (c == 0 && SLOT->ar != ar)\r
+ // printf("%d,%ld %d->%d\n", c, SLOT - CH->SLOT, SLOT->ar, ar);\r
+\r
+ SLOT->ar = ar;\r
SLOT->ar_ksr = SLOT->ar + SLOT->ksr;\r
\r
SLOT->KSR = 3-(v>>6);\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, 811, 860, 910, 965, 1022, 1083, 1147, 1215 };\r
\r
-int mkbd_stolen_channels = (1<<3) | (1<<4);\r
static int mkbd_blk_fn_overide[6+4];\r
\r
static int mkbd_fn_adj_step[6+4];\r
static int mkbd_fn_adj[6+4];\r
+static int mkbd_ym_kon_mask; // on now\r
+static int mkbd_ym_kon_next_mask; // on on next frame\r
+static int mkbd_ym_cooldown_mask;\r
\r
// (144 * fnote * 2^20 / φM) / 2^(B-1)\r
// x *144*2097152/7670443 / 16\r
\r
static void do_key_onoff(int c, int v)\r
{\r
+ assert((u32)c < 6);\r
+ if (v & 0xf0)\r
+ mkbd_ym_kon_mask |= 1 << c;\r
+ else\r
+ mkbd_ym_kon_mask &= ~(1 << c);\r
+ //if (c==4) return;\r
+ //printf("%d c%d k%s %02x\n", Pico.m.frame_count,\r
+ // c, (v & 0xf0) ? "on " : "off", 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
\r
static void echo_add(int c, int blk_fn, int frame_delay);\r
\r
-void ym2612h_inject(int c, unsigned int octave, unsigned int key, int is_on,\r
+static void ym2612h_inject(int c, unsigned int octave, unsigned int key, int is_on,\r
int echo_c, int echo_frames)\r
{\r
UINT32 val = 0;\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
+\r
+ mkbd_ym_kon_next_mask &= ~(1 << c);\r
+ if (mkbd_ym_kon_mask & (1 << c))\r
+ mkbd_ym_cooldown_mask |= 1 << c;\r
+ mkbd_blk_fn_overide[c] = 0;\r
+ do_key_onoff(c, 0);\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
- do_key_onoff(c, 0);\r
set_blk_fn(c, val);\r
+ if (!(mkbd_ym_cooldown_mask & (1 << c)))\r
+ do_key_onoff(c, 0xf0);\r
+ else\r
+ // give a frame for adsr to update\r
+ mkbd_ym_kon_next_mask |= 1 << c;\r
}\r
- else\r
- mkbd_blk_fn_overide[c] = 0;\r
\r
mkbd_fn_adj[c] = mkbd_fn_adj_step[c] = 0;\r
\r
- do_key_onoff(c, is_on ? 0xf0 : 0);\r
-\r
- if (echo_c >= 0)\r
+ if (echo_c >= 0) {\r
+ if (is_on) {\r
+ // must do it always as there might be an older\r
+ // koff that we need to override\r
+ echo_add(echo_c, 0, echo_frames);\r
+ echo_frames++;\r
+ }\r
echo_add(echo_c, val, echo_frames);\r
+ }\r
}\r
\r
static struct\r
int c;\r
int blk_fn;\r
u32 frame;\r
-} echo_ev[8];\r
+} echo_ev[12];\r
\r
#define ECHO_MAX_FRAMES 60u\r
\r
static void echo_add(int c, int blk_fn, int frame_delay)\r
{\r
u32 now = Pico.m.frame_count;\r
- int i;\r
+ int i, di;\r
\r
assert((u32)frame_delay <= ECHO_MAX_FRAMES);\r
if (now + (u32)frame_delay == 0)\r
frame_delay++;\r
- if (blk_fn)\r
- memset(echo_ev, 0, sizeof(echo_ev));\r
+\r
+ // as kon and koff delays may differ, and kon generates a koff event,\r
+ // kill any older event\r
+ for (i = 0; i < ARRAY_SIZE(echo_ev); i++) {\r
+ if (echo_ev[i].frame == 0 || echo_ev[i].c != c)\r
+ continue;\r
+ di = echo_ev[i].frame - now;\r
+ if (di >= frame_delay && !echo_ev[i].blk_fn == !blk_fn)\r
+ echo_ev[i].frame = 0;\r
+ }\r
\r
for (i = 0; i < ARRAY_SIZE(echo_ev); i++) {\r
if (echo_ev[i].frame != 0 && echo_ev[i].frame - now <= ECHO_MAX_FRAMES)\r
printf("echo overflow\n");\r
}\r
\r
-void ym2612h_echo_step(void)\r
+static void ym2612h_echo_do_step(void)\r
{\r
u32 now = Pico.m.frame_count;\r
int i, c, val;\r
if (val) {\r
mkbd_blk_fn_overide[c] = val;\r
set_blk_fn(c, val);\r
- do_key_onoff(c, 0);\r
}\r
do_key_onoff(c, val ? 0xf0 : 0);\r
echo_ev[i].frame = 0;\r
mkbd_fn_adj_step[c] = step;\r
}\r
\r
-void ym2612h_adj_step(void)\r
+static void ym2612h_do_frame(void)\r
{\r
int c;\r
\r
+ mkbd_ym_cooldown_mask = 0;\r
+ for (c = 0; c < 6; c++) {\r
+ if (!(mkbd_ym_kon_next_mask & (1 << c)))\r
+ continue;\r
+ mkbd_ym_kon_next_mask &= ~(1 << c);\r
+ do_key_onoff(c, 0xf0);\r
+ }\r
+\r
for (c = 0; c < 6; c++) {\r
if (mkbd_fn_adj_step[c] == 0)\r
continue;\r
}\r
}\r
\r
+void mkbd_steal_toggle(int c, int shift)\r
+{\r
+ if (shift) {\r
+ mkbd_stolen_channels |= 1u << c;\r
+ if (c == mkbd_kbd_channel) {\r
+ mkbd_kbd_channel = -1;\r
+ mkbd_kbd_echo_channel = c;\r
+ }\r
+ else if (c == mkbd_kbd_echo_channel)\r
+ mkbd_kbd_echo_channel = -1;\r
+ else\r
+ mkbd_kbd_channel = c;\r
+ }\r
+ else {\r
+ mkbd_stolen_channels ^= 1u << c;\r
+ if (!(mkbd_stolen_channels & (1u << c))) {\r
+ if (c == mkbd_kbd_channel)\r
+ mkbd_kbd_channel = -1;\r
+ if (c == mkbd_kbd_echo_channel)\r
+ mkbd_kbd_echo_channel = -1;\r
+ }\r
+ }\r
+ if (c >= 6) {\r
+ sn76496h_steal(c - 6, !!(mkbd_stolen_channels & (1u << c)));\r
+ return;\r
+ }\r
+ if (mkbd_stolen_channels & (1u << c))\r
+ do_key_onoff(c, 0);\r
+}\r
+\r
+void mkbd_snd_inject(int c, unsigned int octave, unsigned int key, int is_on,\r
+ int echo_c, int echo_frames)\r
+{\r
+ if (c < 6)\r
+ ym2612h_inject(c, octave, key, is_on, echo_c, echo_frames);\r
+ else\r
+ ;\r
+}\r
+\r
+void mkbd_snd_adj_start(int c, int step)\r
+{\r
+ if (c < 6)\r
+ ym2612h_adj_start(c, step);\r
+ else\r
+ ;\r
+}\r
+\r
+void mkbd_snd_do_frame(void)\r
+{\r
+ ym2612h_do_frame();\r
+ ym2612h_echo_do_step();\r
+}\r
+\r
+// -----------------\r
+\r
/* write a OPN register (0x30-0xff) */\r
static int OPNWriteReg(int r, int v, int injecting)\r
{\r
if( c == 3 ) { ret=0; break; }\r
if( v&0x04 ) c+=3;\r
if (is_stolen(c)) { ret = 0; break; }\r
- //if (c == 3 || c == 4)\r
- // printf("%d %d konoff %02x\n", Pico.m.frame_count, c, v);\r
+ //if (c == 0 || c == 4)\r
+ // printf("%d %d k%s %02x\n", Pico.m.frame_count, c, (v & 0xf0) ? "on " : "off", 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