note
	description: "An AUDIO_SOUND witch data came from a WAV file."
	author: "Louis Marchand"
	date: "Tue, 21 Feb 2017 00:15:23 +0000"
	revision: "2.1"

class 
	AUDIO_SOUND_WAV_FILE

inherit
	AUDIO_SOUND

create 
	make

feature {NONE} -- Initialization

	make (a_filename: READABLE_STRING_GENERAL)
			-- Initialization of Current using a_filename to load data.
		do
			default_create
			create file.make_with_name (a_filename)
			is_finished := False
		end

	process_header
			-- Look inside the file to see if it is a valid
			-- WAV file and retreive attrbutes
		do
			if file.can_read_32 then
				file.read_natural_32_big_endian
				if file.last_natural_32 /= 1380533830 then
					put_error ("Cannot open WAV file.", "The RIFF header is not found")
				else
					if file.can_read_64 then
						file.read_natural_32_big_endian;
						file.read_natural_32_big_endian
						if file.last_natural_32 /= 1463899717 then
							put_error ("Cannot open WAV file.", "The WAVE header is not found")
						else
							process_chunks
						end
					else
						put_error ("Cannot open WAV file.", "The file has stop before the end of the WAV header.")
					end
				end
			else
				put_error ("Cannot open WAV file.", "The file has stop before the end of the WAV header.")
			end
			has_ressource_error := has_error
		end

	process_chunks
			-- Read every WAV chunks information and store them inside attributes.
		local
			chunk_id, chunk_data_size: NATURAL_32
			cur_offset: INTEGER_32
			fmt_found, data_found: BOOLEAN
		do
			from
				fmt_found := False
				data_found := False
				has_error := False
			until
				data_found or has_error
			loop
				if file.can_read_64 then
					file.read_natural_32_big_endian
					chunk_id := file.last_natural_32;
					file.read_natural_32_little_endian
					chunk_data_size := file.last_natural_32
					cur_offset := file.position
					if chunk_id = 1684108385 then
						data_starting_offset := cur_offset
						data_size := chunk_data_size
						data_found := True
						if not fmt_found then
							put_error ("Cannot read WAV data chunks.", "Data deteted but not the fmt")
						end
					else
						if chunk_id = 1718449184 then
							process_fmt
							fmt_found := True
						end;
						file.go (cur_offset + chunk_data_size.to_integer_32)
					end
				else
					put_error ("Cannot read WAV data chunks.", "The file has stop before the end of a WAV chunk.")
				end
			end
			has_ressource_error := has_error
		end

	process_fmt
			-- Read and store chunk's format informations (fmt tag)
		do
			if file.can_read_64 then
				file.read_natural_16_little_endian
				if file.last_natural_16 /= 1 then
					put_error ("WAV format not supported.", "Audio format is not PCM.")
				else
					file.read_natural_16_little_endian
					channel_count_internal := file.last_natural_16.as_integer_32;
					file.read_natural_32_little_endian
					frequency_internal := file.last_natural_32.as_integer_32
					if file.can_read_64 then
						file.read_natural_32_little_endian
						byte_rate := file.last_natural_32.to_integer_32;
						file.read_natural_16_little_endian
						bytes_per_sample := file.last_natural_16.to_integer_32;
						file.read_natural_16_little_endian
						bits_per_sample_internal := file.last_natural_16.as_integer_32
					else
						put_error ("WAV format not supported.", "The file has stop before the end of a WAV fmt.")
					end
				end
			else
				put_error ("WAV format not supported.", "The file has stop before the end of a WAV fmt.")
			end
			has_ressource_error := has_error
		end
	
feature {AUDIO_SOURCE}{AUDIO_SOURCE} 

	fill_buffer (a_buffer: POINTER; a_max_length: INTEGER_32)
			-- Fill the next data samples in a_buffer (no more than a_max_length byte)
			-- The actual number of byte placed in a_buffer will be available in last_buffer_size
			-- Warning: side effect on a_buffer
		local
			l_managed_pointer: MANAGED_POINTER
		do
			if file.readable then
				create l_managed_pointer.share_from_pointer (a_buffer, a_max_length);
				file.read_to_managed_pointer (l_managed_pointer, 0, a_max_length)
				last_buffer_size := file.bytes_read
			else
				last_buffer_size := 0
			end
			if last_buffer_size = 0 then
				is_finished := True
			end
		end

	byte_per_buffer_sample: INTEGER_32
			-- The number of byte for one frame of Current.
		do
			Result := bytes_per_sample.to_integer_32
		end
	
feature -- Access

	is_openable: BOOLEAN
			-- Can Current be open
		do
			Result := file.exists and then file.is_readable
		end

	open
			-- Open Current
		do
			file.open_read
			process_header
			is_finished := False
			is_open := not has_error
			has_ressource_error := has_error
		end

	close
			-- Stop the management of the stream
		do
			file.close
			is_open := False
		end

	channel_count: INTEGER_32
			-- Get the channel number of Current (1=mono, 2=stereo, etc.).
		do
			Result := channel_count_internal
		end

	frequency: INTEGER_32
			-- Get the frequency (sample rate) of Current.
		do
			Result := frequency_internal
		end

	bits_per_sample: INTEGER_32
			-- Get the bit resolution of one frame of Current.
		do
			Result := bits_per_sample_internal
		end

	is_signed: BOOLEAN
			-- True if the frames in the buffer are signed.
		do
			Result := not (bits_per_sample_internal = 8)
		end

	is_seekable: BOOLEAN
			-- Return true if Current support the seek functionnality.
		do
			Result := True
		end

	restart
			-- Restart Current to the beginning.
		do
			file.go (data_starting_offset)
			is_finished := False
		end

	sample_seek (a_frame_number: INTEGER_64)
			-- Seek at the frame a_frame_number from the beginning of Current
		do
			file.go ((data_starting_offset.to_integer_64 + (byte_per_buffer_sample.to_integer_64 * (a_frame_number - 1))).to_integer_32)
			is_finished := False
		end

	sample_position: INTEGER_64
			-- The number of frames since the beginning of Current
		do
			Result := ((file.position - data_starting_offset) // byte_per_buffer_sample) + 1.to_integer_64
		end

	sample_count: INTEGER_64
			-- The total number of frames in Current
		do
			Result := data_size.to_integer_64 // byte_per_buffer_sample.to_integer_64
		end
	
feature {NONE} -- Implementation - Variable

	byte_rate: INTEGER_32

	file: GAME_FILE
			-- The file used to read Currents data

	channel_count_internal: INTEGER_32
			-- The number of channel of Current

	frequency_internal: INTEGER_32
			-- The frequency (sample rate) of Current.

	bits_per_sample_internal: INTEGER_32
			-- The bit resolution of one frame of Current.

	data_starting_offset: INTEGER_32
			-- Where in the file start the data

	data_size: NATURAL_32
			-- The size of a data in the file

	bytes_per_sample: INTEGER_32
			-- The number of byte for one frame of Current.
	
end -- class AUDIO_SOUND_WAV_FILE

Generated by ISE EiffelStudio