X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fsynthesizer%2Fsynthesizer.C;fp=cinelerra-5.1%2Fplugins%2Fsynthesizer%2Fsynthesizer.C;h=546aaf9167cd5efcf85290049fee8aa3e0df67f7;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/synthesizer/synthesizer.C b/cinelerra-5.1/plugins/synthesizer/synthesizer.C new file mode 100644 index 00000000..546aaf91 --- /dev/null +++ b/cinelerra-5.1/plugins/synthesizer/synthesizer.C @@ -0,0 +1,2222 @@ + +/* + * CINELERRA + * Copyright (C) 1997-2011 Adam Williams + * + * 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 "samples.h" +#include "synthesizer.h" +#include "theme.h" +#include "transportque.inc" +#include "vframe.h" + +#include + + + + +REGISTER_PLUGIN(Synth) + + + + +Synth::Synth(PluginServer *server) + : PluginAClient(server) +{ + reset(); + window_w = 640; + window_h = 480; +} + + + +Synth::~Synth() +{ + +} + +NEW_WINDOW_MACRO(Synth, SynthWindow); + +const char* Synth::plugin_title() { return _("Synthesizer"); } +int Synth::is_realtime() { return 1; } +int Synth::is_synthesis() { return 1; } + + +void Synth::reset() +{ + need_reconfigure = 1; +} + + + + +LOAD_CONFIGURATION_MACRO(Synth, SynthConfig) + + + + + + +void Synth::read_data(KeyFrame *keyframe) +{ + FileXML input; + char string[BCTEXTLEN]; +// cause htal file to read directly from text + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + +//printf("Synth::read_data %s\n", keyframe->get_data()); + int result = 0, current_osc = 0; + //int 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); + + if(is_defaults()) + { + window_w = input.tag.get_property("WINDOW_W", window_w); + window_h = input.tag.get_property("WINDOW_H", window_h); + } + config.momentary_notes = input.tag.get_property("MOMENTARY_NOTES", config.momentary_notes); + +//printf("Synth::read_data %d %d %d\n", __LINE__, window_w, window_h); + for(int i = 0; i < MAX_FREQS; i++) + { + sprintf(string, "BASEFREQ_%d", i); + config.base_freq[i] = input.tag.get_property(string, (double)0); + } + + 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 SynthOscillatorConfig(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 Synth::save_data(KeyFrame *keyframe) +{ + FileXML output; + char string[BCTEXTLEN]; + +// cause htal file to store data directly in text + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + + output.tag.set_title("SYNTH"); + output.tag.set_property("WETNESS", config.wetness); + output.tag.set_property("WINDOW_W", window_w); + output.tag.set_property("WINDOW_H", window_h); + output.tag.set_property("MOMENTARY_NOTES", config.momentary_notes); + + for(int i = 0; i < MAX_FREQS; i++) + { +// if(!EQUIV(config.base_freq[i], 0)) + { + sprintf(string, "BASEFREQ_%d", i); + output.tag.set_property(string, config.base_freq[i]); +//printf("Synth::save_data %d %s\n", __LINE__, string); + } + } + + 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(); +//printf("Synth::save_data %d %s\n", __LINE__, output.string); +// data is now in *text +} + + + +void Synth::update_gui() +{ + if(thread) + { + if(load_configuration()) + { + thread->window->lock_window(); + ((SynthWindow*)thread->window)->update_gui(); + thread->window->unlock_window(); + } + } +} + + +void Synth::add_oscillator() +{ + if(config.oscillator_config.total > 20) return; + + config.oscillator_config.append(new SynthOscillatorConfig(config.oscillator_config.total - 1)); +} + +void Synth::delete_oscillator() +{ + if(config.oscillator_config.total) + { + config.oscillator_config.remove_object(); + } +} + + +double Synth::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 Synth::solve_eqn(double *output, + int length, + double freq, + double normalize_constant, + int oscillator) +{ + SynthOscillatorConfig *config = + this->config.oscillator_config.values[oscillator]; + if(config->level <= INFINITYGAIN) return 0; + + double power = this->db.fromdb(config->level) * normalize_constant; +// Period of fundamental frequency in samples + double orig_period = (double)get_samplerate() / + freq; +// Starting sample in waveform + double x = waveform_sample; + double phase_offset = config->phase * orig_period; +//printf("Synth::solve_eqn %d %f\n", __LINE__, config->phase); +// Period of current oscillator + double period = orig_period / config->freq_factor; + int sample; + double step = 1; + if(get_direction() == PLAY_REVERSE) step = -1; + + switch(this->config.wavefunction) + { + case DC: + for(sample = 0; sample < length; sample++) + { + output[sample] += power; + } + break; + + case SINE: + for(sample = 0; sample < length; sample++) + { + output[sample] += sin((x + phase_offset) / + period * + 2 * + M_PI) * power; + x += step; + } + break; + + case SAWTOOTH: + for(sample = 0; sample < length; sample++) + { + output[sample] += function_sawtooth((x + phase_offset) / + period) * power; + x += step; + } + break; + + case SQUARE: + for(sample = 0; sample < length; sample++) + { + output[sample] += function_square((x + phase_offset) / + period) * power; + x += step; + } + break; + + case TRIANGLE: + for(sample = 0; sample < length; sample++) + { + output[sample] += function_triangle((x + phase_offset) / + period) * power; + x += step; + } + break; + + case PULSE: + for(sample = 0; sample < length; sample++) + { + output[sample] += function_pulse((x + phase_offset) / + period) * power; + x += step; + } + break; + + case NOISE: + for(sample = 0; sample < length; sample++) + { + output[sample] += function_noise() * power; + } + break; + } + return 0; +} + +double Synth::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 Synth::get_oscillator_point(float x, + double normalize_constant, + int oscillator) +{ + SynthOscillatorConfig *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; + } + return 0; +} + +double Synth::function_square(double x) +{ + x -= (int)x; // only fraction counts + return (x < .5) ? -1 : 1; +} + +double Synth::function_pulse(double x) +{ + x -= (int)x; // only fraction counts + return (x < .5) ? 0 : 1; +} + +double Synth::function_noise() +{ + return (double)(rand() % 65536 - 32768) / 32768; +} + +double Synth::function_sawtooth(double x) +{ + x -= (int)x; + return 1 - x * 2; +} + +double Synth::function_triangle(double x) +{ + x -= (int)x; + return (x < .5) ? 1 - x * 4 : -3 + x * 4; +} + +int Synth::process_realtime(int64_t size, + Samples *input_ptr, + Samples *output_ptr) +{ +// sample relative to start of plugin + waveform_sample = get_source_position(); + + need_reconfigure |= load_configuration(); + if(need_reconfigure) reconfigure(); + + double wetness = DB::fromdb(config.wetness); + if(EQUIV(config.wetness, INFINITYGAIN)) wetness = 0; + +// Apply wetness + double *output_samples = output_ptr->get_data(); + double *input_samples = input_ptr->get_data(); + for(int j = 0; j < size; j++) + output_samples[j] = input_samples[j] * wetness; + +// Overlay each frequency + for(int j = 0; j < MAX_FREQS; j++) + { + if(!EQUIV(config.base_freq[j], 0)) + { + +// Compute fragment + overlay_synth( + config.base_freq[j], + size, + input_ptr->get_data(), + output_ptr->get_data()); + //printf("Synth::process_realtime 2\n"); + } + } + +// waveform_sample += size; + return 0; +} + +int Synth::overlay_synth(double freq, + int64_t length, + double *input, + double *output) +{ + double normalize_constant = 1.0 / get_total_power(); + for(int i = 0; i < config.oscillator_config.total; i++) + solve_eqn(output, + length, + freq, + normalize_constant, + i); + return length; +} + +void Synth::reconfigure() +{ + need_reconfigure = 0; +// waveform_sample = 0; +} + +int Synth::freq_exists(double freq) +{ + for(int i = 0; i < MAX_FREQS; i++) + { + +// printf("Synth::freq_exists %d %d %f %f\n", +// __LINE__, +// i, +// freq, +// config.base_freq[i]); + if(fabs(freq - config.base_freq[i]) < 1.0) + return 1; + } + + return 0; +} + +void Synth::new_freq(double freq) +{ +// Check for dupes + for(int i = 0; i < MAX_FREQS; i++) + { + if(EQUIV(config.base_freq[i], freq)) return; + } + + for(int i = 0; i < MAX_FREQS; i++) + { + if(EQUIV(config.base_freq[i], 0)) + { + config.base_freq[i] = freq; +//printf("Synth::new_freq %d\n", __LINE__); + break; + } + } +} + +void Synth::delete_freq(double freq) +{ + for(int i = 0; i < MAX_FREQS; i++) + { + if(EQUIV(config.base_freq[i], freq)) + { +//printf("Synth::delete_freq %d\n", __LINE__); +// Shift frequencies back + for(int j = i; j < MAX_FREQS - 1; j++) + { + config.base_freq[j] = config.base_freq[j + 1]; + } + config.base_freq[MAX_FREQS - 1] = 0; + + i--; + } + } +} + +void Synth::delete_freqs() +{ + for(int i = 0; i < MAX_FREQS; i++) + config.base_freq[i] = 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SynthWindow::SynthWindow(Synth *synth) + : PluginClientWindow(synth, + synth->window_w, + synth->window_h, + 400, + 350, + 1) +{ + this->synth = synth; + white_key[0] = 0; + white_key[1] = 0; + white_key[2] = 0; + black_key[0] = 0; + black_key[1] = 0; + black_key[2] = 0; + bzero(notes, sizeof(SynthNote*) * TOTALNOTES); + current_note = -1; +} + +SynthWindow::~SynthWindow() +{ + delete white_key[0]; + delete white_key[1]; + delete white_key[2]; + delete black_key[0]; + delete black_key[1]; + delete black_key[2]; +} + +static const char *keyboard_map[] = +{ + "q", "2", "w", "3", "e", "r", "5", "t", "6", "y", "7", "u", + "z", "s", "x", "d", "c", "v", "g", "b", "h", "n", "j", "m" +}; + +void SynthWindow::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 SynthLevelInvert(synth)); + levelmenu->add_item(new SynthLevelMax(synth)); + levelmenu->add_item(new SynthLevelRandom(synth)); + levelmenu->add_item(new SynthLevelSine(synth)); + levelmenu->add_item(new SynthLevelSlope(synth)); + levelmenu->add_item(new SynthLevelZero(synth)); + + phasemenu->add_item(new SynthPhaseInvert(synth)); + phasemenu->add_item(new SynthPhaseRandom(synth)); + phasemenu->add_item(new SynthPhaseSine(synth)); + phasemenu->add_item(new SynthPhaseZero(synth)); + + harmonicmenu->add_item(new SynthFreqEnum(synth)); + harmonicmenu->add_item(new SynthFreqEven(synth)); + harmonicmenu->add_item(new SynthFreqFibonacci(synth)); + harmonicmenu->add_item(new SynthFreqOdd(synth)); + harmonicmenu->add_item(new SynthFreqPrime(synth)); + + int x = 10, y = 30; + + add_subwindow(new BC_Title(x, y, _("Waveform"))); + x += 240; + add_subwindow(new BC_Title(x, y, _("Wave Function"))); + y += 20; + x = 10; + add_subwindow(canvas = new SynthCanvas(synth, this, x, y, 230, 160)); + canvas->update(); + + x += 240; + char string[BCTEXTLEN]; + waveform_to_text(string, synth->config.wavefunction); + + add_subwindow(waveform = new SynthWaveForm(synth, x, y, string)); + waveform->create_objects(); + y += 30; + int x1 = x + waveform->get_w() + 10; + + + add_subwindow(new BC_Title(x, y, _("Base Frequency:"))); + y += 30; + add_subwindow(base_freq = new SynthBaseFreq(synth, this, x, y)); + base_freq->update((float)synth->config.base_freq[0]); + x += base_freq->get_w() + synth->get_theme()->widget_border; + add_subwindow(freqpot = new SynthFreqPot(synth, this, x, y - 10)); + base_freq->freq_pot = freqpot; + freqpot->freq_text = base_freq; + x -= base_freq->get_w() + synth->get_theme()->widget_border; + y += 40; + add_subwindow(new BC_Title(x, y, _("Wetness:"))); + add_subwindow(wetness = new SynthWetness(synth, x + 70, y - 10)); + + y += 40; + add_subwindow(new SynthClear(synth, x, y)); + + + x = 50; + y = 220; + add_subwindow(new BC_Title(x, y, _("Level"))); + x += 75; + add_subwindow(new BC_Title(x, y, _("Phase"))); + x += 75; + add_subwindow(new BC_Title(x, y, _("Harmonic"))); + + + + y += 20; x = 10; + add_subwindow(osc_subwindow = new BC_SubWindow(x, y, 265, get_h() - y)); + x += 265; + add_subwindow(osc_scroll = new OscScroll(synth, this, x, y, get_h() - y)); + + + x += 20; + add_subwindow(new SynthAddOsc(synth, this, x, y)); + y += 30; + add_subwindow(new SynthDelOsc(synth, this, x, y)); + +// Create keyboard + y = 30; + +#include "white_up_png.h" +#include "white_hi_png.h" +#include "white_dn_png.h" +#include "white_checked_png.h" +#include "white_checkedhi_png.h" +#include "black_up_png.h" +#include "black_hi_png.h" +#include "black_dn_png.h" +#include "black_checked_png.h" +#include "black_checkedhi_png.h" + white_key[0] = new VFramePng(white_up_png); + white_key[1] = new VFramePng(white_hi_png); + white_key[2] = new VFramePng(white_checked_png); + white_key[3] = new VFramePng(white_dn_png); + white_key[4] = new VFramePng(white_checkedhi_png); + black_key[0] = new VFramePng(black_up_png); + black_key[1] = new VFramePng(black_hi_png); + black_key[2] = new VFramePng(black_checked_png); + black_key[3] = new VFramePng(black_dn_png); + black_key[4] = new VFramePng(black_checkedhi_png); + + + add_subwindow(note_subwindow = new BC_SubWindow(x1, + y, + get_w() - x1, + white_key[0]->get_h() + MARGIN + + get_text_height(MEDIUMFONT) + MARGIN + + get_text_height(MEDIUMFONT) + MARGIN)); + add_subwindow(note_scroll = new NoteScroll(synth, + this, + x1, + note_subwindow->get_y() + note_subwindow->get_h(), + note_subwindow->get_w())); + + add_subwindow(momentary = new SynthMomentary(this, + x1, + note_scroll->get_y() + note_scroll->get_h() + MARGIN, + _("Momentary notes"))); + + + add_subwindow(note_instructions = new BC_Title( + x1, + momentary->get_y() + momentary->get_h() + MARGIN, + _("Ctrl or Shift to select multiple notes."))); + + update_scrollbar(); + update_oscillators(); + update_notes(); + + show_window(); +} + +int SynthWindow::keypress_event() +{ + if(ctrl_down() && get_keypress() == 'w') + { + set_done(1); + return 1; + } + return 0; +} + +int SynthWindow::resize_event(int w, int h) +{ + clear_box(0, 0, w, h); + osc_subwindow->reposition_window(osc_subwindow->get_x(), + osc_subwindow->get_y(), + osc_subwindow->get_w(), + h - osc_subwindow->get_y()); + osc_subwindow->clear_box(0, 0, osc_subwindow->get_w(), osc_subwindow->get_h()); + osc_scroll->reposition_window(osc_scroll->get_x(), + osc_scroll->get_y(), + h - osc_scroll->get_y()); + note_subwindow->reposition_window(note_subwindow->get_x(), + note_subwindow->get_y(), + w - note_subwindow->get_x(), + note_subwindow->get_h()); + note_scroll->reposition_window(note_scroll->get_x(), + note_scroll->get_y(), + w - note_scroll->get_x()); + note_scroll->update_length(white_key[0]->get_w() * TOTALNOTES * 7 / 12 + + white_key[0]->get_w(), + note_scroll->get_position(), + note_subwindow->get_w(), + 0); + + update_scrollbar(); + update_notes(); + update_oscillators(); + synth->window_w = w; + synth->window_h = h; + return 1; +} + +void SynthWindow::update_gui() +{ + char string[BCTEXTLEN]; + freqpot->update((int)synth->config.base_freq[0]); + base_freq->update((float)synth->config.base_freq[0]); + wetness->update(synth->config.wetness); + waveform_to_text(string, synth->config.wavefunction); + waveform->set_text(string); + momentary->update(synth->config.momentary_notes); + + update_scrollbar(); + update_oscillators(); + canvas->update(); + update_note_selection(); + show_window(); +} + +void SynthWindow::update_scrollbar() +{ + osc_scroll->update_length(synth->config.oscillator_config.total * OSCILLATORHEIGHT, + osc_scroll->get_position(), + osc_subwindow->get_h(), + 0); +} + + +void SynthWindow::update_whitekey(int number, + int *current_title, + int x, + int y) +{ + if(!notes[number]) + { + note_subwindow->add_subwindow(notes[number] = new SynthNote(this, + white_key, number, x, y)); + if(number >= FIRST_TITLE && number < LAST_TITLE) + note_subwindow->add_subwindow( + note_titles[(*current_title)++] = new BC_Title( + x + text_white_margin, + y2, + keyboard_map[number - FIRST_TITLE])); +//printf("SynthWindow::update_whitekey %d\n", __LINE__); + } + else + { + notes[number]->reposition_window(x, y); + if(number >= FIRST_TITLE && number < LAST_TITLE) + note_titles[(*current_title)++]->reposition_window(x + text_white_margin, + y2); + } +} + + +void SynthWindow::update_blackkey(int number, + int *current_title, + int x, + int y) +{ + if(!notes[number]) + { + note_subwindow->add_subwindow(notes[number] = new SynthNote(this, + black_key, number, x, y)); + if(number >= FIRST_TITLE && number < LAST_TITLE) + note_subwindow->add_subwindow( + note_titles[(*current_title)++] = new BC_Title(x + text_black_margin, + y1, + keyboard_map[number - FIRST_TITLE])); + } + else + { + notes[number]->reposition_window(x, y); + if(number >= FIRST_TITLE && number < LAST_TITLE) + note_titles[(*current_title)++]->reposition_window(x + text_black_margin, + y1); + } +} + +void SynthWindow::update_notes() +{ + //int octave_w = white_key[0]->get_w() * 7; + int white_w = white_key[0]->get_w(); + int black_w = black_key[0]->get_w(); + int white_w1 = white_w - black_w / 2 - 2; + int white_w2 = white_w / 2; + int white_w3 = white_w * 2 / 3; + int y = 0; + int x = 0; + y1 = y + white_key[0]->get_h() + 10; + y2 = y1 + get_text_height(MEDIUMFONT) + 10; + y3 = y2 + get_text_height(MEDIUMFONT) + 10; + text_black_margin = black_w / 2 - get_text_width(MEDIUMFONT, "O") / 2; + text_white_margin = white_w / 2 - get_text_width(MEDIUMFONT, "O") / 2; + + +//printf("SynthWindow::update_notes %d\n", __LINE__); + note_subwindow->clear_box(0, 0, get_w(), get_h()); + + note_subwindow->set_color(get_resources()->default_text_color); + +// Add new notes +// To get the stacking order: +// pass 0 is white keys +// pass 1 is black keys + int current_title = 0; + for(int pass = 0; pass < 2; pass++) + { + x = -note_scroll->get_position(); + + for(int i = 0; i < TOTALNOTES; i++) + { + int octave_note = i % 12; + if(!pass) + { +// White keys + switch(octave_note) + { + case 0: + update_whitekey(i, ¤t_title, x, y); + break; + case 1: + x += white_w; + break; + case 2: + update_whitekey(i, ¤t_title, x, y); + break; + case 3: + x += white_w; + break; + case 4: + update_whitekey(i, ¤t_title, x, y); + x += white_w; + break; + case 5: + update_whitekey(i, ¤t_title, x, y); + break; + case 6: + x += white_w; + break; + case 7: + update_whitekey(i, ¤t_title, x, y); + break; + case 8: + x += white_w; + break; + case 9: + update_whitekey(i, ¤t_title, x, y); + break; + case 10: + x += white_w; + break; + case 11: + update_whitekey(i, ¤t_title, x, y); + x += white_w; + break; + } + } + else + { +// Black keys + switch(octave_note) + { + case 1: + update_blackkey(i, ¤t_title, x + white_w2, y); + x += white_w; + break; + case 3: + update_blackkey(i, ¤t_title, x + white_w3, y); + x += white_w; + break; + case 4: + x += white_w; + break; + case 6: + update_blackkey(i, ¤t_title, x + white_w2, y); + x += white_w; + break; + case 8: + update_blackkey(i, ¤t_title, x + white_w1, y); + x += white_w; + break; + case 10: + update_blackkey(i, ¤t_title, x + white_w3, y); + x += white_w; + break; + case 11: + x += white_w; + break; + } + } + } + } +} + +void SynthWindow::update_note_selection() +{ + for(int i = 0; i < TOTALNOTES; i++) + { + int got_it = 0; + for(int j = 0; j < MAX_FREQS; j++) + { + if(synth->freq_exists(keyboard_freqs[notes[i]->number])) + { + got_it = 1; + break; + } + } + + if(got_it) + { + notes[i]->set_value(1); + } + else + notes[i]->set_value(0); + } +} + + +void SynthWindow::update_oscillators() +{ + int i, y = -osc_scroll->get_position(); + + + +// Add new oscillators + for(i = 0; + i < synth->config.oscillator_config.total; + i++) + { + SynthOscGUI *gui; + SynthOscillatorConfig *config = synth->config.oscillator_config.values[i]; + + if(oscillators.total <= i) + { + oscillators.append(gui = new SynthOscGUI(this, i)); + gui->create_objects(y); + } + else + { + gui = oscillators.values[i]; + + gui->title->reposition_window(gui->title->get_x(), y + 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 SynthWindow::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; +} + + +SynthMomentary::SynthMomentary(SynthWindow *window, int x, int y, char *text) + : BC_CheckBox(x, + y, + window->synth->config.momentary_notes, + text) +{ + this->window = window; +} + +int SynthMomentary::handle_event() +{ + window->synth->config.momentary_notes = get_value(); + window->synth->send_configure_change(); + return 1; +} + + + + +SynthNote::SynthNote(SynthWindow *window, + VFrame **images, + int number, + int x, + int y) + : BC_Toggle(x, + y, + images, + window->synth->freq_exists(keyboard_freqs[number])) +{ + this->window = window; + this->number = number; + note_on = 0; + set_select_drag(1); + set_radial(1); +} + +void SynthNote::start_note() +{ + if(window->synth->config.momentary_notes) note_on = 1; + +//printf("SynthNote::start_note %d %d\n", __LINE__, ctrl_down()); + if(window->synth->config.momentary_notes || (!ctrl_down() && !shift_down())) + { +// Kill all frequencies + window->synth->delete_freqs(); + } + + window->synth->new_freq(keyboard_freqs[number]); + window->base_freq->update((float)window->synth->config.base_freq[0]); +//printf("SynthNote::start_note %d %f\n", __LINE__, window->synth->config.base_freq[0]); + window->freqpot->update(window->synth->config.base_freq[0]); + window->synth->send_configure_change(); + window->update_note_selection(); +} + +void SynthNote::stop_note() +{ + note_on = 0; + window->synth->delete_freq(keyboard_freqs[number]); + window->base_freq->update((float)window->synth->config.base_freq[0]); + window->freqpot->update(window->synth->config.base_freq[0]); + window->synth->send_configure_change(); + window->update_note_selection(); +} + +int SynthNote::keypress_event() +{ + if(number >= FIRST_TITLE && number < LAST_TITLE) + { + if(get_keypress() == keyboard_map[number - FIRST_TITLE][0]) + { + if((ctrl_down() || shift_down()) && + window->synth->freq_exists(keyboard_freqs[number])) + { + stop_note(); + } + else + { + start_note(); + set_value(1); + } + +// Key releases are repeated, so momentary notes may not work + return 1; + } + } + return 0; +} + +int SynthNote::keyrelease_event() +{ + if(note_on && window->synth->config.momentary_notes) + { + stop_note(); + set_value(0); + return 1; + } + return 0; +} + +int SynthNote::cursor_motion_event() +{ + int result = 0; + if(window->current_note > -1) + { + int cursor_x = get_relative_cursor_x(); + int cursor_y = get_relative_cursor_y(); + if(cursor_x >= 0 && cursor_x < get_w() && + cursor_y >= 0 && cursor_y < get_h()) + { + if(window->starting_notes) + { + start_note(); + } + else + { + stop_note(); + } + + window->current_note = number; + result = 1; + } + } + return result; +} + +int SynthNote::button_press_event() +{ + if(BC_Toggle::button_press_event()) + { +// printf("SynthNote::button_press_event %d %d %d\n", +// __LINE__, +// ctrl_down(), +// window->synth->freq_exists(keyboard_freqs[number])); + window->starting_notes = 1; + if((ctrl_down() || shift_down()) && + window->synth->freq_exists(keyboard_freqs[number])) + { +//printf("SynthNote::button_press_event %d\n", __LINE__); + stop_note(); + window->starting_notes = 0; + } + else + { +//printf("SynthNote::button_press_event %d\n", __LINE__); + start_note(); + window->starting_notes = 1; + } + window->current_note = number; + return 1; + } + return 0; +} + +int SynthNote::button_release_event() +{ +// Change frequency permanently + if(window->current_note == number) + { + if(window->synth->config.momentary_notes) + { +// Mute on button release + stop_note(); + set_value(0); + } + window->current_note = -1; + } + + return BC_Toggle::button_release_event(); +} + +int SynthNote::draw_face(int flash, int flush) +{ + BC_Toggle::draw_face(0, 0); + static const char *titles[] = + { + "C", + "C#", + "D", + "D#", + "E", + "F", + "F#", + "G", + "G#", + "A", + "A#", + "B" + }; + + + const char *text = titles[number % (sizeof(titles) / sizeof(char*))]; + char string[BCTEXTLEN]; + sprintf(string, "%s%d", text, number / 12); +//printf("SynthNote::draw_face %d %d %d %d %s\n", __LINE__, number, get_w(), get_h(), text); + if(text[1] == '#') + { + } + else + { + set_color(BLACK); + draw_text(get_w() / 2 - get_text_width(MEDIUMFONT, string) / 2, + get_h() - get_text_height(MEDIUMFONT, string) - window->synth->get_theme()->widget_border, + string); + } + + if(flash) this->flash(0); + if(flush) this->flush(); + return 0; +} + + + + + + + +SynthOscGUI::SynthOscGUI(SynthWindow *window, int number) +{ + this->window = window; + this->number = number; +} + +SynthOscGUI::~SynthOscGUI() +{ + delete title; + delete level; + delete phase; + delete freq; +} + +void SynthOscGUI::create_objects(int y) +{ + char text[BCTEXTLEN]; + sprintf(text, "%d:", number + 1); + window->osc_subwindow->add_subwindow(title = new BC_Title(10, y + 15, text)); + + window->osc_subwindow->add_subwindow(level = new SynthOscGUILevel(window->synth, this, y)); + window->osc_subwindow->add_subwindow(phase = new SynthOscGUIPhase(window->synth, this, y)); + window->osc_subwindow->add_subwindow(freq = new SynthOscGUIFreq(window->synth, this, y)); +} + + + + +SynthOscGUILevel::SynthOscGUILevel(Synth *synth, SynthOscGUI *gui, int y) + : BC_FPot(50, + y, + synth->config.oscillator_config.values[gui->number]->level, + INFINITYGAIN, + 0) +{ + this->synth = synth; + this->gui = gui; +} + +SynthOscGUILevel::~SynthOscGUILevel() +{ +} + +int SynthOscGUILevel::handle_event() +{ + SynthOscillatorConfig *config = synth->config.oscillator_config.values[gui->number]; + config->level = get_value(); + gui->window->canvas->update(); + synth->send_configure_change(); + return 1; +} + + + +SynthOscGUIPhase::SynthOscGUIPhase(Synth *synth, SynthOscGUI *gui, int y) + : BC_IPot(125, + y, + (int64_t)(synth->config.oscillator_config.values[gui->number]->phase * 360), + 0, + 360) +{ + this->synth = synth; + this->gui = gui; +} + +SynthOscGUIPhase::~SynthOscGUIPhase() +{ +} + +int SynthOscGUIPhase::handle_event() +{ + SynthOscillatorConfig *config = synth->config.oscillator_config.values[gui->number]; + config->phase = (float)get_value() / 360; + gui->window->canvas->update(); + synth->send_configure_change(); + return 1; +} + + + +SynthOscGUIFreq::SynthOscGUIFreq(Synth *synth, SynthOscGUI *gui, int y) + : BC_IPot(200, + y, + (int64_t)(synth->config.oscillator_config.values[gui->number]->freq_factor), + 1, + 100) +{ + this->synth = synth; + this->gui = gui; +} + +SynthOscGUIFreq::~SynthOscGUIFreq() +{ +} + +int SynthOscGUIFreq::handle_event() +{ + SynthOscillatorConfig *config = synth->config.oscillator_config.values[gui->number]; + config->freq_factor = get_value(); + gui->window->canvas->update(); + synth->send_configure_change(); + return 1; +} + + + + + + + +SynthAddOsc::SynthAddOsc(Synth *synth, SynthWindow *window, int x, int y) + : BC_GenericButton(x, y, _("Add")) +{ + this->synth = synth; + this->window = window; +} + +SynthAddOsc::~SynthAddOsc() +{ +} + +int SynthAddOsc::handle_event() +{ + synth->add_oscillator(); + synth->send_configure_change(); + window->update_gui(); + return 1; +} + + + +SynthDelOsc::SynthDelOsc(Synth *synth, SynthWindow *window, int x, int y) + : BC_GenericButton(x, y, _("Delete")) +{ + this->synth = synth; + this->window = window; +} + +SynthDelOsc::~SynthDelOsc() +{ +} + +int SynthDelOsc::handle_event() +{ + synth->delete_oscillator(); + synth->send_configure_change(); + window->update_gui(); + return 1; +} + + +OscScroll::OscScroll(Synth *synth, + SynthWindow *window, + int x, + int y, + int h) + : BC_ScrollBar(x, + y, + SCROLL_VERT, + h, + synth->config.oscillator_config.total * OSCILLATORHEIGHT, + 0, + window->osc_subwindow->get_h()) +{ + this->synth = synth; + this->window = window; +} + +OscScroll::~OscScroll() +{ +} + +int OscScroll::handle_event() +{ + window->update_oscillators(); + return 1; +} + + + +NoteScroll::NoteScroll(Synth *synth, + SynthWindow *window, + int x, + int y, + int w) + : BC_ScrollBar(x, + y, + SCROLL_HORIZ, + w, + window->white_key[0]->get_w() * TOTALNOTES * 7 / 12 + window->white_key[0]->get_w(), + 0, + window->note_subwindow->get_w()) +{ + this->synth = synth; + this->window = window; +} + +NoteScroll::~NoteScroll() +{ +} + +int NoteScroll::handle_event() +{ + window->update_notes(); + return 1; +} + + + + + + + + + + + + + + +SynthClear::SynthClear(Synth *synth, int x, int y) + : BC_GenericButton(x, y, _("Clear")) +{ + this->synth = synth; +} +SynthClear::~SynthClear() +{ +} +int SynthClear::handle_event() +{ + synth->config.reset(); + synth->send_configure_change(); + synth->update_gui(); + return 1; +} + + + + + + +SynthWaveForm::SynthWaveForm(Synth *synth, int x, int y, char *text) + : BC_PopupMenu(x, y, 120, text) +{ + this->synth = synth; +} + +SynthWaveForm::~SynthWaveForm() +{ +} + +void SynthWaveForm::create_objects() +{ +// add_item(new SynthWaveFormItem(synth, _("DC"), DC)); + add_item(new SynthWaveFormItem(synth, _("Sine"), SINE)); + add_item(new SynthWaveFormItem(synth, _("Sawtooth"), SAWTOOTH)); + add_item(new SynthWaveFormItem(synth, _("Square"), SQUARE)); + add_item(new SynthWaveFormItem(synth, _("Triangle"), TRIANGLE)); + add_item(new SynthWaveFormItem(synth, _("Pulse"), PULSE)); + add_item(new SynthWaveFormItem(synth, _("Noise"), NOISE)); +} + +SynthWaveFormItem::SynthWaveFormItem(Synth *synth, char *text, int value) + : BC_MenuItem(text) +{ + this->synth = synth; + this->value = value; +} + +SynthWaveFormItem::~SynthWaveFormItem() +{ +} + +int SynthWaveFormItem::handle_event() +{ + synth->config.wavefunction = value; + ((SynthWindow*)synth->thread->window)->canvas->update(); + get_popup_menu()->set_text(get_text()); + synth->send_configure_change(); + return 1; +} + + +SynthWetness::SynthWetness(Synth *synth, int x, int y) + : BC_FPot(x, + y, + synth->config.wetness, + INFINITYGAIN, + 0) +{ + this->synth = synth; +} + +int SynthWetness::handle_event() +{ + synth->config.wetness = get_value(); + synth->send_configure_change(); + return 1; +} + + + +SynthFreqPot::SynthFreqPot(Synth *synth, SynthWindow *window, int x, int y) + : BC_QPot(x, y, synth->config.base_freq[0]) +{ + this->synth = synth; + this->window = window; +} +SynthFreqPot::~SynthFreqPot() +{ +} +int SynthFreqPot::handle_event() +{ + if(get_value() > 0 && get_value() < 30000) + { + synth->config.base_freq[0] = get_value(); + freq_text->update(get_value()); + synth->send_configure_change(); + window->update_note_selection(); + } + return 1; +} + + + +SynthBaseFreq::SynthBaseFreq(Synth *synth, SynthWindow *window, int x, int y) + : BC_TextBox(x, y, 100, 1, (float)0) +{ + this->synth = synth; + this->window = window; + set_precision(2); +} +SynthBaseFreq::~SynthBaseFreq() +{ +} +int SynthBaseFreq::handle_event() +{ + double new_value = atof(get_text()); +// 0 is mute + if(new_value < 30000) + { + synth->config.base_freq[0] = new_value; + freq_pot->update(synth->config.base_freq[0]); + synth->send_configure_change(); + window->update_note_selection(); + } + return 1; +} + + + + + +SynthCanvas::SynthCanvas(Synth *synth, + SynthWindow *window, + int x, + int y, + int w, + int h) + : BC_SubWindow(x, + y, + w, + h, + BLACK) +{ + this->synth = synth; + this->window = window; +} + +SynthCanvas::~SynthCanvas() +{ +} + +int SynthCanvas::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 +SynthLevelZero::SynthLevelZero(Synth *synth) + : BC_MenuItem(_("Zero")) +{ + this->synth = synth; +} + +SynthLevelZero::~SynthLevelZero() +{ +} + +int SynthLevelZero::handle_event() +{ + for(int i = 0; i < synth->config.oscillator_config.total; i++) + { + synth->config.oscillator_config.values[i]->level = INFINITYGAIN; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelMax::SynthLevelMax(Synth *synth) + : BC_MenuItem(_("Maximum")) +{ + this->synth = synth; +} + +SynthLevelMax::~SynthLevelMax() +{ +} + +int SynthLevelMax::handle_event() +{ + for(int i = 0; i < synth->config.oscillator_config.total; i++) + { + synth->config.oscillator_config.values[i]->level = 0; + } + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelNormalize::SynthLevelNormalize(Synth *synth) + : BC_MenuItem(_("Normalize")) +{ + this->synth = synth; +} + +SynthLevelNormalize::~SynthLevelNormalize() +{ +} + +int SynthLevelNormalize::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelSlope::SynthLevelSlope(Synth *synth) + : BC_MenuItem(_("Slope")) +{ + this->synth = synth; +} + +SynthLevelSlope::~SynthLevelSlope() +{ +} + +int SynthLevelSlope::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelRandom::SynthLevelRandom(Synth *synth) + : BC_MenuItem(_("Random")) +{ + this->synth = synth; +} +SynthLevelRandom::~SynthLevelRandom() +{ +} + +int SynthLevelRandom::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); + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelInvert::SynthLevelInvert(Synth *synth) + : BC_MenuItem(_("Invert")) +{ + this->synth = synth; +} +SynthLevelInvert::~SynthLevelInvert() +{ +} + +int SynthLevelInvert::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthLevelSine::SynthLevelSine(Synth *synth) + : BC_MenuItem(_("Sine")) +{ + this->synth = synth; +} +SynthLevelSine::~SynthLevelSine() +{ +} + +int SynthLevelSine::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +// ============================ phase calculations + +SynthPhaseInvert::SynthPhaseInvert(Synth *synth) + : BC_MenuItem(_("Invert")) +{ + this->synth = synth; +} +SynthPhaseInvert::~SynthPhaseInvert() +{ +} + +int SynthPhaseInvert::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthPhaseZero::SynthPhaseZero(Synth *synth) + : BC_MenuItem(_("Zero")) +{ + this->synth = synth; +} +SynthPhaseZero::~SynthPhaseZero() +{ +} + +int SynthPhaseZero::handle_event() +{ + for(int i = 0; i < synth->config.oscillator_config.total; i++) + { + synth->config.oscillator_config.values[i]->phase = 0; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthPhaseSine::SynthPhaseSine(Synth *synth) + : BC_MenuItem(_("Sine")) +{ + this->synth = synth; +} +SynthPhaseSine::~SynthPhaseSine() +{ +} + +int SynthPhaseSine::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthPhaseRandom::SynthPhaseRandom(Synth *synth) + : BC_MenuItem(_("Random")) +{ + this->synth = synth; +} +SynthPhaseRandom::~SynthPhaseRandom() +{ +} + +int SynthPhaseRandom::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + + +// ============================ freq calculations + +SynthFreqRandom::SynthFreqRandom(Synth *synth) + : BC_MenuItem(_("Random")) +{ + this->synth = synth; +} +SynthFreqRandom::~SynthFreqRandom() +{ +} + +int SynthFreqRandom::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthFreqEnum::SynthFreqEnum(Synth *synth) + : BC_MenuItem(_("Enumerate")) +{ + this->synth = synth; +} +SynthFreqEnum::~SynthFreqEnum() +{ +} + +int SynthFreqEnum::handle_event() +{ + for(int i = 0; i < synth->config.oscillator_config.total; i++) + { + synth->config.oscillator_config.values[i]->freq_factor = (float)i + 1; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthFreqEven::SynthFreqEven(Synth *synth) + : BC_MenuItem(_("Even")) +{ + this->synth = synth; +} +SynthFreqEven::~SynthFreqEven() +{ +} + +int SynthFreqEven::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthFreqOdd::SynthFreqOdd(Synth *synth) + : BC_MenuItem(_("Odd")) +{ this->synth = synth; } +SynthFreqOdd::~SynthFreqOdd() +{ +} + +int SynthFreqOdd::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthFreqFibonacci::SynthFreqFibonacci(Synth *synth) + : BC_MenuItem(_("Fibonnacci")) +{ + this->synth = synth; +} +SynthFreqFibonacci::~SynthFreqFibonacci() +{ +} + +int SynthFreqFibonacci::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; + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +SynthFreqPrime::SynthFreqPrime(Synth *synth) + : BC_MenuItem(_("Prime")) +{ + this->synth = synth; +} +SynthFreqPrime::~SynthFreqPrime() +{ +} + +int SynthFreqPrime::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); + } + + ((SynthWindow*)synth->thread->window)->update_gui(); + synth->send_configure_change(); + return 1; +} + +float SynthFreqPrime::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; +} + + + + + + + + +SynthOscillatorConfig::SynthOscillatorConfig(int number) +{ + reset(); + this->number = number; +} + +SynthOscillatorConfig::~SynthOscillatorConfig() +{ +} + +void SynthOscillatorConfig::reset() +{ + level = 0; + phase = 0; + freq_factor = 1; +} + + +void SynthOscillatorConfig::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 SynthOscillatorConfig::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->append_newline(); +} + +int SynthOscillatorConfig::equivalent(SynthOscillatorConfig &that) +{ + if(EQUIV(level, that.level) && + EQUIV(phase, that.phase) && + EQUIV(freq_factor, that.freq_factor)) + return 1; + else + return 0; +} + +void SynthOscillatorConfig::copy_from(SynthOscillatorConfig& that) +{ + level = that.level; + phase = that.phase; + freq_factor = that.freq_factor; +} + + +SynthConfig::SynthConfig() +{ + reset(); +} + +SynthConfig::~SynthConfig() +{ + oscillator_config.remove_all_objects(); +} + +void SynthConfig::reset() +{ + wetness = 0; + base_freq[0] = 440; + for(int i = 1; i < MAX_FREQS; i++) + base_freq[i] = 0; + wavefunction = SINE; + for(int i = 0; i < oscillator_config.total; i++) + { + oscillator_config.values[i]->reset(); + } + + momentary_notes = 0; +} + +int SynthConfig::equivalent(SynthConfig &that) +{ +//printf("SynthConfig::equivalent %d %d\n", base_freq, that.base_freq); + for(int i = 0; i < MAX_FREQS; i++) + if(base_freq[i] != that.base_freq[i]) return 0; + + if(wavefunction != that.wavefunction || + oscillator_config.total != that.oscillator_config.total || + momentary_notes != that.momentary_notes) 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 SynthConfig::copy_from(SynthConfig& that) +{ + wetness = that.wetness; + for(int i = 0; i < MAX_FREQS; i++) + base_freq[i] = that.base_freq[i]; + wavefunction = that.wavefunction; + momentary_notes = that.momentary_notes; + + 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 SynthOscillatorConfig(i)); + oscillator_config.values[i]->copy_from(*that.oscillator_config.values[i]); + } + + for( ; + i < oscillator_config.total; + i++) + { + oscillator_config.remove_object(); + } + +} + +void SynthConfig::interpolate(SynthConfig &prev, + SynthConfig &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); + + momentary_notes = prev.momentary_notes; +} +