Subversion Repositories DIN Is Noise

Rev

Rev 337 | Rev 354 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* main.cc
* DIN Is Noise is copyright (c) 2006-2017 Jagannathan Sampath
* For more information, please visit http://dinisnoise.org/
*/


#include <SDL/SDL.h>
#include <string>
#include <map>
#include <vector>
#include <iostream>
#include <fstream>
#include <list>
#include <algorithm>
#include <tcl.h>
#include "dingl.h"
#include "main.h"
#include "box.h"
#include "utils.h"
#include "font.h"
#include "ui_list.h"
#include "console.h"
#include "input.h"
#include "viewwin.h"
#include "curve.h"
#include "multi_curve.h"
#include "solver.h"
#include "chrono.h"
#include "curve_editor.h"
#include "font_editor.h"
#include "din.h"
#include "curve_library.h"
#include "console_iterator.h"
#include "delay.h"
#include "random.h"
#include "globals.h"
#include "command.h"
#include "scalelist.h"
#include "morse_code.h"
#include "sine_mixer.h"
#include "tcl_interp.h"
#include "ansi_color_codes.h"
#include "compressor.h"
#include "oscilloscope.h"
#include "keyboard_keyboard.h"
#include "fft.h"
#include "authors_note.h"
#include "midi_in.h"
#include "menu.h"
#include "curve_picker.h"
#include "fractaliser.h"
#include "rose_milker.h"
#include "circler.h"
#include "spiraler.h"
#include "starrer.h"
#include "lissajous.h"
#include "superformula.h"
#include "warper.h"
#include "recorder.h"
#include "mondrian.h"
#include "instrument.h"
#include "countries.h"
#include "morpher.h"
#include "number.h"
#include "sounding_board.h"

using namespace std;

// declare / define all global vars
//

ofstream dlog ("log", ios::out); // log of DIN operation

struct startup_shutdown {

  startup_shutdown () {
    dlog << "!!! started DIN Is Noise !!!" << endl;
    seed_rand_gen (clock());
    dlog << "+++ initialised random number generator +++" << endl;
  }

  ~startup_shutdown () {
    SDL_Quit ();
    dlog << "!!! cleaned up SDL !!!" << endl;
    dlog << "*** [Mondrian] box count = " << rect::ref << " ***" << endl;
    dlog << "*** [Mondrian] ball count = " << ball::ref << " ***" << endl;
    dlog << "*** [Mondrian] slit count = " << slit::ref << " ***" << endl;
    dlog << "*** [Microtonal-Keyboard] drone count = " << drone::ref << " ***" << endl;
    dlog << "!!! DIN Is Noise has shut down !!!" << endl;
    dlog.flush ();
  }

} _startup_shutdown;

std::string STRING;

#if defined (__UNIX_JACK__) || defined (__LINUX_ALSA__)
  string home = getenv ("HOME");
  std::string user_data_dir (home + "/.din/"); // contains defaults, user savings and preferences
  std::string country_data_dir (user_data_dir + "country/"); // contains country data used by countries plugin (see countries.cc)
#elif __WINDOWS_DS__
  std::string user_data_dir = "user\\";
  std::string country_data_dir = "user\\country\\";
#elif __MACOSX_CORE__
  std::string user_data_dir = "user/";
  std::string country_data_dir = "user/country/";
#endif

tcl_interp interpreter; // integrated TCL interpreter

// din clocks
double TIME_NOW = 0; // time now in seconds on the audio clock stored in variable timenow in Tcl interpreter
audio_clock clk; // audio clock
ui_clock ui_clk; // UI clock

// keyboard and mouse state
keyboard keybd;
int mousex = 0, mousey = 0, mouseyy = 0; // absolute mouse x, y & main viewport y
int lmb = 0, rmb = 0, mmb = 0; // mouse buttons
is_lmb_t is_lmb; // for acquiring lmb

int TURN_OFF_UI = 0;

list<crvpt> LIST_OF_POINTS; // list of points (used by curve.h/cc)

int drone::UID = 0; // unique id for drones
int drone::ref = 0; // for ref counting drones
double drone::LIFE_TIME = 10; // for launched drones (seconds)
double drone::INSERT_TIME = 3; // for launched drones to get into orbit (seconds)
int drone::SNAP_TO_NOTES = 0; // snap drones to notes?

// for trails on drones and mondrian
float* trail_t::tpts = 0;
int trail_t::n_tpts = 0;

int basic_editor::hide_cursor = 0; // to hide cursor when mouse is on a ui control

int plugin::change_curve_name = 1;

int* mesh::gl_pts = 0;
int mesh::n_glpts = 0;

double fader::TIME; // loaded from din_info

float curve_mixer::TIME = 5.0f; // curve (waveform/envelope) mixing time in seconds
double curve_mixer::ELAPSEDT = 0.0;
curve_mixer* curve_mixer::LAST_MIXER = 0;
int curve_mixer::NMIXES = 0;

extern const float GOLDEN_RATIO = 1.618033;
extern const float PI = 3.1415961;
extern const float TWO_PI = 2 * PI;
extern const float PI_BY_180 = PI / 180.;
extern const int MILLION = 1000000;
extern const std::string DEFAULT_DELIMITER = " "; // see tokenizer.h
const char SPC = ' ';

// for sprintf
const int BUFFER_SIZE = 1 << 15;
char BUFFER [BUFFER_SIZE] = {0};

// musical key
//

int NUM_NOTES = 13;
const char* WESTERN_SHARP [] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "C"};
const char* WESTERN_FLAT [] = {"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B", "C"};

float WIKIPEDIA_KEY_FREQUENCIES [] = { // for the octave of Middle-C; from wikipedia - piano key frequencies page.
  261.626f, 277.183f, 293.665f, 311.127f, 329.628f, 349.228f, 369.994f, 391.995f, 415.305f,
  440.0f, 466.164f, 493.883f, 523.251f
};

std::string NOTATION = "western";

string NEAREST_NOTE; // note nearest to key of DIN
float NEAREST_NOTE_DISTANCE; // distance in Hz of the nearest note from the key of DIN
float NEAREST_NOTE_FREQUENCY; // frequency in Hz of the nearest note to the key of DIN

widget* widget::focus = 0; // widget that has focus
widget* widget::next_focus = 0; // widget waiting to have focus
widget* ui_list::mm_last = 0; // last button clicked on the label field sliders (see min_max_clicked below)

const std::string VERSION_NUMBER ("30-WIP");
std::string LOCATION (" @ Chennai, India");

std::string APP_NAME;
void make_app_name () {
  #ifdef __EVALUATION__
    APP_NAME = "DIN Is Noise " + VERSION_NUMBER + LOCATION + " | EVALUATION VERSION";
  #elif defined __LICENSED__
    APP_NAME = "DIN Is Noise " + VERSION_NUMBER + LOCATION + " | LICENSED VERSION";
  #endif
}

