sound, add ym2612 channel clipping, ladder effect
authorkub <derkub@gmail.com>
Wed, 17 Nov 2021 21:27:02 +0000 (22:27 +0100)
committerkub <derkub@gmail.com>
Wed, 17 Nov 2021 21:32:04 +0000 (22:32 +0100)
pico/pico.h
pico/sound/sound.c
pico/sound/ym2612.c
pico/sound/ym2612.h
pico/sound/ym2612_arm.S
platform/common/menu_pico.c
platform/common/menu_pico.h

index 05833ce..f474d56 100644 (file)
@@ -75,6 +75,7 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
 #define POPT_EN_PWM         (1<<21)\r
 #define POPT_PWM_IRQ_OPT    (1<<22)\r
 #define POPT_DIS_FM_SSGEG   (1<<23)\r
+#define POPT_EN_FM_LADDER   (1<<24) //x00 0000\r
 \r
 #define PAHW_MCD  (1<<0)\r
 #define PAHW_32X  (1<<1)\r
index 17ac752..ac79a84 100644 (file)
@@ -67,7 +67,9 @@ void PsndRerate(int preserve_state)
     ym2612_pack_state();\r
     memcpy(state, YM2612GetRegs(), 0x204);\r
   }\r
-  YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PicoIn.sndRate, !(PicoIn.opt&POPT_DIS_FM_SSGEG));\r
+  YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PicoIn.sndRate,\r
+        ((PicoIn.opt&POPT_DIS_FM_SSGEG) ? 0 : ST_SSG) |\r
+        ((PicoIn.opt&POPT_EN_FM_LADDER) ? ST_LADDER : 0));\r
   if (preserve_state) {\r
     // feed it back it's own registers, just like after loading state\r
     memcpy(YM2612GetRegs(), state, 0x204);\r
index 75d33de..502326e 100644 (file)
@@ -918,6 +918,14 @@ typedef struct
 \r
 \r
 #if !defined(_ASM_YM2612_C) || defined(EXTERNAL_YM2612)\r
+#include <limits.h>\r
+static int clip(int n) \r
+{\r
+    unsigned b = 14, s = n < 0;\r
+    if (s + (n>>(b-1))) n = (int)(s + INT_MAX) >> (8*sizeof(int)-b);\r
+    return n;\r
+}\r
+\r
 static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)\r
 {\r
        int scounter;                                   /* sample counter */\r
@@ -1226,6 +1234,7 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
 \r
                /* mix sample to output buffer */\r
                if (smp) {\r
+                       smp = clip(smp);  /* saturate to 14 bit */\r
                        if (ct->pack & 1) { /* stereo */\r
                                if (ct->pack & 0x20) /* L */ /* TODO: check correctness */\r
                                        buffer[scounter*2] += smp;\r
@@ -1256,12 +1265,21 @@ static void chan_render_prep(void)
        crct.lfo_inc = ym2612.OPN.lfo_inc;\r
 }\r
 \r
-static void chan_render_finish(void)\r
+static void chan_render_finish(int *buffer, unsigned short length, int active_chans)\r
 {\r
        ym2612.OPN.eg_cnt = crct.eg_cnt;\r
        ym2612.OPN.eg_timer = crct.eg_timer;\r
        g_lfo_ampm = crct.pack >> 16; // need_save\r
        ym2612.OPN.lfo_cnt = crct.lfo_cnt;\r
+\r
+       /* apply ladder effect. NB only works if buffer was empty beforehand! */\r
+       if (active_chans && (ym2612.OPN.ST.flags & ST_LADDER)) {\r
+               length <<= crct.pack & 1;\r
+               while (length--) {\r
+                       *buffer -= (*buffer < 0)*4 << 5;\r
+                       buffer++;\r
+               }\r
+       }\r
 }\r
 \r
 static UINT32 update_lfo_phase(FM_SLOT *SLOT, UINT32 block_fnum)\r
@@ -1835,21 +1853,21 @@ int YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty)
        BIT_IF(flags, 1, (ym2612.ssg_mask & 0xf00000) && (ym2612.OPN.ST.flags & 1));\r
        if (ym2612.slot_mask & 0xf00000) active_chs |= chan_render(buffer, length, 5, flags|((pan&0xc00)>>6)|(!!ym2612.dacen<<2)) << 5;\r
 #undef BIT_IF\r
-       chan_render_finish();\r
+       chan_render_finish(buffer, length, active_chs);\r
 \r
        return active_chs; // 1 if buffer updated\r
 }\r
 \r
 \r
 /* initialize YM2612 emulator */\r
