From 79cc5e39fd794e750094463188363721bf83b1f2 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 29 May 2013 22:49:29 -0700 Subject: [PATCH] Add detune, other note improvements Finally implements detune (using an approximation, but better than nothing). Also minor improvements to note generation, including scaling output level more accurately, and getting rid of dynamic allocations of note objects. --- cpp/src/dx7note.cc | 14 ++++++++------ cpp/src/dx7note.h | 2 +- cpp/src/env.cc | 11 +++++++---- cpp/src/env.h | 1 + cpp/src/fm_core.cc | 3 ++- cpp/src/synth_unit.cc | 11 ++++++----- cpp/src/synth_unit.h | 1 + 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/cpp/src/dx7note.cc b/cpp/src/dx7note.cc index 9e775a7..bddab1e 100644 --- a/cpp/src/dx7note.cc +++ b/cpp/src/dx7note.cc @@ -50,11 +50,13 @@ int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) { // (1 << 24) / log(2) logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5); } - // TODO: detune + // This was measured at 7.213Hz per count at 9600Hz, but the exact + // value is somewhat dependent on midinote. Close enough for now. + logfreq += 12606 * (detune - 7); } else { // ((1 << 24) * log(10) / log(2) * .01) << 3 - logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; - // TODO: detune + logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; + logfreq += detune > 7 ? 13457 * (detune - 7) : 0; } int32_t base_freq = Freqlut::lookup(logfreq); return base_freq; @@ -122,8 +124,7 @@ int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, } } -// Considering making this an init method... -Dx7Note::Dx7Note(const char bulk[128], int midinote, int velocity) { +void Dx7Note::init(const char bulk[128], int midinote, int velocity) { char patch[156]; UnpackPatch(bulk, patch); // TODO: move this out, take unpacked patch for (int op = 0; op < 6; op++) { @@ -135,6 +136,7 @@ Dx7Note::Dx7Note(const char bulk[128], int midinote, int velocity) { levels[i] = patch[off + 4 + i]; } int outlevel = patch[off + 16]; + outlevel = Env::scaleoutlevel(outlevel); #ifdef VERBOSE for (int j = 8; j < 12; j++) { cout << (int)patch[off + j] << " "; @@ -143,7 +145,7 @@ Dx7Note::Dx7Note(const char bulk[128], int midinote, int velocity) { int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], patch[off + 10], patch[off + 11], patch[off + 12]); outlevel += level_scaling; - outlevel = min(99, outlevel); + outlevel = min(127, outlevel); #ifdef VERBOSE cout << op << ": " << level_scaling << " " << outlevel << endl; #endif diff --git a/cpp/src/dx7note.h b/cpp/src/dx7note.h index 34bb00b..da0bb1d 100644 --- a/cpp/src/dx7note.h +++ b/cpp/src/dx7note.h @@ -31,7 +31,7 @@ class Dx7Note { // Interesting question: should the setup be in the constructor, or should // there be an init method? The latter would make it easier to use a fixed // pool of note objects. - Dx7Note(const char patch[128], int midinote, int velocity); + void init(const char patch[128], int midinote, int velocity); // Note: this _adds_ to the buffer. Interesting question whether it's // worth it... diff --git a/cpp/src/env.cc b/cpp/src/env.cc index 65b736c..cb9e335 100644 --- a/cpp/src/env.cc +++ b/cpp/src/env.cc @@ -73,16 +73,19 @@ void Env::setparam(int param, int value) { } const int levellut[] = { - 0, 2, 4, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 + 0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 }; +int Env::scaleoutlevel(int outlevel) { + return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; +} + void Env::advance(int newix) { ix_ = newix; if (ix_ < 4) { int newlevel = levels_[ix_]; - int actuallevel = newlevel >= 19 ? - 14 + (newlevel >> 1) : levellut[newlevel]; - actuallevel = (actuallevel << 6) + outlevel_ - 3360; + int actuallevel = scaleoutlevel(newlevel) >> 1; + actuallevel = (actuallevel << 6) + outlevel_ - 4256; actuallevel = actuallevel < 16 ? 16 : actuallevel; // level here is same as Java impl targetlevel_ = actuallevel << 16; diff --git a/cpp/src/env.h b/cpp/src/env.h index 20e2d5c..d76dc06 100644 --- a/cpp/src/env.h +++ b/cpp/src/env.h @@ -39,6 +39,7 @@ class Env { void keydown(bool down); void setparam(int param, int value); + static int scaleoutlevel(int outlevel); private: int rates_[4]; int levels_[4]; diff --git a/cpp/src/fm_core.cc b/cpp/src/fm_core.cc index a6ebe1d..3c95cb0 100644 --- a/cpp/src/fm_core.cc +++ b/cpp/src/fm_core.cc @@ -109,6 +109,7 @@ void FmCore::dump() { void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) { + const int kLevelThresh = 1120; const FmAlgorithm alg = algorithms[algorithm]; bool has_contents[3] = { true, false, false }; for (int op = 0; op < 6; op++) { @@ -120,7 +121,7 @@ void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); int32_t gain1 = param.gain[0]; int32_t gain2 = param.gain[1]; - if (gain1 != 0 || gain2 != 0) { + if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) { if (!has_contents[outbus]) { add = false; } diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc index f024d0a..946150c 100644 --- a/cpp/src/synth_unit.cc +++ b/cpp/src/synth_unit.cc @@ -44,9 +44,10 @@ char epiano[] = { SynthUnit::SynthUnit(RingBuffer *ring_buffer) { ring_buffer_ = ring_buffer; for (int note = 0; note < max_active_notes; ++note) { - active_note_[note].dx7_note = NULL; + active_note_[note].dx7_note = new Dx7Note; active_note_[note].keydown = false; active_note_[note].sustained = false; + active_note_[note].live = false; } input_buffer_index_ = 0; memcpy(patch_data_, epiano, sizeof(epiano)); @@ -117,13 +118,13 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { // note on int note_ix = AllocateNote(); if (note_ix >= 0) { - delete active_note_[note_ix].dx7_note; active_note_[note_ix].midi_note = buf[1]; active_note_[note_ix].keydown = true; active_note_[note_ix].sustained = sustain_; + active_note_[note_ix].live = true; const uint8_t *patch = patch_data_ + 128 * current_patch_; - active_note_[note_ix].dx7_note = - new Dx7Note((const char *)patch, buf[1], buf[2]); + active_note_[note_ix].dx7_note->init( + (const char *)patch, buf[1], buf[2]); } return 3; } @@ -218,7 +219,7 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) { audiobuf.get()[j] = 0; } for (int note = 0; note < max_active_notes; ++note) { - if (active_note_[note].dx7_note != NULL) { + if (active_note_[note].live) { active_note_[note].dx7_note->compute(audiobuf.get()); } } diff --git a/cpp/src/synth_unit.h b/cpp/src/synth_unit.h index 31cea4f..a484000 100644 --- a/cpp/src/synth_unit.h +++ b/cpp/src/synth_unit.h @@ -22,6 +22,7 @@ struct ActiveNote { int midi_note; bool keydown; bool sustained; + bool live; Dx7Note *dx7_note; };