mirror of
https://github.com/MiSTer-devel/Arcade-ComputerSpace_MiSTer.git
synced 2026-04-19 03:02:33 +00:00
426 lines
17 KiB
VHDL
426 lines
17 KiB
VHDL
---------------------------------------------------------------------------------
|
|
-- Computer_space_sound by Dar (darfpga@aol.fr) (20/11/2017)
|
|
-- http://darfpga.blogspot.fr
|
|
---------------------------------------------------------------------------------
|
|
-- Educational use only
|
|
-- Do not redistribute synthetized file with roms
|
|
-- Do not redistribute roms whatever the form
|
|
-- Use at your own risk
|
|
---------------------------------------------------------------------------------
|
|
-- Principle :
|
|
--
|
|
-- * Exact from original design (schematics)
|
|
-- saucer_missile_sound from motion board
|
|
-- rocket_missile_sound from motion board
|
|
-- turn_sound from motion board
|
|
--
|
|
-- * Filtered white noise & spectrum shaping from 8_11.hex files waveform and
|
|
-- spectrum shapes
|
|
--
|
|
-- Simulation :
|
|
--
|
|
-- * White noise
|
|
-- pseudo-random generator 17bits (X^17 + X^14 +1)
|
|
-- intialisation with 0xACE1
|
|
-- 64 taps memory for filtering input
|
|
--
|
|
-- * Filtering
|
|
--
|
|
-- 12 filters 64 taps dft @ fech = 11kHz
|
|
-- filters centers : 1,3,5,7,9,11,13,15,17,19,21,23 / 64*11kHz
|
|
-- => fc = 172Hz, 516Hz, 860Hz, ... 3953Hz
|
|
-- [k_fc : 0 to 11]
|
|
--
|
|
-- Hamming window weighting
|
|
--
|
|
-- filter_bank(k_fc)(coeff) = cos(2*pi*fc*coeff/64)*hamming_64(coeff)
|
|
-- [coeff : 0 to 63]
|
|
--
|
|
-- filter_output(k_fc) = sum(pseudo_random(coeff)*filter_bank(k_fc)(coeff))
|
|
-- [sum over coeff 0 to 63]
|
|
--
|
|
-- * Noise voices
|
|
--
|
|
-- Spectrum shaping by linear sum of filter output with specific weighting
|
|
-- for each voice (pond).
|
|
--
|
|
-- voice(k_voice) = sum(filter_ouput(k_fc)*pond(k_fc)(k_voice))
|
|
-- [sum over k_fc 0 to 11]
|
|
--
|
|
--
|
|
-- * Explosion
|
|
--
|
|
-- Randomly frequency-modulated square wave centered at about 100 Hz
|
|
-- with intermittent noise.
|
|
--
|
|
---------------------------------------------------------------------------------
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.std_logic_unsigned.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
entity computer_space_sound is
|
|
port(
|
|
clock_50 : in std_logic;
|
|
reset : in std_logic;
|
|
|
|
sound_switch : in std_logic_vector(7 downto 0);
|
|
saucer_missile_sound : in std_logic;
|
|
rocket_missile_sound : in std_logic;
|
|
turn_sound : in std_logic;
|
|
|
|
audio_gate : in std_logic;
|
|
|
|
audio : out integer range -32768 to 32767
|
|
);
|
|
end computer_space_sound;
|
|
|
|
architecture struct of computer_space_sound is
|
|
subtype nb_filters is integer range 0 to 11;
|
|
subtype nb_coeffs is integer range 0 to 63;
|
|
|
|
type t_filter_coeffs is array(nb_coeffs) of integer range -128 to 127;
|
|
type t_filter_bank is array(nb_filters) of t_filter_coeffs;
|
|
|
|
constant filter_bank: t_filter_bank := (
|
|
(10, 10, 11, 12, 14, 15, 17, 18, 20, 20, 20, 20, 18, 15, 11, 6, 0, -7, -16, -25, -35, -46, -57, -68, -79, -89, -98,-107,-114,-120,-124,-126,-127,-126,-123,-118,-112,-104, -96, -86, -76, -65, -54, -44, -33, -24, -15, -7, 0, 6, 10, 14, 16, 17, 18, 18, 17, 16, 14, 13, 12, 11, 10, 10), -- 2
|
|
(10, 10, 9, 8, 6, 2, -4, -11, -20, -28, -36, -42, -44, -41, -32, -19, 0, 22, 45, 67, 86, 97, 101, 95, 79, 54, 23, -12, -47, -79,-105,-121,-127,-121,-104, -78, -46, -12, 22, 53, 76, 91, 96, 92, 81, 63, 42, 20, 0, -17, -29, -37, -39, -37, -32, -25, -17, -10, -3, 1, 5, 7, 9, 10), -- 4
|
|
(10, 9, 6, 1, -6, -13, -20, -23, -20, -9, 7, 27, 44, 53, 49, 30, 0, -36, -68, -87, -86, -62, -20, 31, 79, 110, 116, 94, 47, -12, -70,-112,-127,-111, -70, -12, 46, 92, 113, 107, 76, 30, -19, -59, -81, -81, -63, -33, 0, 28, 44, 47, 39, 23, 6, -8, -17, -19, -17, -11, -5, 1, 6, 9), -- 6
|
|
(10, 8, 2, -6, -14, -17, -11, 2, 20, 32, 31, 12, -18, -47, -57, -41, 0, 48, 80, 77, 35, -28, -85,-107, -79, -11, 66, 116, 114, 59, -25, -98,-127, -98, -24, 58, 112, 113, 64, -11, -76,-102, -81, -27, 33, 72, 74, 44, 0, -37, -52, -42, -16, 11, 27, 28, 17, 2, -10, -14, -12, -5, 2, 8), -- 8
|
|
(10, 7, -2, -11, -14, -5, 11, 24, 20, -3, -31, -40, -18, 25, 57, 50, 0, -59, -80, -41, 35, 94, 85, 11, -79,-115, -66, 35, 114, 110, 25, -81,-127, -80, 24, 109, 112, 34, -64,-111, -76, 10, 81, 89, 33, -38, -74, -54, 0, 45, 52, 22, -16, -35, -27, -3, 17, 20, 10, -4, -12, -10, -2, 6), -- 10
|
|
(10, 5, -6, -13, -6, 11, 20, 7, -20, -31, -7, 32, 44, 5, -49, -57, 0, 67, 68, -9, -86, -76, 20, 103, 79, -33,-116, -77, 47, 125, 70, -60,-127, -60, 70, 123, 46, -75,-113, -32, 76, 98, 19, -72, -81, -8, 63, 62, 0, -52, -44, 5, 39, 29, -6, -27, -17, 6, 17, 9, -5, -11, -6, 5), -- 12
|
|
(10, 3, -9, -10, 6, 17, 4, -21, -20, 15, 36, 4, -44, -33, 32, 61, 0, -73, -45, 55, 86, -10,-101, -51, 79, 102, -23,-121, -47, 97, 105, -37,-127, -37, 104, 95, -46,-118, -22, 98, 76, -48, -96, -9, 81, 52, -42, -67, 0, 56, 29, -30, -39, 4, 32, 13, -17, -18, 3, 15, 5, -9, -9, 3), -- 14
|
|
(10, 1, -11, -4, 14, 8, -17, -15, 20, 25, -20, -37, 18, 50, -11, -64, 0, 75, 16, -83, -35, 86, 57, -83, -79, 73, 98, -57,-114, 36, 124, -12,-127, -12, 123, 36,-112, -56, 96, 71, -76, -79, 54, 82, -33, -78, 15, 70, 0, -58, -10, 45, 16, -33, -18, 21, 17, -13, -14, 7, 12, -3, -10, 1), -- 16
|
|
(10, -1, -11, 4, 14, -8, -17, 15, 20, -25, -20, 37, 18, -50, -11, 64, 0, -75, 16, 83, -35, -86, 57, 83, -79, -73, 98, 57,-114, -36, 124, 12,-127, 12, 123, -36,-112, 56, 96, -71, -76, 79, 54, -82, -33, 78, 15, -70, 0, 58, -10, -45, 16, 33, -18, -21, 17, 13, -14, -7, 12, 3, -10, -1), -- 18
|
|
(10, -3, -9, 10, 6, -17, 4, 21, -20, -15, 36, -4, -44, 33, 32, -61, 0, 73, -45, -55, 86, 10,-101, 51, 79,-102, -23, 121, -47, -97, 105, 37,-127, 37, 104, -95, -46, 118, -22, -98, 76, 48, -96, 9, 81, -52, -42, 67, 0, -56, 29, 30, -39, -4, 32, -13, -17, 18, 3, -15, 5, 9, -9, -3), -- 20
|
|
(10, -5, -6, 13, -6, -11, 20, -7, -20, 31, -7, -32, 44, -5, -49, 57, 0, -67, 68, 9, -86, 76, 20,-103, 79, 33,-116, 77, 47,-125, 70, 60,-127, 60, 70,-123, 46, 75,-113, 32, 76, -98, 19, 72, -81, 8, 63, -62, 0, 52, -44, -5, 39, -29, -6, 27, -17, -6, 17, -9, -5, 11, -6, -5), -- 22
|
|
(10, -7, -2, 11, -14, 5, 11, -24, 20, 3, -31, 40, -18, -25, 57, -50, 0, 59, -80, 41, 35, -94, 85, -11, -79, 115, -66, -35, 114,-110, 25, 81,-127, 80, 24,-109, 112, -34, -64, 111, -76, -10, 81, -89, 33, 38, -74, 54, 0, -45, 52, -22, -16, 35, -27, 3, 17, -20, 10, 4, -12, 10, -2, -6));-- 24
|
|
|
|
signal add : integer range -128*64 to 128*64-1;
|
|
type t_filtered_signal is array(nb_filters) of integer range -128*64 to 128*64-1;
|
|
signal filtered_signal : t_filtered_signal;
|
|
|
|
signal noise_cnt : integer range 0 to 8191;
|
|
signal noise_reg : std_logic_vector(63 downto 0); -- pseudo_random shift_register and filter tap
|
|
|
|
subtype nb_voices is integer range 0 to 2;
|
|
type t_pond is array(nb_filters) of integer range 0 to 16;
|
|
type t_pond_bank is array(nb_voices) of t_pond;
|
|
constant pond : t_pond_bank := (
|
|
( 0, 16, 16, 16, 8, 8, 4, 4, 2, 2, 1, 0 ), -- voice 1 - rocket thrust
|
|
(16, 8, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), -- voice 2 - back ambiance
|
|
( 0, 0, 0, 2, 4, 8, 8, 16, 16, 16, 16, 16));-- voice 3 - explosion background noise
|
|
|
|
signal add_v : integer range -128*64*16 to 128*64*16-1;
|
|
type t_voices is array(nb_voices) of integer range -128*64*16 to 128*64*16-1;
|
|
signal voices : t_voices;
|
|
|
|
signal ambiance : integer range -32768 to 32767;
|
|
signal thrust : integer range -32768 to 32767;
|
|
signal explosion : integer range -32768 to 32767;
|
|
signal turn : integer range -32768 to 32767;
|
|
signal saucer_missile : integer range -32768 to 32767;
|
|
signal rocket_missile : integer range -32768 to 32767;
|
|
|
|
|
|
subtype nb_rndmap is integer range 0 to 15;
|
|
|
|
type t_period_map is array(nb_rndmap) of integer range 0 to 127;
|
|
constant period_map : t_period_map := (1,2,3,4,5,6,8,12,18,22,24,25,26,27,28,29);
|
|
|
|
type t_delta_map is array(nb_rndmap) of integer range 0 to 8191;
|
|
constant delta_map : t_delta_map := (500,1000,1500,2000,2500,3000,4000,4000,4500,5000,5500,6000,6500,7000,7500,8000);
|
|
|
|
constant amplitude : integer range -32768 to 32767 := 25000;
|
|
|
|
signal rndvalue : nb_rndmap;
|
|
signal square_wave : integer range -32768 to 32767;
|
|
signal noise_gate : boolean;
|
|
signal noisy_square_wave_base : integer range -65536 to 65535;
|
|
signal noisy_square_wave : integer range -32768 to 32767;
|
|
signal explosion_base : integer range -32768 to 32767;
|
|
|
|
|
|
signal explosion_cmd_r : std_logic;
|
|
|
|
begin
|
|
|
|
|
|
noise : process(clock_50, reset)
|
|
begin
|
|
if reset = '1' then
|
|
noise_reg <= X"000000000000ACE1";
|
|
noise_cnt <= 0;
|
|
else
|
|
if rising_edge(clock_50) then
|
|
if noise_cnt = 4544 then -- 11kHz
|
|
noise_cnt <= 0;
|
|
noise_reg <= noise_reg(62 downto 0) & not (noise_reg(16) xor noise_reg(13));
|
|
else
|
|
noise_cnt <= noise_cnt + 1;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- compute filtered noise and voices
|
|
filter_and_voice : process(clock_50, reset)
|
|
type t_stage is (s_init, s_filter, s_voice);
|
|
variable stage : t_stage;
|
|
variable filter : nb_filters;
|
|
variable coeff : nb_coeffs;
|
|
variable delta : integer range -128*64 to 128*64-1;
|
|
variable voice : nb_voices;
|
|
variable delta_v : integer range -128*64*16 to 128*64*16-1;
|
|
|
|
begin
|
|
if reset = '1' then
|
|
stage := s_init;
|
|
else
|
|
if rising_edge(clock_50) then
|
|
|
|
case stage is
|
|
|
|
when s_init =>
|
|
if noise_cnt = 0 then -- it's time to read the new produced noise sample
|
|
stage := s_filter;
|
|
filter := 0; -- start with filter 0 and coeff 0
|
|
coeff := 0;
|
|
end if;
|
|
|
|
when s_filter =>
|
|
|
|
if noise_reg(coeff) = '0' then -- 0/1 => -1/+1 for signed computation
|
|
delta := -filter_bank(filter)(coeff);
|
|
else
|
|
delta := filter_bank(filter)(coeff);
|
|
end if;
|
|
|
|
if coeff = 0 then
|
|
add <= delta; -- first coeff, reset accumulator
|
|
else
|
|
add <= add+delta; -- accumulator
|
|
end if;
|
|
|
|
if coeff = 63 then
|
|
filtered_signal(filter) <= add+delta; -- lacth result = final accumulation
|
|
end if;
|
|
|
|
if coeff < 63 then -- scan coeff from 0 to 63
|
|
coeff := coeff + 1;
|
|
else -- last coeff go to next filter
|
|
coeff := 0;
|
|
if filter = 11 then -- last filter go to voice computation
|
|
stage := s_voice;
|
|
filter := 0; -- start with filter 0 and voice 0
|
|
voice := 0;
|
|
else
|
|
filter := filter + 1; -- scan filter from 0 to 11
|
|
end if;
|
|
end if;
|
|
|
|
when s_voice =>
|
|
|
|
delta_v := filtered_signal(filter) * pond(voice)(filter);
|
|
|
|
if filter = 0 then
|
|
add_v <= delta_v; -- first filter, reset accumulator
|
|
else
|
|
add_v <= add_v+delta_v; -- accumulator
|
|
end if;
|
|
|
|
if filter = 11 then
|
|
voices(voice) <= add_v+delta_v; -- latch result, final accumulation
|
|
end if;
|
|
|
|
if filter < 11 then -- scan filter from 0 to 11
|
|
filter := filter + 1;
|
|
else -- last filter go to next voice
|
|
filter := 0;
|
|
if voice = 2 then
|
|
stage := s_init; -- last voice go to wait for next sample
|
|
else
|
|
voice := voice + 1; -- scan voice from 0 to 2
|
|
end if;
|
|
end if;
|
|
|
|
when others => stage := s_init;
|
|
end case;
|
|
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- explosion wave generation
|
|
square_wave_gen : process(clock_50, reset)
|
|
variable rise : boolean := false;
|
|
variable delta : integer range 0 to 8191 := 0;
|
|
variable period_cnt : integer range 0 to 127 := 0;
|
|
begin
|
|
if reset = '1' then
|
|
square_wave <= 0;
|
|
rise := false;
|
|
delta := 0;
|
|
period_cnt := 0;
|
|
else
|
|
if rising_edge(clock_50) then
|
|
if noise_cnt = 0 then -- it's time to make new sample
|
|
if rise and (square_wave < amplitude) then -- rising edge
|
|
if square_wave + delta > amplitude then
|
|
square_wave <= amplitude; -- upper limit
|
|
else
|
|
square_wave <= square_wave + delta; -- inc value
|
|
end if;
|
|
elsif not(rise) and (square_wave > -amplitude) then -- falling edge
|
|
if square_wave - delta < -amplitude then
|
|
square_wave <= -amplitude; -- lower limit
|
|
else
|
|
square_wave <= square_wave - delta; -- dec value
|
|
end if;
|
|
end if;
|
|
|
|
if period_cnt = 70 then -- reached the end of half-cycle
|
|
period_cnt := period_map(rndvalue); -- random start count for modulating frequency
|
|
delta := delta_map(rndvalue); -- random delta value fot obtaining waveform irregularity
|
|
rise := not(rise); -- alternate rise and fall
|
|
noise_gate <= rndvalue < 6; -- random gate for intermittent noise
|
|
else
|
|
period_cnt := period_cnt + 1;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
rndvalue <= to_integer(unsigned(noise_reg(3 downto 0))); -- 4-bit random value
|
|
|
|
noisy_square_wave_base <= square_wave + voices(1) when noise_gate else square_wave; -- add intermittent noise
|
|
|
|
process(noisy_square_wave_base) -- clip noise exceeding amplitude
|
|
begin
|
|
if (noisy_square_wave_base > amplitude) then
|
|
noisy_square_wave <= amplitude;
|
|
elsif (noisy_square_wave_base < -amplitude) then
|
|
noisy_square_wave <= -amplitude;
|
|
else
|
|
noisy_square_wave <= noisy_square_wave_base;
|
|
end if;
|
|
end process;
|
|
|
|
explosion_base <= noisy_square_wave + voices(2) / 64; -- add extra high-freq small noise
|
|
|
|
-- quick ADSR to control explosion level enveloppe
|
|
thrust_level : process(clock_50, reset)
|
|
type t_stage is (s_wait, s_attack, s_decay, s_sustain, s_release);
|
|
variable stage : t_stage := s_wait;
|
|
constant attack : integer range 0 to 32767 := 250;
|
|
constant decay : integer range 0 to 32767 := 200;
|
|
constant sustain : integer range 0 to 32767 := 4000;
|
|
constant release : integer range 0 to 32767 := 31100;
|
|
constant inc_1 : integer range 0 to 32767 := 130;
|
|
constant dec_1 : integer range 0 to 32767 := 7;
|
|
constant dec_2 : integer range 0 to 32767 := 1;
|
|
variable level : integer range 0 to 32767 := 0;
|
|
variable timer : integer range 0 to 32767 := 0;
|
|
variable update_level : std_logic;
|
|
begin
|
|
if reset = '1' then
|
|
stage := s_wait;
|
|
explosion_cmd_r <= '0';
|
|
else
|
|
if rising_edge(clock_50) then
|
|
|
|
update_level := '0';
|
|
if noise_cnt = 0 then -- it's time to update timer
|
|
update_level := '1';
|
|
if timer > 0 then
|
|
timer := timer - 1;
|
|
end if;
|
|
end if;
|
|
|
|
-- always wait for explosion trigger
|
|
explosion_cmd_r <= sound_switch(4);
|
|
if explosion_cmd_r = '0' and sound_switch(4) = '1' then
|
|
stage := s_attack;
|
|
timer := attack; -- set attack duration
|
|
end if;
|
|
|
|
case stage is
|
|
|
|
when s_wait =>
|
|
level := 0;
|
|
|
|
when s_attack => -- increment level during attack
|
|
if update_level = '1' then
|
|
if level < (32767-inc_1) then -- ensure no overflow
|
|
level := level + inc_1;
|
|
else
|
|
level := 32767;
|
|
end if;
|
|
end if;
|
|
if timer = 0 then stage := s_decay; timer := decay; end if; -- set decay duration
|
|
|
|
when s_decay => -- decrement level during decay
|
|
if update_level = '1' then
|
|
if level > dec_1 then -- ensure no underflow
|
|
level := level - dec_1;
|
|
else
|
|
level := 0;
|
|
end if;
|
|
end if;
|
|
if timer = 0 then stage := s_sustain; timer := sustain; end if; -- set sustain duration
|
|
|
|
when s_sustain => -- no level change during sustain
|
|
if timer = 0 then stage := s_release; end if; -- set release duration
|
|
|
|
when s_release => -- decrement level during release
|
|
if update_level = '1' then
|
|
if level > dec_2 then -- ensure no underflow
|
|
level := level - dec_2;
|
|
else
|
|
level := 0; -- release ends when level reachs 0
|
|
stage := s_wait;
|
|
end if;
|
|
end if;
|
|
|
|
when others => stage := s_wait;
|
|
|
|
end case;
|
|
|
|
explosion <= ((explosion_base/2)*level)/32768; -- apply enveloppe
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
-- sound_switch(1): rocket rotate
|
|
-- sound_switch(2): rocket thrust
|
|
-- sound_switch(3): rocket missile
|
|
-- sound_switch(4): explosion
|
|
-- sound_switch(5): saucer missile
|
|
-- sound_switch(7): background ambience (Not use)
|
|
|
|
ambiance <= voices(1)/16; -- divide by power of two
|
|
|
|
thrust <= voices(0)/8 when sound_switch(2) = '1' else 0; -- divide by power of two
|
|
|
|
turn <= 0 when sound_switch(1) = '0' else
|
|
-500 when turn_sound = '0' else
|
|
500;
|
|
|
|
saucer_missile <= 0 when sound_switch(5) = '0' else
|
|
-500 when saucer_missile_sound = '0' else
|
|
500;
|
|
|
|
rocket_missile <= 0 when sound_switch(3) = '0' else
|
|
-500 when rocket_missile_sound = '0' else
|
|
500;
|
|
|
|
audio <= ambiance + thrust + explosion + turn + saucer_missile + rocket_missile when audio_gate = '1' else 0;
|
|
|
|
|
|
end struct;
|