void set_window_caption () {
  make_app_name ();
  SDL_WM_SetCaption (APP_NAME.c_str(), APP_NAME.c_str());
}

int SCREEN_WIDTH, SCREEN_HEIGHT; // initial screen width & height (from user/globals)
const int SCREEN_DEPTH = 24; // fixed color depth
int FULL_SCREEN = 0; // full screen?
viewport view; // display - using OpenGL

int SAMPLE_RATE; // sample rate of DIN
float SAMPLE_DURATION; // duration of an audio sample in seconds

std::string SCALE; // current scale
int NUM_OCTAVES; // number of octaves in DIN

std::string TUNING; // current tuning
int NUM_INTERVALS; // number of intervals in tuning
std::string INTERVALS_FILE; // file name that contains interval info
std::map <std::string, float> INTERVALS; // interval name -> value
std::vector<std::string> INTERVAL_NAMES; // sequential interval names (eg., 1 2b 2 3b 3 ... 7b 7 8)
std::vector<float> INTERVAL_VALUES; // sequential interval values (eg., value of 1 2b 3 3b ... 7b 7 8)
std::map <std::string, int> NOTE_POS; // interval name -> note number (eg., 1 => 0, 2b => 1, 3 => 2, etc)

// for microtonal keyboard
//
int LEFT = 0, BOTTOM, RIGHT, TOP, HEIGHT; // absolute extents
float BOTTOM01, HEIGHT01; // as proportion of window height
int NUM_MICROTONES; // number of microtones in a range
int NUM_VOLUMES, LAST_VOLUME; // number of volume levels
float DELTA_VOLUME; // change in volume every pixel of board height
int TRAIL_LENGTH = 1; // default drone trail length (== number of trail points)
int DRONE_HANDLE_SIZE; // default drone handle size
float DELTA_DRONE_MASTER_VOLUME; // change in drone master volume
float PHRASE_TEMPO = 1.0; // playback recorded phrase at recorded speed

int FPS = 0; // frames per second, wanted
double TIME_PER_FRAME; // in seconds
int GOT_FPS = 0; // got

int IPS; // keyboard/mouse inputs per second
double TIME_PER_INPUT; // in seconds

int PITCH_BEND; // in Hz; used for pitch bending notes on keyboard-keyboard
float PITCH_BEND_PER_PIXEL;

// see curve_samples in curve_editor.h
float HZ;
float curve_samples::dm = 0.0f;
float curve_samples::nsec = 2.0f;

// screens - 1 is instrument; 2 - 8 are editors
//
const Uint8 ui_list::key [] = {SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8}; // keys 2 - 8
ui* ui_list::ed [] = {0};

// solver xhandlers; see solver.cc
atmin _atmin;
atmax _atmax;
tomin _tomin;
loopmin _loopmin;
loopmax _loopmax;
pongmin _pongmin;
pongmax _pongmax;

// see multi_curve.cc
multi_curve curve_editor::copy;
multi_curve mix;

// 0 init correct for oscilloscope; see oscilloscope.cc
float oscilloscope::sample_t::lmin = 0, oscilloscope::sample_t::lmax = 0;
float oscilloscope::sample_t::rmin = 0, oscilloscope::sample_t::rmax = 0;
oscilloscope scope ("scope.osc");

// see range.cc
int range::char_height, range::ybot1, range::ybot2, range::ytop1, range::ytop2;

// see sine_mixer.cc
int sine_mixer::NUM_SINE_SAMPLES = 100;
const int sine_mixer::MIN_SINE_SAMPLES = 4;

// see viewwin.cc
int viewport::handle_radius = 50;
float viewport::handle_factor = 0.0f;
int window::PAN_RATE = 100, window::ZOOM_RATE = 100;
double window::PAN_REPEAT = 1.0 / PAN_RATE, window::ZOOM_REPEAT = 1.0 / ZOOM_RATE;
float window::PAN_AMOUNT = 0.1f, window::ZOOM_AMOUNT = 0.1f;

// load initial globals
load_globals lg;

// custom vector font
#ifdef __SVG_OUT__
  font fnt ("laser.fnt");
#elif __PLOTTER_OUT__
  font fnt ("plotter.fnt");
#else
  font fnt ("jag.fnt");
#endif

// console
// possible text colors of console
const float cc = 0.55f;
const color console::yellow (1, 1, cc);
const color console::green (cc, 1, cc);
const color console::red (1, cc, cc);
const color console::cyan (cc, 1, 1);
const color console::white (1, 1, 1);

string CHARSET [] = { // list of input chars
  // default is US layout
  "abcdefghijklmnopqrstuvwxyz0123456789 .=-/;\\,[]'`", // normal
  "ABCDEFGHIJKLMNOPQRSTUVWXYZ)!@#$%^&*( >+_?:|<{}\"~", // shift
};
const int CHARSET_LENGTH = CHARSET[0].length ();
console cons;

char get_typed_char () { // get char typed on an input area
  const string& norm = CHARSET[0];
  const string& shift = CHARSET[1];
  for (int i = 0; i < CHARSET_LENGTH; ++i) {
    if (keypressedd (norm[i])) {
      if (shift_down() || keydown (SDLK_CAPSLOCK))
        return shift[i];
      else
        return norm[i];
    }
  }
  return 0;
}

// audio output
//
audio_out aout;

// list of scales
scalelist scalelst;

//
// L and R delays
//
delay left_delay (1000, "feedback-l.crv", "volume-l.crv"), right_delay (1000, "feedback-r.crv", "volume-r.crv");
curve_editor delayed ("delay.ed");

// vars in Tcl interpreter
//

// midi bpm from external midi clock
double MIDI_BPM = 0;

// tap bpm from computer keyboard
double TAP_BPM = 0;

// din board height (0 to 1)
float VOLUME = 0;

fft fft0; // FFT of waveform in waveform editors

cmdlist cmdlst; // list of din commands

// 1 oscillator / waveform for lead
//

float VOICE_VOLUME = 0.15f;

//
// curve libraries
//
curve_library wav_lib ("waveforms.lib"); // waveforms
curve_library sin_lib ("sin.lib"); // custom sine curves
curve_library cos_lib ("cos.lib"); // custom cosine curves
curve_library attack_lib ("attack.lib", 1); // attack curves [1 = has_sustain]
curve_library decay_lib ("decay.lib"); // decay curves

// compressor
compressor coml ("coml.crv"), comr ("comr.crv");
curve_editor comed ("compressor.ed");

// octave shift
//
beat2value octave_shift ("os", "octave-shift.crv");
curve_editor octed ("octave-shift.ed");
curve_library octlib ("octave-shift-patterns.lib");
beat2value_listener octlis;

// drone modulation
//
beat2value drone_modulation::am_crv ("drone AM", "dam.crv");
beat2value drone_modulation::fm_crv ("drone FM", "dfm.crv");
curve_editor dmod_ed ("drone-modulation.ed");
beat2value_listener damlis, dfmlis;

