From 6c517292ddb7713e41ef2837ba8202a4ef995736 Mon Sep 17 00:00:00 2001 From: djmaximum17 <61179162+djmaximum17@users.noreply.github.com> Date: Tue, 19 Apr 2022 07:14:12 -0500 Subject: [PATCH] Add files via upload --- extern/NSFplay/legacy/2413tone.h | 22 + extern/NSFplay/legacy/281btone.h | 20 + extern/NSFplay/legacy/emu2149.c | 363 ++++++ extern/NSFplay/legacy/emu2149.h | 94 ++ extern/NSFplay/legacy/emu2212.c | 485 ++++++++ extern/NSFplay/legacy/emu2212.h | 77 ++ extern/NSFplay/legacy/emu2413.c | 1461 +++++++++++++++++++++++++ extern/NSFplay/legacy/emu2413.h | 247 +++++ extern/NSFplay/legacy/emutypes.h | 41 + extern/NSFplay/legacy/vrc7tone_ft35.h | 20 + extern/NSFplay/legacy/vrc7tone_ft36.h | 21 + extern/NSFplay/legacy/vrc7tone_kt1.h | 21 + extern/NSFplay/legacy/vrc7tone_kt2.h | 21 + extern/NSFplay/legacy/vrc7tone_mo.h | 20 + extern/NSFplay/legacy/vrc7tone_nuke.h | 21 + extern/NSFplay/legacy/vrc7tone_rw.h | 21 + extern/NSFplay/nes_apu.cpp | 400 +++++++ extern/NSFplay/nes_apu.h | 88 ++ extern/NSFplay/nes_dmc.cpp | 771 +++++++++++++ extern/NSFplay/nes_dmc.h | 129 +++ extern/NSFplay/nes_fds.cpp | 397 +++++++ extern/NSFplay/nes_fds.h | 83 ++ extern/NSFplay/nes_fme7.cpp | 186 ++++ extern/NSFplay/nes_fme7.h | 42 + extern/NSFplay/nes_mmc5.cpp | 422 +++++++ extern/NSFplay/nes_mmc5.h | 74 ++ extern/NSFplay/nes_n106.cpp | 367 +++++++ extern/NSFplay/nes_n106.h | 74 ++ extern/NSFplay/nes_vrc6.cpp | 264 +++++ extern/NSFplay/nes_vrc6.h | 56 + extern/NSFplay/nes_vrc7.cpp | 189 ++++ extern/NSFplay/nes_vrc7.h | 53 + 32 files changed, 6550 insertions(+) create mode 100644 extern/NSFplay/legacy/2413tone.h create mode 100644 extern/NSFplay/legacy/281btone.h create mode 100644 extern/NSFplay/legacy/emu2149.c create mode 100644 extern/NSFplay/legacy/emu2149.h create mode 100644 extern/NSFplay/legacy/emu2212.c create mode 100644 extern/NSFplay/legacy/emu2212.h create mode 100644 extern/NSFplay/legacy/emu2413.c create mode 100644 extern/NSFplay/legacy/emu2413.h create mode 100644 extern/NSFplay/legacy/emutypes.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft35.h create mode 100644 extern/NSFplay/legacy/vrc7tone_ft36.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt1.h create mode 100644 extern/NSFplay/legacy/vrc7tone_kt2.h create mode 100644 extern/NSFplay/legacy/vrc7tone_mo.h create mode 100644 extern/NSFplay/legacy/vrc7tone_nuke.h create mode 100644 extern/NSFplay/legacy/vrc7tone_rw.h create mode 100644 extern/NSFplay/nes_apu.cpp create mode 100644 extern/NSFplay/nes_apu.h create mode 100644 extern/NSFplay/nes_dmc.cpp create mode 100644 extern/NSFplay/nes_dmc.h create mode 100644 extern/NSFplay/nes_fds.cpp create mode 100644 extern/NSFplay/nes_fds.h create mode 100644 extern/NSFplay/nes_fme7.cpp create mode 100644 extern/NSFplay/nes_fme7.h create mode 100644 extern/NSFplay/nes_mmc5.cpp create mode 100644 extern/NSFplay/nes_mmc5.h create mode 100644 extern/NSFplay/nes_n106.cpp create mode 100644 extern/NSFplay/nes_n106.h create mode 100644 extern/NSFplay/nes_vrc6.cpp create mode 100644 extern/NSFplay/nes_vrc6.h create mode 100644 extern/NSFplay/nes_vrc7.cpp create mode 100644 extern/NSFplay/nes_vrc7.h diff --git a/extern/NSFplay/legacy/2413tone.h b/extern/NSFplay/legacy/2413tone.h new file mode 100644 index 00000000..70397958 --- /dev/null +++ b/extern/NSFplay/legacy/2413tone.h @@ -0,0 +1,22 @@ +/* YM2413 tone by Mitsutaka Okazaki, 2020 */ +/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34 + */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original +0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin +0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar +0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano +0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute +0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet +0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe +0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet +0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ +0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn +0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer +0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord +0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone +0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass +0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass +0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar +0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7) +0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7) +0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7) diff --git a/extern/NSFplay/legacy/281btone.h b/extern/NSFplay/legacy/281btone.h new file mode 100644 index 00000000..bb0068ba --- /dev/null +++ b/extern/NSFplay/legacy/281btone.h @@ -0,0 +1,20 @@ +/* YMF281B tone by Chabin (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16, +0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23, +0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3, +0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, +0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07, +0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17, +0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28, +0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07, +0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11, +0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26, +0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22, +0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4, +0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff, +0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/emu2149.c b/extern/NSFplay/legacy/emu2149.c new file mode 100644 index 00000000..fa5142f7 --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.c @@ -0,0 +1,363 @@ +/**************************************************************************** + + emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001 + + 2001 04-28 : Version 1.00beta -- 1st Beta Release. + 2001 08-14 : Version 1.10 + 2001 10-03 : Version 1.11 -- Added PSG_set_quality(). + 2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close. + 2002 10-13 : Version 1.14 -- Fixed the envelope unit. + 2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask + 2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope + frequency register is written before key-on. + + References: + psg.vhd -- 2000 written by Kazuhiro Tsujikawa. + s_fme7.c -- 1999,2000 written by Mamiya (NEZplug). + ay8910.c -- 1998-2001 Author unknown (MAME). + MSX-Datapack -- 1991 ASCII Corp. + AY-3-8910 data sheet + +*****************************************************************************/ +#include +#include +#include +#include "emu2149.h" + +static e_uint32 voltbl[2][32] = { + {0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x07, 0x09, + 0x0B, 0x0D, 0x0F, 0x12, + 0x16, 0x1A, 0x1F, 0x25, 0x2D, 0x35, 0x3F, 0x4C, 0x5A, 0x6A, 0x7F, 0x97, + 0xB4, 0xD6, 0xEB, 0xFF}, + {0x00, 0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x05, 0x05, 0x07, 0x07, + 0x0B, 0x0B, 0x0F, 0x0F, + 0x16, 0x16, 0x1F, 0x1F, 0x2D, 0x2D, 0x3F, 0x3F, 0x5A, 0x5A, 0x7F, 0x7F, + 0xB4, 0xB4, 0xFF, 0xFF} +}; + +#define GETA_BITS 24 + +static void +internal_refresh (PSG * psg) +{ + if (psg->quality) + { + psg->base_incr = 1 << GETA_BITS; + psg->realstep = (e_uint32) ((1 << 31) / psg->rate); + psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16)); + psg->psgtime = 0; + } + else + { + psg->base_incr = + (e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate)); + } +} + +EMU2149_API void +PSG_set_rate (PSG * psg, e_uint32 r) +{ + psg->rate = r ? r : 44100; + internal_refresh (psg); +} + +EMU2149_API void +PSG_set_quality (PSG * psg, e_uint32 q) +{ + psg->quality = q; + internal_refresh (psg); +} + +EMU2149_API PSG * +PSG_new (e_uint32 c, e_uint32 r) +{ + PSG *psg; + + psg = (PSG *) malloc (sizeof (PSG)); + if (psg == NULL) + return NULL; + + PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT); + psg->clk = c; + psg->rate = r ? r : 44100; + PSG_set_quality (psg, 0); + + return psg; +} + +EMU2149_API void +PSG_setVolumeMode (PSG * psg, int type) +{ + switch (type) + { + case 1: + psg->voltbl = voltbl[EMU2149_VOL_YM2149]; + break; + case 2: + psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910]; + break; + default: + psg->voltbl = voltbl[EMU2149_VOL_DEFAULT]; + break; + } +} + +EMU2149_API e_uint32 +PSG_setMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask = mask; + } + return ret; +} + +EMU2149_API e_uint32 +PSG_toggleMask (PSG *psg, e_uint32 mask) +{ + e_uint32 ret = 0; + if(psg) + { + ret = psg->mask; + psg->mask ^= mask; + } + return ret; +} + +EMU2149_API void +PSG_reset (PSG * psg) +{ + int i; + + psg->base_count = 0; + + for (i = 0; i < 3; i++) + { + psg->cout[i] = 0; + psg->count[i] = 0x1000; + psg->freq[i] = 0; + psg->edge[i] = 0; + psg->volume[i] = 0; + } + + psg->mask = 0; + + for (i = 0; i < 16; i++) + psg->reg[i] = 0; + psg->adr = 0; + + psg->noise_seed = 0xffff; + psg->noise_count = 0x40; + psg->noise_freq = 0; + + psg->env_volume = 0; + psg->env_ptr = 0; + psg->env_freq = 0; + psg->env_count = 0; + psg->env_pause = 1; + + psg->out = 0; +} + +EMU2149_API void +PSG_delete (PSG * psg) +{ + free (psg); +} + +EMU2149_API e_uint8 +PSG_readIO (PSG * psg) +{ + return (e_uint8) (psg->reg[psg->adr]); +} + +EMU2149_API e_uint8 +PSG_readReg (PSG * psg, e_uint32 reg) +{ + return (e_uint8) (psg->reg[reg & 0x1f]); + +} + +EMU2149_API void +PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val) +{ + if (adr & 1) + PSG_writeReg (psg, psg->adr, val); + else + psg->adr = val & 0x1f; +} + +INLINE static e_int16 +calc (PSG * psg) +{ + + int i, noise; + e_uint32 incr; + e_int32 mix = 0; + + psg->base_count += psg->base_incr; + incr = (psg->base_count >> GETA_BITS); + psg->base_count &= (1 << GETA_BITS) - 1; + + /* Envelope */ + psg->env_count += incr; + while (psg->env_count>=0x10000 && psg->env_freq!=0) + { + if (!psg->env_pause) + { + if(psg->env_face) + psg->env_ptr = (psg->env_ptr + 1) & 0x3f ; + else + psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f; + } + + if (psg->env_ptr & 0x20) /* if carry or borrow */ + { + if (psg->env_continue) + { + if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1; + if (psg->env_hold) psg->env_pause = 1; + psg->env_ptr = psg->env_face?0:0x1f; + } + else + { + psg->env_pause = 1; + psg->env_ptr = 0; + } + } + + psg->env_count -= psg->env_freq; + } + + /* Noise */ + psg->noise_count += incr; + if (psg->noise_count & 0x40) + { + if (psg->noise_seed & 1) + psg->noise_seed ^= 0x24000; + psg->noise_seed >>= 1; + psg->noise_count -= psg->noise_freq; + } + noise = psg->noise_seed & 1; + + /* Tone */ + for (i = 0; i < 3; i++) + { + psg->count[i] += incr; + if (psg->count[i] & 0x1000) + { + if (psg->freq[i] > 1) + { + psg->edge[i] = !psg->edge[i]; + psg->count[i] -= psg->freq[i]; + } + else + { + psg->edge[i] = 1; + } + } + + psg->cout[i] = 0; // maintaining cout for stereo mix + + if (psg->mask&PSG_MASK_CH(i)) + continue; + + if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise)) + { + if (!(psg->volume[i] & 32)) + psg->cout[i] = psg->voltbl[psg->volume[i] & 31]; + else + psg->cout[i] = psg->voltbl[psg->env_ptr]; + + mix += psg->cout[i]; + } + } + + return (e_int16) mix; +} + +EMU2149_API e_int16 +PSG_calc (PSG * psg) +{ + if (!psg->quality) + return (e_int16) (calc (psg) << 4); + + /* Simple rate converter */ + while (psg->realstep > psg->psgtime) + { + psg->psgtime += psg->psgstep; + psg->out += calc (psg); + psg->out >>= 1; + } + + psg->psgtime = psg->psgtime - psg->realstep; + + return (e_int16) (psg->out << 4); +} + +EMU2149_API void +PSG_writeReg (PSG * psg, e_uint32 reg, e_uint32 val) +{ + int c; + + if (reg > 15) return; + + psg->reg[reg] = (e_uint8) (val & 0xff); + switch (reg) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + c = reg >> 1; + psg->freq[c] = ((psg->reg[c * 2 + 1] & 15) << 8) + psg->reg[c * 2]; + break; + + case 6: + psg->noise_freq = (val == 0) ? 1 : ((val & 31) << 1); + break; + + case 7: + psg->tmask[0] = (val & 1); + psg->tmask[1] = (val & 2); + psg->tmask[2] = (val & 4); + psg->nmask[0] = (val & 8); + psg->nmask[1] = (val & 16); + psg->nmask[2] = (val & 32); + break; + + case 8: + case 9: + case 10: + psg->volume[reg - 8] = val << 1; + + break; + + case 11: + case 12: + psg->env_freq = (psg->reg[12] << 8) + psg->reg[11]; + break; + + case 13: + psg->env_continue = (val >> 3) & 1; + psg->env_attack = (val >> 2) & 1; + psg->env_alternate = (val >> 1) & 1; + psg->env_hold = val & 1; + psg->env_face = psg->env_attack; + psg->env_pause = 0; + psg->env_count = 0x10000 - psg->env_freq; + psg->env_ptr = psg->env_face?0:0x1f; + break; + + case 14: + case 15: + default: + break; + } + + return; +} \ No newline at end of file diff --git a/extern/NSFplay/legacy/emu2149.h b/extern/NSFplay/legacy/emu2149.h new file mode 100644 index 00000000..a2fba126 --- /dev/null +++ b/extern/NSFplay/legacy/emu2149.h @@ -0,0 +1,94 @@ +/* emu2149.h */ +#ifndef _EMU2149_H_ +#define _EMU2149_H_ +#include "emutypes.h" + +#ifdef EMU2149_DLL_EXPORTS +#define EMU2149_API __declspec(dllexport) +#elif EMU2149_DLL_IMPORTS +#define EMU2149_API __declspec(dllimport) +#else +#define EMU2149_API +#endif + +#define EMU2149_VOL_DEFAULT 1 +#define EMU2149_VOL_YM2149 0 +#define EMU2149_VOL_AY_3_8910 1 + +#define PSG_MASK_CH(x) (1<<(x)) + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct __PSG + { + + /* Volume Table */ + e_uint32 *voltbl; + + e_uint8 reg[0x20]; + e_int32 out; + e_int32 cout[3]; + + e_uint32 clk, rate, base_incr, quality; + + e_uint32 count[3]; + e_uint32 volume[3]; + e_uint32 freq[3]; + e_uint32 edge[3]; + e_uint32 tmask[3]; + e_uint32 nmask[3]; + e_uint32 mask; + + e_uint32 base_count; + + e_uint32 env_volume; + e_uint32 env_ptr; + e_uint32 env_face; + + e_uint32 env_continue; + e_uint32 env_attack; + e_uint32 env_alternate; + e_uint32 env_hold; + e_uint32 env_pause; + e_uint32 env_reset; + + e_uint32 env_freq; + e_uint32 env_count; + + e_uint32 noise_seed; + e_uint32 noise_count; + e_uint32 noise_freq; + + /* rate converter */ + e_uint32 realstep; + e_uint32 psgtime; + e_uint32 psgstep; + + /* I/O Ctrl */ + e_uint32 adr; + + } + PSG; + + EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q); + EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r); + EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate); + EMU2149_API void PSG_reset (PSG *); + EMU2149_API void PSG_delete (PSG *); + EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val); + EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val); + EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg); + EMU2149_API e_uint8 PSG_readIO (PSG * psg); + EMU2149_API e_int16 PSG_calc (PSG *); + EMU2149_API void PSG_setVolumeMode (PSG * psg, int type); + EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask); + EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2212.c b/extern/NSFplay/legacy/emu2212.c new file mode 100644 index 00000000..88d93382 --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.c @@ -0,0 +1,485 @@ +/**************************************************************************** + + emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001 + + 2001 09-30 : Version 1.00 + 2001 10-03 : Version 1.01 -- Added SCC_set_quality(). + 2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type(). + Fixed SCC_write(). + 2002 02-17 : Version 1.11 -- Fixed SCC_write(). + 2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close. + 2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask() + 2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled. + + Registar map for SCC_writeReg() + + $00-1F : WaveTable CH.A + $20-3F : WaveTable CH.B + $40-5F : WaveTable CH.C + $60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+) + $80-9F : WaveTable CH.E + + $C0 : CH.A Freq(L) + $C1 : CH.A Freq(H) + $C2 : CH.B Freq(L) + $C3 : CH.B Freq(H) + $C4 : CH.C Freq(L) + $C5 : CH.C Freq(H) + $C6 : CH.D Freq(L) + $C7 : CH.D Freq(H) + $C8 : CH.E Freq(L) + $C9 : CH.E Freq(H) + + $D0 : CH.A Volume + $D1 : CH.B Volume + $D2 : CH.C Volume + $D3 : CH.D Volume + $D4 : CH.E Volume + + $E0 : Bit0 = 0:SCC, 1:SCC+ + $E1 : CH mask + $E2 : Extra Flags + +*****************************************************************************/ +#include +#include +#include +#include "emu2212.h" + +#define GETA_BITS 22 + +static void +internal_refresh (SCC * scc) +{ + if (scc->quality) + { + scc->base_incr = 2 << GETA_BITS; + scc->realstep = (e_uint32) ((1 << 31) / scc->rate); + scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2)); + scc->scctime = 0; + } + else + { + scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate); + } +} + +EMU2212_API e_uint32 +SCC_setMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask = mask; + } + return ret; +} + +EMU2212_API e_uint32 +SCC_toggleMask (SCC *scc, e_uint32 mask) +{ + e_uint32 ret = 0; + if(scc) + { + ret = scc->mask; + scc->mask ^= mask; + } + return ret; +} + +EMU2212_API void +SCC_set_quality (SCC * scc, e_uint32 q) +{ + scc->quality = q; + internal_refresh (scc); +} + +EMU2212_API void +SCC_set_rate (SCC * scc, e_uint32 r) +{ + scc->rate = r ? r : 44100; + internal_refresh (scc); +} + +EMU2212_API SCC * +SCC_new (e_uint32 c, e_uint32 r) +{ + SCC *scc; + + scc = (SCC *) malloc (sizeof (SCC)); + if (scc == NULL) + return NULL; + memset(scc, 0, sizeof (SCC)); + + scc->clk = c; + scc->rate = r ? r : 44100; + SCC_set_quality (scc, 0); + scc->type = SCC_ENHANCED; + return scc; +} + +EMU2212_API void +SCC_reset (SCC * scc) +{ + int i, j; + + if (scc == NULL) + return; + + scc->mode = 0; + scc->active = 0; + scc->base_adr = 0x9000; + + for (i = 0; i < 5; i++) + { + for (j = 0; j < 5; j++) + scc->wave[i][j] = 0; + scc->count[i] = 0; + scc->freq[i] = 0; + scc->phase[i] = 0; + scc->volume[i] = 0; + scc->offset[i] = 0; + scc->rotate[i] = 0; + } + + memset(scc->reg,0,0x100-0xC0); + + scc->mask = 0; + + scc->ch_enable = 0xff; + scc->ch_enable_next = 0xff; + + scc->cycle_4bit = 0; + scc->cycle_8bit = 0; + scc->refresh = 0; + + scc->out = 0; + scc->prev = 0; + scc->next = 0; + + return; +} + +EMU2212_API void +SCC_delete (SCC * scc) +{ + if (scc != NULL) + free (scc); +} + +INLINE static e_int16 +calc (SCC * scc) +{ + int i; + e_int32 mix = 0; + + for (i = 0; i < 5; i++) + { + scc->count[i] = (scc->count[i] + scc->incr[i]); + + if (scc->count[i] & (1 << (GETA_BITS + 5))) + { + scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1); + scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i]; + scc->ch_enable &= ~(1 << i); + scc->ch_enable |= scc->ch_enable_next & (1 << i); + } + + if (scc->ch_enable & (1 << i)) + { + scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F; + if(!(scc->mask&SCC_MASK_CH(i))) + mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4; + } + } + + return (e_int16) (mix << 4); +} + +EMU2212_API e_int16 +SCC_calc (SCC * scc) +{ + if (!scc->quality) + return calc (scc); + + while (scc->realstep > scc->scctime) + { + scc->scctime += scc->sccstep; + scc->prev = scc->next; + scc->next = calc (scc); + } + + scc->scctime -= scc->realstep; + scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep); + + return (e_int16) (scc->out); +} + +EMU2212_API e_uint32 +SCC_readReg (SCC * scc, e_uint32 adr) +{ + if (adr < 0xA0) + return scc->wave[adr >> 5][adr & 0x1f]; + else if( 0xC0 < adr && adr < 0xF0 ) + return scc->reg[adr-0xC0]; + else + return 0; +} + +EMU2212_API void +SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val) +{ + int ch; + e_uint32 freq; + + adr &= 0xFF; + + if (adr < 0xA0) + { + ch = (adr & 0xF0) >> 5; + if (!scc->rotate[ch]) + { + scc->wave[ch][adr & 0x1F] = (e_int8) val; + if (scc->mode == 0 && ch == 3) + scc->wave[4][adr & 0x1F] = (e_int8) val; + } + } + else if (0xC0 <= adr && adr <= 0xC9) + { + scc->reg[adr-0xC0] = val; + ch = (adr & 0x0F) >> 1; + if (adr & 1) + scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF); + else + scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF); + + if (scc->refresh) + scc->count[ch] = 0; + freq = scc->freq[ch]; + if (scc->cycle_8bit) + freq &= 0xFF; + if (scc->cycle_4bit) + freq >>= 8; + if (freq <= 8) + scc->incr[ch] = 0; + else + scc->incr[ch] = scc->base_incr / (freq + 1); + } + else if (0xD0 <= adr && adr <= 0xD4) + { + scc->reg[adr-0xC0] = val; + scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF); + } + else if (adr == 0xE0) + { + scc->reg[adr-0xC0] = val; + scc->mode = (e_uint8) val & 1; + } + else if (adr == 0xE1) + { + scc->reg[adr-0xC0] = val; + scc->ch_enable_next = (e_uint8) val & 0x1F; + } + else if (adr == 0xE2) + { + scc->reg[adr-0xC0] = val; + scc->cycle_4bit = val & 1; + scc->cycle_8bit = val & 2; + scc->refresh = val & 32; + if (val & 64) + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0x1F; + else + for (ch = 0; ch < 5; ch++) + scc->rotate[ch] = 0; + if (val & 128) + scc->rotate[3] = scc->rotate[4] = 0x1F; + } + + return; +} + +INLINE static void +write_standard (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0x80) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0x8A) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0x80, val); + } + else if (adr < 0x8F) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0x8A, val); + } + else if (adr == 0x8F) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xE0 <= adr) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static void +write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val) +{ + adr &= 0xFF; + + if (adr < 0xA0) /* wave */ + { + SCC_writeReg (scc, adr, val); + } + else if (adr < 0xAA) /* freq */ + { + SCC_writeReg (scc, adr + 0xC0 - 0xA0, val); + } + else if (adr < 0xAF) /* volume */ + { + SCC_writeReg (scc, adr + 0xD0 - 0xAA, val); + } + else if (adr == 0xAF) /* ch enable */ + { + SCC_writeReg (scc, 0xE1, val); + } + else if (0xC0 <= adr && adr <= 0xDF) /* flags */ + { + SCC_writeReg (scc, 0xE2, val); + } +} + +INLINE static e_uint32 +read_enhanced (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if (adr < 0xA0) + return SCC_readReg (scc, adr); + else if (adr < 0xAA) + return SCC_readReg (scc, adr + 0xC0 - 0xA0); + else if (adr < 0xAF) + return SCC_readReg (scc, adr + 0xD0 - 0xAA); + else if (adr == 0xAF) + return SCC_readReg (scc, 0xE1); + else if (0xC0 <= adr && adr <= 0xDF) + return SCC_readReg (scc, 0xE2); + else + return 0; +} + +INLINE static e_uint32 +read_standard (SCC * scc, e_uint32 adr) +{ + adr &= 0xFF; + if(adr<0x80) + return SCC_readReg (scc, adr); + else if (0xA0<=adr&&adr<=0xBF) + return SCC_readReg (scc, 0x80+(adr&0x1F)); + else if (adr < 0x8A) + return SCC_readReg (scc, adr + 0xC0 - 0x80); + else if (adr < 0x8F) + return SCC_readReg (scc, adr + 0xD0 - 0x8A); + else if (adr == 0x8F) + return SCC_readReg (scc, 0xE1); + else if (0xE0 <= adr) + return SCC_readReg (scc, 0xE2); + else return 0; +} + +EMU2212_API e_uint32 +SCC_read (SCC * scc, e_uint32 adr) +{ + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + return (scc->base_adr>>8)&0x20; + + if( adr < scc->base_adr ) return 0; + adr -= scc->base_adr; + + if( adr == 0 ) + { + if(scc->mode) return 0x80; else return 0x3F; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + return read_standard (scc, adr); + break; + case SCC_ENHANCED: + if(!scc->mode) + return read_standard (scc, adr); + else + return read_enhanced (scc, adr); + break; + default: + break; + } + + return 0; +} + +EMU2212_API void +SCC_write (SCC * scc, e_uint32 adr, e_uint32 val) +{ + val = val & 0xFF; + + if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE ) + { + scc->base_adr = 0x9000 | ((val&0x20)<<8); + return; + } + + if( adr < scc->base_adr ) return; + adr -= scc->base_adr; + + if(adr == 0) + { + if( val == 0x3F ) + { + scc->mode = 0; + scc->active = 1; + } + else if( val&0x80 && scc->type == SCC_ENHANCED) + { + scc->mode = 1; + scc->active = 1; + } + else + { + scc->mode = 0; + scc->active = 0; + } + return; + } + + if(!scc->active||adr<0x800||0x8FFtype) + { + case SCC_STANDARD: + write_standard (scc, adr, val); + break; + case SCC_ENHANCED: + if(scc->mode) + write_enhanced (scc, adr, val); + else + write_standard (scc, adr, val); + default: + break; + } + + return; +} + +EMU2212_API void +SCC_set_type (SCC * scc, e_uint32 type) +{ + scc->type = type; +} diff --git a/extern/NSFplay/legacy/emu2212.h b/extern/NSFplay/legacy/emu2212.h new file mode 100644 index 00000000..b79e0c36 --- /dev/null +++ b/extern/NSFplay/legacy/emu2212.h @@ -0,0 +1,77 @@ +#ifndef _EMU2212_H_ +#define _EMU2212_H_ + +#ifdef EMU2212_DLL_EXPORTS + #define EMU2212_API __declspec(dllexport) +#elif defined(EMU2212_DLL_IMPORTS) + #define EMU2212_API __declspec(dllimport) +#else + #define EMU2212_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "emutypes.h" + +#define SCC_STANDARD 0 +#define SCC_ENHANCED 1 + +#define SCC_MASK_CH(x) (1<<(x)) + +typedef struct __SCC { + + e_uint32 clk, rate ,base_incr, quality ; + + e_int32 out, prev, next; + e_uint32 type ; + e_uint32 mode ; + e_uint32 active; + e_uint32 base_adr; + e_uint32 mask ; + + e_uint32 realstep ; + e_uint32 scctime ; + e_uint32 sccstep ; + + e_uint32 incr[5] ; + + e_int8 wave[5][32] ; + + e_uint32 count[5] ; + e_uint32 freq[5] ; + e_uint32 phase[5] ; + e_uint32 volume[5] ; + e_uint32 offset[5] ; + e_uint8 reg[0x100-0xC0]; + + int ch_enable ; + int ch_enable_next ; + + int cycle_4bit ; + int cycle_8bit ; + int refresh ; + int rotate[5] ; + +} SCC ; + + +EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ; +EMU2212_API void SCC_reset(SCC *scc) ; +EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r); +EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ; +EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ; +EMU2212_API void SCC_delete(SCC *scc) ; +EMU2212_API e_int16 SCC_calc(SCC *scc) ; +EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ; +EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ; +EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emu2413.c b/extern/NSFplay/legacy/emu2413.c new file mode 100644 index 00000000..2ced98f8 --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.c @@ -0,0 +1,1461 @@ +/** + * emu2413 v1.5.2 + * https://github.com/digital-sound-antiques/emu2413 + * Copyright (C) 2020 Mitsutaka Okazaki + * + * This source refers to the following documents. The author would like to thank all the authors who have + * contributed to the writing of them. + * - [YM2413 notes](http://www.smspower.org/Development/YM2413) by andete + * - ymf262.c by Jarek Burczynski + * - [VRC7 presets](https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#opll_vrc7_patch_format) by Nuke.YKT + * - YMF281B presets by Chabin + */ +#include "emu2413.h" +#include +#include +#include +#include + +#ifndef INLINE +#if defined(_MSC_VER) +#define INLINE __inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#else +#define INLINE inline +#endif +#endif + +#define _PI_ 3.14159265358979323846264338327950288 + +#define OPLL_TONE_NUM 9 +static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { + { +#include "vrc7tone_nuke.h" + }, + { +#include "vrc7tone_rw.h" + }, + { +#include "vrc7tone_ft36.h" + }, + { +#include "vrc7tone_ft35.h" + }, + { +#include "vrc7tone_mo.h" + }, + { +#include "vrc7tone_kt2.h" + }, + { +#include "vrc7tone_kt1.h" + }, + { +#include "2413tone.h" + }, + { +#include "281btone.h" + }, +}; + +/* phase increment counter */ +#define DP_BITS 19 +#define DP_WIDTH (1 << DP_BITS) +#define DP_BASE_BITS (DP_BITS - PG_BITS) + +/* dynamic range of envelope output */ +#define EG_STEP 0.375 +#define EG_BITS 7 +#define EG_MUTE ((1 << EG_BITS) - 1) +#define EG_MAX (EG_MUTE - 3) + +/* dynamic range of total level */ +#define TL_STEP 0.75 +#define TL_BITS 6 + +/* dynamic range of sustine level */ +#define SL_STEP 3.0 +#define SL_BITS 4 + +/* damper speed before key-on. key-scale affects. */ +#define DAMPER_RATE 12 + +#define TL2EG(d) ((d) << 1) + +/* sine table */ +#define PG_BITS 10 /* 2^10 = 1024 length sine table */ +#define PG_WIDTH (1 << PG_BITS) + +/* clang-format off */ +/* exp_table[x] = round((exp2((double)x / 256.0) - 1) * 1024) */ +static uint16_t exp_table[256] = { +0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, +45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, +93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, +142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, +194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, +248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, +304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, +363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, +424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, +488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, +555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, +625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, +698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, +774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, +854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, +937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 +}; +/* fullsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */ +static uint16_t fullsin_table[PG_WIDTH] = { +2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, +846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, +598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, +453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, +352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, +276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, +215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, +167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, +127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, +94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, +67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, +46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, +29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, +16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, +7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, +2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, +}; +/* clang-format on */ + +static uint16_t halfsin_table[PG_WIDTH]; +static uint16_t *wave_table_map[2] = {fullsin_table, halfsin_table}; + +/* pitch modulator */ +/* offset to fnum, rough approximation of 14 cents depth. */ +static int8_t pm_table[8][8] = { + {0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxxx + {0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxxx + {0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxxx + {0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxxx + {0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxxx + {0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxxx + {0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxxx + {0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxxx +}; + +/* amplitude lfo table */ +/* The following envelop pattern is verified on real YM2413. */ +/* each element repeates 64 cycles */ +static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, // + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // + 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, // + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, // + 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 13, 13, 13, // + 12, 12, 12, 12, 12, 12, 12, 12, // + 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, // + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, // + 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, // + 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, // + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, // + 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}; + +/* envelope decay increment step table */ +/* based on andete's research */ +static uint8_t eg_step_tables[4][8] = { + {0, 1, 0, 1, 0, 1, 0, 1}, + {0, 1, 0, 1, 1, 1, 0, 1}, + {0, 1, 1, 1, 0, 1, 1, 1}, + {0, 1, 1, 1, 1, 1, 1, 1}, +}; + +enum __OPLL_EG_STATE { ATTACK, DECAY, SUSTAIN, RELEASE, DAMP, UNKNOWN }; + +static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, + 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2}; + +#define dB2(x) ((x)*2) +static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125), + dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500), + dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)}; + +static uint32_t tll_table[8 * 16][1 << TL_BITS][4]; +static int32_t rks_table[8 * 2][2]; + +static OPLL_PATCH null_patch = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +static OPLL_PATCH default_patch[OPLL_TONE_NUM][(16 + 3) * 2]; + +#define min(i, j) (((i) < (j)) ? (i) : (j)) +#define max(i, j) (((i) > (j)) ? (i) : (j)) + +/*************************************************** + + Internal Sample Rate Converter + +****************************************************/ +/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */ + +/* + * LW is truncate length of sinc(x) calculation. + * Lower LW is faster, higher LW results better quality. + * LW must be a non-zero positive even number, no upper limit. + * LW=16 or greater is recommended when upsampling. + * LW=8 is practically okay for downsampling. + */ +#define LW 16 + +/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */ +#define SINC_RESO 256 +#define SINC_AMP_BITS 12 + +// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); } +static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); } +static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); } +static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); } + +/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */ +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch) { + OPLL_RateConv *conv = malloc(sizeof(OPLL_RateConv)); + int i; + + conv->ch = ch; + conv->f_ratio = f_inp / f_out; + conv->buf = malloc(sizeof(void *) * ch); + for (i = 0; i < ch; i++) { + conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW); + } + + /* create sinc_table for positive 0 <= x < LW/2 */ + conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2); + for (i = 0; i < SINC_RESO * LW / 2; i++) { + const double x = (double)i / SINC_RESO; + if (f_out < f_inp) { + /* for downsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio); + } else { + /* for upsampling */ + conv->sinc_table[i] = (int16_t)((1 << SINC_AMP_BITS) * windowed_sinc(x)); + } + } + + return conv; +} + +static INLINE int16_t lookup_sinc_table(int16_t *table, double x) { + int16_t index = (int16_t)(x * SINC_RESO); + if (index < 0) + index = -index; + return table[min(SINC_RESO * LW / 2 - 1, index)]; +} + +void OPLL_RateConv_reset(OPLL_RateConv *conv) { + int i; + conv->timer = 0; + for (i = 0; i < conv->ch; i++) { + memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW); + } +} + +/* put original data to this converter at f_inp. */ +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data) { + int16_t *buf = conv->buf[ch]; + int i; + for (i = 0; i < LW - 1; i++) { + buf[i] = buf[i + 1]; + } + buf[LW - 1] = data; +} + +/* get resampled data from this converter at f_out. */ +/* this function must be called f_out / f_inp times per one putData call. */ +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch) { + int16_t *buf = conv->buf[ch]; + int32_t sum = 0; + int k; + double dn; + conv->timer += conv->f_ratio; + dn = conv->timer - floor(conv->timer); + conv->timer = dn; + + for (k = 0; k < LW; k++) { + double x = ((double)k - (LW / 2 - 1)) - dn; + sum += buf[k] * lookup_sinc_table(conv->sinc_table, x); + } + return sum >> SINC_AMP_BITS; +} + +void OPLL_RateConv_delete(OPLL_RateConv *conv) { + int i; + for (i = 0; i < conv->ch; i++) { + free(conv->buf[i]); + } + free(conv->buf); + free(conv->sinc_table); + free(conv); +} + +/*************************************************** + + Create tables + +****************************************************/ + +static void makeSinTable(void) { + int x; + + for (x = 0; x < PG_WIDTH / 4; x++) { + fullsin_table[PG_WIDTH / 4 + x] = fullsin_table[PG_WIDTH / 4 - x - 1]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) { + fullsin_table[PG_WIDTH / 2 + x] = 0x8000 | fullsin_table[x]; + } + + for (x = 0; x < PG_WIDTH / 2; x++) + halfsin_table[x] = fullsin_table[x]; + + for (x = PG_WIDTH / 2; x < PG_WIDTH; x++) + halfsin_table[x] = 0xfff; +} + +static void makeTllTable(void) { + + int32_t tmp; + int32_t fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) { + for (block = 0; block < 8; block++) { + for (TL = 0; TL < 64; TL++) { + for (KL = 0; KL < 4; KL++) { + if (KL == 0) { + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + } else { + tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL); + else + tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } + } + } + } +} + +static void makeRksTable(void) { + int fnum8, block; + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) { + rks_table[(block << 1) | fnum8][1] = (block << 1) + fnum8; + rks_table[(block << 1) | fnum8][0] = block >> 1; + } +} + +static void makeDefaultPatch() { + int i, j; + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + OPLL_getDefaultPatch(i, j, &default_patch[i][j * 2]); +} + +static uint8_t table_initialized = 0; + +static void initializeTables() { + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + table_initialized = 1; +} + +/********************************************************* + + Synthesizing + +*********************************************************/ +#define SLOT_BD1 12 +#define SLOT_BD2 13 +#define SLOT_HH 14 +#define SLOT_SD 15 +#define SLOT_TOM 16 +#define SLOT_CYM 17 + +/* utility macros */ +#define MOD(o, x) (&(o)->slot[(x) << 1]) +#define CAR(o, x) (&(o)->slot[((x) << 1) | 1]) +#define BIT(s, b) (((s) >> (b)) & 1) + +#if OPLL_DEBUG +static void _debug_print_patch(OPLL_SLOT *slot) { + OPLL_PATCH *p = slot->patch; + printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, // + p->AM, p->PM, p->EG, p->KR, p->ML, // + p->KL, p->TL, p->WS, p->FB, // + p->AR, p->DR, p->SL, p->RR); +} + +static char *_debug_eg_state_name(OPLL_SLOT *slot) { + switch (slot->eg_state) { + case ATTACK: + return "attack"; + case DECAY: + return "decay"; + case SUSTAIN: + return "sustain"; + case RELEASE: + return "release"; + case DAMP: + return "damp"; + default: + return "unknown"; + } +} + +static INLINE void _debug_print_slot_info(OPLL_SLOT *slot) { + char *name = _debug_eg_state_name(slot); + printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h, + slot->eg_rate_l); + _debug_print_patch(slot); + fflush(stdout); +} +#endif + +static INLINE int get_parameter_rate(OPLL_SLOT *slot) { + + if ((slot->type & 1) == 0 && slot->key_flag == 0) { + return 0; + } + + switch (slot->eg_state) { + case ATTACK: + return slot->patch->AR; + case DECAY: + return slot->patch->DR; + case SUSTAIN: + return slot->patch->EG ? 0 : slot->patch->RR; + case RELEASE: + if (slot->sus_flag) { + return 5; + } else if (slot->patch->EG) { + return slot->patch->RR; + } else { + return 7; + } + case DAMP: + return DAMPER_RATE; + default: + return 0; + } +} + +enum SLOT_UPDATE_FLAG { + UPDATE_WS = 1, + UPDATE_TLL = 2, + UPDATE_RKS = 4, + UPDATE_EG = 8, + UPDATE_ALL = 255, +}; + +static INLINE void request_update(OPLL_SLOT *slot, int flag) { slot->update_requests |= flag; } + +static void commit_slot_update(OPLL_SLOT *slot) { + +#if OPLL_DEBUG + if (slot->last_eg_state != slot->eg_state) { + _debug_print_slot_info(slot); + slot->last_eg_state = slot->eg_state; + } +#endif + + if (slot->update_requests & UPDATE_WS) { + slot->wave_table = wave_table_map[slot->patch->WS]; + } + + if (slot->update_requests & UPDATE_TLL) { + if ((slot->type & 1) == 0) { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->patch->TL][slot->patch->KL]; + } else { + slot->tll = tll_table[slot->blk_fnum >> 5][slot->volume][slot->patch->KL]; + } + } + + if (slot->update_requests & UPDATE_RKS) { + slot->rks = rks_table[slot->blk_fnum >> 8][slot->patch->KR]; + } + + if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) { + int p_rate = get_parameter_rate(slot); + + if (p_rate == 0) { + slot->eg_shift = 0; + slot->eg_rate_h = 0; + slot->eg_rate_l = 0; + return; + } + + slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2)); + slot->eg_rate_l = slot->rks & 3; + if (slot->eg_state == ATTACK) { + slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (13 - slot->eg_rate_h) : 0; + } else { + slot->eg_shift = (slot->eg_rate_h < 13) ? (13 - slot->eg_rate_h) : 0; + } + } + + slot->update_requests = 0; +} + +static void reset_slot(OPLL_SLOT *slot, int number) { + slot->number = number; + slot->type = number % 2; + slot->pg_keep = 0; + slot->wave_table = wave_table_map[0]; + slot->pg_phase = 0; + slot->output[0] = 0; + slot->output[1] = 0; + slot->eg_state = RELEASE; + slot->eg_shift = 0; + slot->rks = 0; + slot->tll = 0; + slot->key_flag = 0; + slot->sus_flag = 0; + slot->blk_fnum = 0; + slot->blk = 0; + slot->fnum = 0; + slot->volume = 0; + slot->pg_out = 0; + slot->eg_out = EG_MUTE; + slot->patch = &null_patch; +} + +static INLINE void slotOn(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 1; + slot->eg_state = DAMP; + request_update(slot, UPDATE_EG); +} + +static INLINE void slotOff(OPLL *opll, int i) { + OPLL_SLOT *slot = &opll->slot[i]; + slot->key_flag = 0; + if (slot->type & 1) { + slot->eg_state = RELEASE; + request_update(slot, UPDATE_EG); + } +} + +static INLINE void update_key_status(OPLL *opll) { + const uint8_t r14 = opll->reg[0x0e]; + const uint8_t rhythm_mode = BIT(r14, 5); + uint32_t new_slot_key_status = 0; + uint32_t updated_status; + int ch; + + for (ch = 0; ch < 9; ch++) + if (opll->reg[0x20 + ch] & 0x10) + new_slot_key_status |= 3 << (ch * 2); + + if (rhythm_mode) { + if (r14 & 0x10) + new_slot_key_status |= 3 << SLOT_BD1; + + if (r14 & 0x01) + new_slot_key_status |= 1 << SLOT_HH; + + if (r14 & 0x08) + new_slot_key_status |= 1 << SLOT_SD; + + if (r14 & 0x04) + new_slot_key_status |= 1 << SLOT_TOM; + + if (r14 & 0x02) + new_slot_key_status |= 1 << SLOT_CYM; + } + + updated_status = opll->slot_key_status ^ new_slot_key_status; + + if (updated_status) { + int i; + for (i = 0; i < 18; i++) + if (BIT(updated_status, i)) { + if (BIT(new_slot_key_status, i)) { + slotOn(opll, i); + } else { + slotOff(opll, i); + } + } + } + + opll->slot_key_status = new_slot_key_status; +} + +static INLINE void set_patch(OPLL *opll, int32_t ch, int32_t num) { + opll->patch_number[ch] = num; + MOD(opll, ch)->patch = &opll->patch[num * 2 + 0]; + CAR(opll, ch)->patch = &opll->patch[num * 2 + 1]; + request_update(MOD(opll, ch), UPDATE_ALL); + request_update(CAR(opll, ch), UPDATE_ALL); +} + +static INLINE void set_sus_flag(OPLL *opll, int ch, int flag) { + CAR(opll, ch)->sus_flag = flag; + request_update(CAR(opll, ch), UPDATE_EG); + if (MOD(opll, ch)->type & 1) { + MOD(opll, ch)->sus_flag = flag; + request_update(MOD(opll, ch), UPDATE_EG); + } +} + +/* set volume ( volume : 6bit, register value << 2 ) */ +static INLINE void set_volume(OPLL *opll, int ch, int volume) { + CAR(opll, ch)->volume = volume; + request_update(CAR(opll, ch), UPDATE_TLL); +} + +static INLINE void set_slot_volume(OPLL_SLOT *slot, int volume) { + slot->volume = volume; + request_update(slot, UPDATE_TLL); +} + +/* set f-Nnmber ( fnum : 9bit ) */ +static INLINE void set_fnumber(OPLL *opll, int ch, int fnum) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->fnum = fnum; + car->blk_fnum = (car->blk_fnum & 0xe00) | (fnum & 0x1ff); + mod->fnum = fnum; + mod->blk_fnum = (mod->blk_fnum & 0xe00) | (fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +/* set block data (blk : 3bit ) */ +static INLINE void set_block(OPLL *opll, int ch, int blk) { + OPLL_SLOT *car = CAR(opll, ch); + OPLL_SLOT *mod = MOD(opll, ch); + car->blk = blk; + car->blk_fnum = ((blk & 7) << 9) | (car->blk_fnum & 0x1ff); + mod->blk = blk; + mod->blk_fnum = ((blk & 7) << 9) | (mod->blk_fnum & 0x1ff); + request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); + request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL); +} + +static INLINE void update_rhythm_mode(OPLL *opll) { + const uint8_t new_rhythm_mode = (opll->reg[0x0e] >> 5) & 1; + + if (opll->rhythm_mode != new_rhythm_mode) { + + if (new_rhythm_mode) { + opll->slot[SLOT_HH].type = 3; + opll->slot[SLOT_HH].pg_keep = 1; + opll->slot[SLOT_SD].type = 3; + opll->slot[SLOT_TOM].type = 3; + opll->slot[SLOT_CYM].type = 3; + opll->slot[SLOT_CYM].pg_keep = 1; + set_patch(opll, 6, 16); + set_patch(opll, 7, 17); + set_patch(opll, 8, 18); + set_slot_volume(&opll->slot[SLOT_HH], ((opll->reg[0x37] >> 4) & 15) << 2); + set_slot_volume(&opll->slot[SLOT_TOM], ((opll->reg[0x38] >> 4) & 15) << 2); + } else { + opll->slot[SLOT_HH].type = 0; + opll->slot[SLOT_HH].pg_keep = 0; + opll->slot[SLOT_SD].type = 1; + opll->slot[SLOT_TOM].type = 0; + opll->slot[SLOT_CYM].type = 1; + opll->slot[SLOT_CYM].pg_keep = 0; + set_patch(opll, 6, opll->reg[0x36] >> 4); + set_patch(opll, 7, opll->reg[0x37] >> 4); + set_patch(opll, 8, opll->reg[0x38] >> 4); + } + } + + opll->rhythm_mode = new_rhythm_mode; +} + +static void update_ampm(OPLL *opll) { + if (opll->test_flag & 2) { + opll->pm_phase = 0; + opll->am_phase = 0; + } else { + opll->pm_phase += (opll->test_flag & 8) ? 1024 : 1; + opll->am_phase += (opll->test_flag & 8) ? 64 : 1; + } + opll->lfo_am = am_table[(opll->am_phase >> 6) % sizeof(am_table)]; +} + +static void update_noise(OPLL *opll, int cycle) { + int i; + for (i = 0; i < cycle; i++) { + if (opll->noise & 1) { + opll->noise ^= 0x800200; + } + opll->noise >>= 1; + } +} + +static void update_short_noise(OPLL *opll) { + const uint32_t pg_hh = opll->slot[SLOT_HH].pg_out; + const uint32_t pg_cym = opll->slot[SLOT_CYM].pg_out; + + const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8); + const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3); + const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7); + + const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7); + const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5); + + opll->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5); +} + +static INLINE void calc_phase(OPLL_SLOT *slot, int32_t pm_phase, uint8_t reset) { + const int8_t pm = slot->patch->PM ? pm_table[(slot->fnum >> 6) & 7][(pm_phase >> 10) & 7] : 0; + if (reset) { + slot->pg_phase = 0; + } + slot->pg_phase += (((slot->fnum & 0x1ff) * 2 + pm) * ml_table[slot->patch->ML]) << slot->blk >> 2; + slot->pg_phase &= (DP_WIDTH - 1); + slot->pg_out = slot->pg_phase >> DP_BASE_BITS; +} + +static INLINE uint8_t lookup_attack_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 12: + index = (counter & 0xc) >> 1; + return 4 - eg_step_tables[slot->eg_rate_l][index]; + case 13: + index = (counter & 0xc) >> 1; + return 3 - eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = (counter & 0xc) >> 1; + return 2 - eg_step_tables[slot->eg_rate_l][index]; + case 0: + case 15: + return 0; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7] ? 4 : 0; + } +} + +static INLINE uint8_t lookup_decay_step(OPLL_SLOT *slot, uint32_t counter) { + int index; + + switch (slot->eg_rate_h) { + case 0: + return 0; + case 13: + index = ((counter & 0xc) >> 1) | (counter & 1); + return eg_step_tables[slot->eg_rate_l][index]; + case 14: + index = ((counter & 0xc) >> 1); + return eg_step_tables[slot->eg_rate_l][index] + 1; + case 15: + return 2; + default: + index = counter >> slot->eg_shift; + return eg_step_tables[slot->eg_rate_l][index & 7]; + } +} + +static INLINE void start_envelope(OPLL_SLOT *slot) { + if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) { + slot->eg_state = DECAY; + slot->eg_out = 0; + } else { + slot->eg_state = ATTACK; + slot->eg_out = EG_MUTE; + } + request_update(slot, UPDATE_EG); +} + +static INLINE void calc_envelope(OPLL_SLOT *slot, OPLL_SLOT *buddy, uint16_t eg_counter, uint8_t test) { + + uint32_t mask = (1 << slot->eg_shift) - 1; + uint8_t s; + + if (slot->eg_state == ATTACK) { + if (0 < slot->eg_out && 0 < slot->eg_rate_h && (eg_counter & mask & ~3) == 0) { + s = lookup_attack_step(slot, eg_counter); + if (0 < s) { + slot->eg_out = max(0, ((int)slot->eg_out - (slot->eg_out >> s) - 1)); + } + } + } else { + if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) { + slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter)); + } + } + + switch (slot->eg_state) { + case DAMP: + if (slot->eg_out >= EG_MUTE) { + start_envelope(slot); + if (slot->type & 1) { + if (!slot->pg_keep) { + slot->pg_phase = 0; + } + if (buddy && !buddy->pg_keep) { + buddy->pg_phase = 0; + } + } + } + break; + + case ATTACK: + if (slot->eg_out == 0) { + slot->eg_state = DECAY; + request_update(slot, UPDATE_EG); + } + break; + + case DECAY: + if ((slot->eg_out >> 3) == slot->patch->SL) { + slot->eg_state = SUSTAIN; + request_update(slot, UPDATE_EG); + } + break; + + case SUSTAIN: + case RELEASE: + default: + break; + } + + if (test) { + slot->eg_out = 0; + } +} + +static void update_slots(OPLL *opll) { + int i; + opll->eg_counter++; + + for (i = 0; i < 18; i++) { + OPLL_SLOT *slot = &opll->slot[i]; + OPLL_SLOT *buddy = NULL; + if (slot->type == 0) { + buddy = &opll->slot[i + 1]; + } + if (slot->type == 1) { + buddy = &opll->slot[i - 1]; + } + if (slot->update_requests) { + commit_slot_update(slot); + } + calc_envelope(slot, buddy, opll->eg_counter, opll->test_flag & 1); + calc_phase(slot, opll->pm_phase, opll->test_flag & 4); + } +} + +/* output: -4095...4095 */ +static INLINE int16_t lookup_exp_table(uint16_t i) { + /* from andete's expressoin */ + int16_t t = (exp_table[(i & 0xff) ^ 0xff] + 1024); + int16_t res = t >> ((i & 0x7f00) >> 8); + return ((i & 0x8000) ? ~res : res) << 1; +} + +static INLINE int16_t to_linear(uint16_t h, OPLL_SLOT *slot, int16_t am) { + uint16_t att; + if (slot->eg_out >= EG_MAX) + return 0; + + att = min(EG_MAX, (slot->eg_out + slot->tll + am)) << 4; + return lookup_exp_table(h + att); +} + +static INLINE int16_t calc_slot_car(OPLL *opll, int ch, int16_t fm) { + OPLL_SLOT *slot = CAR(opll, ch); + + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + 2 * (fm >> 1)) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_mod(OPLL *opll, int ch) { + OPLL_SLOT *slot = MOD(opll, ch); + + int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0; + uint8_t am = slot->patch->AM ? opll->lfo_am : 0; + + slot->output[1] = slot->output[0]; + slot->output[0] = to_linear(slot->wave_table[(slot->pg_out + fm) & (PG_WIDTH - 1)], slot, am); + + return slot->output[0]; +} + +static INLINE int16_t calc_slot_tom(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 8); + + return to_linear(slot->wave_table[slot->pg_out], slot, 0); +} + +/* Specify phase offset directly based on 10-bit (1024-length) sine table */ +#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10))) + +static INLINE int16_t calc_slot_snare(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 7); + + uint32_t phase; + + if (BIT(slot->pg_out, PG_BITS - 2)) + phase = (opll->noise & 1) ? _PD(0x300) : _PD(0x200); + else + phase = (opll->noise & 1) ? _PD(0x0) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_cym(OPLL *opll) { + OPLL_SLOT *slot = CAR(opll, 8); + + uint32_t phase = opll->short_noise ? _PD(0x300) : _PD(0x100); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +static INLINE int16_t calc_slot_hat(OPLL *opll) { + OPLL_SLOT *slot = MOD(opll, 7); + + uint32_t phase; + + if (opll->short_noise) + phase = (opll->noise & 1) ? _PD(0x2d0) : _PD(0x234); + else + phase = (opll->noise & 1) ? _PD(0x34) : _PD(0xd0); + + return to_linear(slot->wave_table[phase], slot, 0); +} + +#define _MO(x) (-(x) >> 1) +#define _RO(x) (x) + +static void update_output(OPLL *opll) { + int16_t *out; + int i; + + update_ampm(opll); + update_short_noise(opll); + update_slots(opll); + + out = opll->ch_out; + + /* CH1-6 */ + for (i = 0; i < 6; i++) { + if (!(opll->mask & OPLL_MASK_CH(i))) { + out[i] = _MO(calc_slot_car(opll, i, calc_slot_mod(opll, i))); + } + } + + /* CH7 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(6))) { + out[6] = _MO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } else { + if (!(opll->mask & OPLL_MASK_BD)) { + out[9] = _RO(calc_slot_car(opll, 6, calc_slot_mod(opll, 6))); + } + } + update_noise(opll, 14); + + /* CH8 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(7))) { + out[7] = _MO(calc_slot_car(opll, 7, calc_slot_mod(opll, 7))); + } + } else { + if (!(opll->mask & OPLL_MASK_HH)) { + out[10] = _RO(calc_slot_hat(opll)); + } + if (!(opll->mask & OPLL_MASK_SD)) { + out[11] = _RO(calc_slot_snare(opll)); + } + } + update_noise(opll, 2); + + /* CH9 */ + if (!opll->rhythm_mode) { + if (!(opll->mask & OPLL_MASK_CH(8))) { + out[8] = _MO(calc_slot_car(opll, 8, calc_slot_mod(opll, 8))); + } + } else { + if (!(opll->mask & OPLL_MASK_TOM)) { + out[12] = _RO(calc_slot_tom(opll)); + } + if (!(opll->mask & OPLL_MASK_CYM)) { + out[13] = _RO(calc_slot_cym(opll)); + } + } + update_noise(opll, 2); +} + +INLINE static void mix_output(OPLL *opll) { + int16_t out = 0; + int i; + for (i = 0; i < 14; i++) { + out += opll->ch_out[i]; + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out); + } else { + opll->mix_out[0] = out; + } +} + +INLINE static void mix_output_stereo(OPLL *opll) { + int16_t *out = opll->mix_out; + int i; + out[0] = out[1] = 0; + for (i = 0; i < 14; i++) { + if (opll->pan[i] & 2) + out[0] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][0]); + if (opll->pan[i] & 1) + out[1] += (int16_t)(opll->ch_out[i] * opll->pan_fine[i][1]); + } + if (opll->conv) { + OPLL_RateConv_putData(opll->conv, 0, out[0]); + OPLL_RateConv_putData(opll->conv, 1, out[1]); + } +} + +/*********************************************************** + + External Interfaces + +***********************************************************/ + +OPLL *OPLL_new(uint32_t clk, uint32_t rate) { + OPLL *opll; + int i; + + if (!table_initialized) { + initializeTables(); + } + + opll = (OPLL *)calloc(sizeof(OPLL), 1); + if (opll == NULL) + return NULL; + + for (i = 0; i < 19 * 2; i++) + memcpy(&opll->patch[i], &null_patch, sizeof(OPLL_PATCH)); + + opll->clk = clk; + opll->rate = rate; + opll->mask = 0; + opll->conv = NULL; + opll->mix_out[0] = 0; + opll->mix_out[1] = 0; + + OPLL_reset(opll); + OPLL_setChipType(opll, 0); + OPLL_resetPatch(opll, 0); + return opll; +} + +void OPLL_delete(OPLL *opll) { + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + free(opll); +} + +static void reset_rate_conversion_params(OPLL *opll) { + const double f_out = opll->rate; + const double f_inp = opll->clk / 72; + + opll->out_time = 0; + opll->out_step = ((uint32_t)f_inp) << 8; + opll->inp_step = ((uint32_t)f_out) << 8; + + if (opll->conv) { + OPLL_RateConv_delete(opll->conv); + opll->conv = NULL; + } + + if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) { + opll->conv = OPLL_RateConv_new(f_inp, f_out, 2); + } + + if (opll->conv) { + OPLL_RateConv_reset(opll->conv); + } +} + +void OPLL_reset(OPLL *opll) { + int i; + + if (!opll) + return; + + opll->adr = 0; + + opll->pm_phase = 0; + opll->am_phase = 0; + + opll->noise = 0x1; + opll->mask = 0; + + opll->rhythm_mode = 0; + opll->slot_key_status = 0; + opll->eg_counter = 0; + + reset_rate_conversion_params(opll); + + for (i = 0; i < 18; i++) + reset_slot(&opll->slot[i], i); + + for (i = 0; i < 9; i++) { + set_patch(opll, i, 0); + } + + for (i = 0; i < 0x40; i++) + OPLL_writeReg(opll, i, 0); + + for (i = 0; i < 15; i++) { + opll->pan[i] = 3; + opll->pan_fine[i][1] = opll->pan_fine[i][0] = 1.0f; + } + + for (i = 0; i < 14; i++) { + opll->ch_out[i] = 0; + } +} + +void OPLL_forceRefresh(OPLL *opll) { + int i; + + if (opll == NULL) + return; + + for (i = 0; i < 9; i++) { + set_patch(opll, i, opll->patch_number[i]); + } + + for (i = 0; i < 18; i++) { + request_update(&opll->slot[i], UPDATE_ALL); + } +} + +void OPLL_setRate(OPLL *opll, uint32_t rate) { + opll->rate = rate; + reset_rate_conversion_params(opll); +} + +void OPLL_setQuality(OPLL *opll, uint8_t q) {} + +void OPLL_setChipType(OPLL *opll, uint8_t type) { opll->chip_type = type; } + +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t data) { + int ch, i; + + if (reg >= 0x40) + return; + + /* mirror registers */ + if ((0x19 <= reg && reg <= 0x1f) || (0x29 <= reg && reg <= 0x2f) || (0x39 <= reg && reg <= 0x3f)) { + reg -= 9; + } + + opll->reg[reg] = (uint8_t)data; + + switch (reg) { + case 0x00: + opll->patch[0].AM = (data >> 7) & 1; + opll->patch[0].PM = (data >> 6) & 1; + opll->patch[0].EG = (data >> 5) & 1; + opll->patch[0].KR = (data >> 4) & 1; + opll->patch[0].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x01: + opll->patch[1].AM = (data >> 7) & 1; + opll->patch[1].PM = (data >> 6) & 1; + opll->patch[1].EG = (data >> 5) & 1; + opll->patch[1].KR = (data >> 4) & 1; + opll->patch[1].ML = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_RKS | UPDATE_EG); + } + } + break; + + case 0x02: + opll->patch[0].KL = (data >> 6) & 3; + opll->patch[0].TL = (data)&63; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_TLL); + } + } + break; + + case 0x03: + opll->patch[1].KL = (data >> 6) & 3; + opll->patch[1].WS = (data >> 4) & 1; + opll->patch[0].WS = (data >> 3) & 1; + opll->patch[0].FB = (data)&7; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_WS); + request_update(CAR(opll, i), UPDATE_WS | UPDATE_TLL); + } + } + break; + + case 0x04: + opll->patch[0].AR = (data >> 4) & 15; + opll->patch[0].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x05: + opll->patch[1].AR = (data >> 4) & 15; + opll->patch[1].DR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x06: + opll->patch[0].SL = (data >> 4) & 15; + opll->patch[0].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(MOD(opll, i), UPDATE_EG); + } + } + break; + + case 0x07: + opll->patch[1].SL = (data >> 4) & 15; + opll->patch[1].RR = (data)&15; + for (i = 0; i < 9; i++) { + if (opll->patch_number[i] == 0) { + request_update(CAR(opll, i), UPDATE_EG); + } + } + break; + + case 0x0e: + if (opll->chip_type == 1) + break; + update_rhythm_mode(opll); + update_key_status(opll); + break; + + case 0x0f: + opll->test_flag = data; + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = reg - 0x10; + set_fnumber(opll, ch, data + ((opll->reg[0x20 + ch] & 1) << 8)); + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + ch = reg - 0x20; + set_fnumber(opll, ch, ((data & 1) << 8) + opll->reg[0x10 + ch]); + set_block(opll, ch, (data >> 1) & 7); + set_sus_flag(opll, ch, (data >> 5) & 1); + update_key_status(opll); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + if ((opll->reg[0x0e] & 32) && (reg >= 0x36)) { + switch (reg) { + case 0x37: + set_slot_volume(MOD(opll, 7), ((data >> 4) & 15) << 2); + break; + case 0x38: + set_slot_volume(MOD(opll, 8), ((data >> 4) & 15) << 2); + break; + default: + break; + } + } else { + set_patch(opll, reg - 0x30, (data >> 4) & 15); + } + set_volume(opll, reg - 0x30, (data & 15) << 2); + break; + + default: + break; + } +} + +void OPLL_writeIO(OPLL *opll, uint32_t adr, uint8_t val) { + if (adr & 1) + OPLL_writeReg(opll, opll->adr, val); + else + opll->adr = val; +} + +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan) { opll->pan[ch & 15] = pan; } + +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]) { + opll->pan_fine[ch & 15][0] = pan[0]; + opll->pan_fine[ch & 15][1] = pan[1]; +} + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch) { + patch[0].AM = (dump[0] >> 7) & 1; + patch[1].AM = (dump[1] >> 7) & 1; + patch[0].PM = (dump[0] >> 6) & 1; + patch[1].PM = (dump[1] >> 6) & 1; + patch[0].EG = (dump[0] >> 5) & 1; + patch[1].EG = (dump[1] >> 5) & 1; + patch[0].KR = (dump[0] >> 4) & 1; + patch[1].KR = (dump[1] >> 4) & 1; + patch[0].ML = (dump[0]) & 15; + patch[1].ML = (dump[1]) & 15; + patch[0].KL = (dump[2] >> 6) & 3; + patch[1].KL = (dump[3] >> 6) & 3; + patch[0].TL = (dump[2]) & 63; + patch[1].TL = 0; + patch[0].FB = (dump[3]) & 7; + patch[1].FB = 0; + patch[0].WS = (dump[3] >> 3) & 1; + patch[1].WS = (dump[3] >> 4) & 1; + patch[0].AR = (dump[4] >> 4) & 15; + patch[1].AR = (dump[5] >> 4) & 15; + patch[0].DR = (dump[4]) & 15; + patch[1].DR = (dump[5]) & 15; + patch[0].SL = (dump[6] >> 4) & 15; + patch[1].SL = (dump[7] >> 4) & 15; + patch[0].RR = (dump[6]) & 15; + patch[1].RR = (dump[7]) & 15; +} + +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *patch) { + OPLL_dump2patch(default_inst[type] + num * 8, patch); +} + +void OPLL_setPatch(OPLL *opll, const uint8_t *dump) { + OPLL_PATCH patch[2]; + int i; + for (i = 0; i < 19; i++) { + OPLL_dump2patch(dump + i * 8, patch); + memcpy(&opll->patch[i * 2 + 0], &patch[0], sizeof(OPLL_PATCH)); + memcpy(&opll->patch[i * 2 + 1], &patch[1], sizeof(OPLL_PATCH)); + } +} + +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump) { + dump[0] = (uint8_t)((patch[0].AM << 7) + (patch[0].PM << 6) + (patch[0].EG << 5) + (patch[0].KR << 4) + patch[0].ML); + dump[1] = (uint8_t)((patch[1].AM << 7) + (patch[1].PM << 6) + (patch[1].EG << 5) + (patch[1].KR << 4) + patch[1].ML); + dump[2] = (uint8_t)((patch[0].KL << 6) + patch[0].TL); + dump[3] = (uint8_t)((patch[1].KL << 6) + (patch[1].WS << 4) + (patch[0].WS << 3) + patch[0].FB); + dump[4] = (uint8_t)((patch[0].AR << 4) + patch[0].DR); + dump[5] = (uint8_t)((patch[1].AR << 4) + patch[1].DR); + dump[6] = (uint8_t)((patch[0].SL << 4) + patch[0].RR); + dump[7] = (uint8_t)((patch[1].SL << 4) + patch[1].RR); +} + +void OPLL_copyPatch(OPLL *opll, int32_t num, OPLL_PATCH *patch) { + memcpy(&opll->patch[num], patch, sizeof(OPLL_PATCH)); +} + +void OPLL_resetPatch(OPLL *opll, uint8_t type) { + int i; + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch(opll, i, &default_patch[type % OPLL_TONE_NUM][i]); +} + +int16_t OPLL_calc(OPLL *opll) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + opll->mix_out[0] = OPLL_RateConv_getData(opll->conv, 0); + } + return opll->mix_out[0]; +} + +void OPLL_calcStereo(OPLL *opll, int32_t out[2]) { + while (opll->out_step > opll->out_time) { + opll->out_time += opll->inp_step; + update_output(opll); + mix_output_stereo(opll); + } + opll->out_time -= opll->out_step; + if (opll->conv) { + out[0] = OPLL_RateConv_getData(opll->conv, 0); + out[1] = OPLL_RateConv_getData(opll->conv, 1); + } else { + out[0] = opll->mix_out[0]; + out[1] = opll->mix_out[1]; + } +} + +uint32_t OPLL_setMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask = mask; + return ret; + } else + return 0; +} + +uint32_t OPLL_toggleMask(OPLL *opll, uint32_t mask) { + uint32_t ret; + + if (opll) { + ret = opll->mask; + opll->mask ^= mask; + return ret; + } else + return 0; +} diff --git a/extern/NSFplay/legacy/emu2413.h b/extern/NSFplay/legacy/emu2413.h new file mode 100644 index 00000000..2804f3fa --- /dev/null +++ b/extern/NSFplay/legacy/emu2413.h @@ -0,0 +1,247 @@ +#ifndef _EMU2413_H_ +#define _EMU2413_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPLL_DEBUG 0 + +enum OPLL_TONE_ENUM { + OPLL_VRC7_TONE = 0, + OPLL_VRC7_RW_TONE, + OPLL_VRC7_FT36_TONE, + OPLL_VRC7_FT35_TONE, + OPLL_VRC7_MO_TONE, + OPLL_VRC7_KT2_TONE, + OPLL_VRC7_KT1_TONE, + OPLL_2413_TONE, + OPLL_281B_TONE +}; + +/* voice data */ +typedef struct __OPLL_PATCH { + uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS; +} OPLL_PATCH; + +/* slot */ +typedef struct __OPLL_SLOT { + uint8_t number; + + /* type flags: + * 000000SM + * |+-- M: 0:modulator 1:carrier + * +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym) + */ + uint8_t type; + + OPLL_PATCH *patch; /* voice parameter */ + + /* slot output */ + int32_t output[2]; /* output value, latest and previous. */ + + /* phase generator (pg) */ + uint16_t *wave_table; /* wave table */ + uint32_t pg_phase; /* pg phase */ + uint32_t pg_out; /* pg output, as index of wave table */ + uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */ + uint16_t blk_fnum; /* (block << 9) | f-number */ + uint16_t fnum; /* f-number (9 bits) */ + uint8_t blk; /* block (3 bits) */ + + /* envelope generator (eg) */ + uint8_t eg_state; /* current state */ + int32_t volume; /* current volume */ + uint8_t key_flag; /* key-on flag 1:on 0:off */ + uint8_t sus_flag; /* key-sus option 1:on 0:off */ + uint16_t tll; /* total level + key scale level*/ + uint8_t rks; /* key scale offset (rks) for eg speed */ + uint8_t eg_rate_h; /* eg speed rate high 4bits */ + uint8_t eg_rate_l; /* eg speed rate low 2bits */ + uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */ + uint32_t eg_out; /* eg output */ + + uint32_t update_requests; /* flags to debounce update */ + +#if OPLL_DEBUG + uint8_t last_eg_state; +#endif +} OPLL_SLOT; + +/* mask */ +#define OPLL_MASK_CH(x) (1 << (x)) +#define OPLL_MASK_HH (1 << (9)) +#define OPLL_MASK_CYM (1 << (10)) +#define OPLL_MASK_TOM (1 << (11)) +#define OPLL_MASK_SD (1 << (12)) +#define OPLL_MASK_BD (1 << (13)) +#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD) + +/* rate conveter */ +typedef struct __OPLL_RateConv { + int ch; + double timer; + double f_ratio; + int16_t *sinc_table; + int16_t **buf; +} OPLL_RateConv; + +OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch); +void OPLL_RateConv_reset(OPLL_RateConv *conv); +void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data); +int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch); +void OPLL_RateConv_delete(OPLL_RateConv *conv); + +typedef struct __OPLL { + uint32_t clk; + uint32_t rate; + + uint8_t chip_type; + + uint32_t adr; + + uint32_t inp_step; + uint32_t out_step; + uint32_t out_time; + + uint8_t reg[0x40]; + uint8_t test_flag; + uint32_t slot_key_status; + uint8_t rhythm_mode; + + uint32_t eg_counter; + + uint32_t pm_phase; + int32_t am_phase; + + uint8_t lfo_am; + + uint32_t noise; + uint8_t short_noise; + + int32_t patch_number[9]; + OPLL_SLOT slot[18]; + OPLL_PATCH patch[19 * 2]; + + uint8_t pan[16]; + float pan_fine[16][2]; + + uint32_t mask; + + /* channel output */ + /* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */ + int16_t ch_out[14]; + + int16_t mix_out[2]; + + OPLL_RateConv *conv; +} OPLL; + +OPLL *OPLL_new(uint32_t clk, uint32_t rate); +void OPLL_delete(OPLL *); + +void OPLL_reset(OPLL *); +void OPLL_resetPatch(OPLL *, uint8_t); + +/** + * Set output wave sampling rate. + * @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is + * disabled. + */ +void OPLL_setRate(OPLL *opll, uint32_t rate); + +/** + * Set internal calcuration quality. Currently no effects, just for compatibility. + * >= v1.0.0 always synthesizes internal output at clock/72 Hz. + */ +void OPLL_setQuality(OPLL *opll, uint8_t q); + +/** + * Set pan pot (extra function - not YM2413 chip feature) + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan 0:mute 1:right 2:left 3:center + * ``` + * pan: 76543210 + * |+- bit 1: enable Left output + * +-- bit 0: enable Right output + * ``` + */ +void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan); + +/** + * Set fine-grained panning + * @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved + * @param pan output strength of left/right channel. + * pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center. + */ +void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]); + +/** + * Set chip type. If vrc7 is selected, r#14 is ignored. + * This method not change the current ROM patch set. + * To change ROM patch set, use OPLL_resetPatch. + * @param type 0:YM2413 1:VRC7 + */ +void OPLL_setChipType(OPLL *opll, uint8_t type); + +void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val); +void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val); + +/** + * Calculate one sample + */ +int16_t OPLL_calc(OPLL *opll); + +/** + * Calulate stereo sample + */ +void OPLL_calcStereo(OPLL *opll, int32_t out[2]); + +void OPLL_setPatch(OPLL *, const uint8_t *dump); +void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *); + +/** + * Force to refresh. + * External program should call this function after updating patch parameters. + */ +void OPLL_forceRefresh(OPLL *); + +void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch); +void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump); +void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *); + +/** + * Set channel mask + * @param mask mask flag: OPLL_MASK_* can be used. + * - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i)) + * - bit 9: mask for Hi-Hat (OPLL_MASK_HH) + * - bit 10: mask for Top-Cym (OPLL_MASK_CYM) + * - bit 11: mask for Tom (OPLL_MASK_TOM) + * - bit 12: mask for Snare Drum (OPLL_MASK_SD) + * - bit 13: mask for Bass Drum (OPLL_MASK_BD) + */ +uint32_t OPLL_setMask(OPLL *, uint32_t mask); + +/** + * Toggler channel mask flag + */ +uint32_t OPLL_toggleMask(OPLL *, uint32_t mask); + +/* for compatibility */ +#define OPLL_set_rate OPLL_setRate +#define OPLL_set_quality OPLL_setQuality +#define OPLL_set_pan OPLL_setPan +#define OPLL_set_pan_fine OPLL_setPanFine +#define OPLL_calc_stereo OPLL_calcStereo +#define OPLL_reset_patch OPLL_resetPatch +#define OPLL_dump2patch OPLL_dumpToPatch +#define OPLL_patch2dump OPLL_patchToDump +#define OPLL_setChipMode OPLL_setChipType + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/extern/NSFplay/legacy/emutypes.h b/extern/NSFplay/legacy/emutypes.h new file mode 100644 index 00000000..bf5d7e1b --- /dev/null +++ b/extern/NSFplay/legacy/emutypes.h @@ -0,0 +1,41 @@ +#ifndef _EMUTYPES_H_ +#define _EMUTYPES_H_ + +#if defined(_MSC_VER) +#define INLINE __forceinline +#elif defined(__GNUC__) +#define INLINE __inline__ +#elif defined(_MWERKS_) +#define INLINE inline +#else +#define INLINE +#endif + +#if defined(EMU_DLL_IMPORTS) +#define EMU2149_DLL_IMPORTS +#define EMU2212_DLL_IMPORTS +#define EMU2413_DLL_IMPORTS +#define EMU8950_DLL_IMPORTS +#define EMU76489_DLL_IMPORTS +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int e_uint; +typedef signed int e_int; + +typedef unsigned char e_uint8 ; +typedef signed char e_int8 ; + +typedef unsigned short e_uint16 ; +typedef signed short e_int16 ; + +typedef unsigned int e_uint32 ; +typedef signed int e_int32 ; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/extern/NSFplay/legacy/vrc7tone_ft35.h b/extern/NSFplay/legacy/vrc7tone_ft35.h new file mode 100644 index 00000000..7af08bcf --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft35.h @@ -0,0 +1,20 @@ +// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001) +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01, +0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13, +0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32, +0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28, +0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28, +0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8, +0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17, +0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47, +0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8, +0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8, +0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14, +0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07, +0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58, +0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07, +0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84, +0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8, +0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8, +0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_ft36.h b/extern/NSFplay/legacy/vrc7tone_ft36.h new file mode 100644 index 00000000..f693955e --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_ft36.h @@ -0,0 +1,21 @@ +// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6 +// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17, +0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12, +0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02, +0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95, +0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15, +0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11, +0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12, +0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4, +0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16, +0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51, +0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt1.h b/extern/NSFplay/legacy/vrc7tone_kt1.h new file mode 100644 index 00000000..59cff8a3 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt1.h @@ -0,0 +1,21 @@ +// patch set 1 by kevtris (11/14/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4, +0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5, +0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95, +0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95, +0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94, +0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95, +0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10, +0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35, +0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4, +0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5, +0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03, +0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95, +0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75, +0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15, +0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_kt2.h b/extern/NSFplay/legacy/vrc7tone_kt2.h new file mode 100644 index 00000000..92caa42b --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_kt2.h @@ -0,0 +1,21 @@ +// patch set 2 by kevtris (11/15/1999) +// http://kevtris.org/nes/vrcvii.txt +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7, +0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C, +0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B, +0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8, +0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8, +0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE, +0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B, +0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD, +0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D, +0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF, +0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE, +0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC, +0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8, +0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/legacy/vrc7tone_mo.h b/extern/NSFplay/legacy/vrc7tone_mo.h new file mode 100644 index 00000000..3b216f87 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_mo.h @@ -0,0 +1,20 @@ +/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01, +0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13, +0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32, +0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28, +0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28, +0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8, +0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17, +0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47, +0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8, +0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8, +0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14, +0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07, +0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58, +0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07, +0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84, +0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, +0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_nuke.h b/extern/NSFplay/legacy/vrc7tone_nuke.h new file mode 100644 index 00000000..0afcc4ce --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_nuke.h @@ -0,0 +1,21 @@ +// patch set by Nuke.YKT (3/15/2019) +// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27, +0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12, +0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12, +0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27, +0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28, +0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07, +0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17, +0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01, +0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02, +0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12, +0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6, +0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06, +0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D, +0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68, +0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55, diff --git a/extern/NSFplay/legacy/vrc7tone_rw.h b/extern/NSFplay/legacy/vrc7tone_rw.h new file mode 100644 index 00000000..bf226120 --- /dev/null +++ b/extern/NSFplay/legacy/vrc7tone_rw.h @@ -0,0 +1,21 @@ +// patch set by rainwarrior (8/01/2012) +// http://forums.nesdev.com/viewtopic.php?f=6&t=9141 +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27, +0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12, +0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02, +0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27, +0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28, +0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5, +0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07, +0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17, +0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11, +0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02, +0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12, +0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16, +0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52, +0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06, +0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/extern/NSFplay/nes_apu.cpp b/extern/NSFplay/nes_apu.cpp new file mode 100644 index 00000000..9d50dfc7 --- /dev/null +++ b/extern/NSFplay/nes_apu.cpp @@ -0,0 +1,400 @@ +// +// NES 2A03 +// +#include +#include "nes_apu.h" + +namespace xgm +{ + void NES_APU::sweep_sqr (int i) + { + int shifted = freq[i] >> sweep_amount[i]; + if (i == 0 && sweep_mode[i]) shifted += 1; + sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted); + //DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]); + } + + void NES_APU::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence(%d)\n",s); + + if (s > 3) return; // no operation in step 4 + + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // 120hz clock + if ((s&1) == 0) + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + + if (sweep_enable[i]) + { + //DEBUG_OUT("Clock sweep: %d\n", i); + + --sweep_div[i]; + if (sweep_div[i] <= 0) + { + sweep_sqr(i); // calculate new sweep target + + //DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]); + //DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]); + + if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate + { + freq[i] = sfreq[i] < 0 ? 0 : sfreq[i]; + } + sweep_div[i] = sweep_div_period[i] + 1; + + //DEBUG_OUT("freq[%d]=%d\n",i,freq[i]); + } + + if (sweep_write[i]) + { + sweep_div[i] = sweep_div_period[i] + 1; + sweep_write[i] = false; + } + } + } + + } + + INT32 NES_APU::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] -= clocks; + while (scounter[i] < 0) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] += freq[i] + 1; + } + + INT32 ret = 0; + if (length_counter[i] > 0 && + freq[i] >= 8 && + sfreq[i] < 0x800 + ) + { + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (0x4000 <= adr && adr < 0x4008) + { + val |= reg[adr&0x7]; + return true; + } + else if(adr==0x4015) + { + val |= (length_counter[1]?2:0)|(length_counter[0]?1:0); + return true; + } + else + return false; + } + + void NES_APU::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + } + + // 生成される波形の振幅は0-8191 + UINT32 NES_APU::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + + INT32 m[2]; + + if(option[OPT_NONLINEAR_MIXER]) + { + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + } + else + { + m[0] = (out[0] * square_linear) / 15; + m[1] = (out[1] * square_linear) / 15; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] >>= 7; + + return 2; + } + + NES_APU::NES_APU () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_UNMUTE_ON_RESET] = true; + option[OPT_PHASE_REFRESH] = true; + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_DUTY_SWAP] = false; + option[OPT_NEGATE_SWEEP_INIT] = false; + + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear + + for(int c=0;c<2;++c) + for(int t=0;t<2;++t) + sm[c][t] = 128; + } + + NES_APU::~NES_APU () + { + } + + void NES_APU::Reset () + { + int i; + gclock = 0; + mask = 0; + + for (int i=0; i<2; ++i) + { + scounter[i] = 0; + sphase[i] = 0; + duty[i] = 0; + volume[i] = 0; + freq[i] = 0; + sfreq[i] = 0; + sweep_enable[i] = 0; + sweep_mode[i] = 0; + sweep_write[i] = 0; + sweep_div_period[i] = 0; + sweep_div[i] = 1; + sweep_amount[i] = 0; + envelope_disable[i] = 0; + envelope_loop[i] = 0; + envelope_write[i] = 0; + envelope_div_period[i] = 0; + envelope_div[0] = 0; + envelope_counter[i] = 0; + length_counter[i] = 0; + enable[i] = 0; + } + + for (i = 0x4000; i < 0x4008; i++) + Write (i, 0); + + Write (0x4015, 0); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + if (option[OPT_NEGATE_SWEEP_INIT]) + { + Write (0x4001, 0x08); + Write (0x4005, 0x08); + } + + for (i = 0; i < 2; i++) + out[i] = 0; + + SetRate(rate); + } + + void NES_APU::SetOption (int id, int val) + { + if(id 1) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_APU::GetTrackInfo(int trk) + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0); + trkinfo[trk].key = + enable[trk] && + length_counter[trk] > 0 && + freq[trk] >= 8 && + sfreq[trk] < 0x800 && + (envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0)); + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].max_volume = 15; + return &trkinfo[trk]; + } + + bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if (0x4000 <= adr && adr < 0x4008) + { + //DEBUG_OUT("$%04X = %02X\n",adr,val); + + adr &= 0xf; + ch = adr >> 2; + switch (adr) + { + case 0x0: + case 0x4: + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + if (option[OPT_DUTY_SWAP]) + { + if (duty[ch] == 1) duty[ch] = 2; + else if (duty[ch] == 2) duty[ch] = 1; + } + break; + + case 0x1: + case 0x5: + sweep_enable[ch] = (val >> 7) & 1; + sweep_div_period[ch] = (((val >> 4) & 7)); + sweep_mode[ch] = (val >> 3) & 1; + sweep_amount[ch] = val & 7; + sweep_write[ch] = true; + sweep_sqr(ch); + break; + + case 0x2: + case 0x6: + freq[ch] = val | (freq[ch] & 0x700) ; + sweep_sqr(ch); + break; + + case 0x3: + case 0x7: + freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ; + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + sweep_sqr(ch); + break; + + default: + return false; + } + reg[adr] = val; + return true; + } + else if (adr == 0x4015) + { + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + + reg[adr-0x4000] = val; + return true; + } + + // 4017 is handled in nes_dmc.cpp + //else if (adr == 0x4017) + //{ + //} + + return false; + } +} // namespace xgm; diff --git a/extern/NSFplay/nes_apu.h b/extern/NSFplay/nes_apu.h new file mode 100644 index 00000000..2a8e7541 --- /dev/null +++ b/extern/NSFplay/nes_apu.h @@ -0,0 +1,88 @@ +#ifndef _NES_APU_H_ +#define _NES_APU_H_ +#include "../device.h" +#include "nes_dmc.h" + +namespace xgm +{ + /** Upper half of APU **/ + class NES_APU : public ISoundChip + { + public: + enum + { + OPT_UNMUTE_ON_RESET=0, + OPT_PHASE_REFRESH, + OPT_NONLINEAR_MIXER, + OPT_DUTY_SWAP, + OPT_NEGATE_SWEEP_INIT, + OPT_END }; + + enum + { SQR0_MASK = 1, SQR1_MASK = 2, }; + + protected: + int option[OPT_END]; // 各種オプション + int mask; + INT32 sm[2][2]; + + UINT32 gclock; + UINT8 reg[0x20]; + INT32 out[2]; + double rate, clock; + + INT32 square_table[32]; // nonlinear mixer + INT32 square_linear; // linear mix approximation + + int scounter[2]; // frequency divider + int sphase[2]; // phase counter + + int duty[2]; + int volume[2]; + int freq[2]; + int sfreq[2]; + + bool sweep_enable[2]; + bool sweep_mode[2]; + bool sweep_write[2]; + int sweep_div_period[2]; + int sweep_div[2]; + int sweep_amount[2]; + + bool envelope_disable[2]; + bool envelope_loop[2]; + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + bool enable[2]; + + void sweep_sqr (int ch); // calculates target sweep frequency + INT32 calc_sqr (int ch, UINT32 clocks); + TrackInfoBasic trkinfo[2]; + + public: + NES_APU (); + ~NES_APU (); + + void FrameSequence(int s); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetRate (double rate); + virtual void SetClock (double clock); + virtual void SetOption (int id, int b); + virtual void SetMask(int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_dmc.cpp b/extern/NSFplay/nes_dmc.cpp new file mode 100644 index 00000000..8073a22c --- /dev/null +++ b/extern/NSFplay/nes_dmc.cpp @@ -0,0 +1,771 @@ +#include "nes_dmc.h" +#include "nes_apu.h" +#include + +namespace xgm +{ + const UINT32 NES_DMC::wavlen_table[2][16] = { + { // NTSC + 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 + }, + { // PAL + 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 + }}; + + const UINT32 NES_DMC::freq_table[2][16] = { + { // NTSC + 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 + }, + { // PAL + 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 + }}; + + const UINT32 BITREVERSE[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, + }; + + NES_DMC::NES_DMC () : GETA_BITS (20) + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + SetPal (false); + option[OPT_ENABLE_4011] = 1; + option[OPT_ENABLE_PNOISE] = 1; + option[OPT_UNMUTE_ON_RESET] = 1; + option[OPT_DPCM_ANTI_CLICK] = 0; + option[OPT_NONLINEAR_MIXER] = 1; + option[OPT_RANDOMIZE_NOISE] = 1; + option[OPT_RANDOMIZE_TRI] = 1; + option[OPT_TRI_MUTE] = 1; + option[OPT_DPCM_REVERSE] = 0; + tnd_table[0][0][0][0] = 0; + tnd_table[1][0][0][0] = 0; + + apu = NULL; + frame_sequence_count = 0; + frame_sequence_length = 7458; + frame_sequence_steps = 4; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + + NES_DMC::~NES_DMC () + { + } + + void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_DMC::GetTrackInfo(int trk) + { + switch(trk) + { + case 0: + trkinfo[trk].max_volume = 255; + trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]); + trkinfo[0].volume = 0; + trkinfo[0]._freq = tri_freq; + if(trkinfo[0]._freq) + trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1); + else + trkinfo[0].freq = 0; + trkinfo[0].tone = -1; + trkinfo[0].output = out[0]; + break; + case 1: + trkinfo[1].max_volume = 15; + trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0); + trkinfo[1].key = length_counter[1]>0 && enable[1] && + (envelope_disable ? (noise_volume>0) : (envelope_counter>0)); + trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF; + trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1)); + trkinfo[1].tone = noise_tap & (1<<6); + trkinfo[1].output = out[1]; + break; + case 2: + trkinfo[2].max_volume = 127; + trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F; + trkinfo[2].key = dlength > 0; + trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF; + trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]); + trkinfo[2].tone = (0xc000|(adr_reg<<6)); + trkinfo[2].output = (damp<<1)|dac_lsb; + break; + default: + return NULL; + } + return &trkinfo[trk]; + } + + void NES_DMC::FrameSequence(int s) + { + //DEBUG_OUT("FrameSequence: %d\n",s); + + if (s > 3) return; // no operation in step 4 + + if (apu) + { + apu->FrameSequence(s); + } + + if (s == 0 && (frame_sequence_steps == 4)) + { + if (frame_irq_enable) frame_irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable); + } + + // 240hz clock + { + // triangle linear counter + if (linear_counter_halt) + { + linear_counter = linear_counter_reload; + } + else + { + if (linear_counter > 0) --linear_counter; + } + if (!linear_counter_control) + { + linear_counter_halt = false; + } + + // noise envelope + bool divider = false; + if (envelope_write) + { + envelope_write = false; + envelope_counter = 15; + envelope_div = 0; + } + else + { + ++envelope_div; + if (envelope_div > envelope_div_period) + { + divider = true; + envelope_div = 0; + } + } + if (divider) + { + if (envelope_loop && envelope_counter == 0) + envelope_counter = 15; + else if (envelope_counter > 0) + --envelope_counter; + } + } + + // 120hz clock + if ((s&1) == 0) + { + // triangle length counter + if (!linear_counter_control && (length_counter[0] > 0)) + --length_counter[0]; + + // noise length counter + if (!envelope_loop && (length_counter[1] > 0)) + --length_counter[1]; + } + + } + + // 三角波チャンネルの計算 戻り値は0-15 + UINT32 NES_DMC::calc_tri (UINT32 clocks) + { + static UINT32 tritbl[32] = + { + 15,14,13,12,11,10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9,10,11,12,13,14,15, + }; + + if (linear_counter > 0 && length_counter[0] > 0 + && (!option[OPT_TRI_MUTE] || tri_freq > 0)) + { + counter[0] -= clocks; + while (counter[0] < 0) + { + tphase = (tphase + 1) & 31; + counter[0] += (tri_freq + 1); + } + } + + UINT32 ret = tritbl[tphase]; + return ret; + } + + // ノイズチャンネルの計算 戻り値は0-127 + // 低サンプリングレートで合成するとエイリアスノイズが激しいので + // ノイズだけはこの関数内で高クロック合成し、簡易なサンプリングレート + // 変換を行っている。 + UINT32 NES_DMC::calc_noise(UINT32 clocks) + { + UINT32 env = envelope_disable ? noise_volume : envelope_counter; + if (length_counter[1] < 1) env = 0; + + UINT32 last = (noise & 0x4000) ? 0 : env; + if (clocks < 1) return last; + + // simple anti-aliasing (noise requires it, even when oversampling is off) + UINT32 count = 0; + UINT32 accum = counter[1] * last; // samples pending from previous calc + UINT32 accum_clocks = counter[1]; + #ifdef _DEBUG + INT32 start_clocks = counter[1]; + #endif + if (counter[1] < 0) // only happens on startup when using the randomize noise option + { + accum = 0; + accum_clocks = 0; + } + + counter[1] -= clocks; + assert (nfreq > 0); // prevent infinite loop + while (counter[1] < 0) + { + // tick the noise generator + UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); + noise = (noise>>1) | (feedback<<14); + + last = (noise & 0x4000) ? 0 : env; + accum += (last * nfreq); + counter[1] += nfreq; + ++count; + accum_clocks += nfreq; + } + + if (count < 1) // no change over interval, don't anti-alias + { + return last; + } + + accum -= (last * counter[1]); // remove these samples which belong in the next calc + accum_clocks -= counter[1]; + #ifdef _DEBUG + if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal + #endif + + UINT32 average = accum / accum_clocks; + assert(average <= 15); // above this would indicate overflow + return average; + } + + // Tick the DMC for the number of clocks, and return output counter; + UINT32 NES_DMC::calc_dmc (UINT32 clocks) + { + counter[2] -= clocks; + assert (dfreq > 0); // prevent infinite loop + while (counter[2] < 0) + { + counter[2] += dfreq; + + if ( data > 0x100 ) // data = 0x100 when shift register is empty + { + if (!empty) + { + if ((data & 1) && (damp < 63)) + damp++; + else if (!(data & 1) && (0 < damp)) + damp--; + } + data >>=1; + } + + if ( data <= 0x100 ) // shift register is empty + { + if (dlength > 0) + { + memory->Read (daddress, data); + cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4 + // (checking for the 3-cycle case would require sub-instruction emulation) + data &= 0xFF; // read 8 bits + if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; + data |= 0x10000; // use an extra bit to signal end of data + empty = false; + daddress = ((daddress+1)&0xFFFF)|0x8000 ; + --dlength; + if (dlength == 0) + { + if (mode & 1) // looped DPCM = auto-reload + { + daddress = ((adr_reg<<6)|0xC000); + dlength = (len_reg<<4)+1; + } + else if (mode & 2) // IRQ and not looped + { + irq = true; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true); + } + } + } + else + { + data = 0x10000; // DMC will do nothing + empty = true; + } + } + } + + return (damp<<1) + dac_lsb; + } + + void NES_DMC::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > frame_sequence_length) + { + FrameSequence(frame_sequence_step); + frame_sequence_count -= frame_sequence_length; + ++frame_sequence_step; + if(frame_sequence_step >= frame_sequence_steps) + frame_sequence_step = 0; + } + } + + void NES_DMC::Tick (UINT32 clocks) + { + out[0] = calc_tri(clocks); + out[1] = calc_noise(clocks); + out[2] = calc_dmc(clocks); + } + + UINT32 NES_DMC::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + m[0] = tnd_table[0][out[0]][0][0]; + m[1] = tnd_table[0][0][out[1]][0]; + m[2] = tnd_table[0][0][0][out[2]]; + + if (option[OPT_NONLINEAR_MIXER]) + { + INT32 ref = m[0] + m[1] + m[2]; + INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; + if (ref) + { + for (int i=0; i < 3; ++i) + m[i] = (m[i] * voltage) / ref; + } + else + { + for (int i=0; i < 3; ++i) + m[i] = voltage; + } + } + + // anti-click nullifies any 4011 write but preserves nonlinearity + if (option[OPT_DPCM_ANTI_CLICK]) + { + if (dmc_pop) // $4011 will cause pop this frame + { + // adjust offset to counteract pop + dmc_pop_offset += dmc_pop_follow - m[2]; + dmc_pop = false; + + // prevent overflow, keep headspace at edges + const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); + if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; + if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; + } + dmc_pop_follow = m[2]; // remember previous position + + m[2] += dmc_pop_offset; // apply offset + + // TODO implement this in a better way + // roll off offset (not ideal, but prevents overflow) + if (dmc_pop_offset > 0) --dmc_pop_offset; + else if (dmc_pop_offset < 0) ++dmc_pop_offset; + } + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + b[1] >>= 7; + + return 2; + } + + void NES_DMC::SetClock (double c) + { + clock = c; + } + + void NES_DMC::SetRate (double r) + { + rate = (UINT32)(r?r:DEFAULT_RATE); + } + + void NES_DMC::SetPal (bool is_pal) + { + pal = (is_pal ? 1 : 0); + // set CPU cycles in frame_sequence + frame_sequence_length = is_pal ? 8314 : 7458; + } + + void NES_DMC::SetAPU (NES_APU* apu_) + { + apu = apu_; + } + + // Initializing TRI, NOISE, DPCM mixing table + void NES_DMC::InitializeTNDTable(double wt, double wn, double wd) { + + // volume adjusted by 0.95 based on empirical measurements + const double MASTER = 8192.0 * 0.95; + // truthfully, the nonlinear curve does not appear to match well + // with my tests. Do more testing of the APU/DMC DAC later. + // this value keeps the triangle consistent with measured levels, + // but not necessarily the rest of this APU channel, + // because of the lack of a good DAC model, currently. + + { // Linear Mixer + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); + } + } + } + } + { // Non-Linear Mixer + tnd_table[1][0][0][0] = 0; + for(int t=0; t<16 ; t++) { + for(int n=0; n<16; n++) { + for(int d=0; d<128; d++) { + if(t!=0||n!=0||d!=0) + tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); + } + } + } + } + + } + + void NES_DMC::Reset () + { + int i; + mask = 0; + + InitializeTNDTable(8227,12241,22638); + + counter[0] = 0; + counter[1] = 0; + counter[2] = 0; + tphase = 0; + nfreq = wavlen_table[0][0]; + dfreq = freq_table[0][0]; + tri_freq = 0; + linear_counter = 0; + linear_counter_reload = 0; + linear_counter_halt = 0; + linear_counter_control = 0; + noise_volume = 0; + noise = 0; + noise_tap = 0; + envelope_loop = 0; + envelope_disable = 0; + envelope_write = 0; + envelope_div_period = 0; + envelope_div = 0; + envelope_counter = 0; + enable[0] = 0; + enable[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + frame_irq = false; + frame_irq_enable = false; + frame_sequence_count = 0; + frame_sequence_steps = 4; + frame_sequence_step = 0; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + for (i = 0; i < 0x0F; i++) + Write (0x4008 + i, 0); + Write (0x4017, 0x40); + + irq = false; + Write (0x4015, 0x00); + if (option[OPT_UNMUTE_ON_RESET]) + Write (0x4015, 0x0f); + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + + out[0] = out[1] = out[2] = 0; + damp = 0; + dmc_pop = false; + dmc_pop_offset = 0; + dmc_pop_follow = 0; + dac_lsb = 0; + data = 0x100; + empty = true; + adr_reg = 0; + dlength = 0; + len_reg = 0; + daddress = 0; + noise = 1; + noise_tap = (1<<1); + + if (option[OPT_RANDOMIZE_NOISE]) + { + noise |= ::rand(); + counter[1] = -(rand() & 511); + } + if (option[OPT_RANDOMIZE_TRI]) + { + tphase = ::rand() & 31; + counter[0] = -(rand() & 2047); + } + + SetRate(rate); + } + + void NES_DMC::SetMemory (IDevice * r) + { + memory = r; + } + + void NES_DMC::SetOption (int id, int val) + { + if(idUpdateIRQ(NES_CPU::IRQD_DMC, false); + + reg[adr-0x4008] = val; + return true; + } + + if (adr == 0x4017) + { + //DEBUG_OUT("4017 = %02X\n", val); + frame_irq_enable = ((val & 0x40) != 0x40); + if (frame_irq_enable) frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + + frame_sequence_count = 0; + if (val & 0x80) + { + frame_sequence_steps = 5; + frame_sequence_step = 0; + FrameSequence(frame_sequence_step); + ++frame_sequence_step; + } + else + { + frame_sequence_steps = 4; + frame_sequence_step = 1; + } + } + + if (adr<0x4008||0x4013> 7) & 1; + linear_counter_reload = val & 0x7F; + break; + + case 0x4009: + break; + + case 0x400a: + tri_freq = val | (tri_freq & 0x700) ; + break; + + case 0x400b: + tri_freq = (tri_freq & 0xff) | ((val & 0x7) << 8) ; + linear_counter_halt = true; + if (enable[0]) + { + length_counter[0] = length_table[(val >> 3) & 0x1f]; + } + break; + + // noise + + case 0x400c: + noise_volume = val & 15; + envelope_div_period = val & 15; + envelope_disable = (val >> 4) & 1; + envelope_loop = (val >> 5) & 1; + break; + + case 0x400d: + break; + + case 0x400e: + if (option[OPT_ENABLE_PNOISE]) + noise_tap = (val & 0x80) ? (1<<6) : (1<<1); + else + noise_tap = (1<<1); + nfreq = wavlen_table[pal][val&15]; + break; + + case 0x400f: + if (enable[1]) + { + length_counter[1] = length_table[(val >> 3) & 0x1f]; + } + envelope_write = true; + break; + + // dmc + + case 0x4010: + mode = (val >> 6) & 3; + if (!(mode & 2)) + { + irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false); + } + dfreq = freq_table[pal][val&15]; + break; + + case 0x4011: + if (option[OPT_ENABLE_4011]) + { + damp = (val >> 1) & 0x3f; + dac_lsb = val & 1; + dmc_pop = true; + } + break; + + case 0x4012: + adr_reg = val&0xff; + // ここでdaddressは更新されない + break; + + case 0x4013: + len_reg = val&0xff; + // ここでlengthは更新されない + break; + + default: + return false; + } + + return true; + } + + bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + if (adr == 0x4015) + { + val |=(irq ? 0x80 : 0) + | (frame_irq ? 0x40 : 0) + | ((dlength>0) ? 0x10 : 0) + | (length_counter[1] ? 0x08 : 0) + | (length_counter[0] ? 0x04 : 0) + ; + + frame_irq = false; + cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false); + return true; + } + else if (0x4008<=adr&&adr<=0x4014) + { + val |= reg[adr-0x4008]; + return true; + } + else + return false; + } + + // IRQ support requires CPU read access + void NES_DMC::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } +} // namespace diff --git a/extern/NSFplay/nes_dmc.h b/extern/NSFplay/nes_dmc.h new file mode 100644 index 00000000..9152584a --- /dev/null +++ b/extern/NSFplay/nes_dmc.h @@ -0,0 +1,129 @@ +#ifndef _NES_DMC_H_ +#define _NES_DMC_H_ + +#include "../device.h" +#include "../Audio/MedianFilter.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_APU; // forward declaration + + /** Bottom Half of APU **/ + class NES_DMC:public ISoundChip + { + public: + enum + { + OPT_ENABLE_4011=0, + OPT_ENABLE_PNOISE, + OPT_UNMUTE_ON_RESET, + OPT_DPCM_ANTI_CLICK, + OPT_NONLINEAR_MIXER, + OPT_RANDOMIZE_NOISE, + OPT_TRI_MUTE, + OPT_RANDOMIZE_TRI, + OPT_DPCM_REVERSE, + OPT_END + }; + protected: + const int GETA_BITS; + static const UINT32 freq_table[2][16]; + static const UINT32 wavlen_table[2][16]; + UINT32 tnd_table[2][16][16][128]; + + int option[OPT_END]; + int mask; + INT32 sm[2][3]; + UINT8 reg[0x10]; + UINT32 len_reg; + UINT32 adr_reg; + IDevice *memory; + UINT32 out[3]; + UINT32 daddress; + UINT32 dlength; + UINT32 data; + bool empty; + INT16 damp; + int dac_lsb; + bool dmc_pop; + INT32 dmc_pop_offset; + INT32 dmc_pop_follow; + double clock; + UINT32 rate; + int pal; + int mode; + bool irq; + + INT32 counter[3]; // frequency dividers + int tphase; // triangle phase + UINT32 nfreq; // noise frequency + UINT32 dfreq; // DPCM frequency + + UINT32 tri_freq; + int linear_counter; + int linear_counter_reload; + bool linear_counter_halt; + bool linear_counter_control; + + int noise_volume; + UINT32 noise, noise_tap; + + // noise envelope + bool envelope_loop; + bool envelope_disable; + bool envelope_write; + int envelope_div_period; + int envelope_div; + int envelope_counter; + + bool enable[2]; // tri/noise enable + int length_counter[2]; // 0=tri, 1=noise + + TrackInfoBasic trkinfo[3]; + + // frame sequencer + NES_APU* apu; // apu is clocked by DMC's frame sequencer + int frame_sequence_count; // current cycle count + int frame_sequence_length; // CPU cycles per FrameSequence + int frame_sequence_step; // current step of frame sequence + int frame_sequence_steps; // 4/5 steps per frame + bool frame_irq; + bool frame_irq_enable; + + NES_CPU* cpu; // IRQ needs CPU access + + inline UINT32 calc_tri (UINT32 clocks); + inline UINT32 calc_dmc (UINT32 clocks); + inline UINT32 calc_noise (UINT32 clocks); + + public: + NES_DMC (); + ~NES_DMC (); + + void InitializeTNDTable(double wt, double wn, double wd); + void SetPal (bool is_pal); + void SetAPU (NES_APU* apu_); + void SetMemory (IDevice * r); + void FrameSequence(int s); + int GetDamp(){ return (damp<<1)|dac_lsb ; } + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double rate); + virtual void SetClock (double rate); + virtual void SetOption (int, int); + virtual void SetMask(int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_fds.cpp b/extern/NSFplay/nes_fds.cpp new file mode 100644 index 00000000..9d74c62f --- /dev/null +++ b/extern/NSFplay/nes_fds.cpp @@ -0,0 +1,397 @@ +#include +#include "nes_fds.h" + +namespace xgm { + +const int RC_BITS = 12; + +NES_FDS::NES_FDS () +{ + option[OPT_CUTOFF] = 2000; + option[OPT_4085_RESET] = 0; + option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp + + rc_k = 0; + rc_l = (1< 1) return; + sm[0] = mixl; + sm[1] = mixr; +} + +ITrackInfo *NES_FDS::GetTrackInfo(int trk) +{ + trkinfo.max_volume = 32; + trkinfo.volume = last_vol; + trkinfo.key = last_vol > 0; + trkinfo._freq = last_freq; + trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0); + trkinfo.tone = env_out[EMOD]; + for(int i=0;i<64;i++) + trkinfo.wave[i] = wave[TWAV][i]; + + return &trkinfo; +} + +void NES_FDS::SetClock (double c) +{ + clock = c; +} + +void NES_FDS::SetRate (double r) +{ + rate = r; + + // configure lowpass filter + double cutoff = double(option[OPT_CUTOFF]); + double leak = 0.0; + if (cutoff > 0) + leak = ::exp(-2.0 * 3.14159 * cutoff / rate); + rc_k = INT32(leak * double(1<= period) + { + // clock the envelope + if (env_mode[i]) + { + if (env_out[i] < 32) ++env_out[i]; + } + else + { + if (env_out[i] > 0 ) --env_out[i]; + } + env_timer[i] -= period; + } + } + } + } + + // clock the mod table + if (!mod_halt) + { + // advance phase, adjust for modulator + UINT32 start_pos = phase[TMOD] >> 16; + phase[TMOD] += (clocks * freq[TMOD]); + UINT32 end_pos = phase[TMOD] >> 16; + + // wrap the phase to the 64-step table (+ 16 bit accumulator) + phase[TMOD] = phase[TMOD] & 0x3FFFFF; + + // execute all clocked steps + for (UINT32 p = start_pos; p < end_pos; ++p) + { + INT32 wv = wave[TMOD][p & 0x3F]; + if (wv == 4) // 4 resets mod position + mod_pos = 0; + else + { + const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; + mod_pos += BIAS[wv]; + mod_pos &= 0x7F; // 7-bit clamp + } + } + } + + // clock the wav table + if (!wav_halt) + { + // complex mod calculation + INT32 mod = 0; + if (env_out[EMOD] != 0) // skip if modulator off + { + // convert mod_pos to 7-bit signed + INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); + + // multiply pos by gain, + // shift off 4 bits but with odd "rounding" behaviour + INT32 temp = pos * env_out[EMOD]; + INT32 rem = temp & 0x0F; + temp >>= 4; + if ((rem > 0) && ((temp & 0x80) == 0)) + { + if (pos < 0) temp -= 1; + else temp += 2; + } + + // wrap if range is exceeded + while (temp >= 192) temp -= 256; + while (temp < -64) temp += 256; + + // multiply result by pitch, + // shift off 6 bits, round to nearest + temp = freq[TWAV] * temp; + rem = temp & 0x3F; + temp >>= 6; + if (rem >= 32) temp += 1; + + mod = temp; + } + + // advance wavetable position + INT32 f = freq[TWAV] + mod; + phase[TWAV] = phase[TWAV] + (clocks * f); + phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap + + // store for trackinfo + last_freq = f; + } + + // output volume caps at 32 + INT32 vol_out = env_out[EVOL]; + if (vol_out > 32) vol_out = 32; + + // final output + if (!wav_write) + fout = wave[TWAV][(phase[TWAV]>>16)&0x3F] * vol_out; + + // NOTE: during wav_halt, the unit still outputs (at phase 0) + // and volume can affect it if the first sample is nonzero. + // haven't worked out 100% of the conditions for volume to + // effect (vol envelope does not seem to run, but am unsure) + // but this implementation is very close to correct + + // store for trackinfo + last_vol = vol_out; +} + +UINT32 NES_FDS::Render (INT32 b[2]) +{ + // 8 bit approximation of master volume + const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) + const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol + const INT32 MASTER[4] = { + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), + int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; + + INT32 v = fout * MASTER[master_vol] >> 8; + + // lowpass RC filter + INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; + rc_accum = rc_out; + v = rc_out; + + // output mix + INT32 m = mask ? 0 : v; + b[0] = (m * sm[0]) >> 7; + b[1] = (m * sm[1]) >> 7; + return 2; +} + +bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + // $4023 master I/O enable/disable + if (adr == 0x4023) + { + master_io = ((val & 2) != 0); + return true; + } + + if (!master_io) + return false; + if (adr < 0x4040 || adr > 0x408A) + return false; + + if (adr < 0x4080) // $4040-407F wave table write + { + if (wav_write) + wave[TWAV][adr - 0x4040] = val & 0x3F; + return true; + } + + switch (adr & 0x00FF) + { + case 0x80: // $4080 volume envelope + env_disable[EVOL] = ((val & 0x80) != 0); + env_mode[EVOL] = ((val & 0x40) != 0); + env_timer[EVOL] = 0; + env_speed[EVOL] = val & 0x3F; + if (env_disable[EVOL]) + env_out[EVOL] = env_speed[EVOL]; + return true; + case 0x81: // $4081 --- + return false; + case 0x82: // $4082 wave frequency low + freq[TWAV] = (freq[TWAV] & 0xF00) | val; + return true; + case 0x83: // $4083 wave frequency high / enables + freq[TWAV] = (freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8); + wav_halt = ((val & 0x80) != 0); + env_halt = ((val & 0x40) != 0); + if (wav_halt) + phase[TWAV] = 0; + if (env_halt) + { + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + } + return true; + case 0x84: // $4084 mod envelope + env_disable[EMOD] = ((val & 0x80) != 0); + env_mode[EMOD] = ((val & 0x40) != 0); + env_timer[EMOD] = 0; + env_speed[EMOD] = val & 0x3F; + if (env_disable[EMOD]) + env_out[EMOD] = env_speed[EMOD]; + return true; + case 0x85: // $4085 mod position + mod_pos = val & 0x7F; + // not hardware accurate., but prevents detune due to cycle inaccuracies + // (notably in Bio Miracle Bokutte Upa) + if (option[OPT_4085_RESET]) + phase[TMOD] = mod_write_pos << 16; + return true; + case 0x86: // $4086 mod frequency low + freq[TMOD] = (freq[TMOD] & 0xF00) | val; + return true; + case 0x87: // $4087 mod frequency high / enable + freq[TMOD] = (freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8); + mod_halt = ((val & 0x80) != 0); + if (mod_halt) + phase[TMOD] = phase[TMOD] & 0x3F0000; // reset accumulator phase + return true; + case 0x88: // $4088 mod table write + if (mod_halt) + { + // writes to current playback position (there is no direct way to set phase) + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07; + phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF; + mod_write_pos = phase[TMOD] >> 16; // used by OPT_4085_RESET + } + return true; + case 0x89: // $4089 wave write enable, master volume + wav_write = ((val & 0x80) != 0); + master_vol = val & 0x03; + return true; + case 0x8A: // $408A envelope speed + master_env_speed = val; + // haven't tested whether this register resets phase on hardware, + // but this ensures my inplementation won't spam envelope clocks + // if this value suddenly goes low. + env_timer[EMOD] = 0; + env_timer[EVOL] = 0; + return true; + default: + return false; + } + return false; +} + +bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr >= 0x4040 && adr <= 0x407F) + { + // TODO: if wav_write is not enabled, the + // read address may not be reliable? need + // to test this on hardware. + val = wave[TWAV][adr - 0x4040]; + return true; + } + + if (adr == 0x4090) // $4090 read volume envelope + { + val = env_out[EVOL] | 0x40; + return true; + } + + if (adr == 0x4092) // $4092 read mod envelope + { + val = env_out[EMOD] | 0x40; + return true; + } + + return false; +} + +} // namespace diff --git a/extern/NSFplay/nes_fds.h b/extern/NSFplay/nes_fds.h new file mode 100644 index 00000000..212e472b --- /dev/null +++ b/extern/NSFplay/nes_fds.h @@ -0,0 +1,83 @@ +#ifndef _NES_FDS_H_ +#define _NES_FDS_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoFDS : public TrackInfoBasic +{ +public: + INT16 wave[64]; + virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); } +}; + +class NES_FDS : public ISoundChip +{ +public: + enum + { + OPT_CUTOFF=0, + OPT_4085_RESET, + OPT_WRITE_PROTECT, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2]; // stereo mix + INT32 fout; // current output + TrackInfoFDS trkinfo; + int option[OPT_END]; + + bool master_io; + UINT32 master_vol; + UINT32 last_freq; // for trackinfo + UINT32 last_vol; // for trackinfo + + // two wavetables + enum { TMOD=0, TWAV=1 }; + INT32 wave[2][64]; + UINT32 freq[2]; + UINT32 phase[2]; + bool wav_write; + bool wav_halt; + bool env_halt; + bool mod_halt; + UINT32 mod_pos; + UINT32 mod_write_pos; + + // two ramp envelopes + enum { EMOD=0, EVOL=1 }; + bool env_mode[2]; + bool env_disable[2]; + UINT32 env_timer[2]; + UINT32 env_speed[2]; + UINT32 env_out[2]; + UINT32 master_env_speed; + + // 1-pole RC lowpass filter + INT32 rc_accum; + INT32 rc_k; + INT32 rc_l; + +public: + NES_FDS (); + virtual ~ NES_FDS (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double); + virtual void SetClock (double); + virtual void SetOption (int, int); + virtual void SetMask(int m){ mask = m&1; } + virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_fme7.cpp b/extern/NSFplay/nes_fme7.cpp new file mode 100644 index 00000000..9d48de79 --- /dev/null +++ b/extern/NSFplay/nes_fme7.cpp @@ -0,0 +1,186 @@ +#include "nes_fme7.h" + +using namespace xgm; + +const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output + +NES_FME7::NES_FME7 () +{ + psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE); + divider = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; +} + +NES_FME7::~NES_FME7 () +{ + if (psg) + PSG_delete (psg); +} + +void NES_FME7::SetClock (double c) +{ + this->clock = c * 2.0; +} + +void NES_FME7::SetRate (double r) +{ + //rate = r ? r : DEFAULT_RATE; + rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock + if (psg) + PSG_set_rate (psg, (e_uint32)rate); +} + +void NES_FME7::SetOption (int id, int val) +{ + if(id= DIVIDER) + { + divider -= DIVIDER; + if (psg) PSG_calc(psg); + } +} + +xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2]) +{ + b[0] = b[1] = 0; + + for (int i=0; i < 3; ++i) + { + // note negative polarity + b[0] -= psg->cout[i] * sm[0][i]; + b[1] -= psg->cout[i] * sm[1][i]; + } + b[0] >>= (7-4); + b[1] >>= (7-4); + + // master volume adjustment + const INT32 MASTER = INT32(0.64 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; +} + +void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) +{ + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_FME7::GetTrackInfo(int trk) +{ + assert(trk<5); + + if(psg) + { + if (trk<3) + { + trkinfo[trk]._freq = psg->freq[trk]; + if(psg->freq[trk]) + trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk]; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->cout[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = psg->volume[trk] >> 1; + //trkinfo[trk].key = (psg->cout[trk]>0)?true:false; + trkinfo[trk].key = !(psg->tmask[trk]); + trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0); + } + else if (trk == 3) // envelope + { + trkinfo[trk]._freq = psg->env_freq; + if(psg->env_freq) + trkinfo[trk].freq = psg->clk/512.0/psg->env_freq; + else + trkinfo[trk].freq = 0; + + if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave + { + trkinfo[trk].freq *= 0.5f; // sounds an octave down + } + + trkinfo[trk].output = psg->voltbl[psg->env_ptr]; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0); + trkinfo[trk].tone = + (psg->env_continue ?8:0) | + (psg->env_attack ?4:0) | + (psg->env_alternate?2:0) | + (psg->env_hold ?1:0) ; + } + else if (trk == 4) // noise + { + trkinfo[trk]._freq = psg->noise_freq >> 1; + if(trkinfo[trk]._freq > 0) + trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq; + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = psg->noise_seed & 1; + trkinfo[trk].max_volume = 0; + trkinfo[trk].volume = 0; + //trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0); + trkinfo[trk].key = false; + trkinfo[trk].tone = 0; + } + } + return &trkinfo[trk]; +} diff --git a/extern/NSFplay/nes_fme7.h b/extern/NSFplay/nes_fme7.h new file mode 100644 index 00000000..d351b085 --- /dev/null +++ b/extern/NSFplay/nes_fme7.h @@ -0,0 +1,42 @@ +#ifndef _NES_FME7_H_ +#define _NES_FME7_H_ +#include "../device.h" +#include "legacy/emu2149.h" + +namespace xgm +{ + + class NES_FME7:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + //int option[OPT_END]; + INT32 sm[2][3]; // stereo mix + INT16 buf[2]; + PSG *psg; + int divider; // clock divider + double clock, rate; + TrackInfoBasic trkinfo[5]; + public: + NES_FME7 (); + ~NES_FME7 (); + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_mmc5.cpp b/extern/NSFplay/nes_mmc5.cpp new file mode 100644 index 00000000..2b2f8fcb --- /dev/null +++ b/extern/NSFplay/nes_mmc5.cpp @@ -0,0 +1,422 @@ +#include "nes_mmc5.h" + +namespace xgm +{ + + NES_MMC5::NES_MMC5 () + { + cpu = NULL; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + option[OPT_NONLINEAR_MIXER] = true; + option[OPT_PHASE_REFRESH] = true; + frame_sequence_count = 0; + + // square nonlinear mix, same as 2A03 + square_table[0] = 0; + for(int i=1;i<32;i++) + square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); + + // 2A03 style nonlinear pcm mix with double the bits + //pcm_table[0] = 0; + //INT32 wd = 22638; + //for(int d=1;d<256; ++d) + // pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); + + // linear pcm mix (actual hardware seems closer to this) + pcm_table[0] = 0; + double pcm_scale = 32.0; + for (int d=1; d<256; ++d) + pcm_table[d] = (INT32)(double(d) * pcm_scale); + + // stereo mix + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_MMC5::~NES_MMC5 () + { + } + + void NES_MMC5::Reset () + { + int i; + + scounter[0] = 0; + scounter[1] = 0; + sphase[0] = 0; + sphase[1] = 0; + + envelope_div[0] = 0; + envelope_div[1] = 0; + length_counter[0] = 0; + length_counter[1] = 0; + envelope_counter[0] = 0; + envelope_counter[1] = 0; + frame_sequence_count = 0; + + for (i = 0; i < 8; i++) + Write (0x5000 + i, 0); + + Write(0x5015, 0); + + for (i = 0; i < 3; ++i) + out[i] = 0; + + mask = 0; + pcm = 0; // PCM channel + pcm_mode = false; // write mode + + SetRate(rate); + } + + void NES_MMC5::SetOption (int id, int val) + { + if(idclock = c; + } + + void NES_MMC5::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_MMC5::FrameSequence () + { + // 240hz clock + for (int i=0; i < 2; ++i) + { + bool divider = false; + if (envelope_write[i]) + { + envelope_write[i] = false; + envelope_counter[i] = 15; + envelope_div[i] = 0; + } + else + { + ++envelope_div[i]; + if (envelope_div[i] > envelope_div_period[i]) + { + divider = true; + envelope_div[i] = 0; + } + } + if (divider) + { + if (envelope_loop[i] && envelope_counter[i] == 0) + envelope_counter[i] = 15; + else if (envelope_counter[i] > 0) + --envelope_counter[i]; + } + } + + // MMC5 length counter is clocked at 240hz, unlike 2A03 + for (int i=0; i < 2; ++i) + { + if (!envelope_loop[i] && (length_counter[i] > 0)) + --length_counter[i]; + } + } + + INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) + { + static const INT16 sqrtbl[4][16] = { + {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + {1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + }; + + scounter[i] += clocks; + while (scounter[i] > freq[i]) + { + sphase[i] = (sphase[i] + 1) & 15; + scounter[i] -= (freq[i] + 1); + } + + INT32 ret = 0; + if (length_counter[i] > 0) + { + // note MMC5 does not silence the highest 8 frequencies like APU, + // because this is done by the sweep unit. + + int v = envelope_disable[i] ? volume[i] : envelope_counter[i]; + ret = sqrtbl[duty[i]][sphase[i]] ? v : 0; + } + + return ret; + } + + void NES_MMC5::TickFrameSequence (UINT32 clocks) + { + frame_sequence_count += clocks; + while (frame_sequence_count > 7458) + { + FrameSequence(); + frame_sequence_count -= 7458; + } + } + + void NES_MMC5::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0, clocks); + out[1] = calc_sqr(1, clocks); + out[2] = pcm; + } + + UINT32 NES_MMC5::Render (INT32 b[2]) + { + out[0] = (mask & 1) ? 0 : out[0]; + out[1] = (mask & 2) ? 0 : out[1]; + out[2] = (mask & 4) ? 0 : out[2]; + + INT32 m[3]; + + if(option[OPT_NONLINEAR_MIXER]) + { + // squares nonlinear + INT32 voltage = square_table[out[0] + out[1]]; + m[0] = out[0] << 6; + m[1] = out[1] << 6; + INT32 ref = m[0] + m[1]; + if (ref > 0) + { + m[0] = (m[0] * voltage) / ref; + m[1] = (m[1] * voltage) / ref; + } + else + { + m[0] = voltage; + m[1] = voltage; + } + + // pcm nonlinear + m[2] = pcm_table[out[2]]; + } + else + { + // squares + m[0] = out[0] << 6; + m[1] = out[1] << 6; + + // pcm channel + m[2] = out[2] << 5; + } + + // note polarity is flipped on output + + b[0] = m[0] * -sm[0][0]; + b[0] += m[1] * -sm[0][1]; + b[0] += m[2] * -sm[0][2]; + b[0] >>= 7; + + b[1] = m[0] * -sm[1][0]; + b[1] += m[1] * -sm[1][1]; + b[1] += m[2] * -sm[1][2]; + b[1] >>= 7; + + return 2; + } + + bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch; + + static const UINT8 length_table[32] = { + 0x0A, 0xFE, + 0x14, 0x02, + 0x28, 0x04, + 0x50, 0x06, + 0xA0, 0x08, + 0x3C, 0x0A, + 0x0E, 0x0C, + 0x1A, 0x0E, + 0x0C, 0x10, + 0x18, 0x12, + 0x30, 0x14, + 0x60, 0x16, + 0xC0, 0x18, + 0x48, 0x1A, + 0x10, 0x1C, + 0x20, 0x1E + }; + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + ram[adr & 0x3ff] = val; + return true; + } + else if ((0x5000 <= adr) && (adr < 0x5008)) + { + reg[adr & 0x7] = val; + } + + switch (adr) + { + case 0x5000: + case 0x5004: + ch = (adr >> 2) & 1; + volume[ch] = val & 15; + envelope_disable[ch] = (val >> 4) & 1; + envelope_loop[ch] = (val >> 5) & 1; + envelope_div_period[ch] = (val & 15); + duty[ch] = (val >> 6) & 3; + break; + + case 0x5002: + case 0x5006: + ch = (adr >> 2) & 1; + freq[ch] = val + (freq[ch] & 0x700); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + break; + + case 0x5003: + case 0x5007: + ch = (adr >> 2) & 1; + freq[ch] = (freq[ch] & 0xff) + ((val & 7) << 8); + if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch]; + // phase reset + if (option[OPT_PHASE_REFRESH]) + sphase[ch] = 0; + envelope_write[ch] = true; + if (enable[ch]) + { + length_counter[ch] = length_table[(val >> 3) & 0x1f]; + } + break; + + // PCM channel control + case 0x5010: + pcm_mode = ((val & 1) != 0); // 0 = write, 1 = read + break; + + // PCM channel control + case 0x5011: + if (!pcm_mode) + { + val &= 0xFF; + if (val != 0) pcm = val; + } + break; + + case 0x5015: + enable[0] = (val & 1) ? true : false; + enable[1] = (val & 2) ? true : false; + if (!enable[0]) + length_counter[0] = 0; + if (!enable[1]) + length_counter[1] = 0; + break; + + case 0x5205: + mreg[0] = val; + break; + + case 0x5206: + mreg[1] = val; + break; + + default: + return false; + + } + return true; + } + + bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + // in PCM read mode, reads from $8000-$C000 automatically load the PCM output + if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu) + { + pcm_mode = false; // prevent recursive entry + UINT32 pcm_read; + cpu->Read(adr, pcm_read); + pcm_read &= 0xFF; + if (pcm_read != 0) + pcm = pcm_read; + pcm_mode = true; + } + + if ((0x5000 <= adr) && (adr < 0x5008)) + { + val = reg[adr&0x7]; + return true; + } + else if(adr == 0x5015) + { + val = (enable[1]?2:0)|(enable[0]?1:0); + return true; + } + + if ((0x5c00 <= adr) && (adr < 0x5ff0)) + { + val = ram[adr & 0x3ff]; + return true; + } + else if (adr == 0x5205) + { + val = (mreg[0] * mreg[1]) & 0xff; + return true; + } + else if (adr == 0x5206) + { + val = (mreg[0] * mreg[1]) >> 8; + return true; + } + + return false; + } + + void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_MMC5::GetTrackInfo(int trk) + { + assert(trk<3); + + if (trk < 2) // square + { + trkinfo[trk]._freq = freq[trk]; + if(freq[trk]) + trkinfo[trk].freq = clock/16/(freq[trk] + 1); + else + trkinfo[trk].freq = 0; + + trkinfo[trk].output = out[trk]; + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10); + trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0)); + trkinfo[trk].tone = duty[trk]; + } + else // pcm + { + trkinfo[trk]._freq = 0; + trkinfo[trk].freq = 0; + trkinfo[trk].output = out[2]; + trkinfo[trk].max_volume = 255; + trkinfo[trk].volume = pcm; + trkinfo[trk].key = 0; + trkinfo[trk].tone = pcm_mode ? 1 : 0; + } + + return &trkinfo[trk]; + } + + // pcm read mode requires CPU read access + void NES_MMC5::SetCPU(NES_CPU* cpu_) + { + cpu = cpu_; + } + +}// namespace diff --git a/extern/NSFplay/nes_mmc5.h b/extern/NSFplay/nes_mmc5.h new file mode 100644 index 00000000..b05fbb50 --- /dev/null +++ b/extern/NSFplay/nes_mmc5.h @@ -0,0 +1,74 @@ +#ifndef _NES_MMC5_H_ +#define _NES_MMC5_H_ +#include "../device.h" +#include "../CPU/nes_cpu.h" + +namespace xgm +{ + class NES_MMC5:public ISoundChip + { + public: + enum + { OPT_NONLINEAR_MIXER=0, OPT_PHASE_REFRESH, OPT_END }; + + protected: + int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo panning + UINT8 ram[0x6000 - 0x5c00]; + UINT8 reg[8]; + UINT8 mreg[2]; + UINT8 pcm; // PCM channel + bool pcm_mode; // PCM channel + NES_CPU* cpu; // PCM channel reads need CPU access + + UINT32 scounter[2]; // frequency divider + UINT32 sphase[2]; // phase counter + + UINT32 duty[2]; + UINT32 volume[2]; + UINT32 freq[2]; + INT32 out[3]; + bool enable[2]; + + bool envelope_disable[2]; // エンベロープ有効フラグ + bool envelope_loop[2]; // エンベロープループ + bool envelope_write[2]; + int envelope_div_period[2]; + int envelope_div[2]; + int envelope_counter[2]; + + int length_counter[2]; + + int frame_sequence_count; + + double clock, rate; + INT32 calc_sqr (int i, UINT32 clocks); + INT32 square_table[32]; + INT32 pcm_table[256]; + TrackInfoBasic trkinfo[3]; + public: + NES_MMC5 (); + ~NES_MMC5 (); + + void FrameSequence (); + void TickFrameSequence (UINT32 clocks); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetOption (int id, int b); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetMask (int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + + void SetCPU(NES_CPU* cpu_); + }; + +} + +#endif diff --git a/extern/NSFplay/nes_n106.cpp b/extern/NSFplay/nes_n106.cpp new file mode 100644 index 00000000..f53d4ae7 --- /dev/null +++ b/extern/NSFplay/nes_n106.cpp @@ -0,0 +1,367 @@ +#include +#include "nes_n106.h" + +namespace xgm { + +NES_N106::NES_N106 () +{ + option[OPT_SERIAL] = 0; + option[OPT_PHASE_READ_ONLY] = 0; + option[OPT_LIMIT_WAVELENGTH] = 0; + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + for (int i=0; i < 8; ++i) + { + sm[0][i] = 128; + sm[1][i] = 128; + } + Reset(); +} + +NES_N106::~NES_N106 () +{ +} + +void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) +{ + if (trk < 0 || trk >= 8) return; + trk = 7-trk; // displayed channels are inverted + sm[0][trk] = mixl; + sm[1][trk] = mixr; +} + +ITrackInfo *NES_N106::GetTrackInfo (int trk) +{ + int channels = get_channels(); + int channel = 7-trk; // invert the track display + + TrackInfoN106* t = &trkinfo[channel]; + + if (trk >= channels) + { + t->max_volume = 15; + t->volume = 0; + t->_freq = 0; + t->wavelen = 0; + t->tone = -1; + t->output = 0; + t->key = false; + t->freq = 0; + } + else + { + t->max_volume = 15; + t->volume = get_vol(channel); + t->_freq = get_freq(channel); + t->wavelen = get_len(channel); + t->tone = get_off(channel); + t->output = fout[channel]; + + t->key = (t->volume > 0) && (t->_freq > 0); + t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen); + + for (int i=0; i < t->wavelen; ++i) + t->wave[i] = get_sample((i+t->tone)&0xFF); + } + + return t; +} + +void NES_N106::SetClock (double c) +{ + clock = c; +} + +void NES_N106::SetRate (double r) +{ + rate = r; +} + +void NES_N106::SetMask (int m) +{ + // bit reverse the mask, + // N163 waves are displayed in reverse order + mask = 0 + | ((m & (1<<0)) ? (1<<7) : 0) + | ((m & (1<<1)) ? (1<<6) : 0) + | ((m & (1<<2)) ? (1<<5) : 0) + | ((m & (1<<3)) ? (1<<4) : 0) + | ((m & (1<<4)) ? (1<<3) : 0) + | ((m & (1<<5)) ? (1<<2) : 0) + | ((m & (1<<6)) ? (1<<1) : 0) + | ((m & (1<<7)) ? (1<<0) : 0); +} + +void NES_N106::SetOption (int id, int val) +{ + if (id 0) + { + int channel = 7-tick_channel; + + UINT32 phase = get_phase(channel); + UINT32 freq = get_freq(channel); + UINT32 len = get_len(channel); + UINT32 off = get_off(channel); + INT32 vol = get_vol(channel); + + // accumulate 24-bit phase + phase = (phase + freq) & 0x00FFFFFF; + + // wrap phase if wavelength exceeded + UINT32 hilen = len << 16; + while (phase >= hilen) phase -= hilen; + + // write back phase + set_phase(phase, channel); + + // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) + INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); + fout[channel] = sample * vol; + + // cycle to next channel every 15 clocks + tick_clock -= 15; + ++tick_channel; + if (tick_channel >= channels) + tick_channel = 0; + } +} + +UINT32 NES_N106::Render (INT32 b[2]) +{ + b[0] = 0; + b[1] = 0; + if (master_disable) return 2; + + int channels = get_channels(); + + if (option[OPT_SERIAL]) // hardware accurate serial multiplexing + { + // this could be made more efficient than going clock-by-clock + // but this way is simpler + int clocks = render_clock; + while (clocks > 0) + { + int c = 7-render_channel; + if (0 == ((mask >> c) & 1)) + { + b[0] += fout[c] * sm[0][c]; + b[1] += fout[c] * sm[1][c]; + } + + ++render_subclock; + if (render_subclock >= 15) // each channel gets a 15-cycle slice + { + render_subclock = 0; + ++render_channel; + if (render_channel >= channels) + render_channel = 0; + } + --clocks; + } + + // increase output level by 1 bits (7 bits already added from sm) + b[0] <<= 1; + b[1] <<= 1; + + // average the output + if (render_clock > 0) + { + b[0] /= render_clock; + b[1] /= render_clock; + } + render_clock = 0; + } + else // just mix all channels + { + for (int i = (8-channels); i<8; ++i) + { + if (0 == ((mask >> i) & 1)) + { + b[0] += fout[i] * sm[0][i]; + b[1] += fout[i] * sm[1][i]; + } + } + + // mix together, increase output level by 8 bits, roll off 7 bits from sm + INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; + b[0] = (b[0] * MIX[channels]) >> 7; + b[1] = (b[1] * MIX[channels]) >> 7; + // when approximating the serial multiplex as a straight mix, once the + // multiplex frequency gets below the nyquist frequency an average mix + // begins to sound too quiet. To approximate this effect, I don't attenuate + // any further after 6 channels are active. + } + + // 8 bit approximation of master volume + // max N163 vol vs max APU square + // unfortunately, games have been measured as low as 3.4x and as high as 8.5x + // with higher volumes on Erika, King of Kings, and Rolling Thunder + // and lower volumes on others. Using 6.0x as a rough "one size fits all". + const double MASTER_VOL = 6.0 * 1223.0; + const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value + const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); + b[0] = (b[0] * GAIN) >> 8; + b[1] = (b[1] * GAIN) >> 8; + + return 2; +} + +bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) +{ + if (adr == 0xE000) // master disable + { + master_disable = ((val & 0x40) != 0); + return true; + } + else if (adr == 0xF800) // register select + { + reg_select = (val & 0x7F); + reg_advance = (val & 0x80) != 0; + return true; + } + else if (adr == 0x4800) // register write + { + if (option[OPT_PHASE_READ_ONLY]) // old emulators didn't know phase was stored here + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && + (r == 1 || + r == 3 || + r == 5)) + { + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + } + if (option[OPT_LIMIT_WAVELENGTH]) // old emulators ignored top 3 bits of length + { + int c = 15 - (reg_select/8); + int r = reg_select & 7; + if (c < get_channels() && r == 4) + { + val |= 0xE0; + } + } + reg[reg_select] = val; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) +{ + if (adr == 0x4800) // register read + { + val = reg[reg_select]; + if (reg_advance) + reg_select = (reg_select + 1) & 0x7F; + return true; + } + return false; +} + +// +// register decoding/encoding functions +// + +inline UINT32 NES_N106::get_phase (int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + return (reg[0x41 + channel] ) + + (reg[0x43 + channel] << 8 ) + + (reg[0x45 + channel] << 16); +} + +inline UINT32 NES_N106::get_freq (int channel) +{ + // 19-bit frequency stored in channel regs 0/2/4 + channel = channel << 3; + return ( reg[0x40 + channel] ) + + ( reg[0x42 + channel] << 8 ) + + ((reg[0x44 + channel] & 0x03) << 16); +} + +inline UINT32 NES_N106::get_off (int channel) +{ + // 8-bit offset stored in channel reg 6 + channel = channel << 3; + return reg[0x46 + channel]; +} + +inline UINT32 NES_N106::get_len (int channel) +{ + // 6-bit<<3 length stored obscurely in channel reg 4 + channel = channel << 3; + return 256 - (reg[0x44 + channel] & 0xFC); +} + +inline INT32 NES_N106::get_vol (int channel) +{ + // 4-bit volume stored in channel reg 7 + channel = channel << 3; + return reg[0x47 + channel] & 0x0F; +} + +inline INT32 NES_N106::get_sample (UINT32 index) +{ + // every sample becomes 2 samples in regs + return (index&1) ? + ((reg[index>>1] >> 4) & 0x0F) : + ( reg[index>>1] & 0x0F) ; +} + +inline int NES_N106::get_channels () +{ + // 3-bit channel count stored in reg 0x7F + return ((reg[0x7F] >> 4) & 0x07) + 1; +} + +inline void NES_N106::set_phase (UINT32 phase, int channel) +{ + // 24-bit phase stored in channel regs 1/3/5 + channel = channel << 3; + reg[0x41 + channel] = phase & 0xFF; + reg[0x43 + channel] = (phase >> 8 ) & 0xFF; + reg[0x45 + channel] = (phase >> 16) & 0xFF; +} + +} //namespace diff --git a/extern/NSFplay/nes_n106.h b/extern/NSFplay/nes_n106.h new file mode 100644 index 00000000..82d7eaa6 --- /dev/null +++ b/extern/NSFplay/nes_n106.h @@ -0,0 +1,74 @@ +#ifndef _NES_N106_H_ +#define _NES_N106_H_ +#include "../device.h" + +namespace xgm { + +class TrackInfoN106 : public TrackInfoBasic +{ +public: + int wavelen; + INT16 wave[256]; + virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); } +}; + +class NES_N106:public ISoundChip +{ +public: + enum + { + OPT_SERIAL = 0, + OPT_PHASE_READ_ONLY = 1, + OPT_LIMIT_WAVELENGTH = 2, + OPT_END + }; + +protected: + double rate, clock; + int mask; + INT32 sm[2][8]; // stereo mix + INT32 fout[8]; // current output + TrackInfoN106 trkinfo[8]; + int option[OPT_END]; + + bool master_disable; + UINT32 reg[0x80]; // all state is contained here + unsigned int reg_select; + bool reg_advance; + int tick_channel; + int tick_clock; + int render_channel; + int render_clock; + int render_subclock; + + // convenience functions to interact with regs + inline UINT32 get_phase (int channel); + inline UINT32 get_freq (int channel); + inline UINT32 get_off (int channel); + inline UINT32 get_len (int channel); + inline INT32 get_vol (int channel); + inline INT32 get_sample (UINT32 index); + inline int get_channels (); + // for storing back the phase after modifying + inline void set_phase (UINT32 phase, int channel); + +public: + NES_N106 (); + ~NES_N106 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual void SetRate (double); + virtual void SetClock (double); + virtual void SetOption (int, int); + virtual void SetMask (int m); + virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); +}; + +} // namespace xgm + +#endif diff --git a/extern/NSFplay/nes_vrc6.cpp b/extern/NSFplay/nes_vrc6.cpp new file mode 100644 index 00000000..6a19b5ea --- /dev/null +++ b/extern/NSFplay/nes_vrc6.cpp @@ -0,0 +1,264 @@ +#include "nes_vrc6.h" + +namespace xgm +{ + + NES_VRC6::NES_VRC6 () + { + SetClock (DEFAULT_CLOCK); + SetRate (DEFAULT_RATE); + + halt = false; + freq_shift = 0; + + for(int c=0;c<2;++c) + for(int t=0;t<3;++t) + sm[c][t] = 128; + } + + NES_VRC6::~NES_VRC6 () + { + } + + void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) + { + if (trk < 0) return; + if (trk > 2) return; + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC6::GetTrackInfo(int trk) + { + if(trk<2) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = volume[trk]; + trkinfo[trk]._freq = freq2[trk]; + trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0; + trkinfo[trk].tone = duty[trk]; + trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk]; + return &trkinfo[trk]; + } + else if(trk==2) + { + trkinfo[2].max_volume = 255; + trkinfo[2].volume = volume[2]; + trkinfo[2]._freq = freq2[2]; + trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0; + trkinfo[2].tone = -1; + trkinfo[2].key = (enable[2]>0); + return &trkinfo[2]; + } + else + return NULL; + } + + void NES_VRC6::SetClock (double c) + { + clock = c; + } + + void NES_VRC6::SetRate (double r) + { + rate = r ? r : DEFAULT_RATE; + } + + void NES_VRC6::SetOption (int id, int val) + { + if(id freq2[i]) + { + phase[i] = (phase[i] + 1) & 15; + counter[i] -= (freq2[i] + 1); + } + } + + return (gate[i] + || sqrtbl[duty[i]][phase[i]])? volume[i] : 0; + } + + INT16 NES_VRC6::calc_saw (UINT32 clocks) + { + if (!enable[2]) + return 0; + + if (!halt) + { + counter[2] += clocks; + while(counter[2] > freq2[2]) + { + counter[2] -= (freq2[2] + 1); + + // accumulate saw + ++count14; + if (count14 >= 14) + { + count14 = 0; + phase[2] = 0; + } + else if (0 == (count14 & 1)) // only accumulate on even ticks + { + phase[2] = (phase[2] + volume[2]) & 0xFF; // note 8-bit wrapping behaviour + } + } + } + + // only top 5 bits of saw are output + return phase[2] >> 3; + } + + void NES_VRC6::Tick (UINT32 clocks) + { + out[0] = calc_sqr(0,clocks); + out[1] = calc_sqr(1,clocks); + out[2] = calc_saw(clocks); + } + + UINT32 NES_VRC6::Render (INT32 b[2]) + { + INT32 m[3]; + m[0] = out[0]; + m[1] = out[1]; + m[2] = out[2]; + + // note: signal is inverted compared to 2A03 + + m[0] = (mask & 1) ? 0 : -m[0]; + m[1] = (mask & 2) ? 0 : -m[1]; + m[2] = (mask & 4) ? 0 : -m[2]; + + b[0] = m[0] * sm[0][0]; + b[0] += m[1] * sm[0][1]; + b[0] += m[2] * sm[0][2]; + //b[0] >>= (7 - 7); + + b[1] = m[0] * sm[1][0]; + b[1] += m[1] * sm[1][1]; + b[1] += m[2] * sm[1][2]; + //b[1] >>= (7 - 7); + + // master volume adjustment + const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } + + bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) + { + int ch, cmap[4] = { 0, 0, 1, 2 }; + + switch (adr) + { + case 0x9000: + case 0xa000: + ch = cmap[(adr >> 12) & 3]; + volume[ch] = val & 15; + duty[ch] = (val >> 4) & 7; + gate[ch] = (val >> 7) & 1; + break; + case 0xb000: + volume[2] = val & 63; + break; + + case 0x9001: + case 0xa001: + case 0xb001: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = (freq[ch] & 0xf00) | val; + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + break; + + case 0x9002: + case 0xa002: + case 0xb002: + ch = cmap[(adr >> 12) & 3]; + freq[ch] = ((val & 0xf) << 8) + (freq[ch] & 0xff); + freq2[ch] = (freq[ch] >> freq_shift); + if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch]; + if (!enable[ch]) // if enable is being turned on, phase should be reset + { + if (ch == 2) + { + count14 = 0; // reset saw + } + phase[ch] = 0; + } + enable[ch] = (val >> 7) & 1; + break; + + case 0x9003: + halt = val & 1; + freq_shift = + (val & 4) ? 8 : + (val & 2) ? 4 : + 0; + freq2[0] = (freq[0] >> freq_shift); + freq2[1] = (freq[1] >> freq_shift); + freq2[2] = (freq[2] >> freq_shift); + if (counter[0] > freq2[0]) counter[0] = freq2[0]; + if (counter[1] > freq2[1]) counter[1] = freq2[1]; + if (counter[2] > freq2[2]) counter[2] = freq2[2]; + break; + + default: + return false; + + } + + return true; + } + + bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + +} // namespace diff --git a/extern/NSFplay/nes_vrc6.h b/extern/NSFplay/nes_vrc6.h new file mode 100644 index 00000000..2e277e6e --- /dev/null +++ b/extern/NSFplay/nes_vrc6.h @@ -0,0 +1,56 @@ +#ifndef _NES_VRC6_H_ +#define _NES_VRC6_H_ +#include "../device.h" + +namespace xgm +{ + + class NES_VRC6:public ISoundChip + { + public: + enum + { + OPT_END + }; + protected: + UINT32 counter[3]; // frequency divider + UINT32 phase[3]; // phase counter + UINT32 freq2[3]; // adjusted frequency + int count14; // saw 14-stage counter + + //int option[OPT_END]; + int mask; + INT32 sm[2][3]; // stereo mix + int duty[2]; + int volume[3]; + int enable[3]; + int gate[3]; + UINT32 freq[3]; + INT16 calc_sqr (int i, UINT32 clocks); + INT16 calc_saw (UINT32 clocks); + bool halt; + int freq_shift; + double clock, rate; + INT32 out[3]; + TrackInfoBasic trkinfo[3]; + + public: + NES_VRC6 (); + ~NES_VRC6 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ mask = m; } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif diff --git a/extern/NSFplay/nes_vrc7.cpp b/extern/NSFplay/nes_vrc7.cpp new file mode 100644 index 00000000..4123fb36 --- /dev/null +++ b/extern/NSFplay/nes_vrc7.cpp @@ -0,0 +1,189 @@ +#include +#include "nes_vrc7.h" + +namespace xgm +{ + NES_VRC7::NES_VRC7 () + { + use_all_channels = false; + patch_set = OPLL_VRC7_TONE; + patch_custom = NULL; + divider = 0; + + opll = OPLL_new ( 3579545, DEFAULT_RATE); + OPLL_reset_patch (opll, patch_set); + SetClock(DEFAULT_CLOCK); + + for(int c=0;c<2;++c) + //for(int t=0;t<6;++t) + for(int t=0;t<9;++t) // HACK for YM2413 support + sm[c][t] = 128; + } + + NES_VRC7::~NES_VRC7 () + { + OPLL_delete (opll); + } + + void NES_VRC7::UseAllChannels(bool b) + { + use_all_channels = b; + } + + void NES_VRC7::SetPatchSet(int p) + { + patch_set = p; + } + + void NES_VRC7::SetPatchSetCustom (const UINT8* pset) + { + patch_custom = pset; + } + + void NES_VRC7::SetClock (double c) + { + clock = c / 36; + } + + void NES_VRC7::SetRate (double r) + { + //rate = r ? r : DEFAULT_RATE; + (void)r; // rate is ignored + rate = 49716; + OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog) + OPLL_set_rate(opll,(uint32_t)rate); + } + + void NES_VRC7::SetOption (int id, int val) + { + if(id 5) return; + if (trk > 8) return; // HACK YM2413 + sm[0][trk] = mixl; + sm[1][trk] = mixr; + } + + ITrackInfo *NES_VRC7::GetTrackInfo(int trk) + { + //if(opll&&trk<6) + if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though) + { + trkinfo[trk].max_volume = 15; + trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15); + trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8); + int blk = (opll->reg[0x20+trk]>>1)&7; + trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk); + trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15; + //trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false; + trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false; + return &trkinfo[trk]; + } + else + return NULL; + } + + bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id) + { + if (adr == 0x9010) + { + OPLL_writeIO (opll, 0, val); + return true; + } + if (adr == 0x9030) + { + OPLL_writeIO (opll, 1, val); + return true; + } + else + return false; + } + + bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id) + { + return false; + } + + void NES_VRC7::Tick (UINT32 clocks) + { + divider += clocks; + while (divider >= 36) + { + divider -= 36; + OPLL_calc(opll); + } + } + + UINT32 NES_VRC7::Render (INT32 b[2]) + { + b[0] = b[1] = 0; + for (int i=0; i < 6; ++i) + { + INT32 val = (mask & (1<ch_out[i] >> 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + + // HACK for YM2413 support + if (use_all_channels) + { + for (int i=6; i < 9; ++i) + { + if (mask & (1<patch_number[i] > 15) // rhytm mode + { + if (i == 6) val = opll->ch_out[9]; // BD + else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD + else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM + /* (i == 8) is implied */ + } + else + { + val = opll->ch_out[i]; + } + val >>= 4; + b[0] += val * sm[0][i]; + b[1] += val * sm[1][i]; + } + } + + b[0] >>= (7 - 4); + b[1] >>= (7 - 4); + + // master volume adjustment + const INT32 MASTER = INT32(1.15 * 256.0); + b[0] = (b[0] * MASTER) >> 8; + b[1] = (b[1] * MASTER) >> 8; + + return 2; + } +} diff --git a/extern/NSFplay/nes_vrc7.h b/extern/NSFplay/nes_vrc7.h new file mode 100644 index 00000000..e8a91d61 --- /dev/null +++ b/extern/NSFplay/nes_vrc7.h @@ -0,0 +1,53 @@ +#ifndef _NES_VRC7_H_ +#define _NES_VRC7_H_ +#include "../device.h" +#include "legacy/emu2413.h" + +namespace xgm +{ + + class NES_VRC7 : public ISoundChip + { + public: + enum + { + OPT_OPLL=0, + OPT_END + }; + protected: + int option[OPT_END]; + int mask; + int patch_set; + const UINT8* patch_custom; + //INT32 sm[2][6]; // stereo mix + INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413 + INT16 buf[2]; + OPLL *opll; + UINT32 divider; // clock divider + double clock, rate; + //TrackInfoBasic trkinfo[6]; + TrackInfoBasic trkinfo[9]; // HACK to support YM2413 + bool use_all_channels; + public: + NES_VRC7 (); + ~NES_VRC7 (); + + virtual void Reset (); + virtual void Tick (UINT32 clocks); + virtual UINT32 Render (INT32 b[2]); + virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); + virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); + virtual void UseAllChannels (bool b); + virtual void SetPatchSet (int p); + virtual void SetPatchSetCustom (const UINT8* pset); + virtual void SetClock (double); + virtual void SetRate (double); + virtual void SetOption (int, int); + virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); } + virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); + virtual ITrackInfo *GetTrackInfo(int trk); + }; + +} // namespace + +#endif