Subversion Repositories DIN Is Noise

Rev

Rev 2302 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* settings.cc
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/


#include "settings.h"
#include "viewwin.h"
#include "console.h"
#include "font.h"
#include "ui_list.h"
#include "tcl_interp.h"
#include "midi_in.h"
#include "audio.h"
#include "delay.h"
#include "main.h"
#include "instrument.h"
#include "curve_mixer.h"
#include "din.h"
#include "just.h"

#include <sstream>

#include "log.h"

extern int NOTATION;
extern string NEAREST_NOTE;
extern float NEAREST_NOTE_FREQUENCY;
extern float NEAREST_NOTE_DISTANCE;
extern int NUM_OCTAVES;
extern char BUFFER [];
extern int wheel, wheely;
extern int line_height;
extern const char* notation_types[];
extern din din0;

settings::settings () : ol_nearest_note (1), ol_display_modes (1) {
  name = "settings";
  imode = -1;
  num_widgets = 0;
  i_current_tuning = 0;
}

void settings::setup () {

  // setup separators
  separator* seps [] = {&hsep1, &hsep2, &hsep3, &hsep4, &hsep5, &hsep6, &hsep7, &hsep8, &hsep9, &hsep10};
  for (int i = 0; i < 10; ++i) seps[i]->set_extents (480);

  // setup widget array
  widget* wa [] = {
    &b_go_back,
    &hsep1, // 0
    &sp_key,
    &ol_nearest_note,
    &hsep2, // 3
    &sn_scale_notes,
    &sp_num_octaves,
    &ol_notation,
    &ol_tuning, // 8
    &hsep3,
    &l_delay_dur,
    &r_delay_dur,
    &cb_sync_delay, // 12
    &hsep4,
    &binaural.is,
    &binaural.justs,
    &binaural.sep, // 16
    &hsep5,
    &lf_mixing_time,
    &lf_mixing_samples,
    &b_set_to_audio_buffer_time, // 20
    &hsep6,
    &ol_audio_devices,
    &ol_sample_rates,
    &ol_buffer_sizes,
    &b_refresh_audio_devices, // 25
    &hsep7,
    &ol_midi_devices,
    &b_refresh_midi_devices,
    &cb_show_midi_messages, // 29
    &hsep8,
    &ol_display_modes, // 31
    &hsep9,
    &sp_fps,
    &sp_ips, // 34
    &hsep10,
    &b_factory_reset,
  };

  int nw = 37;
  widgets.resize (nw);
  for (int i = 0; i < nw; ++i) widgets[i] = wa[i];
 
  num_widgets = widgets.size ();
  lnspc.resize (num_widgets);

  // setup widget listeners
  b_go_back.set_listener (this);
  b_factory_reset.set_listener (this);
  ol_notation.set_listener (this);
  ol_tuning.set_listener (this);
  ol_midi_devices.set_listener (this);
  cb_show_midi_messages.set_listener (this);
  b_refresh_midi_devices.set_listener (this);
  ol_audio_devices.set_listener (this);
  ol_sample_rates.set_listener (this);
  ol_buffer_sizes.set_listener (this);
  b_refresh_audio_devices.set_listener (this);
  l_delay_dur.fld.change_lsnr = this;
  r_delay_dur.fld.change_lsnr = this;
  cb_sync_delay.set_listener (this);
  ol_nearest_note.set_listener (this);
  ol_nearest_note.apply.set_listener (this);
  sp_num_octaves.set_listener (this);
  ol_display_modes.set_listener (this);
  ol_display_modes.apply.set_listener (this);

  binaural.justs.set_listener (this);
  picked (binaural.justs.option, 0);

  spinner<int>* spn[] = {&sp_fps, &sp_ips};
  const char* lspn[] = {"Frames Per Second", "Inputs Per Second"};
  for (int i = 0; i < 2; ++i) {
    spinner<int>* si = spn[i];
    si->set (lspn[i], 1, 1, MILLION, this, 0);
  }

  lf_mixing_time.set_listener (this);
  lf_mixing_time.fld.fmt = field::inf;
  lf_mixing_time.set_label ("Curve mixing time = ");
  lf_mixing_samples.set_listener (this);
  lf_mixing_samples.set_label ("Curve mixing samples = ");

  b_set_to_audio_buffer_time.set_listener (this);

  cb_show_midi_messages.set_state (0);

  i_current_midi = 0;

  // build audio buffer sizes list
  int bs = 64;
  for (int j = 0; j < 7; ++j) {
    buffer_sizes.push_back (bs);
    bs *= 2;
  }

  i_buffer_size = 0;

  cb_sync_delay.set_text ("Sync");
  cb_sync_delay.turn_on ();

  interpreter ("tuning list");
  tokenizer tz (interpreter.result);
  string name;
  while (1) {
    tz >> name;
    if (name == "") break;
    tunings.push_back (name);
  }
  num_tunings = tunings.size ();

  binaural.is.set_text ("DIN Is Binaural");
  binaural.sep.set ("Separation (Hz)", 0.1f, 0, MILLION, &binsepl, 0);

  sp_key.set ("Key (Hz)", 1.0f, 0, MILLION, this, 0);
  sp_num_octaves.set ("Number of Octaves", 1, 1, MILLION, this, 0);

}