recorder recorder0; // for recording sounds made in DIN

// microtonal-keyboard see din.cc/.h
//

din din0 (cmdlst);

// mondrian (see mondrian.cc/h)
//
rnd<float> rect::rd (0.0f, 1.0f); // to make box color
int rect::ref = 0, ball::ref = 0, slit::ref = 0;
const int mondrian::gutter = 2;
const int mondrian::gutter2 = mondrian::gutter * mondrian::gutter;
float mondrian::min_split_size = 32;
float slit::HALF_SIZE = 20;
float slit::MIN_HALF_SIZE = 1;
float slit::MIN_SIZE = 2 * slit::MIN_HALF_SIZE;
const char* ball::types_str [3] = {"bouncer", "wrecker", "healer"};
float ball::recent_attack_time = 0.01f; // secs
float ball::recent_decay_time = 2.0f; // secs
float ball::recent_pitch_mult = 1.0f; // modulation
const char* mondrian_listener::split_types [3] = {" into 2 boxes", " at notes", " into N boxes"};
const char* mondrian_listener::selection_targets [2] = {"Selection target = slits", "Selection target = balls"};
const double slit::INITIAL_OPEN_CLOSE_TIME = 2.0;

mondrian mondrian0;

// sounding board (see sounding_board.cc/h)
//
sounding_board sounding_board0;
int sounder::NUM_FADERS = 0;

//wavdoh wavdoh0;

gotog _gotomax (1, &mondrian0.attacked);

// keyboard-keyboard
//
float NOTE_VOLUME = 0.75f * VOICE_VOLUME;

// in secs
float ATTACK_TIME = 0.05f;
float DECAY_TIME = 5.0f;
float DELTA_TIME = 0.025f;

color keyboard_keyboard::note_color[2]; // midi keybd note color
const int keyboard_keyboard::color_index [] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}; // 0 - white, 1 - black; 1 octave
keyboard_keyboard keybd2;
gotog _gotog (1, &keybd2.attacked); // default sustain (see keyboard-keyboard.cc)

// available instruments
const char* INSTRUMENTS [] = {"keyboard_keyboard", "microtonal_keyboard", "mondrian", "sounding_board"};
const char* INSTRUMENTS_SHORT [] = {"kkb", "mkb", "mon", "sbd"};
int CURRENT_INSTRUMENT = 0;
extern const int NUM_INSTRUMENTS = 4;
std::string INSTRUMENT = INSTRUMENTS [CURRENT_INSTRUMENT];
instrument* INSTRUMENT_PTR [] = {&keybd2, &din0, &mondrian0, &sounding_board0};
checkbutton* LAST_TABS [NUM_INSTRUMENTS] = {0, 0, 0, 0};

box_selector mkb_selector; // selector for microtonal keyboard
box_selector mon_selector; // selector for mondrian

std::string style_listener::styles [] = {"loop", "pong"};

std::vector<multi_curve*> curve_list;
void setup_curve_list () {
  multi_curve* lst [] = {
    &din0.wave,
    &keybd2.wave,
    &din0.fm.crv,
    &din0.am.crv,
    &din0.gatr.crv,
    &octave_shift.crv,
    &din0.drone_wave,
    &left_delay.fbk_crv,
    &left_delay.vol_crv,
    &right_delay.fbk_crv,
    &right_delay.vol_crv
  };
  for (int i = 0; i < 11; ++i) curve_list.push_back (lst[i]);
}

const char* bpm_com::str [bpm_com::NUM] = {"os", "gr", "am", "fm"};
beat2value* bpm_com::bv [] = {&octave_shift, &din0.gatr, &din0.am, &din0.fm};

#define DEFINE_TCL_FUNC(X) inline int (X) (ClientData cd, Tcl_Interp* ti, int objc, Tcl_Obj* CONST objv[]) { return tcl_run (cd, ti, objc, objv);}

DEFINE_TCL_FUNC(tcl_key)
DEFINE_TCL_FUNC(tcl_setv)
DEFINE_TCL_FUNC(tcl_getv)
DEFINE_TCL_FUNC(tcl_set_delay)
DEFINE_TCL_FUNC(tcl_get_delay)
DEFINE_TCL_FUNC(tcl_set_bpm)
DEFINE_TCL_FUNC(tcl_get_bpm)
DEFINE_TCL_FUNC (tcl_set_beat);
DEFINE_TCL_FUNC (tcl_get_beat);
DEFINE_TCL_FUNC(tcl_set_style)
DEFINE_TCL_FUNC(tcl_get_style)
DEFINE_TCL_FUNC(tcl_set_kern)
DEFINE_TCL_FUNC(tcl_get_kern)
DEFINE_TCL_FUNC(tcl_set_font_size)
DEFINE_TCL_FUNC(tcl_get_font_size)
DEFINE_TCL_FUNC(tcl_note_distance)
DEFINE_TCL_FUNC(tcl_chord)
DEFINE_TCL_FUNC(tcl_notation)
DEFINE_TCL_FUNC(tcl_echo)
DEFINE_TCL_FUNC(tcl_curve_value)
DEFINE_TCL_FUNC(tcl_curve_name)
DEFINE_TCL_FUNC(tcl_curve_library)
DEFINE_TCL_FUNC(tcl_morse_code)
DEFINE_TCL_FUNC (tcl_set_editor);
DEFINE_TCL_FUNC (tcl_set_kb_layout);
DEFINE_TCL_FUNC (tcl_set_scope);
DEFINE_TCL_FUNC (tcl_get_scope);
DEFINE_TCL_FUNC (tcl_get_drone);
DEFINE_TCL_FUNC (tcl_set_drone);
DEFINE_TCL_FUNC (tcl_text_color);
DEFINE_TCL_FUNC (tcl_paste_gater);
DEFINE_TCL_FUNC (tcl_get_intervals);
DEFINE_TCL_FUNC (tcl_num_octaves);
DEFINE_TCL_FUNC (tcl_set_audio);
DEFINE_TCL_FUNC (tcl_load_scale);
DEFINE_TCL_FUNC (tcl_scale_curve);
DEFINE_TCL_FUNC (tcl_sounder);
DEFINE_TCL_FUNC (tcl_set_sine_mixer);
DEFINE_TCL_FUNC (tcl_change_sine_mixer);
DEFINE_TCL_FUNC (tcl_update_sine_mixer);
/*DEFINE_TCL_FUNC (tcl_write_svg);
DEFINE_TCL_FUNC (tcl_write_hpgl);*/



// din commands
//
// format: long name, short name

// for L and R delays
set_delay sd (&left_delay, &right_delay, "set-delay", "sd");
get_delay gd (&left_delay, &right_delay, "get-delay", "gd");

// to load the scale
load_scale los (&din0, "load-scale", "los");

// set and get din variables
set_var sv (&din0, "set-var", "sv");
get_var gv (&din0, "get-var", "gv");

