/***************************************************************************
 * timidity.cpp: implementation of Timidity class
 *
 * This file is part of KTabEdit, a KDE tabulature editor
 *
 * copyright (C) 2003-2006 the KTabEdit development team <mrgwen@gmail.com>
 ***************************************************************************/

/***************************************************************************
 * 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.
 *
 * See the file COPYING for more information.
 ***************************************************************************/

#include "timidity.h"

#include <KConfigGroup>
#include <KUrl>
#include <KFileDialog>
#include <KMessageBox>
#include <KLocale>

#include "settings.h"

Timidity::Timidity(QWidget* parent)
: QWizardPage(parent),
  mMidiTimidity(i18n("&Launch Timidity"), this),
  mTimidityPathLabel(i18n("Timidity path"), this),
  mTimidityPath(this),
  mTimidityButton("...", this),
  mBufferLabel(i18n("Buffer"), this),
  mBuffer(0, 10, 1, this),
  mFragmentLabel(i18n("Fragment"), this),
  mFragment(0, 10, 1,this),
  mOutputLabel(i18n("Output"), this),
  mOutput(this),
  mChorusLabel(i18n("Chorus"), this),
  mChorus(this),
  mDelayLabel(i18n("Delay"), this),
  mDelay(this),
  mReverbLabel(i18n("Reverb"), this),
  mReverb(this),
  mVlpfLabel(i18n("Voice LPF effect"), this),
  mVlpf(this),
  mNoiseLabel(i18n("Noise shaping"), this),
  mNoise(this),
  mResampLabel(i18n("Resample"), this),
  mResamp(this),
  mFrequencyLabel(i18n("Resampling frequency"), this),
  mFrequency(0, 44100, 1000, this),
  mRealtimeEnableLabel(i18n("Realtime priority enable"), this),
  mRealtimeEnable(this),
  mRealtimeLabel(i18n("Realtime priority"), this),
  mRealtime(0, 100, 10, this),
  mTimidityLayout(this)
{
	setTitle("Timidiy");
	setSubTitle(i18n("Timidty is a MIDI-to-WAVE converter\n"
					  "but you can use this program as software\n"
					  "synthesizer.\n"
					  "TiMidity works purely as the software\n"
					  "real-time MIDI render, that is as a\n"
					  "software MIDI synth engine on ALSA."));

	mTimidityPathLayout.addWidget(&mTimidityPathLabel);
	mTimidityPathLayout.addWidget(&mTimidityPath);
	mTimidityPathLayout.addWidget(&mTimidityButton);
	
	mBufferLayout.addWidget(&mBufferLabel);
	mBufferLayout.addWidget(&mBuffer);
	
	mFragmentLayout.addWidget(&mFragmentLabel);
	mFragmentLayout.addWidget(&mFragment);
	
	mOutputLayout.addWidget(&mOutputLabel);
	mOutputLayout.addWidget(&mOutput);
	
	mOutput.insertItem("Default");
	mOutput.insertItem("ALSA");
	mOutput.insertItem("EsounD");
	mOutput.insertItem("JACK");
	mOutput.insertItem("aRts");
	
	mChorusLayout.addWidget(&mChorusLabel);
	mChorusLayout.addWidget(&mChorus);
	
	mChorus.insertItem("Disable this effect");
	mChorus.insertItem("Enable MIDI chorus effect control.");
	mChorus.insertItem("Surround sound, chorus detuned to a lesser degree (default).");
	
	mDelayLayout.addWidget(&mDelayLabel);
	mDelayLayout.addWidget(&mDelay);
	
	mDelay.insertItem("Disable mDelay effect.");
	mDelay.insertItem("Left mDelay.");
	mDelay.insertItem("Right mDelay.");
	mDelay.insertItem("Swap left & right.");
	
	mReverbLayout.addWidget(&mReverbLabel);
	mReverbLayout.addWidget(&mReverb);
	
	mReverb.insertItem("Disable MIDI reverb effect control.");
	mReverb.insertItem("Enable  MIDI reverb effect control. This effect is only available in stereo.");
	mReverb.insertItem("Global reverb effect.");
	mReverb.insertItem("Freeverb MIDI reverb effect control (default).");
	mReverb.insertItem("Global freeverb effect.");
	
	mVlpfLayout.addWidget(&mVlpfLabel);
	mVlpfLayout.addWidget(&mVlpf);
	
	mVlpf.insertItem("Disable LPF effect.");
	mVlpf.insertItem("Chamberlin resonant LPF (12dB/oct) (default).");
	mVlpf.insertItem("Moog resonant low-pass VCF (24dB/oct).");
	
	mNoiseLayout.addWidget(&mNoiseLabel);
	mNoiseLayout.addWidget(&mNoise);
	
	mNoise.insertItem("No noise shaping.");
	mNoise.insertItem("Traditional noise shaping.");
	mNoise.insertItem("Overdrive-like soft-clipping + new noise shaping.");
	mNoise.insertItem("Tube-amplifier-like soft-clipping + new noise shaping.");
	mNoise.insertItem("New noise shaping.");
	
	mResampLayout.addWidget(&mResampLabel);
	mResampLayout.addWidget(&mResamp);
	
	mResamp.insertItem("No interpolation.");
	mResamp.insertItem("Linear interpolation.");
	mResamp.insertItem("Cubic spline interpolation.");
	mResamp.insertItem("Lagrange method.");
	mResamp.insertItem("Newton polynomial interpolation.");
	mResamp.insertItem("Modified Gauss effect (default).");
	
	mFrequencyLayout.addWidget(&mFrequencyLabel);
	mFrequencyLayout.addWidget(&mFrequency);
	
	mRealtimeEnableLayout.addWidget(&mRealtimeEnableLabel);
	mRealtimeEnableLayout.addWidget(&mRealtimeEnable);
	
	mRealtimeLayout.addWidget(&mRealtimeLabel);
	mRealtimeLayout.addWidget(&mRealtime);
	
	mTimidityLayout.addWidget(&mMidiTimidity);
	mTimidityLayout.addLayout(&mTimidityPathLayout);
	mTimidityLayout.addLayout(&mBufferLayout);
	mTimidityLayout.addLayout(&mFragmentLayout);
	mTimidityLayout.addLayout(&mOutputLayout);
	mTimidityLayout.addLayout(&mChorusLayout);
	mTimidityLayout.addLayout(&mDelayLayout);
	mTimidityLayout.addLayout(&mReverbLayout);
	mTimidityLayout.addLayout(&mVlpfLayout);
	mTimidityLayout.addLayout(&mNoiseLayout);
	mTimidityLayout.addLayout(&mResampLayout);
	mTimidityLayout.addLayout(&mFrequencyLayout);
	mTimidityLayout.addLayout(&mRealtimeEnableLayout);
	mTimidityLayout.addLayout(&mRealtimeLayout);
	
	fillOptions();
	
	connect(&mTimidityButton, SIGNAL(clicked()), SLOT(search()));
	connect(&mMidiTimidity, SIGNAL(clicked()), SLOT(launchTimidity()));
}

