Subversion Repositories DIN Is Noise

Rev

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

/*
* triggered_note.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 "triggered_note.h"
#include "chrono.h"
#include "main.h"
#include "utils.h"
#include "color.h"
#include "just.h"

#include <cmath>

extern int SAMPLE_RATE;
extern float SAMPLE_DURATION;
extern float MIN_TIME;

triggered_note::triggered_note (const note& n, float vmax, float xx, float yy, int _what, int _bin, int _just, float _sep, int k, int pmx) {

  tn = n;

  start_hz = tn.hz;

  volume.now = 0;
  volume.max = vmax;

  state = ATTACK;
  abs_attack = 0;

  decay_time = 0;
  decay_start_volume = 0;
  abs_decay = 0;

  x = xx; y = yy;

  key = k;

  prev_mousex = pmx;
  bend = 1;
  bend_x = 0;

  what = _what;
  if (what == NOISE) {
    nsr.set_samples (SAMPLE_RATE / tn.hz);
    nsr.set_spread (volume.max);
    volume.mult = volume.max;
    r = 1.0f; g = r; b = g;
  } else {
    volume.mult = 1.0f;
    set_rnd_color (r, g, b);
  }

  binaural = _bin;
  just = _just;
  sep.hz = _sep;

}

void triggered_note::setup (multi_curve* wav, multi_curve* atk, multi_curve* dk) {

  sol (wav);
  player.set_wave (&sol);

  if (binaural) {

    sol2 (wav);
    player2.set_wave (&sol2);

    hz2step (sep.hz, sep.val);

    if (just == just::RANDOM) just = get_rand_bit ();
    if (just) { // R justified
      player2.fill_pitch (tn.step);
      player.fill_pitch (tn.step - sep.val);
    } else { // L justified
      player.fill_pitch (tn.step);
      player2.fill_pitch (tn.step + sep.val);
    }

  } else {
    player.fill_pitch (tn.step);
  }

  attack (atk);
  decay (dk);

  startt = ui_clk ();

}

void triggered_note::eval (float* L, float* R, float* wav, float* vol, int n, float attack_time, float decay_time, gotog& __gotog ) {


  float& vn_1 = vol[n-1];

  switch (state) {

    case ATTACK:

      if (equals (attack_time, 0.0f)) attack_time = MIN_TIME;
      delta_attack = SAMPLE_DURATION * 1.0f / attack_time;
      attack (abs_attack, delta_attack, n, vol, _atmin, __gotog); // solve attack curve to get bunch of volumes (range 0 to 1)

      if (what == NOTE) {
        for (int i = 0; i < n; ++i) vol[i] *= volume.max; // scale volume
        if (binaural) {
          player.master (L, wav, n, vol);
          player2.master (R, wav, n, vol);
        } else {
          player.master (L, R, wav, n, vol);
        }
        volume.now = vn_1;
      } else {
        nsr (L, R, n, vol);
        volume.now = vn_1 * volume.max;
      }

      decay_start_volume = vn_1;

      break;

    case DECAY:

      if (equals (decay_time, 0.0f)) decay_time = MIN_TIME;
      decay_time = fabs (decay_start_volume) * volume.mult / volume.max * decay_time;
      delta_decay = SAMPLE_DURATION * 1.0f / decay_time;
      decay (abs_decay, delta_decay, n, vol, _atmin, _atmax);
      for (int i = 0; i < n; ++i) vol[i] *= decay_start_volume;
      if (what == NOTE) {
        if (binaural) {
          player.master (L, wav, n, vol);
          player2.master (R, wav, n, vol);
        } else {
          player.master (L, R, wav, n, vol);
        }
        volume.now = vn_1;
      } else {
        nsr (L, R, n, vol);
        volume.now = vn_1 * volume.max;
      }

      float x, y; decay.mcrv->get_vertex (decay.mcrv->last_vertex, x, y);
      if (abs_decay >= x) state = FINISHED;

      break;

  }

}

void triggered_note::eval (float attack_time) {
  if (state == ATTACK) {
    float now = ui_clk ();
    float dt = now - startt;
    if (dt < attack_time) ; else start_decay ();
  }
}

void triggered_note::start_decay () {
  if (state == ATTACK) {
    state = DECAY;
    if (equals (volume.max, 0.0f) || equals (decay_start_volume, 0.0f))
      state = FINISHED;
    else
      abs_decay = 0;
  }
}

void triggered_note::set_freq (float f) {
  if (what == NOTE) {
    tn.set_freq (f);
    if (binaural) {
      if (just) {
        player2.set_interpolated_pitch (tn.step - sep.val);
        player.set_interpolated_pitch (tn.step);
      } else {
        player.set_interpolated_pitch (tn.step);
        player2.set_interpolated_pitch (tn.step + sep.val);
      }
    } else {
      player.set_interpolated_pitch (tn.step);
    }
  } else
    nsr.set_samples (SAMPLE_RATE / f);
}

int triggered_note::update_solver (multi_curve& crv, const string& nam) {

  sol.update ();
  if (binaural) sol2.update ();

  int ret = 0;
  if (crv.num_vertices) {
    player.set_mix (crv, nam);
    ret = player.mixer.active;
    if (binaural) {
      player2.set_mix (crv, nam);
      ret &= player2.mixer.active;
    }
  }
  return ret;
}

void update_triggered_notes (std::list<triggered_note>& tl) {
  for (note_iterator i = tl.begin (), j = tl.end (); i != j; ++i) {
    triggered_note& ti = *i;
    ti.player.realloc ();
  }
}

void update_triggered_noises (std::list<triggered_note>& tl) {
  for (note_iterator i = tl.begin (), j = tl.end (); i != j; ++i) {
    triggered_note& ti = *i;
    ti.nsr.warp (&noiser::interp);
  }
}