// bpm commands
//
set_bpm sb ("set-bpm", "sb");
get_bpm gb ("get-bpm", "gb");
set_beat sn ("set-beat", "sbt");
get_beat gn ("get-beat", "gbt");
set_style ss ("set-style", "ss");
get_style gs ("get-style", "gs");

// set key/tonic
key ky (&din0, "key", "key");

// display notation on the keyboard
notation no (&din0, "notation", "no");

// music utils
note_distance nd ("note-distance", "nd");
chord ch ("chord", "ch");

// font cmds
set_font_size sfs ("set-font-size", "sfs");
get_font_size gfs ("get-font-size", "gfs");
set_kern sk ("set-kern", "sk");
get_kern gk ("get-kern", "gk");

// curve cmds
curve_name cn ("curve-name", "cn");
curve_value cv ("curve-value", "cv");
curve__library cl ("curve-library", "cl");
set_curve_editor sced ("set-curve-editor", "sced");
paste_gater pasg ("paste-gater", "paste-gater");
scale_curve scrv ("scale-curve", "scrv");

// morse code
morse_code mc ("morse-code", "mc");

// to replace curves in a multi curve with a seed curve
fractaliser fractaliser_;

// to generate curve from polar equation R = sin (K * theta)
rose_milker rosemilker;

// to generate a regular polygon
circler circler_;


// to generate a spiral with R = A * theta
spiraler spiraler_;

// to connect dots to make star polygons
starrer starrer_;

// to generate lissajous curve
lissajous lissajous_;

// to generate superformula curve
superformula superformula_;

// to warp XY of segments of a curve
warper warper_;

// to turn country outlines into curves
countries countries_;

// to morph one curve into another
morpher morpher_;

// to convert a number into a curve
number number_;

// sine mixer for waveform edit
sine_mixer sinemixer;

// unix like echo
echo ech ("echo", "eo");

set_kb_layout kbl ("set-kb-layout", "kbl");

// oscilloscope
set_scope ssco ("set-scope", "ssco", &din0, &sounding_board0);
get_scope gsco ("get-scope", "gsco", &din0);

// drone
get_drone gdro ("get-drone", "gdro", din0);
set_drone sdro ("set-drone", "sdro", din0);

// console
set_text_color stc ("set-text-color", "stc");

// get scale intervals
get_intervals gint ("get-intervals", "gint");

// set number of octaves
num_octaves noct ("num-octaves", "noct", din0);

// set audio
set_audio sau ("set-audio", "sa");

#ifdef __SVG__
// write curve into svg file
write_svg wsvg ("write-svg", "wsvg");
#endif

#ifdef __HPGL__
// write curve into hp-gl file for output on graphtec plotters
write_hpgl whpgl ("write-hpgl", "hpgl");
#endif

// used by binaural drones instrument
cmd_sounder snd ("sounder", "snd");

set_sine_mixer ssm ("set-sine-mixer", "ssm");
change_sine_mixer csm ("change-sine-mixer", "csm");
update_sine_mixer usm ("update-sine-mixer", "usm");

void add_commands (Tcl_Interp* interp) { // add din commands to Tcl interpreter

  unsigned int ncmds = 40; // +2 for write-svg, write-hpgl if included
  tclcmd cmd_funcs [] = {
    tcl_key,
    tcl_setv,
    tcl_getv,
    tcl_set_delay,
    tcl_get_delay,
    tcl_set_bpm,
    tcl_get_bpm,
    tcl_set_beat,
    tcl_get_beat,
    tcl_set_style,
    tcl_get_style,
    tcl_set_kern,
    tcl_get_kern,
    tcl_set_font_size,
    tcl_get_font_size,
    tcl_note_distance,
    tcl_chord,
    tcl_notation,
    tcl_echo,
    tcl_curve_value,
    tcl_curve_name,
    tcl_curve_library,
    tcl_morse_code,
    tcl_set_editor,
    tcl_set_scope,
    tcl_get_scope,
    tcl_get_drone,
    tcl_set_drone,
    tcl_text_color,
    tcl_paste_gater,
    tcl_get_intervals,
    tcl_num_octaves,
    tcl_set_kb_layout,
    tcl_set_audio,
    tcl_load_scale,
    tcl_scale_curve,
    tcl_sounder,
    tcl_set_sine_mixer,
    tcl_change_sine_mixer,
    tcl_update_sine_mixer,
    /*tcl_write_svg,
    tcl_write_hpgl,*/

  };

  command* cmds [] = {
    &ky,
    &sv,
    &gv,
    &sd,
    &gd,
    &sb,
    &gb,
    &sn,
    &gn,
    &ss,
    &gs,
    &sk,
    &gk,
    &sfs,
    &gfs,
    &nd,
    &ch,
    &no,
    &ech,
    &cv,
    &cn,
    &cl,
    &mc,
    &sced,
    &ssco,
    &gsco,
    &gdro,
    &sdro,
    &stc,
    &pasg,
    &gint,
    &noct,
    &kbl,
    &sau,
    &los,
    &scrv,
    &snd,
    &ssm,
    &csm,
    &usm,
    /*
    &wsvg,
    &whpgl,*/

  };

  extern cmdlist cmdlst;
  for (unsigned int i = 0; i < ncmds; ++i) {
    command* cmdp = cmds [i];
    cmdlst.add (cmdp);
    Tcl_CreateObjCommand (interp, cmdp->longname.c_str(), cmd_funcs[i], (ClientData) i, 0);
    Tcl_CreateObjCommand (interp, cmdp->shortname.c_str(), cmd_funcs[i], (ClientData) i, 0);
  }

  dlog << "+++ added " << ncmds << " din commands to the Tcl interpreter +++" << endl;

}

authors_note anote;
curve_picker_t curve_picker;
mouse_slider mouse_slider0;
ui_list uis;

void modulate_down () {
  instrument* instr = get_current_instrument ();
  if (instr->osd.active == 0) {
    static const std::string down = "down";
    start_octave_shift (instr, 0, down);
  }
}

void modulate_up () {
  instrument* instr = get_current_instrument ();
  if (instr->osd.active == 0) {
    static const std::string up = "up";
    start_octave_shift (instr, 1, up);
  }
}

void start_octave_shift (instrument* instr, int idir, const std::string& sdir) {
  octave_shift_data& osd = instr->osd;
  osd.tonic = instr->scaleinfo.tonic;
  osd.dir = idir;
  osd.active = 1;
  osd.now = octave_shift.sol.firstx;
}