void Timidity::writeConfig()
{
	KConfigGroup config = KGlobal::config()->group("Timidity");
	
	config.writeEntry("Path", mTimidityPath.text());
	
	config.writeEntry("Buffer", mBuffer.value());
	
	config.writeEntry("Fragment", mFragment.value());
	
	switch(mOutput.currentItem()) {
		case 0:
			config.writeEntry("Output", "d");
			break;
		case 1:
			config.writeEntry("Output", "s");
			break;
		case 2:
			config.writeEntry("Output", "e");
			break;
		case 3:
			config.writeEntry("Output", "j");
			break;
		case 4:
			config.writeEntry("Output", "r");
			break;
	}
	
	switch(mChorus.currentItem()) {
		case 0:
			config.writeEntry("Chorus", "d");
			break;
		case 1:
			config.writeEntry("Chorus", "n");
			break;
		case 2:
			config.writeEntry("Chorus", "s");
			break;
	}
	
	switch(mDelay.currentItem()) {
		case 0:
			config.writeEntry("Delay", "d");
			break;
		case 1:
			config.writeEntry("Delay", "l");
			break;
		case 2:
			config.writeEntry("Delay", "r");
			break;
		case 3:
			config.writeEntry("Delay", "b");
			break;
	}
	
	switch(mReverb.currentItem()) {
		case 0:
			config.writeEntry("Reverb", "d");
			break;
		case 1:
			config.writeEntry("Reverb", "n");
			break;
		case 2:
			config.writeEntry("Reverb", "g");
			break;
		case 3:
			config.writeEntry("Reverb", "f");
			break;
		case 4:
			config.writeEntry("Reverb", "G");
			break;
	}
	
	switch(mVlpf.currentItem()) {
		case 0:
			config.writeEntry("Vlpf", "d");
			break;
		case 1:
			config.writeEntry("Vlpf", "c");
			break;
		case 2:
			config.writeEntry("Vlpf", "m");
			break;
	}
	
	
	config.writeEntry("Noise", mNoise.currentItem());
	
	switch(mResamp.currentItem()) {
		case 0:
			config.writeEntry("Resample", "d");
			break;
		case 1:
			config.writeEntry("Resample", "l");
			break;
		case 2:
			config.writeEntry("Resample", "c");
			break;
		case 3:
			config.writeEntry("Resample", "L");
			break;
		case 4:
			config.writeEntry("Resample", "n");
			break;
		case 5:
			config.writeEntry("Resample", "G");
			break;
	}
	
	config.writeEntry("Frequency", mFrequency.value());
	
	config.writeEntry("RealTime", mRealtime.value());
	
	config.writeEntry("RealTimeEnable", mRealtimeEnable.isChecked());
	
	KConfigGroup midiConfig = KGlobal::config()->group("MIDI");
	
	midiConfig.writeEntry("Timidity", mMidiTimidity.isChecked());
}