void settings::enter () {

  // called b4 settings page is displayed
  // refresh settings

  curin = get_current_instrument ();

  curin->scaleinfo.update_settings ();

  if (prev_mousex == -1) {
    prev_mousex = view.xmax / 2;
    prev_mousey = view.ymax / 2;
  }
 
  sprintf (BUFFER, " notation = %s", notation_types[NOTATION]);
  ol_notation.option.set_text (BUFFER);

  ol_tuning.option.set_text (" tuning = " + tunings [i_current_tuning]);

  // number of octaves
  //
  sp_num_octaves.set_value (NUM_OCTAVES);

  // load l & r delay durations
  const char* const chan [] = {"left", "right"};
  label_field* flds [] = {&l_delay_dur, &r_delay_dur};
  for (int i = 0; i < 2; ++i) {
    stringstream ss_gd;
    ss_gd << "get-delay " << chan[i];
    interpreter (ss_gd.str());
    stringstream ss_res; ss_res << interpreter.result;
    int msecs; ss_res >> msecs;
    flds[i]->fld.set_text (msecs / 1000.0f);
  }

  ui::enter ();

  cons.rollup (1);
 
  cons << GREEN << "Use mouse wheel to scroll!" << eol;

}

void settings::update_widgets () {

  int lh = (int) (1.25f * line_height);
  int x1 = (int) (0.25f * view.xmax), y1 = view.ymax - 2.5 * lh;
 
  for (int i = 0; i < num_widgets; ++i) lnspc [i] = lh;
  lnspc[0]=lnspc[3]=lnspc[8]=lnspc[12]=lnspc[16]=lnspc[20]=lnspc[25]=lnspc[29]=lnspc[31]=lnspc[34]=14;

  sn_scale_notes.update ();

  b_go_back.set_text ("Go back");
  b_factory_reset.set_text ("Reset to Factory Settings!");
  sp_num_octaves.update ();
  cb_show_midi_messages.set_text ("Show incoming MIDI messages");
  b_refresh_midi_devices.set_text ("Refresh MIDI devices");
  b_refresh_audio_devices.set_text ("Refresh Audio Devices");
  l_delay_dur.lbl.set_text ("L delay duration");
  r_delay_dur.lbl.set_text ("R delay duration");

  sp_fps.update ();
  sp_ips.update ();

  float t = get_tonic (get_current_instrument());
  sp_key.set_value (t);

  NEAREST_NOTE_FREQUENCY = t;
  find_nearest_note (NEAREST_NOTE, NEAREST_NOTE_FREQUENCY, NEAREST_NOTE_DISTANCE);
  sprintf (BUFFER, " nearest note = %s @ %0.3f Hz", NEAREST_NOTE.c_str(), NEAREST_NOTE_FREQUENCY);
  ol_nearest_note.set_text (BUFFER);
  ol_nearest_note.set_click_repeat (1);
  ol_nearest_note.apply.set_text ("Set");

  for (int i = 0; i < num_widgets; ++i) {
    widgets[i]->set_pos (x1, y1);
    y1 -= lnspc[i];
  }

  i_current_midi = midiin.input_port;
  ol_midi_devices.option.set_text (" midi = " + midiin.get_name (i_current_midi));
  i_current_device = aout.current_device;
  ol_audio_devices.option.set_text (" audio = " + aout.names [aout.current_device]);
  sprintf (BUFFER, " sample rate = %d", aout.sample_rate);
  ol_sample_rates.option.set_text (BUFFER);
  set_buffer_size (aout.samples_per_channel);

  b_set_to_audio_buffer_time.set_text ("Set to Audio Buffer Time");

}

