diff --git a/mopo/src/arpeggiator.cpp b/mopo/src/arpeggiator.cpp index 1aa77866c0..f5517607f3 100644 --- a/mopo/src/arpeggiator.cpp +++ b/mopo/src/arpeggiator.cpp @@ -55,18 +55,19 @@ namespace mopo { } // Check if it's time to play the next note. + // FIXME: handle channel if (getNumNotes() && new_phase >= 1) { int offset = utils::iclamp((1 - phase_) / delta_phase, 0, buffer_size_ - 1); - std::pair note = getNextNote(); - note_handler_->noteOn(note.first, note.second, offset); - last_played_note_ = note.first; + std::tuple note = getNextNote(); + note_handler_->noteOn(std::get<0>(note), std::get<1>(note), offset, std::get<2>(note), std::get<3>(note)); + last_played_note_ = std::get<0>(note); phase_ = new_phase - 1.0; } else phase_ = new_phase; } - std::pair Arpeggiator::getNextNote() { + std::tuple Arpeggiator::getNextNote() { int octaves = utils::imax(1, input(kOctaves)->at(0)); Pattern type = static_cast(static_cast(input(kPattern)->at(0))); @@ -115,7 +116,10 @@ namespace mopo { mopo_float base_note = pattern->at(note_index_); mopo_float note = base_note + mopo::NOTES_PER_OCTAVE * current_octave_; mopo_float velocity = active_notes_[base_note]; - return std::pair(note, velocity); + int channel = channel_[base_note]; + mopo_float aftertouch = aftertouch_[base_note]; + + return std::tuple(note, velocity, channel, aftertouch); } CircularQueue& Arpeggiator::getPressedNotes() { @@ -152,6 +156,8 @@ namespace mopo { void Arpeggiator::allNotesOff(int sample) { active_notes_.clear(); + channel_.clear(); + aftertouch_.clear(); pressed_notes_.clear(); sustained_notes_.clear(); ascending_.clear(); @@ -160,7 +166,7 @@ namespace mopo { note_handler_->allNotesOff(); } - void Arpeggiator::noteOn(mopo_float note, mopo_float velocity, int sample, int channel) { + void Arpeggiator::noteOn(mopo_float note, mopo_float velocity, int sample, int channel, mopo_float aftertouch) { if (active_notes_.count(note)) return; if (pressed_notes_.size() == 0) { @@ -169,6 +175,8 @@ namespace mopo { phase_ = 1.0; } active_notes_[note] = velocity; + channel_[note] = channel; + aftertouch_[note] = aftertouch; pressed_notes_.push_back(note); addNoteToPatterns(note); } @@ -181,10 +189,30 @@ namespace mopo { sustained_notes_.push_back(note); else { active_notes_.erase(note); + channel_.erase(note); + aftertouch_.erase(note); removeNoteFromPatterns(note); } pressed_notes_.remove(note); return kVoiceOff; } + + void Arpeggiator::setAftertouch(mopo_float note, mopo_float aftertouch, int sample) { + // TODO: take channel into account + for (const auto &n : pressed_notes_) { + if (n == note) { + aftertouch_[n] = aftertouch; + } + } + } + + void Arpeggiator::setPressure(mopo_float pressure, int channel, int sample) { + MOPO_ASSERT(channel >= 0 && channel <= mopo::NUM_MIDI_CHANNELS); + for (const auto &n : pressed_notes_) { + if (channel_[n] == channel) { + aftertouch_[n] = pressure; + } + } + } } // namespace mopo diff --git a/mopo/src/arpeggiator.h b/mopo/src/arpeggiator.h index 699ef873e9..52e8652142 100644 --- a/mopo/src/arpeggiator.h +++ b/mopo/src/arpeggiator.h @@ -61,16 +61,21 @@ namespace mopo { int getNumNotes() { return pressed_notes_.size(); } CircularQueue& getPressedNotes(); - std::pair getNextNote(); + // tuple: note, velocity, channel, aftertouch + std::tuple getNextNote(); void addNoteToPatterns(mopo_float note); void removeNoteFromPatterns(mopo_float note); void allNotesOff(int sample = 0) override; void noteOn(mopo_float note, mopo_float velocity = 1, - int sample = 0, int channel = 0) override; + int sample = 0, int channel = 0, mopo_float aftertouch = 0) override; VoiceEvent noteOff(mopo_float note, int sample = 0) override; void sustainOn(); void sustainOff(); + + void setAftertouch(mopo_float note, mopo_float aftertouch, int sample = 0); + void setPressure(mopo_float pressure, int channel = 0, int sample = 0); + private: Arpeggiator() : Processor(0, 0) { } @@ -89,8 +94,12 @@ namespace mopo { std::vector decending_; std::map active_notes_; + std::map channel_; + std::map aftertouch_; + CircularQueue pressed_notes_; CircularQueue sustained_notes_; + }; } // namespace mopo diff --git a/mopo/src/note_handler.h b/mopo/src/note_handler.h index a99ed32fef..db724aac62 100644 --- a/mopo/src/note_handler.h +++ b/mopo/src/note_handler.h @@ -27,7 +27,7 @@ namespace mopo { virtual ~NoteHandler() { } virtual void allNotesOff(int sample = 0) = 0; virtual void noteOn(mopo_float note, mopo_float velocity = 1, - int sample = 0, int channel = 0) = 0; + int sample = 0, int channel = 0, mopo_float aftertouch = 0) = 0; virtual VoiceEvent noteOff(mopo_float note, int sample = 0) = 0; }; } // namespace mopo diff --git a/mopo/src/voice_handler.cpp b/mopo/src/voice_handler.cpp index ef3b664afa..78d50bcb59 100644 --- a/mopo/src/voice_handler.cpp +++ b/mopo/src/voice_handler.cpp @@ -290,7 +290,7 @@ namespace mopo { return 0; } - void VoiceHandler::noteOn(mopo_float note, mopo_float velocity, int sample, int channel) { + void VoiceHandler::noteOn(mopo_float note, mopo_float velocity, int sample, int channel, mopo_float aftertouch) { MOPO_ASSERT(sample >= 0 && sample < buffer_size_); MOPO_ASSERT(channel >= 0 && channel < NUM_MIDI_CHANNELS); @@ -299,7 +299,7 @@ namespace mopo { if (last_played_note_ < 0) last_played_note_ = note; - voice->activate(note, velocity, last_played_note_, pressed_notes_.size(), sample, channel); + voice->activate(note, velocity, last_played_note_, pressed_notes_.size(), sample, channel, aftertouch); active_voices_.push_back(voice); last_played_note_ = note; } @@ -323,7 +323,7 @@ namespace mopo { pressed_notes_.pop_back(); pressed_notes_.push_front(old_note); new_voice->activate(old_note, voice->state().velocity, last_played_note_, - pressed_notes_.size() + 1, sample); + pressed_notes_.size() + 1, sample); last_played_note_ = old_note; voice_event = kVoiceReset; @@ -342,6 +342,14 @@ namespace mopo { voice->setAftertouch(aftertouch, sample); } } + + void VoiceHandler::setPressure(mopo_float pressure, int channel, int sample) { + MOPO_ASSERT(channel >= 0 && channel <= mopo::NUM_MIDI_CHANNELS); + for (Voice* voice : active_voices_) { + if (voice->state().channel == channel) + voice->setAftertouch(pressure, sample); + } + } void VoiceHandler::setPolyphony(size_t polyphony) { while (all_voices_.size() < polyphony) { diff --git a/mopo/src/voice_handler.h b/mopo/src/voice_handler.h index a9e1001bb2..6350ddece8 100644 --- a/mopo/src/voice_handler.h +++ b/mopo/src/voice_handler.h @@ -59,7 +59,7 @@ namespace mopo { void activate(mopo_float note, mopo_float velocity, mopo_float last_note, int note_pressed = 0, - int sample = 0, int channel = 0) { + int sample = 0, int channel = 0, mopo_float aftertouch = 0) { event_sample_ = sample; state_.event = kVoiceOn; state_.note = note; @@ -67,7 +67,7 @@ namespace mopo { state_.last_note = last_note; state_.note_pressed = note_pressed; state_.channel = channel; - aftertouch_ = velocity; + aftertouch_ = aftertouch; aftertouch_sample_ = sample; key_state_ = kHeld; } @@ -143,9 +143,10 @@ namespace mopo { void allNotesOff(int sample = 0) override; virtual void noteOn(mopo_float note, mopo_float velocity = 1, - int sample = 0, int channel = 0) override; + int sample = 0, int channel = 0, mopo_float aftertouch = 0) override; virtual VoiceEvent noteOff(mopo_float note, int sample = 0) override; void setAftertouch(mopo_float note, mopo_float aftertouch, int sample = 0); + void setPressure(mopo_float pressure, int channel = 0, int sample = 0); void sustainOn(); void sustainOff(int sample = 0); diff --git a/src/common/midi_manager.cpp b/src/common/midi_manager.cpp index 3539343919..6724b43729 100644 --- a/src/common/midi_manager.cpp +++ b/src/common/midi_manager.cpp @@ -91,7 +91,7 @@ void MidiManager::removeNextBlockOfMessages(MidiBuffer& buffer, int num_samples) midi_collector_.removeNextBlockOfMessages(buffer, num_samples); } -void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample_position) { +void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample_position) { if (midi_message.isProgramChange()) { current_patch_ = midi_message.getProgramChangeNumber(); File patch = LoadSave::loadPatch(current_bank_, current_folder_, current_patch_, @@ -114,11 +114,20 @@ void MidiManager::processMidiMessage(const MidiMessage& midi_message, int sample engine_->sustainOn(); else if (midi_message.isSustainPedalOff()) engine_->sustainOff(); + // "aftertouch" is pressure per note else if (midi_message.isAftertouch()) { mopo::mopo_float note = midi_message.getNoteNumber(); mopo::mopo_float value = (1.0 * midi_message.getAfterTouchValue()) / mopo::MIDI_SIZE; engine_->setAftertouch(note, value); } + // "pressure" is aftertouch for a whole channel (e.g. keyboard send only one value even if several keys are pressed) + // TODO: create a separate modifier + else if (midi_message.isChannelPressure()) { + mopo::mopo_float note = midi_message.getNoteNumber(); + mopo::mopo_float value = (1.0 * midi_message.getChannelPressureValue()) / (mopo::MIDI_SIZE - 1.0); + // channel - 1 as with NoteOn above + engine_->setPressure(value, midi_message.getChannel() - 1, sample_position); + } else if (midi_message.isPitchWheel()) { double percent = (1.0 * midi_message.getPitchWheelValue()) / PITCH_WHEEL_RESOLUTION; double value = 2 * percent - 1.0; diff --git a/src/synthesis/helm_engine.cpp b/src/synthesis/helm_engine.cpp index 894653cb48..d638a38834 100644 --- a/src/synthesis/helm_engine.cpp +++ b/src/synthesis/helm_engine.cpp @@ -376,11 +376,11 @@ namespace mopo { arpeggiator_->allNotesOff(sample); } - void HelmEngine::noteOn(mopo_float note, mopo_float velocity, int sample, int channel) { + void HelmEngine::noteOn(mopo_float note, mopo_float velocity, int sample, int channel, mopo_float aftertouch) { if (arp_on_->value()) - arpeggiator_->noteOn(note, velocity, sample); + arpeggiator_->noteOn(note, velocity, sample, channel, aftertouch); else - voice_handler_->noteOn(note, velocity, sample, channel); + voice_handler_->noteOn(note, velocity, sample, channel, aftertouch); } VoiceEvent HelmEngine::noteOff(mopo_float note, int sample) { @@ -396,8 +396,17 @@ namespace mopo { void HelmEngine::setPitchWheel(mopo_float value, int channel) { voice_handler_->setPitchWheel(value, channel); } + + void HelmEngine::setPressure(mopo_float value, int channel, int sample) { + if (arp_on_->value()) + arpeggiator_->setPressure(value, channel, sample); + voice_handler_->setPressure(value, channel, sample); + } void HelmEngine::setAftertouch(mopo_float note, mopo_float value, int sample) { + // TODO: take channel into account + if (arp_on_->value()) + arpeggiator_->setAftertouch(note, value, sample); voice_handler_->setAftertouch(note, value, sample); } diff --git a/src/synthesis/helm_engine.h b/src/synthesis/helm_engine.h index 2790446e3d..b8966168fb 100644 --- a/src/synthesis/helm_engine.h +++ b/src/synthesis/helm_engine.h @@ -52,13 +52,14 @@ namespace mopo { // Keyboard events. void allNotesOff(int sample = 0) override; void noteOn(mopo_float note, mopo_float velocity = 1.0, - int sample = 0, int channel = 0) override; + int sample = 0, int channel = 0, mopo_float aftertouch = 0) override; VoiceEvent noteOff(mopo_float note, int sample = 0) override; void setModWheel(mopo_float value, int channel = 0); void setPitchWheel(mopo_float value, int channel = 0); void setBpm(mopo_float bpm); void correctToTime(mopo_float samples) override; void setAftertouch(mopo_float note, mopo_float value, int sample = 0); + void setPressure(mopo_float value, int channel = 0, int sample = 0); // Sustain pedal events. void sustainOn(); diff --git a/src/synthesis/helm_voice_handler.cpp b/src/synthesis/helm_voice_handler.cpp index 97dddd2d07..d8b9eede3e 100644 --- a/src/synthesis/helm_voice_handler.cpp +++ b/src/synthesis/helm_voice_handler.cpp @@ -756,10 +756,10 @@ namespace mopo { } } - void HelmVoiceHandler::noteOn(mopo_float note, mopo_float velocity, int sample, int channel) { + void HelmVoiceHandler::noteOn(mopo_float note, mopo_float velocity, int sample, int channel, mopo_float aftertouch) { if (getPressedNotes().size() < polyphony() || legato_->value() == 0.0) note_retriggered_.trigger(note, sample); - VoiceHandler::noteOn(note, velocity, sample, channel); + VoiceHandler::noteOn(note, velocity, sample, channel, aftertouch); } VoiceEvent HelmVoiceHandler::noteOff(mopo_float note, int sample) { diff --git a/src/synthesis/helm_voice_handler.h b/src/synthesis/helm_voice_handler.h index ed521dd047..47a086115d 100644 --- a/src/synthesis/helm_voice_handler.h +++ b/src/synthesis/helm_voice_handler.h @@ -53,7 +53,7 @@ namespace mopo { void process() override; void noteOn(mopo_float note, mopo_float velocity = 1, - int sample = 0, int channel = 0) override; + int sample = 0, int channel = 0, mopo_float aftertouch = 0) override; VoiceEvent noteOff(mopo_float note, int sample = 0) override; bool shouldAccumulate(Output* output) override; void setModWheel(mopo_float value, int channel = 0);