void do_octave_shift () {
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) {
    instrument* instr = INSTRUMENT_PTR [i];
    octave_shift_data& osd = instr->osd;
    if (osd.active) {
      osd.now += (octave_shift.delta * aout.samples_per_channel);
      static const std::string octave_shift_str = "Octave Shift ", percent = "%";
      float now = osd.now - octave_shift.sol.firstx;
      float delta = octave_shift.sol.lastx - octave_shift.sol.firstx;
      osd.percent_complete = (int) (now / delta * 100);
      stringstream ss; ss << octave_shift_str << osd.percent_complete << percent;
      string txt (ss.str());
      uis.main_menu.l_octave_shift.set_text (txt);
      uis.l_octave_shift.set_text (txt);
      if (osd.now > octave_shift.sol.lastx) { // finished
        osd.now = octave_shift.sol.lastx;
        osd.active = 0;
        uis.main_menu.l_octave_shift.set_text (octave_shift_str);
        uis.l_octave_shift.set_text (octave_shift_str);
      }
      float shift = octave_shift.sol (osd.now);
      if (shift != 0) {
        if (osd.dir) set_tonic (instr, osd.tonic * shift); // go up
        else set_tonic (instr, osd.tonic / shift); // go down
      }

      if (!osd.active) cons ("key");

    }
  }

}

void setup_screens () {

  dlog << "*** setting up screens ***" << endl;

  left_delay.setup ();
  right_delay.setup ();

  coml.apply (&coml.crv);
  comr.apply (&comr.crv);

  plugin* pgns [] = {&fractaliser_, &rosemilker, &circler_, &spiraler_, &starrer_, &lissajous_, &superformula_, &warper_, &countries_, &sinemixer, &morpher_, &number_};
  for (int i = 0, j = 12; i < j; ++i) {dlog << "*** setting up plugin: " << pgns[i]->name; pgns[i]->setup (); dlog << " +++" << endl;}

  uis.add_widgets ();
  uis.setup ();
  curve_picker.setup ();
  fft0.setup ();
  din0.setup ();
  keybd2.setup ();
  mondrian0.setup ();

  // in delay editor load feedback & volume curves
  delayed.add (&left_delay.fbk_crv, &left_delay.fbk_lis);
  delayed.add (&left_delay.vol_crv, &left_delay.vol_lis);
  delayed.add (&right_delay.fbk_crv, &right_delay.fbk_lis);
  delayed.add (&right_delay.vol_crv, &right_delay.vol_lis);

  // add L & R compressor curves to compressor editor
  comed.add (&coml.crv, &coml.lis);
  comed.add (&comr.crv, &comr.lis);

  // octave shift
  octave_shift.setup ();
  octlis.set (&octave_shift);
  octave_shift.xmin = &_atmin;
  octave_shift.xmax = &_atmax;
  octed.add (&octave_shift.crv, &octlis);
  octed.attach_library (&octlib);

  // drone modulation
  damlis.set (&drone_modulation::am_crv);
  dfmlis.set (&drone_modulation::fm_crv);
  dmod_ed.add (&drone_modulation::am_crv.crv, &damlis);
  dmod_ed.add (&drone_modulation::fm_crv.crv, &dfmlis);
  dmod_ed.attach_library (&wav_lib);
  drone_modulation::am_crv.setup ();
  drone_modulation::fm_crv.setup ();

  dlog << "+++ setup screens complete +++" << endl;

}

void setup_plugin_labels () {
  fractaliser_.b_edit.set_label ("Edit");
  warper_.b_edit.set_label ("Edit");
}

void load_instrument () {
  ui* inst = 0;
  int dont_call_listener = 0;
  scope.load_current_instrument ();
  uis.main_menu.b_microtonal_keyboard.turn_off (dont_call_listener);
  uis.main_menu.b_keyboard_keyboard.turn_off (dont_call_listener);
  uis.main_menu.b_mondrian.turn_off (dont_call_listener);
  uis.main_menu.b_binaural_drones.turn_off (dont_call_listener);
  if (INSTRUMENT == "keyboard_keyboard") {
    inst = &keybd2;
    uis.main_menu.b_keyboard_keyboard.turn_on ();
    uis.main_menu.cb_instrument.turn_on ();
  } else if (INSTRUMENT == "microtonal_keyboard") {
    inst = &din0;
    uis.main_menu.b_microtonal_keyboard.turn_on ();
    checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
    if (pcb) uis.main_menu.changed (*pcb); else uis.main_menu.changed (uis.main_menu.cb_mkb_voice);
  } else if (INSTRUMENT == "mondrian") {
    inst = &mondrian0;
    uis.main_menu.b_mondrian.turn_on ();
    uis.main_menu.cb_instrument.turn_on ();
    checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
    if (pcb) uis.main_menu.changed (*pcb); else uis.main_menu.changed (uis.main_menu.cb_mon_tools);
  } else {
    inst = &sounding_board0;
    uis.main_menu.b_binaural_drones.turn_on ();
    uis.main_menu.cb_instrument.turn_on ();
    checkbutton* pcb = LAST_TABS [CURRENT_INSTRUMENT];
    if (pcb) uis.main_menu.changed (*pcb); else uis.main_menu.changed (uis.main_menu.cb_sounding_board_tools);
  }
  uis.set_current (inst);
  uis.main_menu.hide_editors ();
  uis.main_menu.show_editors (inst);
  if (inst != &anote) uis.main_menu.setup_tabs (inst);
  setup_plugin_labels ();
  cons ("setup-editors");
  uis.update_bottom_line ();
}

void goto_next_instrument () {
  scope.save_current_instrument ();
  ++CURRENT_INSTRUMENT;
  if (CURRENT_INSTRUMENT >= NUM_INSTRUMENTS) CURRENT_INSTRUMENT = 0;
  INSTRUMENT = INSTRUMENTS [CURRENT_INSTRUMENT];
}

void find_instrument () {
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) {
    if (INSTRUMENT == INSTRUMENTS[i]) {
      CURRENT_INSTRUMENT = i;
      break;
    }
  }
}

int is_instrument (ui* u) {
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) if ((void *) u == (void *) INSTRUMENT_PTR) return 1;
  return 0;
}

instrument* find_instrument (const string& inst) {
  for (int i = 0; i < NUM_INSTRUMENTS; ++i) if (inst == INSTRUMENTS[i] || inst == INSTRUMENTS_SHORT[i]) return INSTRUMENT_PTR[i];
  return get_current_instrument ();
}

instrument* get_current_instrument () {
  return INSTRUMENT_PTR [CURRENT_INSTRUMENT];
}

void update_window (int wnow, int hnow, int wprev, int hprev) { // called when main window resized
 
  glViewport (0, 0, wnow, hnow);

  view (wnow, hnow);

  curve_picker.update ();

  plugin* pgns [] = {&fractaliser_, &rosemilker, &circler_, &spiraler_, &starrer_, &lissajous_, &superformula_, &warper_, &countries_, &sinemixer, &morpher_, &number_};
  for (int i = 0, j = 12; i < j; ++i) pgns[i]->update ();

  fft0.update ();

  int wx = wnow - 1, hy = hnow - 1;
  cons.set_window (box<int>(0, 0, wx, hy));

  din0.win (din0.win.left, din0.win.bottom, din0.win.left + wx, din0.win.bottom + hy);

  HEIGHT = HEIGHT01 * view.ymax;
  din0.board_height_changed ();

  int quiet = 1;
  din0.clear_all_phrases (quiet);

  uis.update_widgets (wnow, hnow, wprev, hprev);

  keybd2.calc_visual_params ();

  if (uis.crved) uis.crved->calc_visual_params ();

  if (uis.current == &din0) {
    if (FULL_SCREEN) warp_mouse (0, view.ymax);
    din0.prev_mousex = mousex;
    din0.prev_mousey = mousey;
    din0.find_current_range ();
  }

  mondrian0.calc_visual_params ();


}