void settings::set_buffer_size (int bs) {
  for (size_t i = 0, j = buffer_sizes.size (); i < j; ++i) {
    if (buffer_sizes[i] == bs) {
      i_buffer_size = i;
      break;
    }
  }

  sprintf (BUFFER, " buffer size = %d", buffer_sizes [i_buffer_size]);
  ol_buffer_sizes.option.set_text (BUFFER);
}

int settings::handle_input () {
  if (wheel && !widget::HOVER) move_widgets (wheel * wheely);
  for (int i = 0; i < num_widgets; ++i) if (widgets[i]->handle_input ()) return 1;
  return 0;
}

void settings::draw () {
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrtho (0, view.xmax, 0, view.ymax, -1, 1);
  for (int i = 0; i < num_widgets; ++i) widgets[i]->draw ();
}

void settings::move_widgets (int dy) {
  for (int i = 0; i < num_widgets; ++i) widgets[i]->move (0, dy);
}

void settings::clicked (button& b) {
  if (&b == &ol_nearest_note.apply) {
    instrument* i = get_current_instrument ();
    scale_info& si = i->scaleinfo;
    scale_info::nearest_note_t& nn = si.nearest_note;
    find_nearest_note (nn.name, nn.freq, nn.distance);
    si.set_tonic (nn.freq);
    sp_key.set_value (nn.freq);
  } else if (&b == &b_set_to_audio_buffer_time) {
    lf_mixing_time.fld.set_text (aout.samples_per_channel * 1.0f / aout.sample_rate);
    changed (lf_mixing_time.fld);
  } else if (&b == &b_factory_reset) {
    cons ("factory-reset");
  } else if (&b == &b_go_back) {
    uis.set_current (uis.prev);
  } else if (&b == &b_refresh_midi_devices) {
    midiin.probe ();
    ol_midi_devices.option.set_text (" midi = " + midiin.get_name (i_current_midi));
    midiin.open (i_current_midi);
  } else if (&b == &b_refresh_audio_devices) {
    aout.close ();
    aout.probe ();
    aout.open ();
    aout.start ();
    ol_audio_devices.option.set_text (" audio = " + aout.names [i_current_device]);
    sprintf (BUFFER, " sample rate = %d", aout.sample_rate);
    ol_sample_rates.option.set_text (BUFFER);
    set_buffer_size (aout.samples_per_channel);
  } else if (&b == &ol_display_modes.apply) {
    display_mode& dm = display_modes [imode];
    extern int FULL_SCREEN; FULL_SCREEN = dm.fullscreen;
    setup_video_mode (dm.w, dm.h, dm.w, dm.h, dm.fullscreen);
  }
}

