library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity synthesizer is
	port(
		clk, reset_n : in std_logic;
		
		--FM synthesis inputs (from NIOS processor via Avalon bus)
		fm_dat_1 : in std_logic_vector(15 downto 0);
		fm_en_1  : in std_logic;  
		
		-- Switches 
		SW  : in std_logic_vector(17 downto 0);

		-- I2C bus (from pins)    
		I2C_SDAT : inout std_logic; -- I2C Data
		I2C_SCLK : out std_logic;   -- I2C Clock

		--Audio CODEC (from pins)
		AUD_ADCLRCK : inout std_logic; --ADC LR Clock
		AUD_ADCDAT : in std_logic;                          -- ADC Data
		AUD_DACLRCK : inout std_logic;                      -- DAC LR Clock
		AUD_DACDAT : out std_logic;                         -- DAC Data
		AUD_BCLK : inout std_logic;                         -- Bit-Stream Clock
		AUD_XCK : out std_logic                            -- Chip Clock
	);
end synthesizer;

architecture srtl of synthesizer is
	
component bpfilterbank_imp is
	port( clk : in std_logic;
   inmod : in signed (31 downto 0);
   outmod1 : out signed(31 downto 0);
   outmod2 : out signed(31 downto 0);
   outmod3 : out signed(31 downto 0);
   outmod4 : out signed(31 downto 0);
   outmod5 : out signed(31 downto 0);
   outmod6 : out signed(31 downto 0);
   outmod7 : out signed(31 downto 0);
   outmod8 : out signed(31 downto 0);
   outmod9 : out signed(31 downto 0);
   outmod10 : out signed(31 downto 0));
end component;

component sq_lowpass_imp is
	port ( 	clk : in std_logic;
			input1 : in signed(31 downto 0);	-- 16 bit long amplitude input comes from the audio codec
			input2 : in signed(31 downto 0);
			input3 : in signed(31 downto 0);
			input4 : in signed(31 downto 0);
			input5 : in signed(31 downto 0);
			input6 : in signed(31 downto 0);
			input7 : in signed(31 downto 0);
			input8 : in signed(31 downto 0);
			input9 : in signed(31 downto 0);
			input10 : in signed(31 downto 0);
			output1 : out signed(31 downto 0);
			output2 : out signed(31 downto 0);
			output3 : out signed(31 downto 0);
			output4 : out signed(31 downto 0);
			output5 : out signed(31 downto 0);
			output6 : out signed(31 downto 0);
			output7 : out signed(31 downto 0);
			output8 : out signed(31 downto 0);
			output9 : out signed(31 downto 0);
			output10 : out signed(31 downto 0));
end component;

	--FM Patch declaration
	component fmPatch is
		port(
			clk, reset_n : in std_logic;
		
			--1 when key is pressed and sounding, 0 when not sounding
			enable : in std_logic;
		
			--Allow up to 32 different patches
			mode   : in std_logic_vector(4 downto 0);
	
			--pitch (encoded as a carrier oscillator period)
			pitch  : in std_logic_vector(15 downto 0);

			--24-bit audio output for one voice
			fm_out : out std_logic_vector(23 downto 0)
		);
	end component;

	--Audio CODEC interface declaration
	component de2_wm8731_audio is
		port(
			clk: in std_logic; --Audio CODEC chip clock (AUD_XCK)
			reset_n : in std_logic; 
			data_in : in std_logic_vector(23 downto 0); --Sample data out
			data_out: out std_logic_vector(23 downto 0); --Microphone sample data
			-- Audio interface signals
			AUD_ADCLRCK  : out std_logic;       --    Audio CODEC ADC LR Clock
			AUD_ADCDAT   : in  std_logic;       --    Audio CODEC ADC Data
			AUD_DACLRCK  : out std_logic;       --    Audio CODEC DAC LR Clock
			AUD_DACDAT   : out std_logic;       --    Audio CODEC DAC Data
			AUD_BCLK     : inout std_logic      --    Audio CODEC Bit-Stream Clock
		);
	end component;
	
	--Audio CODEC configurator (drives i2C on reset)
	component de2_i2c_av_config is
		port(
			iCLK : in std_logic;
			iRST_N : in std_logic;
			I2C_SCLK : out std_logic;
			I2C_SDAT : inout std_logic
		);
	end component;
	
	--signals
	signal audio_clock 	: unsigned(1 downto 0) := "00";
	signal vocoder_clock 	: unsigned(2 downto 0) := "000";
	
	--select vocoder or synthesizer output
	signal out_select   : std_logic;
	
	signal out_sample  	: std_logic_vector(23 downto 0);
	signal in_sample   	: std_logic_vector(23 downto 0);
	
	signal fm_sample_out : std_logic_vector(23 downto 0);
	signal vocoder_out   : std_logic_vector(23 downto 0);
	signal vocoder_outbuffer   : signed(63 downto 0);
		signal vocoder_outbuffer2   : signed(31 downto 0);
	signal vocoder_in : std_logic_vector(23 downto 0);
		
	signal fm_out_1 : std_logic_vector(23 downto 0);
	signal fm_out_1buffer : std_logic_vector(23 downto 0);
	signal fm_out_2 : std_logic_vector(23 downto 0);
	signal fm_out_3 : std_logic_vector(23 downto 0);
	signal fm_out_4 : std_logic_vector(23 downto 0);
	signal fm_out_5 : std_logic_vector(23 downto 0);
	
	signal filteroutbuffer1 : signed(31 downto 0);
	signal filteroutbuffer2 : signed(31 downto 0);
	signal filteroutbuffer3 : signed(31 downto 0);
	signal filteroutbuffer4 : signed(31 downto 0);
	signal filteroutbuffer5 : signed(31 downto 0);
	signal filteroutbuffer6 : signed(31 downto 0);
	signal filteroutbuffer7 : signed(31 downto 0);
	signal filteroutbuffer8 : signed(31 downto 0);
	signal filteroutbuffer9 : signed(31 downto 0);
	signal filteroutbuffer10 : signed(31 downto 0);
	
	signal modoutbuffer1 : signed(31 downto 0);
	signal modoutbuffer2 : signed(31 downto 0);
	signal modoutbuffer3 : signed(31 downto 0);
	signal modoutbuffer4 : signed(31 downto 0);
	signal modoutbuffer5 : signed(31 downto 0);
	signal modoutbuffer6 : signed(31 downto 0);
	signal modoutbuffer7 : signed(31 downto 0);
	signal modoutbuffer8 : signed(31 downto 0);
	signal modoutbuffer9 : signed(31 downto 0);
	signal modoutbuffer10 : signed(31 downto 0);
	
	signal caroutbuffer1 : signed(31 downto 0);
	signal caroutbuffer2 : signed(31 downto 0);
	signal caroutbuffer3 : signed(31 downto 0);
	signal caroutbuffer4 : signed(31 downto 0);
	signal caroutbuffer5 : signed(31 downto 0);
	signal caroutbuffer6 : signed(31 downto 0);
	signal caroutbuffer7 : signed(31 downto 0);
	signal caroutbuffer8 : signed(31 downto 0);
	signal caroutbuffer9 : signed(31 downto 0);
	signal caroutbuffer10 : signed(31 downto 0);
	
	signal finmodbuffer1 : signed(31 downto 0);
	signal finmodbuffer2 : signed(31 downto 0);
	signal finmodbuffer3 : signed(31 downto 0);
	signal finmodbuffer4 : signed(31 downto 0);
	signal finmodbuffer5 : signed(31 downto 0);
	signal finmodbuffer6 : signed(31 downto 0);
	signal finmodbuffer7 : signed(31 downto 0);
	signal finmodbuffer8 : signed(31 downto 0);
	signal finmodbuffer9 : signed(31 downto 0);
	signal finmodbuffer10 : signed(31 downto 0);
	
	signal counter : signed(3 downto 0);
					
begin

	out_select <= SW(0);
	
	process (clk) begin
		-- FM synthesizer output is sum of fm voices
		if reset_n = '0' then
			fm_sample_out <= x"000000";
--			vocoder_out   <= x"000000";
		else
			fm_sample_out <= std_logic_vector(signed(fm_out_1) + signed(fm_out_2) 
							+ signed(fm_out_3) + signed(fm_out_4) + signed(fm_out_5));
		end if;
			
		-- Output can be straight from syntehsizer or vocoder
		if reset_n = '0' then
			out_sample <= x"000000";
		elsif out_select = '0' then
			out_sample <= fm_sample_out;
		else
			out_sample <= std_logic_vector(signed(vocoder_out));
		end if;
		
		if SW(17) = '0' then
			fm_out_1 <= fm_out_1buffer;
		else 
			fm_out_1 <= x"000000";
		end if;
			
--		if counter(3) = '0' then
--			vocoder_in <= in_sample;
--		else
--			vocoder_in <= fm_out_1;
--		end if;

		if SW(16) = '0' then
			vocoder_outbuffer <= finmodbuffer1*caroutbuffer1 + 
								finmodbuffer5*caroutbuffer5 +
								finmodbuffer10*caroutbuffer10; 
		else 
			vocoder_outbuffer2 <= caroutbuffer1 + caroutbuffer2 + caroutbuffer3 + caroutbuffer4 +
			caroutbuffer5 + caroutbuffer6 + caroutbuffer7 + caroutbuffer8 +
			caroutbuffer9 + caroutbuffer10;
			
			vocoder_outbuffer <= vocoder_outbuffer2 & x"00000000";
		end if;

	end process;
			

	-- Instantiate an FM patch for each voice 
	fmPatch1 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,
		enable => fm_en_1,		
		mode => "00" & SW(4 downto 2), -- sin="00000", saw="00001" 
		pitch  => fm_dat_1,		
		fm_out => fm_out_1buffer
	);
