/*  $Id: soundprovider_wave.cpp,v 1.4 2003/08/21 22:58:00 mbn Exp $
**
**  ClanLib Game SDK
**  Copyright (C) 2003  The ClanLib Team
**  For a total list of contributers see the file CREDITS.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This library 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
**  Lesser General Public License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
**

	File purpose:
		Simple sample support.

*/

#include "Sound/precomp.h"
#include "API/Sound/SoundProviders/soundprovider_wave.h"
#include "API/Core/IOData/inputsource_provider.h"
#include "API/Core/IOData/inputsource.h"
#include "API/Core/System/clanstring.h"
#include "API/Core/System/log.h"
#include "soundprovider_wave_generic.h"
#include "soundprovider_wave_session.h"

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Wave construction:

CL_SoundProvider_Wave::CL_SoundProvider_Wave(
	const std::string &filename,
	CL_InputSourceProvider *provider,
	bool stream) : impl(new CL_SoundProvider_Wave_Generic)
{
	if (provider == 0) provider = CL_InputSourceProvider::create_file_provider(".");
	else provider = provider->clone();

	impl->provider = provider;
	impl->filename = filename;
	impl->stream = stream;

	CL_InputSource *source = provider->open_source(filename);

	// Check to see if this is really a .wav-file
	char temp[12];
	source->read(temp, 4);
	source->seek(4, CL_InputSource::seek_cur);
	source->read(&temp[4], 8);
	source->seek(4, CL_InputSource::seek_cur);

	if (memcmp(temp, "RIFFWAVEfmt ", 12) != 0)
	{
		delete impl;
		throw CL_Error("Invalid RIFF WAVE header!");
	}

	CL_SoundProvider_Wave_Generic::WAVE_FORMAT format;
	format.formatTag = source->read_short16();
	format.nChannels = source->read_ushort16();
	format.nSamplesPerSec = source->read_uint32();
	format.nAvgBytesPerSec = source->read_uint32();
	format.nBlockAlign = source->read_ushort16();

	// FIXME: find a better way to handle format struct padding
	source->read(temp, 2);

	// Another sanity check
	source->read(temp, 4);
	temp[4] = 0;
	if (memcmp(temp, "data", 4) != 0)
	{
		delete impl;
		throw CL_Error("Invalid RIFF data chunk!");
	}

	int data_size = source->read_uint32();
	int bytes_per_sample = format.nAvgBytesPerSec / format.nSamplesPerSec;

	impl->frequency = format.nSamplesPerSec;
	impl->num_channels = format.nChannels;
	impl->num_samples = data_size / bytes_per_sample;

	if (bytes_per_sample / format.nChannels == 2) impl->format = sf_16bit_signed;
	else if (bytes_per_sample / format.nChannels == 1) impl->format = sf_8bit_signed;
	else
	{
		delete impl;
		throw CL_Error(
			CL_String::format(
				"Unsupported wave file format (channels=%1, bytes per sample=%2)",
				format.nChannels,
				bytes_per_sample));
	}

	impl->data = new char[data_size];
	source->read(impl->data, data_size);

	// 8 bit wave is unsigned, while 16 bit wave is signed!
	if (impl->format == sf_8bit_signed)
	{
		for (int i=0; i<data_size; i++)
			((unsigned char *) impl->data)[i] -= 128;
	}

	delete source;
}