void settings::picked (label& l, int d) {

  static const float TWELFTH_ROOT_OF_2 = 1.0594630943f;
  const static char *bjus [] = {" Justification = Left", " Justification = Right", " Justification = Random"};

  if (&l == &binaural.justs.option) {
    extern int JUSTIFICATION;
    JUSTIFICATION += d;
    wrap<int> (just::LEFT, JUSTIFICATION, just::RANDOM);
    const char* dj = bjus [JUSTIFICATION];
    l.set_text (dj);
  } else
  if (&l == &ol_nearest_note.option) {
    instrument* ci = get_current_instrument ();
    float tonic = get_tonic (ci);
    if (d > 0) {
      tonic *= TWELFTH_ROOT_OF_2;
    } else {
      tonic /= TWELFTH_ROOT_OF_2;
    }
    set_tonic (ci, tonic);
    ol_nearest_note.set_apply_pos ();
  } else if (&l == &ol_notation.option) {
    NOTATION += d;
    wrap<int> (NUMERIC, NOTATION, INDIAN);
    set_notation (NOTATION);
    sprintf (BUFFER, " notation = %s", notation_types [NOTATION]);
    ol_notation.option.set_text (BUFFER);
  } else if (&l == &ol_tuning.option) {
    i_current_tuning += d;
    wrap (0, i_current_tuning, num_tunings - 1);
    s_current_tuning = tunings [i_current_tuning];
    ol_tuning.option.set_text (" tuning = " + s_current_tuning);
    sprintf (BUFFER, "tuning set %s %d", s_current_tuning.c_str(), i_current_tuning);
    interpreter (BUFFER);
  } else if (&l == &ol_midi_devices.option) {
    if (midiin.num_ports) {
      i_current_midi += d;
      if (i_current_midi < 0) i_current_midi = midiin.num_ports - 1;
      else if (i_current_midi >= midiin.num_ports) i_current_midi = 0;
      midiin.open (i_current_midi);
      ol_midi_devices.option.set_text (" midi = " + midiin.get_name (i_current_midi));
    }
  } else if (&l == &ol_audio_devices.option) {
    if (aout.goto_next_device (d)) {
      sprintf (BUFFER, "set-audio device %d", aout.current_device);
      cons (BUFFER);
      ol_audio_devices.option.set_text (" audio = " + aout.names [aout.current_device]);
    }
  } else if (&l == &ol_sample_rates.option) {
    int srate = aout.goto_next_sample_rate_id (d);
    sprintf (BUFFER, " sample rate = %d", srate);
    ol_sample_rates.option.set_text (BUFFER);
    sprintf (BUFFER, "set-audio sample_rate %d", srate);
    cons (BUFFER);
  } else if (&l == &ol_buffer_sizes.option) {
    i_buffer_size += d;
    int n = buffer_sizes.size ();
    if (i_buffer_size < 0) i_buffer_size = n - 1; else if (i_buffer_size >= n) i_buffer_size = 0;
    int bs = buffer_sizes [i_buffer_size];
    sprintf (BUFFER, " buffer size = %d", bs);
    ol_buffer_sizes.option.set_text (BUFFER);
    sprintf (BUFFER, "set-audio samples_per_channel %d", bs);
    cons (BUFFER);
    MENU.sp_scope_samples.set_limits (1, bs);
  } else if (&l == &ol_display_modes.option) {
    imode += d;
    if (imode >= num_modes) imode = 0; else if (imode < 0) imode = num_modes - 1;
    update_mode_display ();
  }

}

void settings::changed (checkbutton& cb) {
  int state = cb_show_midi_messages.state;
  if (state) interpreter ("load-patch midimap 1"); else interpreter ("unload_midimap");
}

void settings::do_delay_dur (delay& d, field& f) {
  float v = f;
  if (v <= 0) {
    cons << RED << "bad delay duration" << eol;
    d.get (v);
    v /= 1000.0;
    f.set_text (v);
    return;
  }
  string cmd ("set-delay ");
  if (cb_sync_delay.state) {
    l_delay_dur.fld.set_text (v);
    r_delay_dur.fld.set_text (v);
    cmd += "all ";
  }
  else if (&f == &l_delay_dur.fld) cmd += "left ";
  else cmd += "right ";
  float msecs = v * 1000.0f;
  stringstream ss; ss << msecs;
  cmd += ss.str();
  cons (cmd);
}

