--
-- EnvelopeGenerator.vhd
-- The envelope generator module of VM2413
--
-- Copyright (c) 2006 Mitsutaka Okazaki (brezza@pokipoki.org)
-- All rights reserved.
--
-- Redistribution and use of this source code or any derivative works, are
-- permitted provided that the following conditions are met:
--
-- 1. Redistributions of source code must retain the above copyright notice,
--    this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Redistributions may not be sold, nor may they be used in a commercial
--    product or activity without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-- OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
--

--
--  modified by t.hara
--

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.std_logic_unsigned.all;
    use work.vm2413.all;

entity envelopegenerator is
    port (
        clk     : in    std_logic;
        reset   : in    std_logic;
        clkena  : in    std_logic;

        slot    : in    slot_type;
        stage   : in    stage_type;
        rhythm  : in    std_logic;

        am      : in    am_type;
        tl      : in    db_type;
        ar      : in    ar_type;
        dr      : in    dr_type;
        sl      : in    sl_type;
        rr      : in    rr_type;
        rks     : in    rks_type;
        key     : in    std_logic;

        egout   : out   std_logic_vector( 12 downto 0 )     --  小数部 6bit
    );
end envelopegenerator;

architecture rtl of envelopegenerator is

    component EnvelopeMemory
        port (
            clk     : in    std_logic;
            reset   : in    std_logic;

            waddr   : in    slot_type;
            wr      : in    std_logic;
            wdata   : in    egdata_type;
            raddr   : in    slot_type;
            rdata   : out   egdata_type
        );
    end component;

    component AttackTable
        port(
            clk     : in    std_logic;
            clkena  : in    std_logic;
            addr    : in    std_logic_vector( 21 downto 0 );    -- 小数部 15bit
            data    : out   std_logic_vector( 12 downto 0 )
        );
    end component;

    signal rslot    : slot_type;
    signal memin    : egdata_type;
    signal memout   : egdata_type;
    signal memwr    : std_logic;

    signal aridx    : std_logic_vector( 21 downto 0 );
    signal ardata   : std_logic_vector( 12 downto 0 );  --  小数部 6bit