void setup_sdl_surface (SDL_Surface* surface, int w, int h, int fs = 0) {
  if (surface) SDL_FreeSurface (surface);
  if (fs)
    surface = SDL_SetVideoMode (w, h, SCREEN_DEPTH, SDL_OPENGL | SDL_FULLSCREEN);
  else {
    char* sdl_pos = (char *) "SDL_VIDEO_CENTERED=center";
    SDL_putenv (sdl_pos);
    surface = SDL_SetVideoMode (w, h, SCREEN_DEPTH, SDL_OPENGL | SDL_RESIZABLE);
  }
  if (surface) dlog << "+++ setup video mode " << w << ' ' << h << ' ' << SCREEN_DEPTH << " +++" << endl; else {
    dlog << "!!! couldnt set video mode: " << w << ' ' << h << ' ' << SCREEN_DEPTH << endl;
    exit (1);
  }
}

void save_window () {
  std::string fname = user_data_dir + "window";
  ofstream file (fname.c_str(), ios::out);
  extern viewport view;
  if (file) {
    file << "view_width " << view.width << eol;
    file << "view_height " << view.height << eol;
    file << "full_screen " << FULL_SCREEN << eol;
    file << "mode " << uis.settings_scr.imode << eol;
    file << "win_left " << din0.win.left << eol;
    file << "win_bottom " << din0.win.bottom << eol;
  } else {
    dlog << "!!! couldnt save window in " << fname << " !!!" << endl;
    return;
  }

  dlog << "+++ saved window in " << fname << " +++" << endl;


}

void restore_last_window () {

  dlog << "*** reading last window ***" << endl;

  int width = 0, height = 0;

  std::string fname (user_data_dir + "window");
  ifstream file (fname.c_str(), ios::in);

  std::string ignore;

  // last window size
  file >> ignore >> width;
  file >> ignore >> height;
  file >> ignore >> FULL_SCREEN;
  file >> ignore >> uis.settings_scr.imode;
  if (FULL_SCREEN == 0) {
    uis.settings_scr.add_display_mode (width, height);
    uis.settings_scr.imode = uis.settings_scr.num_modes - 1;
  } else
    uis.settings_scr.add_display_mode (800, 600); // one windowed mode for sanity

  // last microtonal board position
  file >> ignore >> din0.win.left;
  file >> ignore >> din0.win.bottom;

  view (width, height);
  din0.prev_mousey = view.ymax;

}

SDL_Surface* surface = 0;

struct dialog_pos {

  // dialogs
  point<int> dx_plugin_browser;
  point<int> dx_fft;
  point<int> dx_parameters;

  void note_position_of_dialogs () {
    point<int>* dp[] = {&dx_plugin_browser, &dx_fft, &dx_parameters};
    label* lbl [] = {&uis.plugin__browser.l_title, &fft0.l_title, &uis.d_parameters};
    for (int i = 0; i < 3; ++i) {
      label* li = lbl[i];
      point<int>* dpi = dp[i];
      dpi->x = view.xmax - li->posx;
      dpi->y = view.ymax - li->posy;
    }
  }
  void reposition_dialogs () {
    point<int>* dp[] = {&dx_plugin_browser, &dx_fft, &dx_parameters};
    label* lbl [] = {&uis.plugin__browser.l_title, &fft0.l_title, &uis.d_parameters};
    for (int i = 0; i < 3; ++i) {
      label* li = lbl[i];
      point<int>* dpi = dp[i];
      li->move (view.xmax - dpi->x - li->posx, view.ymax - dpi->y - li->posy);
    }
  }
};

void setup_video_mode (int w, int h, int vw, int vh, int fs) {
  dialog_pos dp; dp.note_position_of_dialogs ();
  setup_sdl_surface (surface, w, h, fs); 
  update_window (w, h, vw, vh);
  dp.reposition_dialogs ();
  glEnableClientState (GL_VERTEX_ARRAY);
}

int quit = DONT;

void try_quit () {
  dlog << "*** started to quit DIN Is Noise ***" << endl;
  din0.save_scale ();
  din0.dinfo.save ();
  keybd2.scaleinfo.save_scale ();
  mondrian0.scaleinfo.save_scale ();
  sounding_board0.scaleinfo.save_scale ();
  save_window ();
  interpreter ("src save_settings");
  dlog << "+++ saved DIN settings +++" << endl;
  uis.cb_voice.turn_off ();
  uis.cb_delay.turn_off ();
  din0.delete_all_drones ();
  sounding_board0.save ();
  uis.main_menu.bdl.clicked (uis.main_menu.b_delete_all_binaurals);
  scope.save_current_instrument ();
  quit = SOON;
  dlog << "!!! Quitting SOON !!!" << endl;
}

int read_input () {

  // handle window events

  static SDL_Event event;
  while (SDL_PollEvent(&event)) {
    switch(event.type) {
      case SDL_VIDEORESIZE:
        if (FULL_SCREEN == 0) {
          setup_video_mode (event.resize.w, event.resize.h, view.width, view.height, FULL_SCREEN);
          uis.settings_scr.update_windowed_mode (event.resize.w, event.resize.h);
        }
        break;
       
      case SDL_QUIT:
        quit = IMMEDIATE;
        break;
    }
  }

  // read keyboard
  keybd.read ();

  // read mouse
  int buttons = SDL_GetMouseState (&mousex, &mousey);
  mouseyy = view.ymax - mousey;
  lmb = buttons & SDL_BUTTON_LMASK;
  rmb = buttons & SDL_BUTTON_RMASK;
  //if (!lmb) is_lmb.clear ();

  return 1;

}

midi_in midiin;

const char* menu::modulation_targets [2] = {" Modulate Drones", " Modulate Voice"};

void applyfx (float* out0, float* out1, int do_delay, int do_compress) {
  //
  // apply delays
  //
  float fdr = uis.fdr_delay.amount;
  float* outl = out0; left_delay (outl, aout.samples_per_channel, fdr);
  float* outr = out1; right_delay (outr, aout.samples_per_channel, fdr);

  // apply compression
  //
  if (do_compress) {
    float* outl = out0, *outr = out1;
    coml.apply (outl, aout.samples_per_channel);
    comr.apply (outr, aout.samples_per_channel);
  }

}