bool Timidity::isTimidityWanted() const
{
	return mMidiTimidity.isChecked();
}

QString Timidity::program() const
{
	if (mTimidityPath.text().isEmpty() == false)
		return mTimidityPath.text();
	else
		return "/usr/bin/timidity";
}

QStringList Timidity::buildCmdLine() const
{
	QString buffer;
	QString fragment;
	QString noise;
	QString realtime;
	QString frequency;
	QStringList arguments;
	
	arguments << "-iA";
	
	buffer.setNum(mBuffer.value());
	fragment.setNum(mFragment.value());
	arguments << "-B" + buffer + ',' + fragment;
	
	switch(mOutput.currentItem()) {
		case 0:
			arguments << "-Od";
			break;
		case 1:
			arguments << "-Os";
			break;
		case 2:
			arguments << "-Oe";
			break;
		case 3:
			arguments << "-Oj";
			break;
		case 4:
			arguments << "-Or";
			break;
	}
	
	switch(mChorus.currentItem()) {
		case 0:
			arguments << "-EFChorus=d";
			break;
		case 1:
			arguments << "-EFChorus=n";
			break;
		case 2:
			arguments << "-EFChorus=s";
			break;
	}
	
	switch(mDelay.currentItem()) {
		case 0:
			arguments << "-EFDelay=d";
			break;
		case 1:
			arguments << "-EFDelay=l";
			break;
		case 2:
			arguments << "-EFDelay=r";
			break;
		case 3:
			arguments << "-EFDelay=b";
			break;
	}
	
	noise.setNum(mNoise.currentItem());
	arguments << "-EFns=" + noise;
	
	switch(mResamp.currentItem()) {
		case 0:
			arguments << "-EFResamp=d";
			break;
		case 1:
			arguments << "-EFResamp=l";
			break;
		case 2:
			arguments << "-EFResamp=c";
			break;
		case 3:
			arguments << "-EFResamp=L";
			break;
		case 4:
			arguments << "-EFResamp=n";
			break;
		case 5:
			arguments << "-EFResamp=G";
			break;
	}
	
	switch(mReverb.currentItem()) {
		case 0:
			arguments << "-EFReverb=d";
			break;
		case 1:
			arguments << "-EFReverb=n";
			break;
		case 2:
			arguments << "-EFReverb=g";
			break;
		case 3:
			arguments << "-EFReverb=f";
			break;
		case 4:
			arguments << "-EFReverb=G";
			break;
	}
	
	switch(mVlpf.currentItem()) {
		case 0:
			arguments << "-EFvlpf=d";
			break;
		case 1:
			arguments << "-EFvlpf=c";
			break;
		case 2:
			arguments << "-EFvlpf=m";
			break;
	}
	
	
	if (mRealtimeEnable.isChecked()) {
		realtime.setNum(mRealtime.value());
		arguments << "--realtime-priority=" + realtime;
	} else realtime = "no realtime enabled";
	
	frequency.setNum(mFrequency.value());
	arguments << "-s " + frequency;
	
	return arguments;
}

void Timidity::search()
{
	QString temp = KFileDialog::getOpenFileName(KUrl(mTimidityPath.text()), "timidity", this, "Choose timidity" );
	
	if (!temp.isEmpty()) {
		mTimidityPath.setText(temp);
	}
}

void Timidity::launchTimidity()
{
	if (mMidiTimidity.isChecked() == false)
		return ;
	
	if (KMessageBox::questionYesNo(this, "KTabEdit", i18n("Restart KTabEdit and try to launch the timidity sequencer with alsa emulation? "
							  "(It must be compiled with alsa support.)")) == KMessageBox::Yes)
		return ;
	
	mMidiTimidity.setChecked(false);
}

void Timidity::fillOptions()
{
	mMidiTimidity.setChecked(false);
	
	mTimidityPath.setText("/usr/bin/timidity");
	
	// Maximum number of mBuffers mFragments
	//
	mBuffer.setValue(2);
	
	// Number of mFragments in interactive mode
	//
	mFragment.setValue(8);
	
	// Output to ALSA
	//
	mOutput.setCurrentItem(1);
	
	// Disable this effect
	//
	mChorus.setCurrentItem(0);
	
	// Disabled mDelay effect
	//
	mDelay.setCurrentItem(0);
	
	// Disable MIDI mReverb effect control
	//
	mReverb.setCurrentItem(0);
	
	// Disable LPF effect
	//
	mVlpf.setCurrentItem(0);
	
	// No mNoise shaping
	//
	mNoise.setCurrentItem(0);
	
	// Resample linear interporlation
	//
	mResamp.setCurrentItem(1);
	
	// Frequency high quality
	//
	mFrequency.setValue(44100);
	
	// Realtime is disabled by default
	//
	mRealtimeEnable.setChecked(false);
	
	// Realtime priority
	//
	mRealtime.setValue(50);
}

#include "timidity.moc"
