From 05355d7ef514aac9e5981c29cc66598be3d78cf6 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 30 May 2013 01:15:11 -0700 Subject: [PATCH] Pitch envelope Adds implementation of pitch envelope. Also fixes a problem with kLevelThresh. --- android/jni/Android.mk | 1 + cpp/src/core.gyp | 1 + cpp/src/dx7note.cc | 18 ++++++--- cpp/src/dx7note.h | 3 ++ cpp/src/fm_core.cc | 2 + cpp/src/pitchenv.cc | 89 ++++++++++++++++++++++++++++++++++++++++++ cpp/src/pitchenv.h | 48 +++++++++++++++++++++++ 7 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 cpp/src/pitchenv.cc create mode 100644 cpp/src/pitchenv.h diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 676b606..c4a9db2 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -11,6 +11,7 @@ LOCAL_SRC_FILES := android_glue.cc \ fm_op_kernel.cc \ freqlut.cc \ patch.cc \ + pitchenv.cc \ resofilter.cc \ ringbuffer.cc \ sawtooth.cc \ diff --git a/cpp/src/core.gyp b/cpp/src/core.gyp index cd0f8d1..7c6570d 100644 --- a/cpp/src/core.gyp +++ b/cpp/src/core.gyp @@ -12,6 +12,7 @@ 'freqlut.cc', 'lfo.cc', 'patch.cc', + 'pitchenv.cc', 'resofilter.cc', 'ringbuffer.cc', 'sawtooth.cc', diff --git a/cpp/src/dx7note.cc b/cpp/src/dx7note.cc index bddab1e..805b7b5 100644 --- a/cpp/src/dx7note.cc +++ b/cpp/src/dx7note.cc @@ -58,8 +58,7 @@ int32_t osc_freq(int midinote, int mode, int coarse, int fine, int 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; + return logfreq; } const uint8_t velocity_data[64] = { @@ -127,10 +126,10 @@ int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, void Dx7Note::init(const char bulk[128], int midinote, int velocity) { char patch[156]; UnpackPatch(bulk, patch); // TODO: move this out, take unpacked patch + int rates[4]; + int levels[4]; for (int op = 0; op < 6; op++) { int off = op * 21; - int rates[4]; - int levels[4]; for (int i = 0; i < 4; i++) { rates[i] = patch[off + i]; levels[i] = patch[off + 4 + i]; @@ -160,22 +159,30 @@ void Dx7Note::init(const char bulk[128], int midinote, int velocity) { int fine = patch[off + 19]; int detune = patch[off + 20]; int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); - params_[op].freq = freq; + basepitch_[op] = freq; // cout << op << " freq: " << freq << endl; params_[op].phase = 0; params_[op].gain[1] = 0; } + for (int i = 0; i < 4; i++) { + rates[i] = patch[126 + i]; + levels[i] = patch[130 + i]; + } + pitchenv_.init(rates, levels); algorithm_ = patch[134]; int feedback = patch[135]; fb_shift_ = feedback != 0 ? 8 - feedback : 16; } void Dx7Note::compute(int32_t *buf) { + int32_t pitchenvlevel = pitchenv_.getsample(); for (int op = 0; op < 6; op++) { params_[op].gain[0] = params_[op].gain[1]; int32_t level = env_[op].getsample(); int32_t gain = Exp2::lookup(level - (14 * (1 << 24))); //int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24))); + // TODO: probably don't apply pitch env to fixed freq + params_[op].freq = Freqlut::lookup(basepitch_[op] + pitchenvlevel); params_[op].gain[1] = gain; } core_.compute(buf, params_, algorithm_, fb_buf_, fb_shift_); @@ -184,6 +191,7 @@ void Dx7Note::compute(int32_t *buf) { void Dx7Note::keyup() { for (int op = 0; op < 6; op++) { env_[op].keydown(false); + pitchenv_.keydown(false); } } diff --git a/cpp/src/dx7note.h b/cpp/src/dx7note.h index da0bb1d..8e12159 100644 --- a/cpp/src/dx7note.h +++ b/cpp/src/dx7note.h @@ -24,6 +24,7 @@ // and real-time control of parameters live here. #include "env.h" +#include "pitchenv.h" #include "fm_core.h" class Dx7Note { @@ -50,6 +51,8 @@ class Dx7Note { FmCore core_; Env env_[6]; FmOpParams params_[6]; + PitchEnv pitchenv_; + int32_t basepitch_[6]; int32_t fb_buf_[2]; int32_t fb_shift_; diff --git a/cpp/src/fm_core.cc b/cpp/src/fm_core.cc index 3c95cb0..52383ac 100644 --- a/cpp/src/fm_core.cc +++ b/cpp/src/fm_core.cc @@ -143,6 +143,8 @@ void FmCore::compute(int32_t *output, FmOpParams *params, int algorithm, param.phase, param.freq, gain1, gain2, add); } has_contents[outbus] = true; + } else if (!add) { + has_contents[outbus] = false; } param.phase += param.freq << LG_N; } diff --git a/cpp/src/pitchenv.cc b/cpp/src/pitchenv.cc new file mode 100644 index 0000000..146421f --- /dev/null +++ b/cpp/src/pitchenv.cc @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "synth.h" +#include "pitchenv.h" + +using namespace std; + +void PitchEnv::init(const int r[4], const int l[4]) { + for (int i = 0; i < 4; i++) { + rates_[i] = r[i]; + levels_[i] = l[i]; + } + level_ = 0; + down_ = true; + advance(0); +} + +int32_t PitchEnv::getsample() { + if (ix_ < 3 || (ix_ < 4) && !down_) { + if (rising_) { + level_ += inc_; + if (level_ >= targetlevel_) { + level_ = targetlevel_; + advance(ix_ + 1); + } + } else { // !rising + level_ -= inc_; + if (level_ <= targetlevel_) { + level_ = targetlevel_; + advance(ix_ + 1); + } + } + } + return level_; +} + +void PitchEnv::keydown(bool d) { + if (down_ != d) { + down_ = d; + advance(d ? 0 : 3); + } +} + +static uint8_t ratetab[] = { + 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, + 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 30, 31, 33, 34, 36, 37, 38, 39, 41, 42, 44, 46, 47, + 49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 79, 82, + 85, 88, 91, 94, 98, 102, 106, 110, 115, 120, 125, 130, 135, 141, 147, + 153, 159, 165, 171, 178, 185, 193, 202, 211, 232, 243, 254, 255 +}; + +static int8_t pitchtab[] = { + -128, -116, -104, -95, -85, -76, -68, -61, -56, -52, -49, -46, -43, + -41, -39, -37, -35, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, + -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, + -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 38, 40, 43, 46, 49, 53, 58, 65, 73, + 82, 92, 103, 115, 127 +}; + +void PitchEnv::advance(int newix) { + ix_ = newix; + if (ix_ < 4) { + int newlevel = levels_[ix_]; + targetlevel_ = pitchtab[newlevel] << 19; + rising_ = (targetlevel_ > level_); + + // TODO: adapt to sample rate + // const is 1<<12 / 0.0104 * 44100 + inc_ = ratetab[rates_[ix_]] * (8 * N); + } +} + diff --git a/cpp/src/pitchenv.h b/cpp/src/pitchenv.h new file mode 100644 index 0000000..65893db --- /dev/null +++ b/cpp/src/pitchenv.h @@ -0,0 +1,48 @@ +/* + * Copyright 2013 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PITCHENV_H +#define __PITCHENV_H + +// Computation of the DX7 pitch envelope + +class PitchEnv { + public: + + // The rates and levels arrays are calibrated to match the Dx7 parameters + // (ie, value 0..99). + void init(const int rates[4], const int levels[4]); + + // Result is in Q24/octave + int32_t getsample(); + + void keydown(bool down); + private: + int rates_[4]; + int levels_[4]; + int32_t level_; + int targetlevel_; + bool rising_; + int ix_; + int inc_; + + bool down_; + + void advance(int newix); +}; + +#endif // __PITCHENV_H +