Add pitch bend control

This patch adds the pitch bend controller and also a bit more
infrastructure for MIDI controllers in general.
master
Raph Levien 12 years ago
parent 64ad13bb4a
commit 0f0061fa19
  1. 30
      cpp/src/controllers.h
  2. 10
      cpp/src/dx7note.cc
  3. 6
      cpp/src/dx7note.h
  4. 16
      cpp/src/synth_unit.cc
  5. 6
      cpp/src/synth_unit.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

@ -22,6 +22,7 @@
#include "freqlut.h" #include "freqlut.h"
#include "patch.h" #include "patch.h"
#include "exp2.h" #include "exp2.h"
#include "controllers.h"
#include "dx7note.h" #include "dx7note.h"
using namespace std; using namespace std;
@ -178,13 +179,18 @@ void Dx7Note::init(const char patch[156], int midinote, int velocity) {
pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; 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(); int32_t pitchmod = pitchenv_.getsample();
uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32 uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32
// TODO: add modulation sources (mod wheel, etc) // TODO: add modulation sources (mod wheel, etc)
int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23));
pitchmod += (((int64_t)pmd) * (int64_t)senslfo) >> 39; 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++) { for (int op = 0; op < 6; op++) {
params_[op].gain[0] = params_[op].gain[1]; params_[op].gain[0] = params_[op].gain[1];
int32_t level = env_[op].getsample(); int32_t level = env_[op].getsample();

@ -29,14 +29,12 @@
class Dx7Note { class Dx7Note {
public: 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); void init(const char patch[128], int midinote, int velocity);
// Note: this _adds_ to the buffer. Interesting question whether it's // Note: this _adds_ to the buffer. Interesting question whether it's
// worth it... // 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(); void keyup();

@ -64,10 +64,11 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
} }
input_buffer_index_ = 0; input_buffer_index_ = 0;
memcpy(patch_data_, epiano, sizeof(epiano)); memcpy(patch_data_, epiano, sizeof(epiano));
current_patch_ = 0; ProgramChange(0);
current_note_ = 0; current_note_ = 0;
filter_control_[0] = 258847126; filter_control_[0] = 258847126;
filter_control_[1] = 0; filter_control_[1] = 0;
controllers_.values_[kControllerPitch] = 0x2000;
sustain_ = false; sustain_ = false;
extra_buf_size_ = 0; extra_buf_size_ = 0;
} }
@ -113,6 +114,10 @@ void SynthUnit::ProgramChange(int p) {
lfo_.reset(unpacked_patch_ + 137); 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) { int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
uint8_t cmd = buf[0]; uint8_t cmd = buf[0];
uint8_t cmd_type = cmd & 0xf0; uint8_t cmd_type = cmd & 0xf0;
@ -150,6 +155,8 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
return 0; return 0;
} else if (cmd_type == 0xb0) { } else if (cmd_type == 0xb0) {
if (buf_size >= 3) { if (buf_size >= 3) {
// controller
// TODO: move more logic into SetController
int controller = buf[1]; int controller = buf[1];
int value = buf[2]; int value = buf[2];
if (controller == 1) { if (controller == 1) {
@ -184,6 +191,10 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
return 2; return 2;
} }
return 0; return 0;
} else if (cmd == 0xe0) {
// pitch bend
SetController(kControllerPitch, buf[1] | (buf[2] << 7));
return 3;
} else if (cmd == 0xf0) { } else if (cmd == 0xf0) {
// sysex // sysex
if (buf_size >= 6 && buf[1] == 0x43 && buf[2] == 0x00 && buf[3] == 0x09 && 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(); int32_t lfodelay = lfo_.getdelay();
for (int note = 0; note < max_active_notes; ++note) { for (int note = 0; note < max_active_notes; ++note) {
if (active_note_[note].live) { 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() }; const int32_t *bufs[] = { audiobuf.get() };

@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include "controllers.h"
#include "dx7note.h" #include "dx7note.h"
#include "lfo.h" #include "lfo.h"
#include "ringbuffer.h" #include "ringbuffer.h"
@ -46,6 +47,8 @@ class SynthUnit {
// zero-based // zero-based
void ProgramChange(int p); void ProgramChange(int p);
void SetController(int controller, int value);
int ProcessMidiMessage(const uint8_t *buf, int buf_size); int ProcessMidiMessage(const uint8_t *buf, int buf_size);
RingBuffer *ring_buffer_; RingBuffer *ring_buffer_;
@ -63,6 +66,9 @@ class SynthUnit {
// The original DX7 had one single LFO. Later units had an LFO per note. // The original DX7 had one single LFO. Later units had an LFO per note.
Lfo lfo_; Lfo lfo_;
// in MIDI units (0x4000 is neutral)
Controllers controllers_;
ResoFilter filter_; ResoFilter filter_;
int32_t filter_control_[2]; int32_t filter_control_[2];
bool sustain_; bool sustain_;

Loading…
Cancel
Save