int main (int argc, char** argv) {

  // MIDI note colors
  keyboard_keyboard::note_color [0] = color (0.9f, 0.9f, 0.9f); // white
  keyboard_keyboard::note_color [1] = color (0.2f, 0.2f, 0.2f); // black

  dlog << "*** Starting DIN Is Noise " << VERSION_NUMBER << " ***" << endl;

  //
  // bring up OpenGL window
  //

  if (SDL_Init (SDL_INIT_VIDEO) < 0) {
    dlog << "!!! couldnt initialise SDL video !!!" << endl;
    exit (1);
  } else {
    dlog << "+++ initialised SDL video +++" << endl;
  }

  uis.settings_scr.load_fullscreen_modes ();

  if (SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1) < 0) {
    dlog << "!!! couldnt setup SDL/OpenGL double buffer !!!" << endl;
    exit(1);
  } else {
    dlog << "+++ setup SDL/OpenGL double buffer +++" << endl;
  }

  restore_last_window ();

  setup_screens ();
  setup_curve_list ();

  interpreter.add_din_specific ();

  string init_tcl ("source " + user_data_dir + "init.tcl");
  string load_init_tcl (init_tcl); // init scripts
  interpreter (load_init_tcl);
  dlog << "+++ loaded DIN scripts (init.tcl) +++" << endl;

  /*string load_settings_tcl ("source " + user_data_dir + "settings.tcl"); // init din settings
  interpreter (load_settings_tcl);*/


  interpreter ("src settings");
  dlog << "+++ loaded DIN settings (settings.tcl) +++" << endl;

  midiin.open ();

  #ifdef __EVALUATION__
    //uis.set_current (&anote, 0, 0); // open with author's note
    uis.set_current (&anote);
  #elif defined __LICENSED__
    load_instrument (); // open with last used instrument
  #endif

  // init menu items
  uis.lfs_attack_time.set_val (ATTACK_TIME);
  uis.lfs_decay_time.set_val (DECAY_TIME);
  uis.lfs_num_voices.set_val ((int)(1.0f / NOTE_VOLUME + 0.5f));
  uis.sp_pitch_bend.set_value (PITCH_BEND_PER_PIXEL);
  float obpm = octave_shift.get_bpm ();
  uis.main_menu.sp_octave_shift_bpm.set_value (obpm);
  uis.sp_octave_shift_bpm.set_value (obpm);
  uis.main_menu.sp_gater_bpm.set_value (din0.gatr.get_bpm());
  uis.main_menu.sp_voice_volume.set_value (VOICE_VOLUME);
  uis.main_menu.cb_show_anchors.set_state (din0.dinfo.anchor);
  uis.main_menu.cb_show_vel.set_state (din0.dinfo.vel);
  uis.main_menu.cb_show_accel.set_state (din0.dinfo.accel);
  uis.main_menu.cb_show_gravity.set_state (din0.dinfo.gravity.visible);
  uis.main_menu.dcl.changed (uis.main_menu.cb_show_gravity);
  uis.main_menu.lf_delta_drone_master_volume.fld.set_text (DELTA_DRONE_MASTER_VOLUME);
  uis.main_menu.td_tap_display.set_bpm (din0.gatr.get_bpm ());
  uis.main_menu.mark_tap_target ();
  uis.main_menu.set_drone_master_volume ();
  uis.main_menu.sp_mesh_rows.set_value (din0.dinfo.rows);
  uis.main_menu.sp_mesh_cols.set_value (din0.dinfo.cols);
  uis.main_menu.sp_bounces.set_value (din0.dinfo.bounces);
  uis.main_menu.sp_rebound.set_value (din0.dinfo.rebound);
  uis.main_menu.sp_mondrian_min_voices.set_value (mondrian0.min_voices);
  uis.main_menu.sp_mondrian_change_attack_time.set_value (0);
  uis.main_menu.sp_mondrian_change_decay_time.set_value (0);
  uis.main_menu.sp_mondrian_change_speed.set_delta (mondrian0.delta_speed);
  uis.main_menu.sp_mondrian_change_dir.set_delta (mondrian0.delta_rotate_velocity);
  uis.main_menu.cb_mondrian_auto_adjust_voices.set_state (mondrian0.auto_adjust_voices);
  uis.main_menu.cb_draw_balls.set_state (mondrian0.draw__balls);
  uis.main_menu.cb_draw_boxes.set_state (mondrian0.draw__boxes);
  uis.main_menu.cb_fill_boxes.set_state (mondrian0.fill_boxes);
  uis.main_menu.cb_draw_notes.set_state (mondrian0.draw__notes);
  uis.main_menu.cb_label_notes.set_state (mondrian0.label_notes);
  uis.main_menu.ol_ball_types.set_text (ball::types_str[mondrian0.added_ball_type]);
  uis.main_menu.sp_mondrian_num_boxes.set_value (mondrian0.num_boxes);

  uis.settings_scr.update_mode_display ();

  mondrian0.make_notes ();

  sounding_board0.load (); // load binaural drones

  curve_picker.widget::hide();

  cons.home (); // display all text in console

  setup_video_mode (view.width, view.height, view.width, view.height, FULL_SCREEN);

  glClearColor(0, 0, 0, 0); // black bg

  aout.start (); // start audio loop in a separate thread. see audio_wanted (..)
 
  // ui loop
  double framet = 0;
  double inputt = 0;

  /*wavdoh0.open_file ("input.wav");
  while (wavdoh0.read_some ());*/


  const string loop ("loop");
  const char* timenow = "timenow";
  const string s_goodbye ("Goodbye!"), s_drones (" | Drones: "), s_kbkb (" | Keyboard-Keyboard: "), s_mondrian (" | Mondrian: "), s_binaurals (" | Fading Binaurals: ");
  const char percent = '%';
  while (1) {

    // try to write audio
    if (aout.can_write ()) {

      float* out0 = aout.writep; // left channel
      float* out1 = out0 + aout.samples_per_channel; // right channel

      do_octave_shift ();

      memset (aout.writep, 0, aout.samples_buffer_size); // silence everything

      interpreter (loop); // run loop on tcl proc

      // audio from instruments
      din0.render_audio (out0, out1);
      keybd2.render_audio (out0, out1);
      mondrian0.render_audio (out0, out1);
      sounding_board0.render_audio (out0, out1);

      // delay and compressor
      applyfx (out0, out1, din0.dinfo.delay, din0.dinfo.compress);

      // to oscilloscope
      if (scope.visible) scope.add_samples (out0, out1, aout.samples_per_channel);

      // record?
      if (uis.cb_record.state) {
        recorder0.add (aout.writep, aout.samples_buffer_size, aout.samples_per_buffer, uis.cb_record, uis.main_menu.cb_record);
      }

      // stream status
      aout.available [aout.writei] = 1;
      if (++aout.writei >= aout.num_samples_buffers) {
        aout.writep = aout.samples_buffers;
        aout.writei = 0;
      } else aout.writep += aout.samples_per_buffer;

      // store timenow in TCL. timenow is seconds on din audio clock
      TIME_NOW = clk.secs;
      Tcl_UpdateLinkedVar (interpreter.interp, timenow);

    }

    if (recorder0.saving_started) { // save recording to disk
      if (recorder0.save_some (uis.main_menu.cb_record) == 0)
        uis.main_menu.b_save.set_label ("Overwrite");
    }

    // draw frame
    double T = ui_clk ();
    if (T >= framet) {
      double deltat = T - framet;
      GOT_FPS = 1.0 / deltat;
      uis.draw ();
      SDL_GL_SwapBuffers ();
      framet = TIME_PER_FRAME + ui_clk ();
    }

    // handle input [mouse, keys and midi] and bg
    T = ui_clk ();
    if (T >= inputt) {
      read_input ();
      if (quit == DONT);
      else if (quit == SOON) {
        cons << console::yellow << s_goodbye << s_drones << din0.num_drones << s_kbkb << keybd2.num_triggered_notes << s_mondrian << mondrian0.num_triggered_notes;
        if (sounding_board0.num_sounders) cons << s_binaurals << int(sounding_board0.xt*100+0.5) << percent;
        cons << eol;
        if (
            (din0.num_drones == 0) &&
            (uis.fdr_voice.on == 0) &&
            (uis.fdr_delay.on == 0) &&
            (keybd2.num_triggered_notes == 0) &&
            (mondrian0.num_triggered_notes == 0) &&
            (sounding_board0.fading == 0)
           )
          break;
      }
      else if (quit == IMMEDIATE) break;

      uis.handle_input ();
      midiin.handle_input ();
      keybd.save ();
      uis.bg ();
      inputt = TIME_PER_INPUT + ui_clk ();
    }
  }

  aout.close ();
  mesh::destroy ();
  if (trail_t::tpts) delete[] trail_t::tpts;

  #if defined (__SVG_OUT__) || defined (__PLOTTER_OUT__)
  ifstream textf ("text.txt", ios::in);
  if (textf) {
    int x, y;
    textf >> x >> y;
    string line;
    int lh = get_line_height ();
    while (!textf.eof()) {
      getline (textf, line);
      #ifdef __SVG_OUT__
        write_string (line, x, y);
        y += lh;
      #elif __PLOTTER_OUT__
        plot_string (line, x, y);
        y -= lh;
      #endif
    }
  }
  #endif
 
  return 0;

}