void settings::changed (field& f) {
  if (&f == &sp_key.f_value) {
    set_tonic (get_current_instrument (), sp_key.value);
  } else if (&f == &lf_mixing_time.fld) {
    float mixt = f;
    if (mixt <= 0.0f) {
      cons << RED << "Bad mix time" << eol;
      return;
    }
    curve_mixer::TIME = f;
    curve_mixer::SAMPLES = curve_mixer::TIME * SAMPLE_RATE;
    lf_mixing_samples.fld.set_text (curve_mixer::SAMPLES);
  } else if (&f == &lf_mixing_samples.fld) {
    int s = f;
    if (s < 1) {
      cons << RED << "Bad mix samples" << eol;
      return;
    }
    curve_mixer::SAMPLES = s;
    curve_mixer::TIME = curve_mixer::SAMPLES * 1.0f / SAMPLE_RATE;
    lf_mixing_time.fld.set_text (curve_mixer::TIME);
  } else if (&f == &l_delay_dur.fld) {
    do_delay_dur (left_delay, l_delay_dur.fld);
  } else if (&f == &r_delay_dur.fld) {
    do_delay_dur (right_delay, r_delay_dur.fld);
  } else if (&f == &sp_num_octaves.f_value) {
    set_num_octaves (sp_num_octaves.value);
  } else if (&f == &sp_fps.f_value) {
    extern int FPS;
    extern double TIME_PER_FRAME;
    FPS = sp_fps.value;
    TIME_PER_FRAME = 1.0 / FPS;
  } else if (&f == &sp_ips.f_value) {
    extern int IPS;
    extern double TIME_PER_INPUT;
    IPS = sp_ips.value;
    TIME_PER_INPUT = 1.0 / IPS;
  }
}

void settings::load_fullscreen_modes () {

  dlog << "*** loading fullscreen modes ***" << endl;

  SDL_Rect **modes;

  // get available full screen modes
  modes = SDL_ListModes (0, SDL_FULLSCREEN | SDL_OPENGL | SDL_HWSURFACE);

  // Check is there are any modes available
  if (modes == (SDL_Rect **) 0){
    dlog << "No full-screen modes available" << endl;
    return;
  }

  // Check if our resolution is restricted
  if (modes == (SDL_Rect **) -1) {
  } else {
    static const int fullscreen = 1;
    static const int min_width = 1024;
    for(int i =0; modes[i]; ++i) {
      SDL_Rect* moder = modes[i];
      if (moder->w >= min_width) {
        display_modes.push_back (display_mode(moder->w, moder->h, fullscreen));
        dlog << " found mode: " << moder->w << 'x' << moder->h << endl;
      }
    }
  }

  num_modes = display_modes.size ();

  dlog << "number of fullscreen modes = " << num_modes << endl;
  dlog << "+++ loaded all full screen modes +++" << endl;

}

void settings::add_display_mode (int w, int h) {
  display_mode dm (w, h);
  display_modes.push_back (dm);
  num_modes = display_modes.size ();
}

void settings::update_windowed_mode (int w, int h) {
  int last_mode = num_modes - 1;
  display_mode& dm = display_modes [last_mode];
  dm.w = w; dm.h = h;
  if (last_mode == imode) update_mode_display ();
}

void settings::update_mode_display () {
  display_mode& dm = display_modes [imode];
  static const char* const fs_str[] = {"windowed", "full screen"};
  sprintf (BUFFER, " display = %d x %d, %s", dm.w, dm.h, fs_str[dm.fullscreen]);
  ol_display_modes.set_text (BUFFER);
}

void settings::sample_rate_changed () {
  curve_mixer::SAMPLES = curve_mixer::TIME * SAMPLE_RATE;
  lf_mixing_samples.fld.set_text (curve_mixer::SAMPLES);
}

settings::~settings () {}

VALUE_CHANGED (settings, bin_sep_lis) {
  extern float SEPARATION;
  SEPARATION = uis.settings_scr.binaural.sep.value;
  cons << GREEN << "Default binaural separation (Hz) = " << SEPARATION << eol;
}