From 32683898054209b3f8d21ff0ab549767b5d0f8ed Mon Sep 17 00:00:00 2001 From: heck Date: Fri, 13 Sep 2024 01:48:58 +0200 Subject: [PATCH] YUP. added the SEQUENCER finally :) --- src/main_perkons.cc | 328 +++++++++++++++++++++++++++++++------------- 1 file changed, 235 insertions(+), 93 deletions(-) diff --git a/src/main_perkons.cc b/src/main_perkons.cc index 31c5ae3..4e0c92a 100644 --- a/src/main_perkons.cc +++ b/src/main_perkons.cc @@ -31,27 +31,18 @@ namespace Heck { std::array tracks; - // function prototypes - int create_tracks(); - - void AudioCallback( - ld::AudioHandle::InterleavingInputBuffer in, - ld::AudioHandle::InterleavingOutputBuffer out, - size_t size); - - void midi_realtime_handler(ld::MidiEvent& msg); - - struct Clock { - void advance() + struct MidiClock { + public: + void tick_advance() { if (enabled) { - time++; + time_inf++; } }; void reset() { - time = 0; + time_inf = 0; }; void enable(bool enabled) @@ -59,17 +50,148 @@ namespace Heck { this->enabled = enabled; }; - int gettime() + // get time + + int tick_infinite() + { + return time_inf; + } + + int tick_4n() + { + return time_inf % PPQ; + } + + int tick_1n() + { + return time_inf % (PPQ * 4); + } + + int count_8n() { - return time; + return std::floor(tick_1n() / 12); + } + + int count_16n() + { + return std::floor(tick_1n() / 6); + } + + int count_bar() + { + return std::floor(time_inf / 96.); } private: + static constexpr int PPQ = 24; bool enabled{ true }; - int time{ 0 }; + int time_inf{ 0 }; + }; + + struct Sequencer { + + Sequencer() + { + clear_sequence(); + } + + // its a MIDI CC step sequencer + constexpr static int MIDI_MAX = 127; + constexpr static int STEP_COUNT = 16; + constexpr static int TRACK_COUNT = 16; + + using Step = std::array; + using Track = std::array; + using Sequence = std::array; + + + void reset() + { + step_current = 0; + } + + void next_step() + { + step_current++; + if(step_current >= STEP_COUNT) { + step_current = 0; + } + } + + void clear_track(int track_nr) + { + for (int step = 0; step < STEP_COUNT; step++) { + for (int cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { + sequence[track_nr][step][cc_nr] = -1; + } + } + } + + void clear_track_cc(int track_nr, int cc_nr) + { + for (int step = 0; step < STEP_COUNT; step++) { + sequence[track_nr][step][cc_nr] = -1; + } + } + + void clear_sequence() + { + for (int track = 0; track < TRACK_COUNT; track++) { + clear_track(track); + } + } + + void midi_in(ld::MidiEvent& msg) + { + if (recording) { + ld::ControlChangeEvent cc = msg.AsControlChange(); + sequence[cc.channel][step_current][cc.control_number] = cc.value; + } + } + + std::vector midi_out() + { + std::vector ret{}; + for (int track = 0; track < TRACK_COUNT; track++) { + for (int cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { + int cc_val = sequence[track][step_current][cc_nr]; + if (cc_val != -1) { + ld::MidiEvent ev{}; + ev.channel = track; + ev.type = ld::ControlChange; + ev.data[0] = cc_nr; + ev.data[1] = cc_val; + ret.push_back(ev); + } + } + } + return ret; + } + + int step_current{ 0 }; + bool recording{ false }; + Sequence sequence{}; }; - Clock clock{}; + MidiClock clock{}; + Sequencer sequencer{}; + + // function prototypes + void AudioCallback( + ld::AudioHandle::InterleavingInputBuffer in, + ld::AudioHandle::InterleavingOutputBuffer out, + size_t size); + + void midi_realtime_handler(ld::MidiEvent& msg); + + + void init_tracks() + { + tracks[0].init(instrument0); + tracks[1].init(instrument1); + tracks[2].init(instrument2); + tracks[3].init(instrument3); + } void init() { @@ -105,38 +227,21 @@ namespace Heck { break; } - hw.PrintLine("Creating Tracks"); - create_tracks(); - - - hw.PrintLine("Setting up MIDI"); + hw.PrintLine("Initializing MIDI"); ld::MidiUartHandler::Config midi_config{}; midi.Init(midi_config); - midi.realtime_callback = &midi_realtime_handler; + init_tracks(); + hw.PrintLine("Starting MIDI Receive"); midi.StartReceive(); midi.Listen(); hw.PrintLine("Starting Audio"); hw.StartAudio(AudioCallback); - - hw.PrintLine("Entering MainLoop"); } - int create_tracks() - { - instrument0.init(); - tracks[0].init(instrument0); - instrument1.init(); - tracks[1].init(instrument1); - instrument2.init(); - tracks[2].init(instrument2); - instrument3.init(); - tracks[3].init(instrument3); - return 0; - } // ============================================================================================= // RUN @@ -163,15 +268,17 @@ namespace Heck { event_log.PushBack(msg); switch (msg.srt_type) { case daisy::TimingClock: - clock.advance(); + clock.tick_advance(); break; case daisy::Start: { + sequencer.reset(); clock.reset(); clock.enable(true); } break; case daisy::Stop: { clock.enable(false); clock.reset(); + sequencer.reset(); } break; case daisy::Reset: break; @@ -180,67 +287,82 @@ namespace Heck { } } + void tracks_midi_in(ld::MidiEvent msg) + { + switch (msg.type) { + case ld::MidiMessageType::NoteOn: { + if (msg.channel >= 0 && msg.channel < Constants::TRACK_COUNT) { + tracks[msg.channel].instrument->trigger(); + } + } break; + case ld::MidiMessageType::ControlChange: { + sequencer.midi_in(msg); + ld::ControlChangeEvent cc = msg.AsControlChange(); + float val_normalized = cc.value / 127.; + switch (cc.control_number) { + // Pots + case Constants::MIDI_Mapping::TRACK_PITCH: + tracks[cc.channel].instrument->ctl(0, val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_DECAY: + tracks[cc.channel].instrument->ctl(1, val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_PARAM1: + tracks[cc.channel].instrument->ctl(2, val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_PARAM2: + tracks[cc.channel].instrument->ctl(3, val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_FILTER: + tracks[cc.channel].filter(val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_DRIVE: + tracks[cc.channel].drive(val_normalized); + break; + case Constants::MIDI_Mapping::TRACK_VOLUME: + tracks[cc.channel].volume(val_normalized); + break; + // Switches + case Constants::MIDI_Mapping::TRACK_MODE1: + tracks[cc.channel].instrument->switch_mode1(int(val_normalized * 2.)); + break; + case Constants::MIDI_Mapping::TRACK_MODE2: + tracks[cc.channel].instrument->switch_mode2(int(val_normalized * 2.)); + break; + case Constants::MIDI_Mapping::TRACK_FILTERMODE: + tracks[cc.channel].filtermode(val_normalized); + break; + default: + break; + } + } break; + default: { + // Other MIDI message + } break; + } + } + void midi_async_handler() { while (midi.HasEvents()) { ld::MidiEvent msg = midi.PopEvent(); event_log.PushBack(msg); - switch (msg.type) { - case ld::MidiMessageType::NoteOn: { - if (msg.channel >= 0 && msg.channel < Constants::TRACK_COUNT) { - tracks[msg.channel].instrument->trigger(); - } - } break; - case ld::MidiMessageType::ControlChange: { - ld::ControlChangeEvent cc = msg.AsControlChange(); - float val_normalized = cc.value / 127.; - switch (cc.control_number) { - // Pots - case Constants::MIDI_Mapping::TRACK_PITCH: - tracks[cc.channel].instrument->ctl(0, val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_DECAY: - tracks[cc.channel].instrument->ctl(1, val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_PARAM1: - tracks[cc.channel].instrument->ctl(2, val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_PARAM2: - tracks[cc.channel].instrument->ctl(3, val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_FILTER: - tracks[cc.channel].filter(val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_DRIVE: - tracks[cc.channel].drive(val_normalized); - break; - case Constants::MIDI_Mapping::TRACK_VOLUME: - tracks[cc.channel].volume(val_normalized); - break; - // Switches - case Constants::MIDI_Mapping::TRACK_MODE1: - tracks[cc.channel].instrument->switch_mode1(int(val_normalized * 2.)); - break; - case Constants::MIDI_Mapping::TRACK_MODE2: - tracks[cc.channel].instrument->switch_mode2(int(val_normalized * 2.)); - break; - case Constants::MIDI_Mapping::TRACK_FILTERMODE: - tracks[cc.channel].filtermode(val_normalized); - break; - default: - break; - } - } break; - default: { - // Other MIDI message - } break; - } + tracks_midi_in(msg); + } + } + + void sequencer_midi_handler() + { + std::vector queue = sequencer.midi_out(); + for (ld::MidiEvent msg : queue) { + tracks_midi_in(msg); } } void mainloop() { - u32 systick_now = ld::System::GetNow(); + hw.PrintLine("Entering MainLoop"); + u32 systick_now{}; u32 midi_log_systick_last{}; bool heartbeat_led_state{ false }; @@ -252,16 +374,21 @@ namespace Heck { int clock_time_new{}; int clock_time_current{}; + int clock_bar_new{}; + int clock_bar_current{}; + + int clock_16n_new{}; + int clock_16n_current{}; + while (1) { // Get current values for state // if update is different // Update state // handle change - systick_now = ld::System::GetNow(); - clock_time_new = clock.gettime(); + clock_time_new = clock.tick_infinite(); if (clock_time_current != clock_time_new) { clock_time_current = clock_time_new; if (0) { @@ -269,6 +396,20 @@ namespace Heck { } } + clock_bar_new = clock.count_bar(); + if (clock_bar_new != clock_bar_current) { + clock_bar_current = clock_bar_new; + hw.PrintLine("Bar: %i", clock_bar_current); + } + + clock_16n_new = clock.count_16n(); + if (clock_16n_new != clock_16n_current) { + clock_16n_current = clock_16n_new; + hw.PrintLine("16n: %i", clock_16n_current); + sequencer.next_step(); + } + + sequencer_midi_handler(); midi_async_handler(); // REC @@ -281,6 +422,7 @@ namespace Heck { } else { hw.PrintLine("RECORDING OFF"); } + sequencer.recording = seq_recording_current; } if (systick_now - midi_log_systick_last > 5) { @@ -303,7 +445,7 @@ namespace Heck { case ld::NoteOn: case ld::NoteOff: case ld::MidiMessageType::ControlChange: { - if (1) { + if (0) { char outstr[256]; char type_str[32]; GetMidiTypeAsString(msg, type_str);