CL_SoundProvider_Wave::~CL_SoundProvider_Wave()
{
	delete[] impl->data;
	delete impl->provider;
	delete impl;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Wave operations:

CL_SoundProvider_Session *CL_SoundProvider_Wave::begin_session()
{
	return new CL_SoundProvider_Wave_Session(impl);
}

void CL_SoundProvider_Wave::end_session(CL_SoundProvider_Session *session)
{
	delete session;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_Wave implementation:

/*
#ifdef WIN32
#pragma warning (disable:4786)
#endif

#include "API/Core/IOData/cl_endian.h"
#include "API/Core/IOData/inputsource_provider.h"
#include "API/Core/IOData/inputsource.h"
#include "API/Core/System/cl_assert.h"
#include "API/Core/System/error.h"
#include "API/Sound/SoundProviders/static_provider_raw.h"
#include "API/Sound/SoundProviders/static_provider_wave.h"

CL_SoundBuffer *CL_Sample::create(const std::string &sample_id, CL_InputSourceProvider *provider)
{
	return new CL_SoundBuffer(new CL_Sample(sample_id, provider), true);
}

CL_Sample::CL_Sample(const std::string &s_id, CL_InputSourceProvider *_provider) : sample_id(s_id)
{
	if (_provider == NULL)
	{
		provider = CL_InputSourceProvider::create_file_provider(".");
	}
	else
	{
		provider = _provider->clone();
	}
	sample_data = NULL;
}

CL_Sample::~CL_Sample()
{
	delete[] sample_data;
	delete provider;
}

void CL_Sample::load_data()
{
	CL_InputSource *source = provider->open_source(sample_id.c_str());
	cl_assert(source != NULL);

	// Check to see if this is really a .wav-file
	char temp[12];
	source->read(temp, 4);
	source->seek(4, CL_InputSource::seek_cur);
	source->read(&temp[4], 8);
	source->seek(4, CL_InputSource::seek_cur);

	if (memcmp(temp, "RIFFWAVEfmt ", 12) != 0)
	{
		throw CL_Error("Invalid RIFF WAVE header!");
	}
//	cl_assert(!(memcmp(temp, "RIFFWAVEfmt ", 12)));

	WAVE_FORMAT format;
	format.formatTag = source->read_short16();
	format.nChannels = source->read_ushort16();
	format.nSamplesPerSec = source->read_uint32();
	format.nAvgBytesPerSec = source->read_uint32();
	format.nBlockAlign = source->read_ushort16();

	// FIXME: find a better way to handle format struct padding
	source->read(temp, 2);

	// Another sanity check
	source->read(temp, 4);
	temp[4] = 0;
	if (memcmp(temp, "data", 4) != 0)
	{
		throw CL_Error("Invalid RIFF data chunk!");
	}
//	cl_assert(!(memcmp(temp, "data", 4)));

	sample_size = source->read_uint32();
	sample_freq = format.nSamplesPerSec;

	int bytes_per_sample = format.nAvgBytesPerSec / format.nSamplesPerSec;

	if (format.nChannels == 2 && bytes_per_sample == 4) sample_format = sf_16bit_signed_stereo;
	else if (format.nChannels == 2 && bytes_per_sample == 2) sample_format = sf_8bit_signed_stereo;
	else if (format.nChannels == 1 && bytes_per_sample == 2) sample_format = sf_16bit_signed;
	else if (format.nChannels == 1 && bytes_per_sample == 1) sample_format = sf_8bit_signed;
	else
	{
		CL_Log::log("debug", "Invalid wave file format");
		CL_Log::log("debug", "Sample size: %1", sample_size);
		CL_Log::log("debug", "Sample frequency: %1", sample_freq);
		CL_Log::log("debug", "Number of channels: %1", format.nChannels);
		CL_Log::log("debug", "Number of bytes pr. sample: %1", bytes_per_sample);

		throw CL_Error("Invalid wave file format");
	}

	sample_data = new signed char[sample_size];
	if (sample_format == sf_8bit_signed || sample_format == sf_8bit_signed_stereo)
	{
		for (int i=0;i<sample_size;i++)
			sample_data[i] = source->read_uchar8()-128;
	}
	else	// 16 bit
	{
		for (int i=0;i<sample_size;i+=2)
			*(short *)&sample_data[i] = source->read_short16();
	}

	delete source;
}

void CL_Sample::lock()
{
	if (sample_data != NULL) return;

	load_data();
}

void CL_Sample::unlock()
{
	delete[] sample_data;
	sample_data = NULL;
}

SoundFormat CL_Sample::get_format() const
{
	return sample_format;
}

int CL_Sample::data_size() const
{
	return sample_size;
}

void *CL_Sample::get_data() const
{
	return sample_data;
}

int CL_Sample::get_frequency() const
{
	return sample_freq;
}
*/
