From 0f0061fa199834b575f11ac57a1d65b7aee29c70 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 3 Jun 2013 00:05:23 -0700 Subject: [PATCH] Add pitch bend control This patch adds the pitch bend controller and also a bit more infrastructure for MIDI controllers in general. --- cpp/src/controllers.h | 30 ++++++++++++++++++++++++++++++ cpp/src/dx7note.cc | 10 ++++++++-- cpp/src/dx7note.h | 6 ++---- cpp/src/synth_unit.cc | 16 ++++++++++++++-- cpp/src/synth_unit.h | 6 ++++++ 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 cpp/src/controllers.h diff --git a/cpp/src/controllers.h b/cpp/src/controllers.h new file mode 100644 index 0000000..b63d7ed --- /dev/null +++ b/cpp/src/controllers.h @@ -0,0 +1,30 @@ +/* + * 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 __CONTROLLERS_H +#define __CONTROLLERS_H + +// State of MIDI controllers + +const int kControllerPitch = 128; + +class Controllers { + public: + int values_[129]; +}; + +#endif // __CONTROLLERS_H + diff --git a/cpp/src/dx7note.cc b/cpp/src/dx7note.cc index dc33e90..88364cf 100644 --- a/cpp/src/dx7note.cc +++ b/cpp/src/dx7note.cc @@ -22,6 +22,7 @@ #include "freqlut.h" #include "patch.h" #include "exp2.h" +#include "controllers.h" #include "dx7note.h" using namespace std; @@ -178,13 +179,18 @@ void Dx7Note::init(const char patch[156], int midinote, int velocity) { pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; } -void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay) { +void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, + const Controllers *ctrls) { int32_t pitchmod = pitchenv_.getsample(); uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32 // TODO: add modulation sources (mod wheel, etc) int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39; - // TODO: add pitch bend + + // hardcodes a pitchbend range of 3 semitones, TODO make configurable + int pitchbend = ctrls->values_[kControllerPitch]; + int32_t pb = (pitchbend - 0x2000) << 9; + pitchmod += pb; for (int op = 0; op < 6; op++) { params_[op].gain[0] = params_[op].gain[1]; int32_t level = env_[op].getsample(); diff --git a/cpp/src/dx7note.h b/cpp/src/dx7note.h index 947967a..9621309 100644 --- a/cpp/src/dx7note.h +++ b/cpp/src/dx7note.h @@ -29,14 +29,12 @@ class Dx7Note { public: - // 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. void init(const char patch[128], int midinote, int velocity); // Note: this _adds_ to the buffer. Interesting question whether it's // worth it... - void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay); + void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, + const Controllers *ctrls); void keyup(); diff --git a/cpp/src/synth_unit.cc b/cpp/src/synth_unit.cc index f2b3ec1..6dafc9f 100644 --- a/cpp/src/synth_unit.cc +++ b/cpp/src/synth_unit.cc @@ -64,10 +64,11 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) { } input_buffer_index_ = 0; memcpy(patch_data_, epiano, sizeof(epiano)); - current_patch_ = 0; + ProgramChange(0); current_note_ = 0; filter_control_[0] = 258847126; filter_control_[1] = 0; + controllers_.values_[kControllerPitch] = 0x2000; sustain_ = false; extra_buf_size_ = 0; } @@ -113,6 +114,10 @@ void SynthUnit::ProgramChange(int p) { lfo_.reset(unpacked_patch_ + 137); } +void SynthUnit::SetController(int controller, int value) { + controllers_.values_[controller] = value; +} + int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { uint8_t cmd = buf[0]; uint8_t cmd_type = cmd & 0xf0; @@ -150,6 +155,8 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { return 0; } else if (cmd_type == 0xb0) { if (buf_size >= 3) { + // controller + // TODO: move more logic into SetController int controller = buf[1]; int value = buf[2]; if (controller == 1) { @@ -184,6 +191,10 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) { return 2; } return 0; + } else if (cmd == 0xe0) { + // pitch bend + SetController(kControllerPitch, buf[1] | (buf[2] << 7)); + return 3; } else if (cmd == 0xf0) { // sysex if (buf_size >= 6 && buf[1] == 0x43 && buf[2] == 0x00 && buf[3] == 0x09 && @@ -242,7 +253,8 @@ void SynthUnit::GetSamples(int n_samples, int16_t *buffer) { int32_t lfodelay = lfo_.getdelay(); for (int note = 0; note < max_active_notes; ++note) { if (active_note_[note].live) { - active_note_[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay); + active_note_[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, + &controllers_); } } const int32_t *bufs[] = { audiobuf.get() }; diff --git a/cpp/src/synth_unit.h b/cpp/src/synth_unit.h index 94c0347..597345f 100644 --- a/cpp/src/synth_unit.h +++ b/cpp/src/synth_unit.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "controllers.h" #include "dx7note.h" #include "lfo.h" #include "ringbuffer.h" @@ -46,6 +47,8 @@ class SynthUnit { // zero-based void ProgramChange(int p); + void SetController(int controller, int value); + int ProcessMidiMessage(const uint8_t *buf, int buf_size); RingBuffer *ring_buffer_; @@ -63,6 +66,9 @@ class SynthUnit { // The original DX7 had one single LFO. Later units had an LFO per note. Lfo lfo_; + // in MIDI units (0x4000 is neutral) + Controllers controllers_; + ResoFilter filter_; int32_t filter_control_[2]; bool sustain_;