void set_tonic (instrument* instr, float f) {
  instr->scaleinfo.set_tonic (f);
  if (instr == get_current_instrument ()) instr->scaleinfo.update_settings ();
}

float get_tonic (instrument* instr) {
  return instr->scaleinfo.tonic;
}

void set_notation (const string& s) {
  if (s == "w" || s == "west" || s == "western") NOTATION = "western";  else NOTATION = "numeric";
  din0.notate_ranges ();
  keybd2.setup_notes (0);
  mondrian0.calc_visual_params ();
  uis.settings_scr.sn_scale_notes.refresh ();
}

void set_num_octaves (int n) {
  if (n < 1) n = 1;
  NUM_OCTAVES = n;
  din0.setup_ranges (0); // 0 => dont load from disk
  din0.update_drone_ranges ();
  din0.update_drone_tone ();
}

int hide_menu () {
  uis.main_menu.unfilter ();
  if (uis.main_menu.show) {
    uis.main_menu.toggle ();
    return 1;
  }
  return 0;
}

void show_menu () {
  uis.main_menu.show = 0;
  uis.main_menu.toggle ();
}

void overlay_instrument () {
  get_current_instrument ()->draw ();
}

void hz2step (float& hz, float& step) {
  step = hz * 1.0f / SAMPLE_RATE;
}

void set_snap_drones (int what) {
  stringstream ss; ss << "set-var snap_drones " << what;
  cons (ss.str());
}

void set_snap (int what) {
  uis.main_menu.set_snap (what);
}

void dont_call_listener (checkbutton& cb, int state) {
  int _dont_call_listener = 0;
  if (state) cb.turn_on (_dont_call_listener); else cb.turn_off (_dont_call_listener);
}

int find_nearest_note (string& note, float& frequency, float& dist) {

  float left = WIKIPEDIA_KEY_FREQUENCIES [0] / 2048, right = 2 * left;
  float outf = frequency;
  if (outf < left) return 0;

  while (1) {
    if ((left <= outf) && (right > outf)) break;
    else {
      left*=2;
      right*=2;
    }
  }

  float oct = left / WIKIPEDIA_KEY_FREQUENCIES[0];
  dist = outf - left;
  int id = 0;
  float tone = 0;
  for (int i = 0; i < 13; ++i) {
    tone = WIKIPEDIA_KEY_FREQUENCIES[i] * oct;
    float newdist = tone - outf; if (newdist < 0) newdist = -newdist;
    if (newdist < dist) {
      dist = newdist;
      id = i;
    }
  }

  string nn;
  if (WESTERN_FLAT[id] == WESTERN_SHARP[id])
    nn = WESTERN_SHARP[id];
  else
    nn = (string)WESTERN_FLAT[id] + " / " + (string)WESTERN_SHARP[id];

  note = nn;
  frequency = WIKIPEDIA_KEY_FREQUENCIES[id] * oct;
  dist = outf - frequency;
  return id;

}

void turn_on_ui () {
  TURN_OFF_UI = 0;
  uis.add_widgets ();
}

void turn_off_ui () {
  if (!uis.current->ed) {
    TURN_OFF_UI = 1;
    if (uis.main_menu.show) uis.main_menu.toggle ();
    uis.widgets_of.clear ();
  } else cons << console::red << "Cant turn off UI in curve editors" << eol;
}

void tween (float* buf1, float* buf2, int n, float amount) {// interpolate buf2 -> buf1 and store in buf1
  for (int i = 0; i < n; ++i) {
    float b1 = buf1 [i], b2 = buf2[i];
    buf1[i] = amount * (b1 - b2) + b2;
  }
}

void tween (float* buf1, float* buf2, int n, float* amount) {
  for (int i = 0; i < n; ++i) {
    float b1 = buf1 [i], b2 = buf2[i];
    buf1[i] = amount[i] * (b1 - b2) + b2;
  }
}

void fill (float* buf, float start, float end, int n) {
  float es = end - start;
  float a = 0, da = 1./ (n - 1);
  for (int i = 0; i < n; ++i) {
    buf[i] = start + a * es;
    a += da;
  }
}

void multiply (float* out, float* mul, int n) {
  for (int i = 0; i < n; ++i) out[i] *= mul[i];
}

void multiply (float* out, int n, float depth) {
  for (int i = 0; i < n; ++i) out[i] *= depth;
}

void warp_mouse (int x, int y) {
  SDL_WarpMouse (x, y);
  mousex = x;
  mousey = y;
  mouseyy = view.ymax - y;
}