
#define	STRICT

#include "foo_dsp_ssrc.h"
#include "Config.h"

extern HINSTANCE	WSLhInstance;

int __fastcall	CanResample(int sfrq, int dfrq);

service_factory_t<dsp_ssrc_entry>	foo;

DECLARE_COMPONENT_VERSION(
						NAME,
						VER,
						NAME " v" VER "\n\n"
							"by Otachan\n\n"
							"http://shibatch.sourceforge.net/\n"
							"http://otachan.com/");

dsp_ssrc_entry::dsp_ssrc_entry(void)
{
	WSLhInstance = core_api::get_my_instance();
}

void
dsp_ssrc_entry::get_name(pfc::string_base& p_out)
{
	p_out.set_string(NAME2);
}

bool
dsp_ssrc_entry::get_default_preset(dsp_preset& p_out)
{
	p_out.set_owner(get_guid());

	PARAM_GLOBAL	ParamGlobal;

	ParamGlobal.srate = 88200;
	ParamGlobal.quality = 3;

	p_out.set_data(&ParamGlobal, sizeof ParamGlobal);

	return true;
}

bool
dsp_ssrc_entry::instantiate(service_ptr_t<dsp>& p_out, const dsp_preset& p_preset)
{
	bool	RetCode;

	if(p_preset.get_owner() == get_guid()) {
		PARAM_GLOBAL	ParamGlobal;

		memcpy(&ParamGlobal, p_preset.get_data(), sizeof ParamGlobal);

		p_out = reinterpret_cast<dsp*>(new service_impl_t<dsp_ssrc>(&ParamGlobal));

		RetCode = p_out.is_valid();
	} else {
		RetCode = false;
	}

	return RetCode;
}

bool
dsp_ssrc_entry::show_config_popup(dsp_preset& p_data, HWND p_parent)
{
	bool	RetCode;
	PARAM_GLOBAL	ParamGlobal;

	memcpy(&ParamGlobal, p_data.get_data(), sizeof ParamGlobal);

	if(RetCode = DialogOption(p_parent, &ParamGlobal).Execute() == IDOK) {
		p_data.set_data(&ParamGlobal, sizeof ParamGlobal);
	}

	return RetCode;
}

bool
dsp_ssrc_entry::is_conversion_supported(unsigned int src_srate, unsigned int dst_srate)
{
	return !!CanResample(src_srate, dst_srate);
}

bool
dsp_ssrc_entry::create_preset(dsp_preset& p_out, unsigned p_target_srate, float p_qualityscale)
{
	p_out.set_owner(get_guid());

	PARAM_GLOBAL	ParamGlobal;

	ParamGlobal.srate = p_target_srate;
	ParamGlobal.quality = p_qualityscale * 4;

	p_out.set_data(&ParamGlobal, sizeof ParamGlobal);

	return true;
}

dsp_ssrc::dsp_ssrc(PARAM_GLOBAL* ParamGlobal)
{
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

	res = NULL;
	error = false;
	srate = 0;
	nch = 0;
	dest_srate = ParamGlobal->srate;
	quality = ParamGlobal->quality;
}

dsp_ssrc::~dsp_ssrc(void)
{
	if(res) delete res;
}

void
dsp_ssrc::on_endoftrack(abort_callback& p_abort)
{
}

void
dsp_ssrc::on_endofplayback(abort_callback& p_abort)
{
	if(res) {
		res->Finish();
		setup_chunk(0);

		delete res;

		res = NULL;
	}
}

bool
dsp_ssrc::on_chunk(audio_chunk* chunk, abort_callback& p_abort)
{
	if(res && ((chunk->get_channels() != nch) || (chunk->get_srate() != srate))) {
		res->Finish();
		setup_chunk(0);

		delete res;

		res = NULL;
		error = false;
	}

	nch = chunk->get_channels();
	srate = chunk->get_srate();

	if(res == NULL) {
		if(dest_srate == srate) return true;

		res = SSRC_create(
						srate,
						dest_srate,
						DATA_FORMAT_IEEE_FLOAT,
						DATA_FORMAT_IEEE_FLOAT,
						sizeof audio_sample << 3,
						sizeof audio_sample << 3,
						nch,
						quality);

		if(res == NULL) {
			if(error == false) {
				console::error(uStringPrintf(
										"cannot resample from %d Hz to %d Hz",
										srate,
										dest_srate));
				error = true;
			}

			return true;
		}
	}

	res->Write(chunk->get_data(), chunk->get_data_length() * sizeof audio_sample);

	return setup_chunk(chunk);
}

void
dsp_ssrc::flush(void)
{
	if(res) {
		delete res;

		res = NULL;
	}
}

double
dsp_ssrc::get_latency(void)
{
	return res ? static_cast<double>(res->GetLatency()) / 1000. : 0.;
}

bool
dsp_ssrc::setup_chunk(audio_chunk* chunk)
{
	bool	RetCode;
	UINT	size;
	audio_sample*	ptr = reinterpret_cast<audio_sample*>(res->GetBuffer(&size));

	if(RetCode = size > 0) {
		const UINT	sizeSamples = size / sizeof audio_sample;

		if(chunk == 0) chunk = insert_chunk(sizeSamples);
		chunk->set_data(ptr, sizeSamples / nch, nch, dest_srate);
		res->Read(size);
	}

	return RetCode;
}