--	fmPatch2 : fmPatch port map (
--		clk => clk,
--		reset_n => reset_n,		
--		enable => fm_en_2,		
--		mode => "00" & SW(14 downto 12),		
--		pitch  => fm_dat_2,		
--		fm_out => fm_out_2
--	);
--	fmPatch3 : fmPatch port map (
--		clk => clk,
--		reset_n => reset_n,		
--		enable => fm_en_3,		
--		mode => "00" & SW(11 downto 9),		
--		pitch  => fm_dat_3,		
--		fm_out => fm_out_3
--	);
--	fmPatch4 : fmPatch port map (
--		clk => clk,
--		reset_n => reset_n,		
--		enable => fm_en_4,		
--		mode => "000" & SW(8 downto 7),		
--		pitch  => fm_dat_4,		
--		fm_out => fm_out_4 
--	);
--	fmPatch5 : fmPatch port map (
--		clk => clk,
--		reset_n => reset_n,		
--		enable => fm_en_5,		
--		mode => "00000",		
--		pitch  => fm_dat_5,		
--		fm_out => fm_out_5
--	);


	bpfilter1 : bpfilterbank_imp port map (
		clk => vocoder_clock(2),
		inmod => signed(in_sample) & "00000000",
		outmod1 => modoutbuffer1,
		outmod2 => modoutbuffer2,
		outmod3 => modoutbuffer3,
		outmod4 => modoutbuffer4,
		outmod5 => modoutbuffer5,
		outmod6 => modoutbuffer6,
		outmod7 => modoutbuffer7,
		outmod8 => modoutbuffer8,
		outmod9 => modoutbuffer9,
		outmod10 => modoutbuffer10);
		
	bpfilter2 : bpfilterbank_imp port map (
		clk => vocoder_clock(2),
		inmod => signed(fm_out_1) & "00000000",
		outmod1 => caroutbuffer1,
		outmod2 => caroutbuffer2,
		outmod3 => caroutbuffer3,
		outmod4 => caroutbuffer4,
		outmod5 => caroutbuffer5,
		outmod6 => caroutbuffer6,
		outmod7 => caroutbuffer7,
		outmod8 => caroutbuffer8,
		outmod9 => caroutbuffer9,
		outmod10 => caroutbuffer10);
		
		sqlpfilter1: sq_lowpass_imp port map 
		(vocoder_clock(2),modoutbuffer1,modoutbuffer2,modoutbuffer3,modoutbuffer4,modoutbuffer5,
		modoutbuffer6,modoutbuffer7,modoutbuffer8,modoutbuffer9,modoutbuffer10,
		finmodbuffer1,finmodbuffer2,finmodbuffer3,finmodbuffer4,finmodbuffer5,
		finmodbuffer6,finmodbuffer7,finmodbuffer8,finmodbuffer9,finmodbuffer10);
		

--		output => vocoder_outbuffer);
	

	vocoder_out <= "0" & std_logic_vector(vocoder_outbuffer(63 downto 41));
	--vocoder_out <= in_sample;
				
	--generate audio clock for audio codec driver
	process (clk)
		begin
			if rising_edge(clk) then
				audio_clock <= audio_clock + "1";
				vocoder_clock <= vocoder_clock + "1";
				counter <= counter + "1";
			end if;
	end process;

	AUD_XCK <= audio_clock(1);

	--audio codec configurator (sets audio codec to 24-bit audio @ 48kHz)
	i2c : de2_i2c_av_config port map (
		iCLK     => clk,
		iRST_n   => '1',
		I2C_SCLK => I2C_SCLK,
		I2C_SDAT => I2C_SDAT
	);
	
	--audio codec driver (serializes out_sample and deserializes mic into in_sample)
	V1: de2_wm8731_audio port map (
		clk => audio_clock(1),
		reset_n => '1',
		data_in => out_sample,
  
		data_out => in_sample,
		-- Audio interface signals
		AUD_ADCLRCK  => AUD_ADCLRCK,
		AUD_ADCDAT   => AUD_ADCDAT,
		AUD_DACLRCK  => AUD_DACLRCK,
		AUD_DACDAT   => AUD_DACDAT,
		AUD_BCLK     => AUD_BCLK
	);
end architecture;