diff --git a/src/firmware/application/global/BPM.h b/src/firmware/application/global/BPM.h new file mode 100644 index 000000000..f9493342e --- /dev/null +++ b/src/firmware/application/global/BPM.h @@ -0,0 +1,99 @@ +/* + +Copyright 2015-2023 Igor Petrovic + +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. + +*/ + +#pragma once + +#include +#include "application/util/incdec/IncDec.h" +#include "application/messaging/Messaging.h" + +namespace global +{ + class BPM + { + public: + // This class can be used across various application modules but + // state must be preserved - hence the singleton approach. + + static BPM& instance() + { + static BPM program; + return program; + } + + bool increment(uint8_t steps) + { + auto newBPM = BPMIncDec::increment(_bpm, + steps, + BPMIncDec::type_t::EDGE); + + if (newBPM != _bpm) + { + set(newBPM); + return true; + } + + return false; + } + + bool decrement(uint8_t steps) + { + auto newBPM = BPMIncDec::decrement(_bpm, + steps, + BPMIncDec::type_t::EDGE); + + if (newBPM != _bpm) + { + set(newBPM); + return true; + } + + return false; + } + + uint8_t value() + { + return _bpm; + } + + uint32_t bpmToUsec(uint32_t bpm) + { + return 60000000 / PPQN / bpm; + } + + private: + BPM() = default; + + void set(uint8_t bpm) + { + _bpm = bpm; + + messaging::event_t event; + event.systemMessage = messaging::systemMessage_t::MIDI_BPM_CHANGE; + event.value = bpm; + + MIDIDispatcher.notify(messaging::eventType_t::SYSTEM, event); + } + + using BPMIncDec = util::IncDec; + static constexpr uint32_t PPQN = 24; + uint8_t _bpm = 120; + }; +} // namespace global + +#define BPM global::BPM::instance() \ No newline at end of file diff --git a/src/firmware/application/io/buttons/Buttons.cpp b/src/firmware/application/io/buttons/Buttons.cpp index 9fd512d89..096a589e8 100644 --- a/src/firmware/application/io/buttons/Buttons.cpp +++ b/src/firmware/application/io/buttons/Buttons.cpp @@ -20,6 +20,7 @@ limitations under the License. #include "SysExConf/SysExConf.h" #include "application/system/Config.h" #include "application/global/MIDIProgram.h" +#include "application/global/BPM.h" #ifdef BUTTONS_SUPPORTED @@ -400,6 +401,32 @@ void Buttons::sendMessage(size_t index, bool state, buttonDescriptor_t& descript } break; + case messageType_t::BPM_INC: + { + descriptor.event.value = 0; + + if (!BPM.increment(1)) + { + send = false; + } + + descriptor.event.index = BPM.value(); + } + break; + + case messageType_t::BPM_DEC: + { + descriptor.event.value = 0; + + if (!BPM.decrement(1)) + { + send = false; + } + + descriptor.event.index = BPM.value(); + } + break; + default: { send = false; @@ -536,6 +563,8 @@ void Buttons::fillButtonDescriptor(size_t index, buttonDescriptor_t& descriptor) case messageType_t::PROGRAM_CHANGE_OFFSET_DEC: case messageType_t::NOTE_OFF_ONLY: case messageType_t::CONTROL_CHANGE0_ONLY: + case messageType_t::BPM_INC: + case messageType_t::BPM_DEC: { descriptor.type = type_t::MOMENTARY; } diff --git a/src/firmware/application/io/buttons/Buttons.h b/src/firmware/application/io/buttons/Buttons.h index 6d20b7b31..41df34a32 100644 --- a/src/firmware/application/io/buttons/Buttons.h +++ b/src/firmware/application/io/buttons/Buttons.h @@ -86,6 +86,8 @@ namespace io RESERVED, PROGRAM_CHANGE_OFFSET_INC, PROGRAM_CHANGE_OFFSET_DEC, + BPM_INC, + BPM_DEC, AMOUNT }; @@ -179,6 +181,8 @@ namespace io MIDI::messageType_t::INVALID, // RESERVED MIDI::messageType_t::INVALID, // PROGRAM_CHANGE_OFFSET_INC MIDI::messageType_t::INVALID, // PROGRAM_CHANGE_OFFSET_DEC + MIDI::messageType_t::INVALID, // BPM_INC + MIDI::messageType_t::INVALID, // BPM_DEC }; }; } // namespace io diff --git a/src/firmware/application/io/encoders/Encoders.cpp b/src/firmware/application/io/encoders/Encoders.cpp index 2ae118eae..f1ae81425 100644 --- a/src/firmware/application/io/encoders/Encoders.cpp +++ b/src/firmware/application/io/encoders/Encoders.cpp @@ -24,6 +24,7 @@ limitations under the License. #include "core/util/Util.h" #include "application/util/conversion/Conversion.h" #include "application/util/configurable/Configurable.h" +#include "application/global/BPM.h" using namespace io; @@ -327,6 +328,27 @@ void Encoders::sendMessage(size_t index, position_t encoderState, encoderDescrip } break; + case type_t::BPM_CHANGE: + { + if (encoderState == position_t::CCW) + { + if (!BPM.increment(1)) + { + send = false; // edge value reached, nothing more to send + } + } + else + { + if (!BPM.decrement(1)) + { + send = false; // edge value reached, nothing more to send + } + } + + descriptor.event.value = BPM.value(); + } + break; + default: { send = false; diff --git a/src/firmware/application/io/encoders/Encoders.h b/src/firmware/application/io/encoders/Encoders.h index 17c1c5337..51759e9fa 100644 --- a/src/firmware/application/io/encoders/Encoders.h +++ b/src/firmware/application/io/encoders/Encoders.h @@ -55,6 +55,7 @@ namespace io NRPN_14BIT, CONTROL_CHANGE_14BIT, CONTROL_CHANGE_41H01H, + BPM_CHANGE, AMOUNT }; @@ -221,6 +222,7 @@ namespace io MIDI::messageType_t::NRPN_14BIT, // NRPN_14BIT MIDI::messageType_t::CONTROL_CHANGE_14BIT, // CONTROL_CHANGE_14BIT MIDI::messageType_t::CONTROL_CHANGE, // RESERVED + MIDI::messageType_t::INVALID, // BPM_CHANGE }; }; } // namespace io diff --git a/src/firmware/application/messaging/Messaging.h b/src/firmware/application/messaging/Messaging.h index c14303c50..af1e2e4d7 100644 --- a/src/firmware/application/messaging/Messaging.h +++ b/src/firmware/application/messaging/Messaging.h @@ -51,7 +51,8 @@ namespace messaging RESTORE_START, RESTORE_END, FACTORY_RESET_START, - FACTORY_RESET_END + FACTORY_RESET_END, + MIDI_BPM_CHANGE, }; struct event_t diff --git a/src/firmware/application/protocol/midi/MIDI.cpp b/src/firmware/application/protocol/midi/MIDI.cpp index 92601e200..9fcb6fff4 100644 --- a/src/firmware/application/protocol/midi/MIDI.cpp +++ b/src/firmware/application/protocol/midi/MIDI.cpp @@ -17,12 +17,14 @@ limitations under the License. */ #include "MIDI.h" +#include "core/Timing.h" #include "application/system/Config.h" #include "application/util/conversion/Conversion.h" #include "application/messaging/Messaging.h" #include "application/util/configurable/Configurable.h" #include "application/util/logger/Logger.h" #include "application/global/MIDIProgram.h" +#include "application/global/BPM.h" using namespace io; @@ -81,6 +83,16 @@ protocol::MIDI::MIDI(HWAUSB& hwaUSB, } break; + case messaging::systemMessage_t::MIDI_BPM_CHANGE: + { + if (isSettingEnabled(setting_t::DIN_ENABLED)) + { + core::mcu::timers::setPeriod(_clockTimerIndex, BPM.bpmToUsec(BPM.value())); + core::mcu::timers::start(_clockTimerIndex); + } + } + break; + default: break; } @@ -171,6 +183,24 @@ bool protocol::MIDI::setupDINMIDI() _dinMIDI.setNoteOffMode(isSettingEnabled(setting_t::STANDARD_NOTE_OFF) ? noteOffType_t::STANDARD_NOTE_OFF : noteOffType_t::NOTE_ON_ZERO_VEL); _hwaDIN.setLoopback(isDinLoopbackRequired()); + if (!_clockTimerAllocated) + { + if (core::mcu::timers::allocate(_clockTimerIndex, [this]() + { + _dinMIDI.sendRealTime(messageType_t::SYS_REAL_TIME_CLOCK); + })) + { + _clockTimerAllocated = true; + + core::mcu::timers::setPeriod(_clockTimerIndex, BPM.bpmToUsec(BPM.value())); + + if (isSettingEnabled(setting_t::SEND_MIDI_CLOCK_DIN)) + { + core::mcu::timers::start(_clockTimerIndex); + } + } + } + return true; } @@ -1073,6 +1103,13 @@ std::optional protocol::MIDI::sysConfigSet(sys::Config::Section::global } break; + case setting_t::SEND_MIDI_CLOCK_DIN: + { + value ? core::mcu::timers::start(_clockTimerIndex) : core::mcu::timers::stop(_clockTimerIndex); + result = sys::Config::status_t::ACK; + } + break; + default: { result = sys::Config::status_t::ACK; diff --git a/src/firmware/application/protocol/midi/MIDI.h b/src/firmware/application/protocol/midi/MIDI.h index f7f0dc802..eb476baa0 100644 --- a/src/firmware/application/protocol/midi/MIDI.h +++ b/src/firmware/application/protocol/midi/MIDI.h @@ -69,6 +69,7 @@ namespace protocol BLE_THRU_BLE, USE_GLOBAL_CHANNEL, GLOBAL_CHANNEL, + SEND_MIDI_CLOCK_DIN, AMOUNT }; @@ -136,5 +137,7 @@ namespace protocol MIDIlib::BLEMIDI _bleMIDI = MIDIlib::BLEMIDI(_hwaBLE); Database& _database; std::array _midiInterface; + bool _clockTimerAllocated = false; + size_t _clockTimerIndex = 0; }; } // namespace protocol \ No newline at end of file