/*
 * audio.v
 * 
 * Audio controller - FPGA class project
 * 
 * 
 * Authors: Dario De Micco - Marta Martelossi
 * Contact: <email???>
 * Release date: oct 2013
 * 
 * References: "Tetris" class project
 *
 * 
 * This program is free software; you can redistribute it and/or modify
 * 
 * it under the terms of the GNU General Public License as published by
 * 
 * the Free Software Foundation; either version 2 of the License, or
 * 
 * (at your option) any later version.
 * 
 * 
 * 
 * This program is distributed in the hope that it will be useful,
 * 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * 
 * GNU General Public License for more details.
 * 
 * 
 * 
 * You should have received a copy of the GNU General Public License
 * 
 * along with this program; if not, write to the Free Software
 * 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * 
 * MA 02110-1301, USA.
 * 
 */


// this module contains FSM to manage audio playback from Flash memory to Codec's DAC
module audio_fsm(
	clock, reset,
	flash_address, flash_data,
	fifo_full,
	enable,
	audio_data,
	fifo_WR,
	sample_idx,
	audioready,
	ost_enable
);


// there are operating parametrs for codec. audio data must be of the same type.
parameter	REF_CLK			=	18432000;	//	18.432	MHz (reference/master clock)
parameter	SAMPLE_RATE		=	48000;		//	48		KHz (sampling rate)
parameter	DATA_WIDTH		=	16;			//	16		Bits (bits per sample)
parameter	CHANNEL_NUM		=	2;				//	Stereo

// lenght of audio files (in samples)
 parameter	campioni_totali_ost = ((22'd1711060-22'd1)*CHANNEL_NUM);	
 parameter	audio_start_address_ost = 22'd0;	

reg [21:0]	campioni_totali;


input clock, reset;
input enable, fifo_full;
input ost_enable;

input [1:0] sample_idx;
input [7:0] flash_data;
output reg [21:0]	flash_address;

output audioready;

reg [21:0]	audio_start_address;

output fifo_WR;
output reg [DATA_WIDTH-1:0]	audio_data;

// status registers
reg	[9:0]	stato;
reg	[15:0]	dati;
reg [21:0]	campioni_playati;
reg [21:0]	campioni_playati_ost;


// if enable goes high (sfxplay), play new sfx
reg sfx_play_req, sfx_play_ack;
wire playreset;
assign playreset = reset | sfx_play_ack;
reg [1:0] sfx_play_idx;

always @ (posedge enable or posedge playreset)
begin
	if (enable) begin
		sfx_play_req<=1'b1;
		
	end else begin
		sfx_play_req<=1'b0;
		
	end

end

