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, fm_dat_2, fm_dat_3, fm_dat_4, fm_dat_5 : in std_logic_vector(15 downto 0);
		fm_en_1,  fm_en_2,  fm_en_3,  fm_en_4,  fm_en_5  : in std_logic;  
		
		--Select output (synthesizer or vocoder)
		select_out : 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

	--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;
		
	-- Vocoder declaration
	component bpfilterbank_imp is
		port ( Clk : in std_logic;
			input : in signed (31 downto 0);
			output : out signed(31 downto 0)
		);
			--	outp : out signed(0 downto 0)); --TEST
	end component;

	
	--signals
	signal audio_clock 	: unsigned(1 downto 0) := "00";
	
	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 voc_tmp_out   : signed(31 downto 0);
	
	signal fm_out_1 : 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);
begin
	
	process (clk) begin
		-- FM synthesizer output is sum of fm voices
		if reset_n = '0' then
			fm_sample_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;
			
	end process;
			
	-- Output can be straight from syntehsizer or vocoder
	out_sample <= fm_sample_out when select_out = '0' else vocoder_out;
			
	-- Instantiate vocoder
	voc : bpfilterbank_imp port map (
		Clk => clk,
		input => signed(x"00" & in_sample),
		output => voc_tmp_out
	);
	vocoder_out <= std_logic_vector(voc_tmp_out(23 downto 0));			

	-- Instantiate an FM patch for each voice 
	fmPatch1 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,
		enable => fm_en_1,		
		mode => SW(17 downto 13),		
		pitch  => fm_dat_1,		
		fm_out => fm_out_1
	);
	fmPatch2 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,		
		enable => fm_en_2,		
		mode => SW(17 downto 13),		
		pitch  => fm_dat_2,		
		fm_out => fm_out_2
	);
	fmPatch3 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,		
		enable => fm_en_3,		
		mode => SW(17 downto 13),		
		pitch  => fm_dat_3,		
		fm_out => fm_out_3
	);
	fmPatch4 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,		
		enable => fm_en_4,		
		mode => SW(17 downto 13),		
		pitch  => fm_dat_4,		
		fm_out => fm_out_4
	);
	fmPatch5 : fmPatch port map (
		clk => clk,
		reset_n => reset_n,		
		enable => fm_en_5,		
		mode => SW(17 downto 13),		
		pitch  => fm_dat_5,		
		fm_out => fm_out_5
	);

	--generate audio clock for audio codec driver
	process (clk)
		begin
			if rising_edge(clk) then
				audio_clock <= audio_clock + "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;