begin

    --  Attack テーブル
    u_attack_table: AttackTable
    port map (
        clk     =>  clk,
        clkena  =>  clkena,
        addr    =>  aridx,
        data    =>  ardata
    );

    u_envelope_memory: EnvelopeMemory
    port map (
        clk     =>  clk,
        reset   =>  reset,
        waddr   =>  slot,
        wr      =>  memwr,
        wdata   =>  memin,
        raddr   =>  rslot,
        rdata   =>  memout
    );

    --  EnvelopeMemory のプリフェッチ
    process( reset, clk )
    begin
        if( reset = '1' )then
            rslot   <= (others => '0');
        elsif( clk'event and clk='1' )then
            if( clkena = '1' )then
                if( stage = "10" )then
                    if( slot = "10001" )then
                        rslot <= (others => '0');
                    else
                        rslot <= slot + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;

    process( reset, clk )
        variable lastkey    : std_logic_vector(18-1 downto 0);
        variable rm         : std_logic_vector(4 downto 0);
        variable egtmp      : std_logic_vector(db_type'high + 8 downto 0);  --  小数部 6bit
        variable amphase    : std_logic_vector(19 downto 0);
        variable egphase    : egphase_type;
        variable egstate    : egstate_type;
        variable dphase     : egphase_type;
        variable ntable     : std_logic_vector(17 downto 0);
    begin
        if( reset = '1' )then
            rm      := (others=>'0');
            lastkey := (others=>'0');
            memwr   <= '0';
            egstate := Finish;
            egphase := (others=>'0');
            ntable  := (others => '1');
            amphase(amphase'high downto amphase'high-4) := "00001";
            amphase(amphase'high-5 downto 0)            := (others=>'0');

        elsif( clk'event and clk='1' )then

            aridx <= egphase( 22-1 downto 0 );

            if( clkena = '1' )then

                ntable( 17 downto 1 )   := ntable( 16 downto 0 );
                ntable( 0 )             := ntable( 17 ) xor ntable( 14 );

                -- Amplitude oscillator ( -4.8dB to 0dB , 3.7Hz )
                amphase := amphase + '1';
                if amphase(amphase'high downto amphase'high-4) = "11111" then
                    amphase(amphase'high downto amphase'high-4) := "00001";
                end if;

                if stage = 0 then
                    egstate := memout.state;
                    egphase := memout.phase;

                elsif stage = 1 then
                    -- Wait for AttackTable

                elsif stage = 2 then
                    case egstate is
                        when Attack =>
                            rm := '0'&ar;
                            egtmp := ("00"&tl&"000000") + ("00"&ardata);        -- カーブを描いて上昇する
                        when Decay  =>
                            rm := '0'&dr;
                            egtmp := ("00"&tl&"000000") + ("00"&egphase(22-1 downto 22-7-6));
                        when Release=>
                            rm := '0'&rr;
                            egtmp := ("00"&tl&"000000") + ("00"&egphase(22-1 downto 22-7-6));
                        when Finish =>
                            egtmp(egtmp'high downto egtmp'high -1) := "00";
                            egtmp(egtmp'high-2 downto 0) := (others=>'1');
                    end case;

                    -- SD and HH
                    if ntable(0) = '1' and conv_integer(slot)/2 = 7 and rhythm = '1' then
                        egtmp := egtmp + "010000000000000";
                    end if;

                    -- Amplitude LFO
                    if am ='1' then
                        if (amphase(amphase'high) = '0') then
                            -- 上りの場合
                            egtmp := egtmp + ("00000"&(amphase(amphase'high-1 downto amphase'high-4-6)-"0001000000"));
                        else
                            -- 下りの場合
                            egtmp := egtmp + ("00000"&("1111000000"-amphase(amphase'high-1 downto amphase'high-4-6)));
                        end if;
                    end if;

                    -- Generate output
                    if egtmp(egtmp'high downto egtmp'high-1) = "00" then    -- リミッタ
                        egout <= egtmp(egout'range);
                    else
                        egout <= (others=>'1');
                    end if;

                    if rm /= "00000" then

                        rm := rm + rks(3 downto 2);
                        if rm(rm'high)='1' then
                            rm(3 downto 0):="1111";
                        end if;

                        case egstate is
                            when Attack =>
                                dphase(dphase'high downto 5) := (others=>'0');
                                dphase(5 downto 0) := "110" * ('1'&rks(1 downto 0));
                                dphase := SHL( dphase, rm(3 downto 0) );
                                egphase := egphase - dphase(egphase'range);
                            when Decay | Release =>
                                dphase(dphase'high downto 3) := (others=>'0');
                                dphase(2 downto 0) := '1'&rks(1 downto 0);
                                dphase  := SHL(dphase, rm(3 downto 0) - '1');
                                egphase := egphase + dphase(egphase'range);
                            when Finish =>
                                null;
                        end case;

                    end if;

                    case egstate is
                        when Attack =>
                            if egphase(egphase'high) = '1' then
                                egphase := (others=>'0');
                                egstate := Decay;
                            end if;
                        when Decay =>
                            if egphase(egphase'high downto egphase'high-4) >= '0'&sl then
                                egstate := Release;
                            end if;
                        when Release =>
                            if( egphase(egphase'high downto egphase'high-4) >= "01111" ) then
                                egstate:= Finish;
                            end if;
                        when Finish =>
                            egphase := (others => '1');
                    end case;

                    if lastkey(conv_integer(slot)) = '0' and key = '1' then
                        egphase(egphase'high):= '0';
                        egphase(egphase'high-1 downto 0) := (others =>'1');
                        egstate:= Attack;
                    elsif lastkey(conv_integer(slot)) = '1' and key = '0' and egstate /= Finish then
                        egstate:= Release;
                    end if;
                    lastkey(conv_integer(slot)) := key;

                    -- update phase and state memory
                    memin <= ( state => egstate, phase => egphase );
                    memwr <='1';
                elsif stage = 3 then
                    -- wait for phase memory
                    memwr <='0';
                end if;
            end if;
        end if;
    end process;

end rtl;

