Revision 1467
Added by markw 7 months ago
atari_chips/pokeyv2/audio_signal_detector.vhd | ||
---|---|---|
---------------------------------------------------------------------------
|
||
-- (c) 2024 mark watson
|
||
-- I am happy for anyone to use this for non-commercial use.
|
||
-- If my vhdl files are used commercially or otherwise sold,
|
||
-- please contact me for explicit permission at scrameta (gmail).
|
||
-- This applies for source and binary form and derived works.
|
||
---------------------------------------------------------------------------
|
||
|
||
LIBRARY ieee;
|
||
USE ieee.std_logic_1164.all;
|
||
use ieee.numeric_std.all;
|
||
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
|
||
use IEEE.STD_LOGIC_MISC.all;
|
||
use work.AudioTypes.all;
|
||
|
||
LIBRARY work;
|
||
|
||
ENTITY audio_signal_detector IS
|
||
PORT
|
||
(
|
||
CLK : IN STD_LOGIC;
|
||
RESET_N : IN STD_LOGIC;
|
||
AUDIO : IN SIGNED(15 downto 0);
|
||
SAMPLE : IN STD_LOGIC;
|
||
VOLUME: IN STD_LOGIC_VECTOR(1 downto 0);
|
||
|
||
DETECT_OUT : OUT STD_LOGIC
|
||
);
|
||
END audio_signal_detector;
|
||
|
||
ARCHITECTURE vhdl OF audio_signal_detector IS
|
||
-- find zero
|
||
signal moving_avg_reg : signed(15 downto 0);
|
||
signal moving_avg_next : signed(15 downto 0);
|
||
|
||
-- amplitude detector
|
||
signal min_level_reg : signed(11 downto 0);
|
||
signal min_level_next : signed(11 downto 0);
|
||
|
||
signal max_level_reg : signed(11 downto 0);
|
||
signal max_level_next : signed(11 downto 0);
|
||
|
||
signal amplitude_reg : unsigned(11 downto 0);
|
||
signal amplitude_next : unsigned(11 downto 0);
|
||
|
||
-- zero crossing
|
||
signal zero_crosses_reg : unsigned(10 downto 0);
|
||
signal zero_crosses_next : unsigned(10 downto 0);
|
||
|
||
signal above_zero_reg : std_logic;
|
||
signal above_zero_next : std_logic;
|
||
|
||
-- when to check
|
||
signal enable_check_pre : std_logic;
|
||
signal enable_check : std_logic;
|
||
|
||
-- how to interpret results
|
||
signal enabled_reg : unsigned(6 downto 0);
|
||
signal enabled_next : unsigned(6 downto 0);
|
||
|
||
BEGIN
|
||
process(CLK,reset_n)
|
||
begin
|
||
if (reset_n='0') then
|
||
enabled_reg <= (others=>'0');
|
||
min_level_reg <= (others=>'1');
|
||
max_level_reg <= (others=>'0');
|
||
amplitude_reg <= (others=>'0');
|
||
zero_crosses_reg <= (others=>'0');
|
||
above_zero_reg <= '0';
|
||
moving_avg_reg <= (others=>'0');
|
||
elsif (CLK'event and CLK='1') then
|
||
enabled_reg <= enabled_next;
|
||
min_level_reg <= min_level_next;
|
||
max_level_reg <= max_level_next;
|
||
amplitude_reg <= amplitude_next;
|
||
zero_crosses_reg <= zero_crosses_next;
|
||
above_zero_reg <= above_zero_next;
|
||
moving_avg_reg <= moving_avg_next;
|
||
end if;
|
||
end process;
|
||
|
||
enable_div : work.enable_divider
|
||
generic map (COUNT=>1024)
|
||
port map(clk=>CLK,reset_n=>reset_n,enable_in=>'1',enable_out=>enable_check_pre);
|
||
|
||
enable_div2 : work.enable_divider
|
||
generic map (COUNT=>1024)
|
||
port map(clk=>CLK,reset_n=>reset_n,enable_in=>enable_check_pre,enable_out=>enable_check);
|
||
|
||
-- moving avg
|
||
process(moving_avg_reg,sample,audio)
|
||
begin
|
||
moving_avg_next <= moving_avg_reg;
|
||
|
||
if (sample='1') then
|
||
--moving_avg_next <= resize(moving_avg_reg(15 downto 1),16) + resize(moving_avg_reg(15 downto 2),16) + resize(moving_avg_reg(15 downto 3),16) + resize(moving_avg_reg(15 downto 4),16)+ resize(moving_avg_reg(15 downto 5),16) + resize(audio(15 downto 5),16);
|
||
moving_avg_next <= (moving_avg_reg - resize(moving_avg_reg(15 downto 5),16)) + resize(audio(15 downto 5),16);
|
||
end if;
|
||
end process;
|
||
|
||
-- zero crossing
|
||
process(audio,moving_avg_reg,zero_crosses_reg,above_zero_reg,enable_check)
|
||
variable audio_nodc : signed(15 downto 0);
|
||
begin
|
||
above_zero_next <= above_zero_reg;
|
||
zero_crosses_next <= zero_crosses_reg;
|
||
|
||
audio_nodc := audio - moving_avg_reg;
|
||
|
||
above_zero_next <= not(audio_nodc(15));
|
||
|
||
if ((above_zero_reg xor not(audio_nodc(15)))='1') then
|
||
zero_crosses_next <= zero_crosses_reg+1;
|
||
end if;
|
||
|
||
if (enable_check='1') then
|
||
zero_crosses_next <= (others=>'0');
|
||
end if;
|
||
end process;
|
||
|
||
-- amplitude
|
||
process(audio,min_level_reg,max_level_reg,amplitude_reg,enable_check,enabled_reg)
|
||
begin
|
||
min_level_next <= min_level_reg;
|
||
max_level_next <= max_level_reg;
|
||
amplitude_next <= unsigned(max_level_reg-min_level_reg);
|
||
|
||
if (audio(15 downto 4)<min_level_reg) then
|
||
min_level_next <= audio(15 downto 4);
|
||
end if;
|
||
|
||
if (audio(15 downto 4)>max_level_reg) then
|
||
max_level_next <= audio(15 downto 4);
|
||
end if;
|
||
|
||
if (enable_check='1') then
|
||
min_level_next(11) <= '0';
|
||
min_level_next(10 downto 0) <= (others=>'1');
|
||
max_level_next(11) <= '1';
|
||
max_level_next(10 downto 0) <= (others=>'0');
|
||
end if;
|
||
end process;
|
||
|
||
process(audio,amplitude_reg,zero_crosses_reg,enable_check,enabled_reg,volume)
|
||
variable detected : std_logic;
|
||
variable amplitude_threshold : unsigned(11 downto 0);
|
||
variable zero_crosses_threshold : unsigned(10 downto 0);
|
||
begin
|
||
enabled_next <= enabled_reg;
|
||
|
||
case volume is
|
||
when "01" =>
|
||
amplitude_threshold := to_unsigned(32,12);
|
||
when "10" =>
|
||
amplitude_threshold := to_unsigned(64,12);
|
||
when others =>
|
||
amplitude_threshold := to_unsigned(128,12);
|
||
end case;
|
||
|
||
zero_crosses_threshold := to_unsigned(256,11);
|
||
|
||
if (enable_check='1') then
|
||
detected := '0';
|
||
if (amplitude_reg>amplitude_threshold and zero_crosses_reg<zero_crosses_threshold) then
|
||
detected := '1';
|
||
end if;
|
||
if (detected='1' and enabled_reg<119) then
|
||
enabled_next <= enabled_reg+8;
|
||
end if;
|
||
if (detected='0' and enabled_reg>0) then
|
||
enabled_next <= enabled_reg-1;
|
||
end if;
|
||
end if;
|
||
end process;
|
||
|
||
process(enabled_reg)
|
||
begin
|
||
detect_out <= '0';
|
||
if (enabled_reg>=64) then
|
||
detect_out <= '1';
|
||
end if;
|
||
|
||
end process;
|
||
|
||
END vhdl;
|
atari_chips/pokeyv2/build.sh | ||
---|---|---|
`cp -r fir_*vhdl $dir`;
|
||
`cp -r fir_sample_buffer* $dir`;
|
||
`cp -r fir_buffer* $dir`;
|
||
`cp -r audio_sig* $dir`;
|
||
|
||
chdir $dir;
|
||
|
atari_chips/pokeyv2/pokeymax.vhd | ||
---|---|---|
signal adc_use_reg : signed(15 downto 0);
|
||
signal adc_use_next : signed(15 downto 0);
|
||
|
||
signal adc_frozen_reg : signed(15 downto 0);
|
||
signal adc_frozen_next : signed(15 downto 0);
|
||
|
||
signal sio_noise : signed(15 downto 0);
|
||
|
||
signal adc_in_signed : signed(15 downto 0);
|
||
signal adc_out_signed : signed(15 downto 0);
|
||
|
||
signal adc_min_reg : signed(11 downto 0);
|
||
signal adc_min_next : signed(11 downto 0);
|
||
signal adc_enabled : std_logic;
|
||
|
||
signal adc_max_reg : signed(11 downto 0);
|
||
signal adc_max_next : signed(11 downto 0);
|
||
|
||
signal adc_diff_reg : unsigned(11 downto 0);
|
||
signal adc_diff_next : unsigned(11 downto 0);
|
||
|
||
signal adc_enabled_reg : unsigned(5 downto 0);
|
||
signal adc_enabled_next : unsigned(5 downto 0);
|
||
|
||
signal enable_reset_min_max_pre : std_logic;
|
||
signal enable_reset_min_max : std_logic;
|
||
|
||
signal adc_valid : std_logic;
|
||
signal adc_output : std_logic_vector(19 downto 0);
|
||
|
||
... | ... | |
begin
|
||
if (reset_n='0') then
|
||
adc_reg <= (others=>'0');
|
||
adc_enabled_reg <= (others=>'0');
|
||
adc_use_reg <= (others=>'0');
|
||
adc_min_reg <= (others=>'1');
|
||
adc_max_reg <= (others=>'0');
|
||
adc_diff_reg <= (others=>'0');
|
||
adc_frozen_reg <= (others=>'0');
|
||
elsif (CLK49152'event and CLK49152='1') then
|
||
adc_reg <= adc_next;
|
||
adc_enabled_reg <= adc_enabled_next;
|
||
adc_use_reg <= adc_use_next;
|
||
adc_min_reg <= adc_min_next;
|
||
adc_max_reg <= adc_max_next;
|
||
adc_diff_reg <= adc_diff_next;
|
||
adc_frozen_reg <= adc_frozen_next;
|
||
end if;
|
||
end process;
|
||
|
||
... | ... | |
);
|
||
SIO_AUDIO <= unsigned(not(adc_use_reg(15))&adc_use_reg(14 downto 0));
|
||
|
||
enable_div : work.enable_divider
|
||
generic map (COUNT=>128)
|
||
port map(clk=>CLK49152,reset_n=>reset_n,enable_in=>'1',enable_out=>enable_reset_min_max_pre);
|
||
|
||
enable_div2 : work.enable_divider
|
||
generic map (COUNT=>128)
|
||
port map(clk=>CLK49152,reset_n=>reset_n,enable_in=>enable_reset_min_max_pre,enable_out=>enable_reset_min_max);
|
||
|
||
process(adc_reg,adc_output,adc_valid,ADC_VOLUME_REG)
|
||
variable adc_shrunk : signed(19 downto 0);
|
||
begin
|
||
adc_next <= adc_reg;
|
||
|
||
if (adc_valid='1') then
|
||
adc_shrunk := signed(not(adc_output(19)) & adc_output(18 downto 0));
|
||
adc_shrunk := (signed(not(adc_output(19)) & adc_output(18 downto 0)));
|
||
case ADC_VOLUME_REG is
|
||
when "01" =>
|
||
adc_next <= adc_shrunk(19 downto (19-16+1)); --*1
|
||
... | ... | |
end if;
|
||
end process;
|
||
|
||
process(adc_out_signed,adc_min_reg,adc_max_reg,adc_diff_reg,enable_reset_min_max,adc_enabled_reg,adc_volume_reg)
|
||
variable detected : std_logic;
|
||
variable threshold : unsigned(11 downto 0);
|
||
begin
|
||
adc_min_next <= adc_min_reg;
|
||
adc_max_next <= adc_max_reg;
|
||
adc_diff_next <= unsigned(adc_max_reg-adc_min_reg);
|
||
adc_enabled_next <= adc_enabled_reg;
|
||
audio_signal_detector1 : work.audio_signal_detector
|
||
port map(clk=>CLK49152,reset_n=>reset_n,audio=>adc_in_signed,sample=>adc_valid,volume=>adc_volume_reg,detect_out=>adc_enabled);
|
||
|
||
if (adc_out_signed(15 downto 4)<adc_min_reg) then
|
||
adc_min_next <= adc_out_signed(15 downto 4);
|
||
end if;
|
||
|
||
if (adc_out_signed(15 downto 4)>adc_max_reg) then
|
||
adc_max_next <= adc_out_signed(15 downto 4);
|
||
end if;
|
||
process(adc_use_reg,adc_frozen_reg,adc_enabled,adc_out_signed,sio_noise)
|
||
begin
|
||
adc_frozen_next <= adc_frozen_reg;
|
||
|
||
case adc_volume_reg is
|
||
when "01" =>
|
||
threshold := to_unsigned(8,12);
|
||
when "10" =>
|
||
threshold := to_unsigned(16,12);
|
||
when others =>
|
||
threshold := to_unsigned(32,12);
|
||
end case;
|
||
adc_use_next <= adc_frozen_reg xor sio_noise;
|
||
|
||
if (adc_enabled='1') then
|
||
adc_frozen_next <= adc_out_signed;
|
||
end if;
|
||
|
||
if (enable_reset_min_max='1') then
|
||
detected := '0';
|
||
if (adc_diff_reg>threshold) then
|
||
detected := '1';
|
||
end if;
|
||
if (detected='1' and adc_enabled_reg<63) then
|
||
adc_enabled_next <= adc_enabled_reg+1;
|
||
end if;
|
||
if (detected='0' and adc_enabled_reg>0) then
|
||
adc_enabled_next <= adc_enabled_reg-1;
|
||
end if;
|
||
adc_min_next(11) <= '0';
|
||
adc_min_next(10 downto 0) <= (others=>'1');
|
||
adc_max_next(11) <= '1';
|
||
adc_max_next(10 downto 0) <= (others=>'0');
|
||
end if;
|
||
end process;
|
||
|
||
process(adc_reg,adc_enabled_reg,adc_out_signed,SIO_RXD_SYNC,SIO_DATA_VOLUME_REG)
|
||
process(SIO_RXD_SYNC,SIO_DATA_VOLUME_REG)
|
||
begin
|
||
adc_use_next <= adc_use_reg;
|
||
if (adc_enabled_reg>=32) then
|
||
adc_use_next <= adc_out_signed;
|
||
else
|
||
case SIO_DATA_VOLUME_REG is
|
||
when "01" =>
|
||
adc_use_next(10) <= SIO_RXD_SYNC;
|
||
when "10" =>
|
||
adc_use_next(11) <= SIO_RXD_SYNC;
|
||
when "11" =>
|
||
adc_use_next(12) <= SIO_RXD_SYNC;
|
||
when others =>
|
||
end case;
|
||
end if;
|
||
sio_noise <= (others=>'0');
|
||
|
||
case SIO_DATA_VOLUME_REG is
|
||
when "01" =>
|
||
sio_noise(10) <= not(SIO_RXD_SYNC);
|
||
when "10" =>
|
||
sio_noise(11) <= not(SIO_RXD_SYNC);
|
||
when "11" =>
|
||
sio_noise(12) <= not(SIO_RXD_SYNC);
|
||
when others =>
|
||
end case;
|
||
end process;
|
||
|
||
|
||
end generate adc_on;
|
||
|
||
adc_off : if enable_adc=0 generate
|
atari_chips/pokeyv2/pokeymaxv4.qsf | ||
---|---|---|
set_instance_assignment -name IO_STANDARD "3.3 V SCHMITT TRIGGER" -to BCLK
|
||
|
||
set_global_assignment -name OPTIMIZATION_MODE BALANCED
|
||
set_global_assignment -name VHDL_FILE audio_signal_detector.vhd
|
||
set_global_assignment -name VHDL_FILE flash_controller.vhd
|
||
set_global_assignment -name VHDL_FILE stereo_detect.vhd
|
||
set_global_assignment -name VHDL_FILE iox_glue.vhdl
|
Also available in: Unified diff
Break out audio detector to own module and improve. Now with zero crossing support.