// returns audioready=1 if FSM is not reproducing a sfx.
assign audioready=(&(stato & 10'h80))?1'b0:1'b1;

// is 1 when audio data is ready in audio_data output bus. so these data can be written in the FIFO.
reg	audio_WR;

assign fifo_WR = audio_WR;

always @ (posedge clock or posedge reset)
begin
	if (reset) begin
		// reset
		flash_address <= 22'd0;
		stato <= 10'd0;
		audio_data <= 0;
		campioni_playati <= 22'd0;
		campioni_playati_ost <= 22'd0;
		sfx_play_ack<=0;
	end else begin
		
		case (stato)
				10'd0:
					begin
						// preload, start of music
						flash_address <= 2*audio_start_address_ost;
						audio_data <=  16'd0;
						audio_WR<=1'b0;
						campioni_playati_ost <= 22'd0;
						
						// play ost (music on background)
						if (ost_enable) begin 
							stato <= 10'd1;
						end else begin
							if (sfx_play_req) begin 					// need to play a sfx?
								sfx_play_idx<=sample_idx;
								stato <= 10'd128;
							end
						end
					end
				10'd1:
					begin
						// disable writing to FIFO  (until full 16bit word is ready)
						audio_WR<=1'b0;
						stato <= stato + 10'd1;
						// wait states for timig requirements of flash memory
						
					end
				10'd2:
					stato <= stato + 10'd1;
				10'd3:
					stato <= stato + 10'd1;
				10'd4:
					stato <= stato + 10'd1;
				10'd5:
					begin
						// if fifo has free space (codec steals data from fifo)
						if (~fifo_full) 
							stato <= stato + 10'd1;	// proceed
						// 
						
					end
				10'd6:
				
					begin
						// got first byte from flash
						audio_data<={flash_data,8'd0}; // compose half word 
						// prepare to get 2nd byte
						flash_address <= flash_address + 22'd1;
						
						
						stato <= stato + 10'd1;
					end
						
				10'd7:
					stato <= stato + 10'd1;
				10'd8:
					stato <= stato + 10'd1;
				10'd9:
					stato <= stato + 10'd1;
				10'd10:
					stato <= stato + 10'd1;
				10'd11:
					stato <= stato + 10'd1;
				10'd12:
					begin
						// got 2nd byte
						
						//audio_data<={audio_data[15],audio_data[15],audio_data[14:8],flash_data[7:1]};
						audio_data<={audio_data[15:8],flash_data[7:0]};
						// full word ready (1 sample)
						
						// write it to the fifo
						audio_WR=1'b1;
						stato <= stato + 10'd1;
					end
				10'd13:
					begin
						audio_WR=1'b0;
						stato <= stato + 10'd1;
					end
				10'd14:
					begin
						
						// if fifo has free space (codec steals data from fifo)
						if (~fifo_full) begin
							stato <= stato + 10'd1;
							audio_WR=1'b1;
						
						end
					end
				10'd15:
					begin
						// write two times same sample (2ch)
						// increment played sample counter
						audio_WR=1'b0;
						campioni_playati_ost <= campioni_playati_ost+ 21'd1;
						
						if (sfx_play_req) begin 					// need to play a sfx?
							sfx_play_idx<=sample_idx;
							stato <= 10'd128;
							
						end else begin

							if ((campioni_playati_ost != campioni_totali_ost) && (ost_enable) ) begin
							
								stato <= 10'd1;			// keep going with ost
														
								
								flash_address <= flash_address + 22'd1;
							end else begin
								// ended, restart
								stato <= 10'd0;
							end
						end
					end
				10'd128:
					begin
						// play sfx
						audio_WR<=1'b0;
						
						
						// sfx enable
						if (sfx_play_req) begin
							sfx_play_idx<=sample_idx;
							stato <= 10'd130;
							sfx_play_ack<=1'b1;
						end else begin
							// no more sound

							// resume ost
							if ((campioni_playati_ost != campioni_totali_ost) && (ost_enable) ) begin
								stato <= 10'd1;		
														
								
								flash_address <= 2*(audio_start_address_ost+campioni_playati_ost);
							end else
								stato <= 10'd0;
						end
							
					end
				10'd130:
					begin
						// preload, start of sfx. 
						sfx_play_ack<=1'b0;
						flash_address <= 2*audio_start_address;
						audio_data <= 16'd0;
						campioni_playati <=  22'd0;
						stato <= 10'd135;
					end
				10'd135:
				
					begin
						if (sfx_play_req) begin // another request, change sound?
							sfx_play_idx<=sample_idx;
							stato <=10'd128;
						end else begin
							stato <= stato + 10'd1;
						end
						audio_WR<=1'b0;
						
						
						
					end
				10'd136:
					stato <= stato + 10'd1;
				10'd137:
					stato <= stato + 10'd1;
				10'd138:
					stato <= stato + 10'd1;
				10'd139:
					begin
						
						if (~fifo_full) 
							stato <= stato + 10'd1;	
						
						
					end
				10'd140:
				
					begin
						
						audio_data<={flash_data,8'd0};
						
						flash_address <= flash_address + 22'd1;
						
						
						stato <= stato + 10'd1;
					end
						
				10'd141:
					stato <= stato + 10'd1;
				10'd142:
					stato <= stato + 10'd1;
				10'd143:
					stato <= stato + 10'd1;
				10'd144:
					stato <= stato + 10'd1;
				10'd145:
					stato <= stato + 10'd1;
				10'd146:
					begin
				
						// got 2nd byte
						
						//audio_data<={audio_data[15],audio_data[15],audio_data[14:8],flash_data[7:1]};
						audio_data<={audio_data[15:8],flash_data[7:0]};
						// full word ready (1 sample)
						
						// write it to the fifo
						audio_WR=1'b1;
						stato <= stato + 10'd1;
					end
				10'd147:
					begin
						audio_WR=1'b0;
						stato <= stato + 10'd1;
					end
				10'd148:
					begin
						
						// if fifo has free space (codec steals data from fifo)
						if (~fifo_full) begin
							stato <= stato + 10'd1;
							audio_WR=1'b1;
						
						end
					end
				10'd149:
					begin
						// write two times same sample (2ch)
						// increment played sample counter
						audio_WR=1'b0;
				
						flash_address <= flash_address + 22'd1;
						
						campioni_playati <= campioni_playati+ 21'd1;
						
						
						if ((campioni_playati != campioni_totali))
							stato <= 10'd135;			// keep going with this sfx
						else
							stato <= 10'd128;			// check if there is another sfx pending
						
					end	 
		
		endcase
	end
	

end

////////////////////////////////////////////////////////////////////
/////////////////////	Audio mapping LUT	  ////////////////////////	
/* sfxs - index: desc #samples
0: mangia 33024
1: levelup 35432
2: morte 262265
*/
always
begin
	case(sfx_play_idx)
	//	Audio Config Data
	0	:
		begin
			audio_start_address	<=	22'd01711060;
			campioni_totali <= 22'd33024;
		end
	1	:
		begin
			audio_start_address	<=	22'd33024+22'd01711060;
			campioni_totali <= 22'd35432;
		end
	2	:
		begin
			audio_start_address	<=	22'd35432+22'd01711060+22'd33024;
			campioni_totali <= 22'd262265;
		end
	3	:
		begin
			audio_start_address	<=	22'd35432+22'd01711060+22'd33024;
			campioni_totali <= 22'd262265;
		end	

	endcase
end


////////////////////////////////////////////////////////////////////

endmodule