-void YM2612Init_(int clock, int rate, int ssg)\r
+void YM2612Init_(int clock, int rate, int flags)\r
 {\r
        memset(&ym2612, 0, sizeof(ym2612));\r
        init_tables();\r
 \r
        ym2612.OPN.ST.clock = clock;\r
        ym2612.OPN.ST.rate = rate;\r
-       ym2612.OPN.ST.flags = (ssg ? 1:0);\r
+       ym2612.OPN.ST.flags = flags;\r
 \r
        OPNSetPres( 6*24 );\r
 \r
index 7d16efb..9b7fff4 100644 (file)
@@ -108,6 +108,9 @@ typedef struct
        INT32   dt_tab[8][32];/* DeTune table       */\r
 } FM_ST;\r
 \r
+#define ST_SSG         1\r
+#define ST_LADDER      2\r
+\r
 /***********************************************************/\r
 /* OPN unit                                                */\r
 /***********************************************************/\r
@@ -162,7 +165,7 @@ typedef struct
 extern YM2612 ym2612;\r
 #endif\r
 \r
-void YM2612Init_(int baseclock, int rate, int ssg);\r
+void YM2612Init_(int baseclock, int rate, int flags);\r
 void YM2612ResetChip_(void);\r
 int  YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty);\r
 \r
@@ -184,9 +187,9 @@ int  YM2612PicoStateLoad2(int *tat, int *tbt);
 #else\r
 /* GP2X specific */\r
 #include <platform/gp2x/940ctl.h>\r
-#define YM2612Init(baseclock,rate,ssg) do { \\r
-       if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate, ssg); \\r
-       else               YM2612Init_(baseclock, rate, ssg); \\r
+#define YM2612Init(baseclock,rate,flags) do { \\r
+       if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate, flags); \\r
+       else               YM2612Init_(baseclock, rate, flags); \\r
 } while (0)\r
 #define YM2612ResetChip() do { \\r
        if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \\r
index b1d0326..5cb42f2 100644 (file)
@@ -924,6 +924,10 @@ crl_algo_done:
     tst     r0, r0
     beq     ctl_sample_skip
     orr     r4, r4, #8              @ have_output
+    lsr     r1, r0, #31             @ clip (saturate) sample to 14 bit
+    adds    r2, r1, r0, asr #13
+    subne   r0, r1, #0x80000001
+    asrne   r0, r0, #18
     tst     r12, #1
     beq     ctl_sample_mono
 
index e0f9da0..b5b4453 100644 (file)
@@ -561,6 +561,7 @@ static menu_entry e_menu_adv_options[] =
        mee_onoff     ("Emulate Z80",              MA_OPT2_ENABLE_Z80,    PicoIn.opt, POPT_EN_Z80),
        mee_onoff     ("Emulate YM2612 (FM)",      MA_OPT2_ENABLE_YM2612, PicoIn.opt, POPT_EN_FM),
        mee_onoff     ("Disable YM2612 SSG-EG",    MA_OPT2_DISABLE_YM_SSG,PicoIn.opt, POPT_DIS_FM_SSGEG),
+       mee_onoff     ("Enable YM2612 ladder effect",MA_OPT2_DISABLE_YM_LAD,PicoIn.opt, POPT_EN_FM_LADDER),
        mee_onoff     ("Emulate SN76496 (PSG)",    MA_OPT2_ENABLE_SN76496,PicoIn.opt, POPT_EN_PSG),
        mee_onoff     ("Emulate Game Gear LCD",    MA_OPT2_ENABLE_GGLCD  ,PicoIn.opt, POPT_EN_GG_LCD),
        mee_onoff     ("Disable idle loop patching",MA_OPT2_NO_IDLE_LOOPS,PicoIn.opt, POPT_DIS_IDLE_DET),
index c74a96d..5b1800d 100644 (file)
@@ -50,6 +50,7 @@ typedef enum
        MA_OPT2_ENABLE_Z80,
        MA_OPT2_ENABLE_YM2612,
        MA_OPT2_DISABLE_YM_SSG,
+       MA_OPT2_DISABLE_YM_LAD,
        MA_OPT2_ENABLE_SN76496,
        MA_OPT2_ENABLE_YM2413,
        MA_OPT2_ENABLE_GGLCD,