/*  $Id: soundprovider_mikmod_session.cpp,v 1.6 2003/10/07 20:27:40 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
*/

#include "Sound/precomp.h"
#include "soundprovider_mikmod_session.h"
#include "soundprovider_mikmod_generic.h"
#include "module_reader.h"
#include "API/Sound/soundformat.h"
#include "API/Core/IOData/inputsource.h"
#include "API/Core/IOData/inputsource_provider.h"
#include "API/Core/System/error.h"

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_MikMod_Session construction:

CL_SoundProvider_MikMod_Session::CL_SoundProvider_MikMod_Session(CL_SoundProvider_MikMod_Generic *data) :
	data(data), num_samples(0), position(0)
{
	CL_InputSource *source = data->provider->open_source(data->filename);

	MREADER *reader = new_clanlib_reader((void *) source);
	if (reader == 0) throw CL_Error("new_clanlib_reader failed!");

	module = Player_LoadGeneric(reader,CLANLIB_READER_CHANNELS,0);
	if (module == 0)
	{
		delete_clanlib_reader(reader);
		delete source;
		throw CL_Error(MikMod_strerror(MikMod_errno));
	}

	module->wrap = false;
	module->loop = false;
	module->fadeout = true;

	delete_clanlib_reader(reader);
	delete source;

	frequency = md_mixfreq;
	format = (md_mode & DMODE_16BITS) ? sf_16bit_signed : sf_8bit_signed;
	num_channels = (md_mode&DMODE_STEREO) ? 2 : 1;
}

CL_SoundProvider_MikMod_Session::~CL_SoundProvider_MikMod_Session()
{
	Player_Stop();
//	Player_Free(module); -- this crashes for some weird reason, help! -- mbn 23. april 2003
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_MikMod_Session attributes:

int CL_SoundProvider_MikMod_Session::get_num_samples() const
{
	return num_samples;
}

int CL_SoundProvider_MikMod_Session::get_frequency() const
{
	return frequency;
}

CL_SoundFormat CL_SoundProvider_MikMod_Session::get_format() const
{
	return format;
}

int CL_SoundProvider_MikMod_Session::get_num_channels() const
{
	return num_channels;
}

int CL_SoundProvider_MikMod_Session::get_position() const
{
	return position;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_MikMod_Session operations:

bool CL_SoundProvider_MikMod_Session::set_looping(bool loop)
{
	module->wrap = loop;
	module->loop = loop;

	return true;
}

bool CL_SoundProvider_MikMod_Session::eof() const
{
	return !Player_Active();
}

void CL_SoundProvider_MikMod_Session::stop()
{
	Player_Stop();
}

bool CL_SoundProvider_MikMod_Session::play()
{
	Player_Start (module);
	return true;
}
	
bool CL_SoundProvider_MikMod_Session::set_position(int pos)
{
	Player_SetPosition(pos);
	position = pos;
	if (num_samples < position) num_samples = position;
	return true;
}

int CL_SoundProvider_MikMod_Session::get_data(void **data_ptr, int data_requested)
{
	int total_written = 0;
	int bytes_per_sample = (format == sf_16bit_signed) ? 2 : 1;
	bytes_per_sample *= num_channels;

	while (data_requested > 0)
	{
		MikMod_Update();
		int written = VC_WriteBytes(buffer, ((data_requested > 16*1024) ? 16*1024 : data_requested) * bytes_per_sample);
		written /= bytes_per_sample;

		if (format == sf_16bit_signed)
		{
			short **channels = (short **) data_ptr;

			if (num_channels == 2)
			{
				short *src = (short *) buffer;
				for (int i=0; i<written; i++)
				{
					channels[0][i+total_written] = *(src++);
					channels[1][i+total_written] = *(src++);
				}
			}
			else
			{
				memcpy(channels[0]+total_written, buffer, written*2);
			}
		}
		else if (format == sf_8bit_signed)
		{
			char **channels = (char **) data_ptr;

			if (num_channels == 2)
			{
				char *src = (char *) buffer;
				for (int i=0; i<written; i++)
				{
					channels[0][i+total_written] = *(src++);
					channels[1][i+total_written] = *(src++);
				}
			}
			else
			{
				memcpy(channels[0]+total_written, buffer, written);
			}
		}

		position += written;
		total_written += written;
		data_requested -= written;

		// Check if we reached the end of the song:
		if (module->sngpos >= module->numpos)
		{
			stop();
			break;
		}

		// If no more bytes to write from input stream:
		// (This never happens with this version of mikmod, but its a good check anyway)
		if (written < 16*1024) break;
	}

	if (num_samples < position) num_samples = position;
	return total_written;
}

/////////////////////////////////////////////////////////////////////////////
// CL_SoundProvider_MikMod_Session implementation:
