From fe5758fd331bddfba7c96815fd451eb3875b837b Mon Sep 17 00:00:00 2001 From: paulb-nl Date: Thu, 5 Sep 2024 07:42:36 +0200 Subject: [PATCH] Add option to remove audio pops (#257) Some audio can be inaccurate with this option enabled. --- Gameboy.sv | 4 +++- rtl/gb.v | 2 ++ rtl/gbc_snd.vhd | 44 ++++++++++++++++++++++++++++++-------------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/Gameboy.sv b/Gameboy.sv index b3e1ee6..88e365d 100644 --- a/Gameboy.sv +++ b/Gameboy.sv @@ -200,7 +200,7 @@ assign AUDIO_MIX = status[8:7]; // 0 1 2 3 4 5 6 // 01234567890123456789012345678901 23456789012345678901234567890123 // 0123456789ABCDEFGHIJKLMNOPQRSTUV 0123456789ABCDEFGHIJKLMNOPQRSTUV -// XXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX XXXXXXXXXXX +// XXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXX `include "build_id.v" localparam CONF_STR = { @@ -241,6 +241,7 @@ localparam CONF_STR = { "P1o2,Analog width,Narrow,Wide;", "P1-;", "P1O78,Stereo mix,none,25%,50%,100%;", + "P1O[43],Audio mode,Accurate,No Pops;", "P2,Bootroms;", "P2-;", @@ -665,6 +666,7 @@ gb gb ( // audio .audio_l ( GB_AUDIO_L ), .audio_r ( GB_AUDIO_R ), + .audio_no_pops (status[43]), // interface to the lcd .lcd_clkena ( lcd_clkena ), diff --git a/rtl/gb.v b/rtl/gb.v index ba9ea84..44e6715 100644 --- a/rtl/gb.v +++ b/rtl/gb.v @@ -58,6 +58,7 @@ module gb ( // audio output [15:0] audio_l, output [15:0] audio_r, + input audio_no_pops, // Megaduck? input megaduck, @@ -445,6 +446,7 @@ gbc_snd audio ( .reset ( reset_ss ), .is_gbc ( isGBC ), + .remove_pops ( audio_no_pops ), .s1_read ( audio_rd ), .s1_write ( audio_wr ), diff --git a/rtl/gbc_snd.vhd b/rtl/gbc_snd.vhd index 1b91b0f..3f770f9 100644 --- a/rtl/gbc_snd.vhd +++ b/rtl/gbc_snd.vhd @@ -19,6 +19,7 @@ entity gbc_snd is reset : in std_logic; is_gbc : in std_logic; + remove_pops : in std_logic; -- 0: Accurate output, 1: Toggling DACs will not cause pops but some audio can be inaccurate. s1_read : in std_logic; s1_write : in std_logic; @@ -45,6 +46,7 @@ architecture SYN of gbc_snd is clk : in std_logic; ce : in std_logic; dac_en : in std_logic; + dac_invert : in std_logic; dac_input : in std_logic_vector(3 downto 0); dac_output : out signed(8 downto 0) ); @@ -1643,13 +1645,13 @@ begin -- Analog hardware emulation - sq1_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>sq1_dac_en, dac_input=>sq1_wav,dac_output=>sq1_dac_out); - sq2_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>sq2_dac_en, dac_input=>sq2_wav,dac_output=>sq2_dac_out); - wav_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>wav_enable, dac_input=>wav_wav,dac_output=>wav_dac_out); - noi_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>noi_dac_en, dac_input=>noi_wav,dac_output=>noi_dac_out); + sq1_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>sq1_dac_en, dac_invert => not remove_pops, dac_input=>sq1_wav,dac_output=>sq1_dac_out); + sq2_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>sq2_dac_en, dac_invert => not remove_pops, dac_input=>sq2_wav,dac_output=>sq2_dac_out); + wav_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>wav_enable, dac_invert => not remove_pops, dac_input=>wav_wav,dac_output=>wav_dac_out); + noi_dac : apu_dac port map (clk=>clk, ce=>ce, dac_en=>noi_dac_en, dac_invert => not remove_pops, dac_input=>noi_wav,dac_output=>noi_dac_out); - mixer : process (sq1_dac_out, sq2_dac_out, wav_dac_out, noi_dac_out, ch_map, ch_vol) + mixer : process (sq1_dac_out, sq2_dac_out, wav_dac_out, noi_dac_out, ch_map, ch_vol, remove_pops) variable snd_left_in : signed(10 downto 0); variable snd_right_in : signed(10 downto 0); variable snd_tmp : signed(15 downto 0); @@ -1678,12 +1680,20 @@ begin snd_left_in := snd_left_in + wav_analog; end if; end loop; - - snd_tmp := snd_right_in * signed(("00" & ch_vol(2 downto 0)) + '1'); - snd_right <= std_logic_vector(snd_tmp); - snd_tmp := snd_left_in * signed(("00" & ch_vol(6 downto 4)) + '1'); - snd_left <= std_logic_vector(snd_tmp); + snd_tmp := snd_right_in * signed(("00" & ch_vol(2 downto 0)) + '1'); + if (remove_pops = '1') then + snd_right <= std_logic_vector(shift_left(snd_tmp,1)); -- Compensate lower volume + else + snd_right <= std_logic_vector(snd_tmp); + end if; + + snd_tmp := snd_left_in * signed(("00" & ch_vol(6 downto 4)) + '1'); + if (remove_pops = '1') then + snd_left <= std_logic_vector(shift_left(snd_tmp,1)); + else + snd_left <= std_logic_vector(snd_tmp); + end if; end process; end SYN; @@ -1697,6 +1707,7 @@ entity apu_dac is clk : in std_logic; ce : in std_logic; dac_en : in std_logic; + dac_invert : in std_logic; dac_input : in std_logic_vector(3 downto 0); dac_output : out signed(8 downto 0) ); @@ -1712,10 +1723,15 @@ architecture apu_dac_arch of apu_dac is -- Convert a DAC input code to a pseudo-analog value function dac_out( - wav : std_logic_vector(3 downto 0) + wav : std_logic_vector(3 downto 0); + invert : std_logic ) return signed is begin - return signed((wav xor "0111") & "00000"); + if (invert = '1') then + return signed((wav xor "0111") & "00000"); -- 0xF = -256, 0x0 = 224. + else + return signed("0" & wav & "0000"); -- 0xF = 240, 0x0 = 0. + end if; end function; begin dac_output <= dac_analog; @@ -1735,11 +1751,11 @@ begin end if; end process timers; - process(clk, ce, dac_en, dac_input, dac_decay_timer) + process(clk, ce, dac_en, dac_invert, dac_input, dac_decay_timer) begin if rising_edge(clk) and ce = '1' then if dac_en = '1' then - dac_analog <= dac_out(dac_input); + dac_analog <= dac_out(dac_input, dac_invert); elsif dac_decay_timer = 0 then if dac_analog < 0 then dac_analog <= dac_analog + 1;