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

entity 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 fmPatch;

architecture fmPrtl of fmPatch is

	-- FM operator declaration
	component fmOperator is
		port (
			clk : in std_logic;  
			reset_n : in std_logic;
			mute    : in std_logic;
			-- Oscillator select:
			--	0 = sine
			--  1 = saw
			--  2 = square
			--  3 = white noise (random)
			osc_select   : in std_logic_vector(1 downto 0);
		
			-- Oscillator frequency
			osc_freq   : in unsigned(15 downto 0);
			
			-- FM modulator
			modulator  : in unsigned(23 downto 0);
			mod_factor : in unsigned(4 downto 0);
			  
			-- Sample out
			fm_out   : out std_logic_vector(23 downto 0)
		);
	end component;

	--outputs of different patches
	--		patch 0 - 3 are basic single waveforms (sin, saw, sqaure, random)
	signal patch0, patch1, patch2, patch3 : std_logic_vector(23 downto 0);
	
	--fmOperator outputs
	signal fmOp_out_0, fmOp_out_1, fmOp_out_2, fmOp_out_3 : std_logic_vector(23 downto 0);
	signal modTemp : unsigned(31 downto 0);
begin
	
	-- construct patches
	process(clk) begin
		if reset_n = '0' then
			patch0 <= x"000000";
			patch1 <= x"000000";
			patch2 <= x"000000";
			patch3 <= x"000000";
		else
			patch0 <= fmOp_out_0; --sin
			patch1 <= fmOp_out_1; --saw
			patch2 <= fmOp_out_2; --sqaure
			patch3 <= fmOp_out_3; --random
		end if;
	end process;

	-- select patch to output
	with mode select fm_out <= 
		patch0 when "00000",
		patch1 when "00001",
		patch2 when "00010",
		patch3 when "00011",
		x"000000" when others;	
		
		modTemp <= unsigned(pitch) * 3;

	-- Instantiate some FM operators from which to construct patches
	
	--fmOp0-3 take no modulators, use as first operators (unless want a loop)
	fmOp0 : fmOperator port map(
		clk => clk,
		reset_n => reset_n,
		mute => not enable,
		
		osc_select => "00", -- SINE
		
		osc_freq => unsigned(pitch),
		modulator => (others => '0'), --unsigned(fmOp_out_1),		
		mod_factor => "01000",
		
		fm_out => fmOp_out_0
	);
	
	fmOp1 : fmOperator port map(
		clk => clk,
		reset_n => reset_n,
		mute => not enable,
		
		osc_select => "01", -- SAW
		
		osc_freq => unsigned(pitch),
		modulator => (others => '0'),
		mod_factor => "00010",		
		
		fm_out => fmOp_out_1
	);
	
	fmOp2 : fmOperator port map(
		clk => clk,
		reset_n => reset_n,
		mute => not enable,
		
		osc_select => "00", -- SQUARE
		
		osc_freq => unsigned(pitch),
		modulator => unsigned(fmOp_out_3),
		mod_factor => "01111",		
		
		fm_out => fmOp_out_2
	);
	
	fmOp3 : fmOperator port map(
		clk => clk,
		reset_n => reset_n,
		mute => not enable,
		
		osc_select => "00",
		
		osc_freq => modTemp(15 downto 0),
		modulator => (others => '0'),
		mod_factor => "00010",		
		
		fm_out => fmOp_out_3
	);


end architecture;