Add sustain and note stealing

More sophisticated note stealing (better than round robin) and also
implementation of sustain (on midi controller 64). This implementation
matches the DX7 precisely.
bklimt
Raph Levien 12 years ago
parent 904ff23afc
commit 31f14b5de5
  1. 2
      cpp/src/core.xcodeproj/project.pbxproj
  2. 44
      cpp/src/synth_unit.cc
  3. 7
      cpp/src/synth_unit.h

@ -22,7 +22,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
2082841A11DF6E62596265CF /* synth_unit.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = synth_unit.cc; sourceTree = "<group>"; }; 2082841A11DF6E62596265CF /* synth_unit.cc */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = synth_unit.cc; sourceTree = "<group>"; tabWidth = 2; };
48B6535400CF3AC8BABB3299 /* sawtooth.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sawtooth.cc; sourceTree = "<group>"; }; 48B6535400CF3AC8BABB3299 /* sawtooth.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sawtooth.cc; sourceTree = "<group>"; };
509D811344DB98984FD6C126 /* fm_op_kernel.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fm_op_kernel.cc; sourceTree = "<group>"; }; 509D811344DB98984FD6C126 /* fm_op_kernel.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = fm_op_kernel.cc; sourceTree = "<group>"; };
521793C71CAA078F5598EFC0 /* test_ringbuffer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_ringbuffer.cc; sourceTree = "<group>"; }; 521793C71CAA078F5598EFC0 /* test_ringbuffer.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_ringbuffer.cc; sourceTree = "<group>"; };

@ -46,6 +46,7 @@ SynthUnit::SynthUnit(RingBuffer *ring_buffer) {
current_note_ = 0; current_note_ = 0;
filter_control_[0] = 258847126; filter_control_[0] = 258847126;
filter_control_[1] = 0; filter_control_[1] = 0;
sustain_ = false;
} }
// Transfer as many bytes as possible from ring buffer to input buffer. // Transfer as many bytes as possible from ring buffer to input buffer.
@ -70,6 +71,18 @@ void SynthUnit::ConsumeInput(int n_input_bytes) {
input_buffer_index_ -= n_input_bytes; input_buffer_index_ -= n_input_bytes;
} }
int SynthUnit::AllocateNote() {
int note = current_note_;
for (int i = 0; i < max_active_notes; i++) {
if (!active_note_[note].keydown) {
current_note_ = (note + 1) % max_active_notes;
return note;
}
note = (note + 1) % max_active_notes;
}
return -1;
}
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;
@ -78,9 +91,14 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
// note off // note off
for (int note = 0; note < max_active_notes; ++note) { for (int note = 0; note < max_active_notes; ++note) {
if (active_note_[note].midi_note == buf[1] && if (active_note_[note].midi_note == buf[1] &&
active_note_[note].dx7_note != NULL) { active_note_[note].keydown) {
if (sustain_) {
active_note_[note].sustained = true;
} else {
active_note_[note].dx7_note->keyup(); active_note_[note].dx7_note->keyup();
} }
active_note_[note].keydown = false;
}
} }
return 3; return 3;
} }
@ -88,14 +106,16 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
} else if (cmd_type == 0x90) { } else if (cmd_type == 0x90) {
if (buf_size >= 3) { if (buf_size >= 3) {
// note on // note on
// Note that this implements round-robin (same as original Dx7). A more int note_ix = AllocateNote();
// sophisticated note-stealing algorithm is probably worthwhile. if (note_ix >= 0) {
delete active_note_[current_note_].dx7_note; delete active_note_[note_ix].dx7_note;
active_note_[current_note_].midi_note = buf[1]; active_note_[note_ix].midi_note = buf[1];
active_note_[note_ix].keydown = true;
active_note_[note_ix].sustained = sustain_;
const uint8_t *patch = patch_data_ + 128 * current_patch_; const uint8_t *patch = patch_data_ + 128 * current_patch_;
active_note_[current_note_].dx7_note = active_note_[note_ix].dx7_note =
new Dx7Note((const char *)patch, buf[1], buf[2]); new Dx7Note((const char *)patch, buf[1], buf[2]);
current_note_ = (current_note_ + 1) % max_active_notes; }
return 3; return 3;
} }
return 0; return 0;
@ -107,6 +127,16 @@ int SynthUnit::ProcessMidiMessage(const uint8_t *buf, int buf_size) {
filter_control_[0] = 129423563 + value * 1019083; filter_control_[0] = 129423563 + value * 1019083;
} else if (controller == 2) { } else if (controller == 2) {
filter_control_[1] = value * 528416; filter_control_[1] = value * 528416;
} else if (controller == 64) {
sustain_ = value != 0;
if (!sustain_) {
for (int note = 0; note < max_active_notes; note++) {
if (active_note_[note].sustained && !active_note_[note].keydown) {
active_note_[note].dx7_note->keyup();
active_note_[note].sustained = false;
}
}
}
} }
return 3; return 3;
} return 0; } return 0;

@ -20,6 +20,8 @@
struct ActiveNote { struct ActiveNote {
int midi_note; int midi_note;
bool keydown;
bool sustained;
Dx7Note *dx7_note; Dx7Note *dx7_note;
}; };
@ -33,6 +35,10 @@ class SynthUnit {
void ConsumeInput(int n_input_bytes); void ConsumeInput(int n_input_bytes);
// Choose a note for a new key-down, returns note number, or -1 if
// none available.
int AllocateNote();
int ProcessMidiMessage(const uint8_t *buf, int buf_size); int ProcessMidiMessage(const uint8_t *buf, int buf_size);
RingBuffer *ring_buffer_; RingBuffer *ring_buffer_;
@ -47,4 +53,5 @@ class SynthUnit {
ResoFilter filter_; ResoFilter filter_;
int32_t filter_control_[2]; int32_t filter_control_[2];
bool sustain_;
}; };

Loading…
Cancel
Save