Subversion Repositories DIN Is Noise

Rev

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

/*
* point_modulator.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 "audio.h"
#include "point_modulator.h"
#include "ui_list.h"
#include "console.h"
#include "file-utils.h"
#include <map>
using namespace std;

#define DECLARE_WIDGET_ARRAY_wa widget* wa [] = {&title, &all, &none, &invert, &sync, &play, &kill, &plus, &fold, &x.depth, &x.bpm, &y.depth, &y.bpm, &scrub};
#define NUM_WIDGETS 14
#define NUM_WIDGETS_1 (NUM_WIDGETS - 1)

extern audio_out aout;
extern char BUFFER[];

typedef std::vector<mod_dat>::iterator mod_dat_iter;

void point_modulator::setup (curve_editor* _ed) {

  ed = _ed;
  name = ed->name;

  DECLARE_WIDGET_ARRAY_wa
  const char* texts [] = {"All", "None", "Invert", "Sync"};
  for (int i = 1; i < 5; ++i)  {
    label* l = dynamic_cast<label*>(wa[i]);
    l->set_text (texts[i-1]);
  }

  for (int i = 1; i < 9; ++i) dynamic_cast<button*>(wa[i])->set_listener (this);

  makefam (&title, &wa[1], NUM_WIDGETS_1);

  for (int i = 5; i < 8; ++i) {
    button* wi = dynamic_cast<button*>(wa [i]);
    wi->set_size (16);
  }

  title.set_moveable (1);
  title.movlis = this;

  fold.set_dir (arrow_button::right);
  play.set_dir (arrow_button::right);

  widget_load ("d_point_modulator", wa, NUM_WIDGETS);

  x.depth.set ("X depth", 0.01f, -MILLION, MILLION, this, 0);
  x.depth.orient = mouse_slider_listener::Y;

  x.bpm.set ("X BPM", 0.1f, 0, MILLION, this, 0);
  x.bpm.orient = mouse_slider_listener::Y;

  y.depth.set ("Y depth", 0.01f, -MILLION, MILLION, this, 0);
  y.bpm.set ("Y BPM", 0.1f, 0, MILLION, this, 0);

  scrub.set ("Scrub", 0.01f, -MILLION, MILLION, this, 0);
  scrub.set_value (0.0f);
  scrub.orient = mouse_slider_listener::Y;

  folded = 1;

  nlst = nsel = 0;
  multisel = 0;

  load ();

  set_title ();

  moved ();

  synced = 0;

}

extern multi_curve pomo_x_crv, pomo_y_crv;
mod_dat::mod_dat (hit_t& h, state_button* _sb, float _bpm) : hit (h), sb (_sb), mod (&pomo_x_crv, &pomo_y_crv), init (h.get()) {
  mod.active = 1;
  mod.am.bv.set_bpm (_bpm);
  mod.fm.bv.set_bpm (_bpm);
}

mod_dat::mod_dat () : sb (0), mod (&pomo_x_crv, &pomo_y_crv) {}


void point_modulator::set_title () {
  sprintf (BUFFER, "Point Modulator [%d]", nlst);
  title.set_text (BUFFER);
}

point_modulator::point_modulator () {
  ++ref;
}

point_modulator::~point_modulator () {

  save ();

  if (nlst) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      state_button* sb = md.sb;
      delete sb;
    }
  }

  if (--ref == 0) {
    DECLARE_WIDGET_ARRAY_wa
    widget_save ("d_point_modulator", wa, NUM_WIDGETS);
  }

}

void point_modulator::load () {

  file_in fi ("pomo." + name);
  if (fi.opened) {
    ifstream& f = fi ();

    string ignore;
    f >> ignore >> nlst;

    if (nlst) {
      for (int i = 0; i < nlst; ++i) {
        hit_t h;
        f >> h.crv_id >> h.what >> h.id;
        h.crv = ed->curveinfo[h.crv_id].curve;
        mod_dat md;
        md.hit = h;
        modulator& mod = md.mod;
        f >> mod.active >> mod.am.depth >> mod.am.bv.bpm >> mod.fm.depth >> mod.fm.bv.bpm;
        f >> md.init.x >> md.init.y;
        f >> mod.am.bv.now >> mod.fm.bv.now;
        mod.am.bv.set_bpm (mod.am.bv.bpm);
        mod.fm.bv.set_bpm (mod.fm.bv.bpm);
        state_button* sb = new state_button;
        sb->set_listener (this);
        md.sb = sb;
        lst.push_back (md);
      }
    }
  }
}

void point_modulator::save () {
  file_out fo ("pomo." + name);
  ofstream& f = fo ();
  f << "num_pomos " << nlst << endl;
  if (nlst) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      hit_t& hit = md.hit;
      modulator& mod = md.mod;
      f << hit.crv_id << spc << hit.what << spc << hit.id << spc ;
      f << mod.active << spc << mod.am.depth << spc << mod.am.bv.bpm << spc << mod.fm.depth << spc << mod.fm.bv.bpm << spc;
      f << md.init.x << spc << md.init.y << spc;
      f << mod.am.bv.now << spc << mod.fm.bv.now << spc;
    }
  }
}

int point_modulator::handle_input () {

  if (folded == 0) {

    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      state_button* sb = md.sb;
      if (sb->handle_input()) return 1;
    }

    DECLARE_WIDGET_ARRAY_wa
    for (int i = 0; i < NUM_WIDGETS; ++i) {
      widget* wi = wa[i];
      if (wi->handle_input()) return 1;
    }

    multisel = SHIFT;

  } else {
    if (fold.handle_input ()) return 1;
    else return title.handle_input ();
  }

  return 0;

}

void point_modulator::remove (const hit_t& h) {
  int p = nlst;
  int hv = h();
  for (mod_dat_iter i = lst.begin (), j = lst.end(); i != j;) {
    mod_dat& md = *i;
    state_button* sb = md.sb;
    int s;
    if (hv)
      s = md.hit.matched_id (h);
    else {
      s = sb->state;
      nsel -= s;
    }
    if (s) {
      title.remove_child (sb);
      delete sb;
      i = lst.erase (i);
      j = lst.end ();
      --nlst;
    } else ++i;
  }
  ensure_hit_ids ();
  rearrange ();
  int q = p - nlst;
  if (q) cons << RED << "Removed " << q << " point modulations" << eol;
  set_title ();
}

void point_modulator::ensure_hit_ids () {
  for (mod_dat_iter i = lst.begin (), j = lst.end(); i != j; ++i) {
    mod_dat& md = *i;
    md.hit.ensure_id ();
  }
}

void point_modulator::rearrange () {
  int sx = title.extents.left;
  if (nlst) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      state_button* sb = md.sb;
      sb->set_pos (sx, sby);
      sx += state_button::SIZE2;
    }
  }
  sbx = sx;
}

void point_modulator::on_lst (const item_op& op) {
  int st;
  nsel = 0;
  for (int i = 0; i < nlst; ++i) {
    mod_dat& md = lst[i];
    state_button* sb = md.sb;
    st = op (sb->state);
    sb->set_state (st, 0);
    nsel += st;
  }
}

void point_modulator::changed (checkbutton& cb) {
  int cbs = cb.state;
  if (multisel == 0) {
    on_lst (_desel);
    if (cbs == 0) cbs = 1;
    cb.set_state (cbs, 0);
  }
  if (cbs) ++nsel; else if (--nsel < 0) nsel = 0;
  set_ui ();
}

void point_modulator::set_ui () {
  if (nsel != 1) {
    spinner<float>* sp[] = {&x.depth, &x.bpm, &y.depth, &y.bpm};
    for (int i = 0; i < 4; ++i) sp[i]->set_value (0.0f);
    x.bpm.set_limits (-MILLION, MILLION);
    y.bpm.set_limits (-MILLION, MILLION);
  } else {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      modulator& mod = md.mod;
      if (md.sb->state) {
        float v [] = {mod.fm.depth, mod.fm.bv.bpm, mod.am.depth, mod.am.bv.bpm};
        spinner<float>* sp[] = {&x.depth, &x.bpm, &y.depth, &y.bpm};
        for (int i = 0; i < 4; ++i) sp[i]->set_value (v[i]);
        x.bpm.set_limits (0, MILLION);
        y.bpm.set_limits (0, MILLION);
        break;
      }
    }
  }
}

void point_modulator::changed (field& f) {
  if (&f == &x.depth.f_value) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.sb->state) {
        md.mod.fm.depth += x.depth();
        cons << "modulator " << i << ", X depth = " << md.mod.fm.depth << eol;
      }
    }
  } else if (&f == &y.depth.f_value) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.sb->state) {
        md.mod.am.depth += y.depth();
        cons << "modulator " << i << ", Y depth = " << md.mod.am.depth << eol;
      }
    }
  } else if (&f == &x.bpm.f_value) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.sb->state) {
        md.mod.fm.bv.set_bpm (md.mod.fm.bv.bpm + x.bpm());
        cons << "modulator " << i << ", X BPM = " << md.mod.fm.bv.bpm << eol;
      }
    }
  } else if (&f == &y.bpm.f_value) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.sb->state) {
        md.mod.am.bv.set_bpm (md.mod.am.bv.bpm + y.bpm());
        cons << "modulator " << i << ", Y BPM = " << md.mod.am.bv.bpm << eol;
      }
    }
  } else {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.sb->state) {
        float s = scrub();
        modulator& mod = md.mod;
        mod.am.bv.now += s;
        mod.fm.bv.now += s;
        mod.scrubbed = 1;
      }
    }
  }
}

void point_modulator::clicked (button& b) {
  if (&b == &plus) {
    ed->modulate_point ();
  } else if (&b == &play) {
    if (nlst) {
      int g = 0;
      for (int i = 0; i < nlst; ++i) {
        mod_dat& md = lst[i];
        modulator& mod = md.mod;
        if (md.sb->state) {
          mod.active = !mod.active;
          if (mod.active) mod.t = ui_clk ();
          ++g;
        }
      }
      cons << GREEN << "Toggled " << g << " modulations" << eol;
    }
  } else if (&b == &kill) {
    remove ();
  } else if (&b == &all) {
    on_lst (_sel); set_ui ();
  } else if (&b == &invert) {
    on_lst (_togg); set_ui ();
  } else if (&b == &none) {
    on_lst (_desel); set_ui ();
  } else if (&b == &sync) {
    if (nlst) {
      int g = 0;
      for (int i = 0; i < nlst; ++i) {
        mod_dat& md = lst[i];
        modulator& mod = md.mod;
        if (md.sb->state) {
          mod.am.bv.now = mod.fm.bv.now = 0;
          ++g;
        }
      }
      cons << GREEN << "Synced " << g << " modulations" << eol;
    }
  } else if (&b == &fold) {
    if (fold.dir == arrow_button::down) {
      title.hide (widget::only_children);
      fold.show ();
      fold.set_dir (arrow_button::right);
      folded = 1;
    } else {
      title.show ();
      fold.set_dir (arrow_button::down);
      folded = 0;
    }
  }
}

void point_modulator::draw () {
  if (folded == 0) {
    DECLARE_WIDGET_ARRAY_wa
    for (int i = 0; i < NUM_WIDGETS; ++i) {
      widget* wi = wa[i];
      if (wi->visible) wi->draw ();
    }
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      state_button* sb = md.sb;
      if (sb->visible) sb->draw ();
    }
  } else {
    fold.draw ();
    title.draw ();
  }

  glEnable (GL_LINE_STIPPLE);
  glLineStipple (1, 0xf00f);
  for (int i = 0; i < nlst; ++i) {
    mod_dat& md = lst[i];
    state_button* sb = md.sb;
    const point<float>& p = md.hit.get ();
    int vx, vy; ed->obj2view (p.x, p.y, vx, vy);
    if (sb->state) glColor3f (0, 1, 0); else glColor3f (1, 0, 0);
    glBegin (GL_LINES);
      glVertex2i (vx, vy);
      glVertex2i (sb->extents.midx, sb->extents.midy);
    glEnd ();
  }
  glDisable (GL_LINE_STIPPLE);

}

void point_modulator::moved () {
  sbx = title.extents.left + nlst * state_button::SIZE2;
  sby = title.extents.bottom - title.extents.height;
  rearrange ();
}

void point_modulator::validate () {
  if (nlst) {
    on_lst (_desel);
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      state_button* sb = md.sb;
      if (md.hit(1)) // curve item exists?
        ;
      else
        sb->set_state (1);
    }
    remove ();
  }
}

int point_modulator::hit (hit_t& h) {
  if (nlst) {
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      if (md.hit == h) {
        state_button* sb = md.sb;
        sb->toggle ();
        return 1;
      }
    }
  }
  return 0;
}

void point_modulator::bg () {
  if (nlst) {
    int ncrvs = ed->curves;
    int evals [ncrvs];
    int nevals = 0;
    for (int i = 0; i < nlst; ++i) {
      mod_dat& md = lst[i];
      modulator& mod = md.mod;
      if (mod.active || mod.scrubbed) {
        mod.calc ();
        ed->move (md.hit, md.init.x + mod.fm.result, md.init.y + mod.am.result, 0);
        evals [md.hit.crv_id] = 1;
        ++nevals;
        mod.scrubbed = 0;
      }
    }
    if (nevals) {
      for (int i = 0, j = ncrvs; i < j; ++i) {
        if (evals[i]) {
          curve_info& ci = ed->curveinfo[i];
          multi_curve* crv = ci.curve;
          crv->evaluate ();
          ci.lisner->edited (ed, i);
        }
      }
    }
  }
}

void point_modulator::update_solvers (int k) {
  if (nlst) {
    if (k) {
      for (int i = 0; i < nlst; ++i) {
        mod_dat& md = lst[i];
        md.mod.am.bv.sol.update ();
      }
    } else {
      for (int i = 0; i < nlst; ++i) {
        mod_dat& md = lst[i];
        md.mod.fm.bv.sol.update ();
      }
    }
  }
}