
/*
 * CINELERRA
 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
 *
 * 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.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "bcdisplayinfo.h"
#include "clip.h"
#include "bchash.h"
#include "filexml.h"
#include "language.h"
#include "piano.h"
#include "vframe.h"

#include <string.h>



REGISTER_PLUGIN(Piano)





Piano::Piano(PluginServer *server)
 : PluginAClient(server)
{
	reset();
}



Piano::~Piano()
{
	if(dsp_buffer) delete [] dsp_buffer;
}


char* Piano::plugin_title() { return N_("Pianoesizer"); }
int Piano::is_realtime() { return 1; }
int Piano::is_synthesis() { return 1; }


void Piano::reset()
{
	thread = 0;
	need_reconfigure = 1;
	dsp_buffer = 0;
}




LOAD_CONFIGURATION_MACRO(Piano, PianoConfig)





void Piano::read_data(KeyFrame *keyframe)
{
	FileXML input;
// cause htal file to read directly from text
	input.set_shared_input(keyframe->xbuf);

//printf("Piano::read_data %s\n", keyframe->get_data());
	int result = 0, current_osc = 0, total_oscillators = 0;
	while(!result)
	{
		result = input.read_tag();

		if(!result)
		{
			if(input.tag.title_is("SYNTH"))
			{
				config.wetness = input.tag.get_property("WETNESS", config.wetness);
				config.base_freq = input.tag.get_property("BASEFREQ", config.base_freq);
				config.wavefunction = input.tag.get_property("WAVEFUNCTION", config.wavefunction);
				total_oscillators = input.tag.get_property("OSCILLATORS", 0);
			}
			else
			if(input.tag.title_is("OSCILLATOR"))
			{
				if(current_osc >= config.oscillator_config.total)
					config.oscillator_config.append(new PianoOscillatorConfig(current_osc));

				config.oscillator_config.values[current_osc]->read_data(&input);
				current_osc++;
			}
		}
	}

	while(config.oscillator_config.total > current_osc)
		config.oscillator_config.remove_object();
}

void Piano::save_data(KeyFrame *keyframe)
{
	FileXML output;
// cause htal file to store data directly in text
	output.set_shared_output(keyframe->xbuf);

	output.tag.set_title("SYNTH");
	output.tag.set_property("WETNESS", config.wetness);
	output.tag.set_property("BASEFREQ", config.base_freq);
	output.tag.set_property("WAVEFUNCTION", config.wavefunction);
	output.tag.set_property("OSCILLATORS", config.oscillator_config.total);
	output.append_tag();
	output.append_newline();

	for(int i = 0; i < config.oscillator_config.total; i++)
	{
		config.oscillator_config.values[i]->save_data(&output);
	}

	output.tag.set_title("/SYNTH");
	output.append_tag();
	output.append_newline();
	output.terminate_string();
// data is now in *text
}

int Piano::show_gui()
{
	load_configuration();

	thread = new PianoThread(this);
	thread->start();
	return 0;
}

int Piano::set_string()
{
	if( thread )
		thread->window->set_title(gui_string);
	return 0;
}

void Piano::raise_window()
{
	if(thread)
	{
		thread->window->raise_window();
		thread->window->flush();
	}
}

void Piano::update_gui()
{
	if( !thread ) return;
	PianoWindow *window = (PianoWindow *)thread->window:
// load_configuration,read_data deletes oscillator_config
	window->lock_window("Piano::update_gui");
	load_configuration();
	window->update_gui();
	window->unlock_window();
}


void Piano::add_oscillator()
{
	if(config.oscillator_config.total > 20) return;

	config.oscillator_config.append(new PianoOscillatorConfig(config.oscillator_config.total - 1));
}

void Piano::delete_oscillator()
{
	if(config.oscillator_config.total)
	{
		config.oscillator_config.remove_object();
	}
}


double Piano::get_total_power()
{
	double result = 0;

	if(config.wavefunction == DC) return 1.0;

	for(int i = 0; i < config.oscillator_config.total; i++)
	{
		result += db.fromdb(config.oscillator_config.values[i]->level);
	}

	if(result == 0) result = 1;  // prevent division by 0
	return result;
}


double Piano::solve_eqn(double *output,
	double x1,
	double x2,
	double normalize_constant,
	int oscillator)
{
	PianoOscillatorConfig *config = this->config.oscillator_config.values[oscillator];
	if(config->level <= INFINITYGAIN) return 0;

	double result;
	double x;
	double power = this->db.fromdb(config->level) * normalize_constant;
	double phase_offset = config->phase * this->period;
	double x3 = x1 + phase_offset;
	double x4 = x2 + phase_offset;
	double period = this->period / config->freq_factor;
	int sample;

	switch(this->config.wavefunction)
	{
		case DC:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += power;
			}
			break;
		case SINE:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += sin(x / period * 2 * M_PI) * power;
			}
			break;
		case SAWTOOTH:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += function_sawtooth(x / period) * power;
			}
			break;
		case SQUARE:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += function_square(x / period) * power;
			}
			break;
		case TRIANGLE:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += function_triangle(x / period) * power;
			}
			break;
		case PULSE:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += function_pulse(x / period) * power;
			}
			break;
		case NOISE:
			for(sample = (int)x1, x = x3; x < x4; x++, sample++)
			{
				output[sample] += function_noise() * power;
			}
			break;
	}
}

double Piano::get_point(float x, double normalize_constant)
{
	double result = 0;
	for(int i = 0; i < config.oscillator_config.total; i++)
		result += get_oscillator_point(x, normalize_constant, i);

	return result;
}

double Piano::get_oscillator_point(float x,
		double normalize_constant,
		int oscillator)
{
	PianoOscillatorConfig *config = this->config.oscillator_config.values[oscillator];
	double power = db.fromdb(config->level) * normalize_constant;
	switch(this->config.wavefunction)
	{
		case DC:
			return power;
			break;
		case SINE:
			return sin((x + config->phase) * config->freq_factor * 2 * M_PI) * power;
			break;
		case SAWTOOTH:
			return function_sawtooth((x + config->phase) * config->freq_factor) * power;
			break;
		case SQUARE:
			return function_square((x + config->phase) * config->freq_factor) * power;
			break;
		case TRIANGLE:
			return function_triangle((x + config->phase) * config->freq_factor) * power;
			break;
		case PULSE:
			return function_pulse((x + config->phase) * config->freq_factor) * power;
			break;
		case NOISE:
			return function_noise() * power;
			break;
	}
}

double Piano::function_square(double x)
{
	x -= (int)x; // only fraction counts
	return (x < .5) ? -1 : 1;
}

double Piano::function_pulse(double x)
{
	x -= (int)x; // only fraction counts
	return (x < .5) ? 0 : 1;
}

double Piano::function_noise()
{
	return (double)(rand() % 65536 - 32768) / 32768;
}

double Piano::function_sawtooth(double x)
{
	x -= (int)x;
	return 1 - x * 2;
}

double Piano::function_triangle(double x)
{
	x -= (int)x;
	return (x < .5) ? 1 - x * 4 : -3 + x * 4;
}

int Piano::process_realtime(int64_t size, double *input_ptr, double *output_ptr)
{


	need_reconfigure |= load_configuration();
	if(need_reconfigure) reconfigure();

	double wetness = DB::fromdb(config.wetness);
	if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0;

	for(int j = 0; j < size; j++)
		output_ptr[j] = input_ptr[j] * wetness;

	int64_t fragment_len;
	for(int64_t i = 0; i < size; i += fragment_len)
	{
		fragment_len = size;
		if(i + fragment_len > size) fragment_len = size - i;

//printf("Piano::process_realtime 1 %d %d %d\n", i, fragment_len, size);
		fragment_len = overlay_synth(i, fragment_len, input_ptr, output_ptr);
//printf("Piano::process_realtime 2\n");
	}


	return 0;
}

int Piano::overlay_synth(int64_t start, int64_t length, double *input, double *output)
{
	if(waveform_sample + length > waveform_length)
		length = waveform_length - waveform_sample;

//printf("Piano::overlay_synth 1 %d %d\n", length, waveform_length);

// calculate some more data
// only calculate what's needed to speed it up
	if(waveform_sample + length > samples_rendered)
	{
		int64_t start = waveform_sample, end = waveform_sample + length;
		for(int i = start; i < end; i++) dsp_buffer[i] = 0;

		double normalize_constant = 1 / get_total_power();
		for(int i = 0; i < config.oscillator_config.total; i++)
			solve_eqn(dsp_buffer,
				start,
				end,
				normalize_constant,
				i);


		samples_rendered = end;
	}
//printf("Piano::overlay_synth 2\n");


	double *buffer_in = &input[start];
	double *buffer_out = &output[start];

	for(int i = 0; i < length; i++)
	{
		buffer_out[i] += dsp_buffer[waveform_sample++];
	}
//printf("Piano::overlay_synth 3\n");

	if(waveform_sample >= waveform_length) waveform_sample = 0;

	return length;
}

void Piano::reconfigure()
{
	need_reconfigure = 0;

	if(dsp_buffer)
	{
		delete [] dsp_buffer;
	}

//printf("Piano::reconfigure 1 %d\n", PluginAClient::project_sample_rate);
	waveform_length = PluginAClient::project_sample_rate;
	period = (float)PluginAClient::project_sample_rate / config.base_freq;
	dsp_buffer = new double[waveform_length + 1];

	samples_rendered = 0;     // do some calculations on the next process_realtime
	waveform_sample = 0;
}





















PianoThread::PianoThread(Piano *synth)
 : Thread()
{
	this->synth = synth;
	set_synchronous(0);
	completion.lock();
}

PianoThread::~PianoThread()
{
	delete window;
}

void PianoThread::run()
{
	BC_DisplayInfo info;
	window = new PianoWindow(synth,
		info.get_abs_cursor_x() - xS(125),
		info.get_abs_cursor_y() - yS(115));
	window->create_objects();
	int result = window->run_window();
	completion.unlock();
// Last command executed in thread
	if(result) synth->client_side_close();
}











PianoWindow::PianoWindow(Piano *synth, int x, int y)
 : BC_Window(synth->gui_string, x, y, xS(380), synth->h,
	xS(380), yS(10), 1, 0, 1)
{
	this->synth = synth;
}

PianoWindow::~PianoWindow()
{
}

int PianoWindow::create_objects()
{
	BC_MenuBar *menu;
	add_subwindow(menu = new BC_MenuBar(0, 0, get_w()));

	BC_Menu *levelmenu, *phasemenu, *harmonicmenu;
	menu->add_menu(levelmenu = new BC_Menu(_("Level")));
	menu->add_menu(phasemenu = new BC_Menu(_("Phase")));
	menu->add_menu(harmonicmenu = new BC_Menu(_("Harmonic")));

	levelmenu->add_item(new PianoLevelInvert(synth));
	levelmenu->add_item(new PianoLevelMax(synth));
	levelmenu->add_item(new PianoLevelRandom(synth));
	levelmenu->add_item(new PianoLevelSine(synth));
	levelmenu->add_item(new PianoLevelSlope(synth));
	levelmenu->add_item(new PianoLevelZero(synth));

	phasemenu->add_item(new PianoPhaseInvert(synth));
	phasemenu->add_item(new PianoPhaseRandom(synth));
	phasemenu->add_item(new PianoPhaseSine(synth));
	phasemenu->add_item(new PianoPhaseZero(synth));

	harmonicmenu->add_item(new PianoFreqEnum(synth));
	harmonicmenu->add_item(new PianoFreqEven(synth));
	harmonicmenu->add_item(new PianoFreqFibonacci(synth));
	harmonicmenu->add_item(new PianoFreqOdd(synth));
	harmonicmenu->add_item(new PianoFreqPrime(synth));

	int xs10 = xS(10), xs240 = xS(240);
	int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys35 = yS(35);
	int x = xs10, y = ys30, i;
	add_subwindow(new BC_Title(x, y, _("Waveform")));
	x += xs240;
	add_subwindow(new BC_Title(x, y, _("Wave Function")));
	y += ys20;
	x = xs10;
	add_subwindow(canvas = new PianoCanvas(synth, this, x, y, xS(230), yS(160)));
	canvas->update();

	x += xS(240);
	char string[BCTEXTLEN];
	waveform_to_text(string, synth->config.wavefunction);

	add_subwindow(waveform = new PianoWaveForm(synth, x, y, string));
	waveform->create_objects();
	y += ys30;


	add_subwindow(new BC_Title(x, y, _("Base Frequency:")));
	y += ys30;
	add_subwindow(base_freq = new PianoBaseFreq(synth, x, y));
	x += xs80;
	add_subwindow(freqpot = new PianoFreqPot(synth, this, x, y - ys10));
	base_freq->freq_pot = freqpot;
	freqpot->freq_text = base_freq;
	x -= xs80;
	y += ys40;
	add_subwindow(new BC_Title(x, y, _("Wetness:")));
	add_subwindow(wetness = new PianoWetness(synth, x + xs70, y - ys10));

	y += ys40;
	add_subwindow(new PianoClear(synth, x, y));


	x = xs50;
	y = yS(220);
	add_subwindow(new BC_Title(x, y, _("Level")));
	x += xs75;
	add_subwindow(new BC_Title(x, y, _("Phase")));
	x += xs75;
	add_subwindow(new BC_Title(x, y, _("Harmonic")));



	y += ys20; x = xs10;
	add_subwindow(subwindow = new PianoSubWindow(synth, x, y, xS(265), get_h() - y));
	x += xS(265);
	add_subwindow(scroll = new PianoScroll(synth, this, x, y, get_h() - y));


	x += xs20;
	add_subwindow(new PianoAddOsc(synth, this, x, y));
	y += ys30;
	add_subwindow(new PianoDelOsc(synth, this, x, y));

	update_scrollbar();
	update_oscillators();

	show_window();
	flush();
	return 0;
}

int PianoWindow::close_event()
{
// Set result to 1 to indicate a client side close
	set_done(1);
	return 1;
}

int PianoWindow::resize_event(int w, int h)
{
	clear_box(0, 0, w, h);
	subwindow->reposition_window(subwindow->get_x(),
		subwindow->get_y(),
		subwindow->get_w(),
		h - subwindow->get_y());
	subwindow->clear_box(0, 0, subwindow->get_w(), subwindow->get_h());
	scroll->reposition_window(scroll->get_x(),
		scroll->get_y(),
		h - scroll->get_y());
	update_scrollbar();
	update_oscillators();
	synth->w = w;
	synth->h = h;
	return 1;
}

void PianoWindow::update_gui()
{
	char string[BCTEXTLEN];
	freqpot->update(synth->config.base_freq);
	base_freq->update((int64_t)synth->config.base_freq);
	wetness->update(synth->config.wetness);
	waveform_to_text(string, synth->config.wavefunction);
	waveform->set_text(string);

	update_scrollbar();
	update_oscillators();
	canvas->update();
}

void PianoWindow::update_scrollbar()
{
	scroll->update_length(synth->config.oscillator_config.total * OSCILLATORHEIGHT,
		scroll->get_position(),
		subwindow->get_h());
}

void PianoWindow::update_oscillators()
{
	int i, y = -scroll->get_position();



// Add new oscillators
	for(i = 0;
		i < synth->config.oscillator_config.total;
		i++)
	{
		PianoOscGUI *gui;
		PianoOscillatorConfig *config = synth->config.oscillator_config.values[i];

		if(oscillators.total <= i)
		{
			oscillators.append(gui = new PianoOscGUI(this, i));
			gui->create_objects(y);
		}
		else
		{
			gui = oscillators.values[i];

			gui->title->reposition_window(gui->title->get_x(), y + yS(15));

			gui->level->reposition_window(gui->level->get_x(), y);
			gui->level->update(config->level);

			gui->phase->reposition_window(gui->phase->get_x(), y);
			gui->phase->update((int64_t)(config->phase * 360));

			gui->freq->reposition_window(gui->freq->get_x(), y);
			gui->freq->update((int64_t)(config->freq_factor));
		}
		y += OSCILLATORHEIGHT;
	}

// Delete old oscillators
	for( ;
		i < oscillators.total;
		i++)
		oscillators.remove_object();
}


int PianoWindow::waveform_to_text(char *text, int waveform)
{
	switch(waveform)
	{
		case DC:              sprintf(text, _("DC"));           break;
		case SINE:            sprintf(text, _("Sine"));           break;
		case SAWTOOTH:        sprintf(text, _("Sawtooth"));       break;
		case SQUARE:          sprintf(text, _("Square"));         break;
		case TRIANGLE:        sprintf(text, _("Triangle"));       break;
		case PULSE:           sprintf(text, _("Pulse"));       break;
		case NOISE:           sprintf(text, _("Noise"));       break;
	}
	return 0;
}







PianoOscGUI::PianoOscGUI(PianoWindow *window, int number)
{
	this->window = window;
	this->number = number;
}

PianoOscGUI::~PianoOscGUI()
{
	delete title;
	delete level;
	delete phase;
	delete freq;
}

int PianoOscGUI::create_objects(int y)
{
	char text[BCTEXTLEN];
	sprintf(text, "%d:", number + 1);
	window->subwindow->add_subwindow(title = new BC_Title(xs10, y + ys15, text));

	window->subwindow->add_subwindow(level = new PianoOscGUILevel(window->synth, this, y));
	window->subwindow->add_subwindow(phase = new PianoOscGUIPhase(window->synth, this, y));
	window->subwindow->add_subwindow(freq = new PianoOscGUIFreq(window->synth, this, y));
	return 1;
}




PianoOscGUILevel::PianoOscGUILevel(Piano *synth, PianoOscGUI *gui, int y)
 : BC_FPot(xS(50),
 	y,
	synth->config.oscillator_config.values[gui->number]->level,
	INFINITYGAIN,
	0)
{
	this->synth = synth;
	this->gui = gui;
}

PianoOscGUILevel::~PianoOscGUILevel()
{
}

int PianoOscGUILevel::handle_event()
{
	PianoOscillatorConfig *config = synth->config.oscillator_config.values[gui->number];
	config->level = get_value();
	gui->window->canvas->update();
	synth->send_configure_change();
	return 1;
}



PianoOscGUIPhase::PianoOscGUIPhase(Piano *synth, PianoOscGUI *gui, int y)
 : BC_IPot(xS(125),
 	y,
	(int64_t)(synth->config.oscillator_config.values[gui->number]->phase * 360),
	0,
	xS(360))
{
	this->synth = synth;
	this->gui = gui;
}

PianoOscGUIPhase::~PianoOscGUIPhase()
{
}

int PianoOscGUIPhase::handle_event()
{
	PianoOscillatorConfig *config = synth->config.oscillator_config.values[gui->number];
	config->phase = (float)get_value() / 360;
	gui->window->canvas->update();
	synth->send_configure_change();
	return 1;
}



PianoOscGUIFreq::PianoOscGUIFreq(Piano *synth, PianoOscGUI *gui, int y)
 : BC_IPot(xS(200),
 	y,
	(int64_t)(synth->config.oscillator_config.values[gui->number]->freq_factor),
	yS(1),
	xS(100))
{
	this->synth = synth;
	this->gui = gui;
}

PianoOscGUIFreq::~PianoOscGUIFreq()
{
}

int PianoOscGUIFreq::handle_event()
{
	PianoOscillatorConfig *config = synth->config.oscillator_config.values[gui->number];
	config->freq_factor = get_value();
	gui->window->canvas->update();
	synth->send_configure_change();
	return 1;
}







PianoAddOsc::PianoAddOsc(Piano *synth, PianoWindow *window, int x, int y)
 : BC_GenericButton(x, y, _("Add"))
{
	this->synth = synth;
	this->window = window;
}

PianoAddOsc::~PianoAddOsc()
{
}

int PianoAddOsc::handle_event()
{
	synth->add_oscillator();
	synth->send_configure_change();
	window->update_gui();
	return 1;
}



PianoDelOsc::PianoDelOsc(Piano *synth, PianoWindow *window, int x, int y)
 : BC_GenericButton(x, y, _("Delete"))
{
	this->synth = synth;
	this->window = window;
}

PianoDelOsc::~PianoDelOsc()
{
}

int PianoDelOsc::handle_event()
{
	synth->delete_oscillator();
	synth->send_configure_change();
	window->update_gui();
	return 1;
}


PianoScroll::PianoScroll(Piano *synth,
	PianoWindow *window,
	int x,
	int y,
	int h)
 : BC_ScrollBar(x,
 	y,
	SCROLL_VERT,
	h,
	synth->config.oscillator_config.total * OSCILLATORHEIGHT,
	0,
	window->subwindow->get_h())
{
	this->synth = synth;
	this->window = window;
}

PianoScroll::~PianoScroll()
{
}

int PianoScroll::handle_event()
{
	window->update_gui();
	return 1;
}








PianoSubWindow::PianoSubWindow(Piano *synth, int x, int y, int w, int h)
 : BC_SubWindow(x, y, w, h)
{
	this->synth = synth;
}
PianoSubWindow::~PianoSubWindow()
{
}









PianoClear::PianoClear(Piano *synth, int x, int y)
 : BC_GenericButton(x, y, _("Clear"))
{
	this->synth = synth;
}
PianoClear::~PianoClear()
{
}
int PianoClear::handle_event()
{
	synth->config.reset();
	synth->send_configure_change();
	synth->update_gui();
	return 1;
}






PianoWaveForm::PianoWaveForm(Piano *synth, int x, int y, char *text)
 : BC_PopupMenu(x, y, xS(120), text)
{
	this->synth = synth;
}

PianoWaveForm::~PianoWaveForm()
{
}

int PianoWaveForm::create_objects()
{
//	add_item(new PianoWaveFormItem(synth, _("DC"), DC));
	add_item(new PianoWaveFormItem(synth, _("Sine"), SINE));
	add_item(new PianoWaveFormItem(synth, _("Sawtooth"), SAWTOOTH));
	add_item(new PianoWaveFormItem(synth, _("Square"), SQUARE));
	add_item(new PianoWaveFormItem(synth, _("Triangle"), TRIANGLE));
	add_item(new PianoWaveFormItem(synth, _("Pulse"), PULSE));
	add_item(new PianoWaveFormItem(synth, _("Noise"), NOISE));
	return 0;
}

PianoWaveFormItem::PianoWaveFormItem(Piano *synth, char *text, int value)
 : BC_MenuItem(text)
{
	this->synth = synth;
	this->value = value;
}

PianoWaveFormItem::~PianoWaveFormItem()
{
}

int PianoWaveFormItem::handle_event()
{
	synth->config.wavefunction = value;
	synth->thread->window->canvas->update();
	synth->send_configure_change();
	return 1;
}


PianoWetness::PianoWetness(Piano *synth, int x, int y)
 : BC_FPot(x,
		y,
		synth->config.wetness,
		INFINITYGAIN,
		0)
{
	this->synth = synth;
}

int PianoWetness::handle_event()
{
	synth->config.wetness = get_value();
	synth->send_configure_change();
	return 1;
}



PianoFreqPot::PianoFreqPot(Piano *synth, PianoWindow *window, int x, int y)
 : BC_QPot(x, y, synth->config.base_freq)
{
	this->synth = synth;
}
PianoFreqPot::~PianoFreqPot()
{
}
int PianoFreqPot::handle_event()
{
	if(get_value() > 0 && get_value() < 30000)
	{
		synth->config.base_freq = get_value();
		freq_text->update(get_value());
		synth->send_configure_change();
	}
	return 1;
}



PianoBaseFreq::PianoBaseFreq(Piano *synth, int x, int y)
 : BC_TextBox(x, y, xS(70), 1, (int)synth->config.base_freq)
{
	this->synth = synth;
}
PianoBaseFreq::~PianoBaseFreq()
{
}
int PianoBaseFreq::handle_event()
{
	int new_value = atol(get_text());

	if(new_value > 0 && new_value < 30000)
	{
		synth->config.base_freq = new_value;
		freq_pot->update(synth->config.base_freq);
		synth->send_configure_change();
	}
	return 1;
}





PianoCanvas::PianoCanvas(Piano *synth,
	PianoWindow *window,
	int x,
	int y,
	int w,
	int h)
 : BC_SubWindow(x,
 	y,
	w,
	h,
	BLACK)
{
	this->synth = synth;
	this->window = window;
}

PianoCanvas::~PianoCanvas()
{
}

int PianoCanvas::update()
{
	int y1, y2, y = 0;

	clear_box(0, 0, get_w(), get_h());
	set_color(RED);

	draw_line(0, get_h() / 2 + y, get_w(), get_h() / 2 + y);

	set_color(GREEN);

	double normalize_constant = (double)1 / synth->get_total_power();
	y1 = (int)(synth->get_point((float)0, normalize_constant) * get_h() / 2);

	for(int i = 1; i < get_w(); i++)
	{
		y2 = (int)(synth->get_point((float)i / get_w(), normalize_constant) * get_h() / 2);
		draw_line(i - 1, get_h() / 2 - y1, i, get_h() / 2 - y2);
		y1 = y2;
	}
	flash();
	return 0;
}








// ======================= level calculations
PianoLevelZero::PianoLevelZero(Piano *synth)
 : BC_MenuItem(_("Zero"))
{
	this->synth = synth;
}

PianoLevelZero::~PianoLevelZero()
{
}

int PianoLevelZero::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->level = INFINITYGAIN;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelMax::PianoLevelMax(Piano *synth)
 : BC_MenuItem(_("Maximum"))
{
	this->synth = synth;
}

PianoLevelMax::~PianoLevelMax()
{
}

int PianoLevelMax::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->level = 0;
	}
	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelNormalize::PianoLevelNormalize(Piano *synth)
 : BC_MenuItem(_("Normalize"))
{
	this->synth = synth;
}

PianoLevelNormalize::~PianoLevelNormalize()
{
}

int PianoLevelNormalize::handle_event()
{
// get total power
	float total = 0;

	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		total += synth->db.fromdb(synth->config.oscillator_config.values[i]->level);
	}

	float scale = 1 / total;
	float new_value;

	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		new_value = synth->db.fromdb(synth->config.oscillator_config.values[i]->level);
		new_value *= scale;
		new_value = synth->db.todb(new_value);

		synth->config.oscillator_config.values[i]->level = new_value;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelSlope::PianoLevelSlope(Piano *synth)
 : BC_MenuItem(_("Slope"))
{
	this->synth = synth;
}

PianoLevelSlope::~PianoLevelSlope()
{
}

int PianoLevelSlope::handle_event()
{
	float slope = (float)INFINITYGAIN / synth->config.oscillator_config.total;

	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->level = i * slope;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelRandom::PianoLevelRandom(Piano *synth)
 : BC_MenuItem(_("Random"))
{
	this->synth = synth;
}
PianoLevelRandom::~PianoLevelRandom()
{
}

int PianoLevelRandom::handle_event()
{
	srand(time(0));
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->level = -(rand() % -INFINITYGAIN);
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelInvert::PianoLevelInvert(Piano *synth)
 : BC_MenuItem(_("Invert"))
{
	this->synth = synth;
}
PianoLevelInvert::~PianoLevelInvert()
{
}

int PianoLevelInvert::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->level =
			INFINITYGAIN - synth->config.oscillator_config.values[i]->level;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoLevelSine::PianoLevelSine(Piano *synth)
 : BC_MenuItem(_("Sine"))
{
	this->synth = synth;
}
PianoLevelSine::~PianoLevelSine()
{
}

int PianoLevelSine::handle_event()
{
	float new_value;

	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		new_value = (float)i / synth->config.oscillator_config.total * 2 * M_PI;
		new_value = sin(new_value) * INFINITYGAIN / 2 + INFINITYGAIN / 2;
		synth->config.oscillator_config.values[i]->level = new_value;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

// ============================ phase calculations

PianoPhaseInvert::PianoPhaseInvert(Piano *synth)
 : BC_MenuItem(_("Invert"))
{
	this->synth = synth;
}
PianoPhaseInvert::~PianoPhaseInvert()
{
}

int PianoPhaseInvert::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->phase =
			1 - synth->config.oscillator_config.values[i]->phase;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoPhaseZero::PianoPhaseZero(Piano *synth)
 : BC_MenuItem(_("Zero"))
{
	this->synth = synth;
}
PianoPhaseZero::~PianoPhaseZero()
{
}

int PianoPhaseZero::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->phase = 0;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoPhaseSine::PianoPhaseSine(Piano *synth)
 : BC_MenuItem(_("Sine"))
{
	this->synth = synth;
}
PianoPhaseSine::~PianoPhaseSine()
{
}

int PianoPhaseSine::handle_event()
{
	float new_value;
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		new_value = (float)i / synth->config.oscillator_config.total * 2 * M_PI;
		new_value = sin(new_value) / 2 + .5;
		synth->config.oscillator_config.values[i]->phase = new_value;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoPhaseRandom::PianoPhaseRandom(Piano *synth)
 : BC_MenuItem(_("Random"))
{
	this->synth = synth;
}
PianoPhaseRandom::~PianoPhaseRandom()
{
}

int PianoPhaseRandom::handle_event()
{
	srand(time(0));
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->phase =
			(float)(rand() % 360) / 360;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}


// ============================ freq calculations

PianoFreqRandom::PianoFreqRandom(Piano *synth)
 : BC_MenuItem(_("Random"))
{
	this->synth = synth;
}
PianoFreqRandom::~PianoFreqRandom()
{
}

int PianoFreqRandom::handle_event()
{
	srand(time(0));
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = rand() % 100;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoFreqEnum::PianoFreqEnum(Piano *synth)
 : BC_MenuItem(_("Enumerate"))
{
	this->synth = synth;
}
PianoFreqEnum::~PianoFreqEnum()
{
}

int PianoFreqEnum::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = (float)i + 1;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoFreqEven::PianoFreqEven(Piano *synth)
 : BC_MenuItem(_("Even"))
{
	this->synth = synth;
}
PianoFreqEven::~PianoFreqEven()
{
}

int PianoFreqEven::handle_event()
{
	if(synth->config.oscillator_config.total)
		synth->config.oscillator_config.values[0]->freq_factor = (float)1;

	for(int i = 1; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = (float)i * 2;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoFreqOdd::PianoFreqOdd(Piano *synth)
 : BC_MenuItem(_("Odd"))
{ this->synth = synth; }
PianoFreqOdd::~PianoFreqOdd()
{
}

int PianoFreqOdd::handle_event()
{
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = (float)1 + i * 2;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoFreqFibonacci::PianoFreqFibonacci(Piano *synth)
 : BC_MenuItem(_("Fibonnacci"))
{
	this->synth = synth;
}
PianoFreqFibonacci::~PianoFreqFibonacci()
{
}

int PianoFreqFibonacci::handle_event()
{
	float last_value1 = 0, last_value2 = 1;
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = last_value1 + last_value2;
		if(synth->config.oscillator_config.values[i]->freq_factor > 100) synth->config.oscillator_config.values[i]->freq_factor = 100;
		last_value1 = last_value2;
		last_value2 = synth->config.oscillator_config.values[i]->freq_factor;
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

PianoFreqPrime::PianoFreqPrime(Piano *synth)
 : BC_MenuItem(_("Prime"))
{
	this->synth = synth;
}
PianoFreqPrime::~PianoFreqPrime()
{
}

int PianoFreqPrime::handle_event()
{
	float number = 1;
	for(int i = 0; i < synth->config.oscillator_config.total; i++)
	{
		synth->config.oscillator_config.values[i]->freq_factor = number;
		number = get_next_prime(number);
	}

	synth->thread->window->update_gui();
	synth->send_configure_change();
}

float PianoFreqPrime::get_next_prime(float number)
{
	int result = 1;

	while(result)
	{
		result = 0;
		number++;

		for(float i = number - 1; i > 1 && !result; i--)
		{
			if((number / i) - (int)(number / i) == 0) result = 1;
		}
	}

	return number;
}








PianoOscillatorConfig::PianoOscillatorConfig(int number)
{
	reset();
	this->number = number;
}

PianoOscillatorConfig::~PianoOscillatorConfig()
{
}

void PianoOscillatorConfig::reset()
{
	level = 0;
	phase = 0;
	freq_factor = 1;
}

void PianoOscillatorConfig::read_data(FileXML *file)
{
	level = file->tag.get_property("LEVEL", (float)level);
	phase = file->tag.get_property("PHASE", (float)phase);
	freq_factor = file->tag.get_property("FREQFACTOR", (float)freq_factor);
}

void PianoOscillatorConfig::save_data(FileXML *file)
{
	file->tag.set_title("OSCILLATOR");
	file->tag.set_property("LEVEL", (float)level);
	file->tag.set_property("PHASE", (float)phase);
	file->tag.set_property("FREQFACTOR", (float)freq_factor);
	file->append_tag();
	file->tag.set_title("/OSCILLATOR");
	file->append_tag();
	file->append_newline();
}

int PianoOscillatorConfig::equivalent(PianoOscillatorConfig &that)
{
	if(EQUIV(level, that.level) &&
		EQUIV(phase, that.phase) &&
		EQUIV(freq_factor, that.freq_factor))
		return 1;
	else
		return 0;
}

void PianoOscillatorConfig::copy_from(PianoOscillatorConfig& that)
{
	level = that.level;
	phase = that.phase;
	freq_factor = that.freq_factor;
}












PianoConfig::PianoConfig()
{
	reset();
}

PianoConfig::~PianoConfig()
{
	oscillator_config.remove_all_objects();
}

void PianoConfig::reset()
{
	wetness = 0;
	base_freq = 440;
	wavefunction = SINE;
	for(int i = 0; i < oscillator_config.total; i++)
	{
		oscillator_config.values[i]->reset();
	}
}

int PianoConfig::equivalent(PianoConfig &that)
{
//printf("PianoConfig::equivalent %d %d\n", base_freq, that.base_freq);
	if(base_freq != that.base_freq ||
		wavefunction != that.wavefunction ||
		oscillator_config.total != that.oscillator_config.total) return 0;

	for(int i = 0; i < oscillator_config.total; i++)
	{
		if(!oscillator_config.values[i]->equivalent(*that.oscillator_config.values[i]))
			return 0;
	}

	return 1;
}

void PianoConfig::copy_from(PianoConfig& that)
{
	wetness = that.wetness;
	base_freq = that.base_freq;
	wavefunction = that.wavefunction;

	int i;
	for(i = 0;
		i < oscillator_config.total && i < that.oscillator_config.total;
		i++)
	{
		oscillator_config.values[i]->copy_from(*that.oscillator_config.values[i]);
	}

	for( ;
		i < that.oscillator_config.total;
		i++)
	{
		oscillator_config.append(new PianoOscillatorConfig(i));
		oscillator_config.values[i]->copy_from(*that.oscillator_config.values[i]);
	}

	for( ;
		i < oscillator_config.total;
		i++)
	{
		oscillator_config.remove_object();
	}
}

void PianoConfig::interpolate(PianoConfig &prev,
	PianoConfig &next,
	int64_t prev_frame,
	int64_t next_frame,
	int64_t current_frame)
{
	double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
	double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);

	copy_from(prev);
	wetness = (int)(prev.wetness * prev_scale + next.wetness * next_scale);
	base_freq = (int)(prev.base_freq * prev_scale + next.base_freq * next_scale);
}





