// Hecks perkons extension #include #include #include "globals.hh" #include "track.hh" #include "utils.hh" #include "midiclock.hh" #include "instr_abstract.hh" #include "instr_kick.hh" #include "instr_noise.hh" #include "instr_fm.hh" #include "instr_grainlet.hh" namespace ld = daisy; namespace dsp = daisysp; namespace Heck { // ============================================================================================= // INIT // ============================================================================================= ld::DaisySeed hw{}; ld::Switch but_rec{}; ld::Switch but_clear{}; static ld::MidiUartHandler midi{}; static ld::FIFO event_log{}; Instrument::FM instrument0{}; Instrument::Grainlet instrument1{}; Instrument::FM instrument2{}; Instrument::Grainlet instrument3{}; std::array tracks; 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{}; }; 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() { hw.Configure(); hw.Init(); hw.StartLog(Constants::Developer::LOG_BLOCKS_BOOT); but_rec.Init(hw.GetPin(Constants::Hardware::PIN_BUTTON_RECORD), 0); but_clear.Init(hw.GetPin(Constants::Hardware::PIN_BUTTON_CLEAR), 0); hw.PrintLine("Setting Blocksize: %i", Constants::BUFFERSIZE); hw.SetAudioBlockSize(Constants::BUFFERSIZE); hw.PrintLine("Setting Samplerate: %i", Constants::SAMPLERATE); switch (Constants::SAMPLERATE) { case 8000: hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_8KHZ); break; case 16000: hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_16KHZ); break; case 32000: hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_32KHZ); break; case 48000: hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ); break; case 96000: hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ); break; default: hw.PrintLine("Samplerate not supported, fallback to 48000"); hw.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_48KHZ); break; } 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); } // ============================================================================================= // RUN // ============================================================================================= void AudioCallback( ld::AudioHandle::InterleavingInputBuffer in, ld::AudioHandle::InterleavingOutputBuffer out, size_t size) { float sig_out{}; for (size_t i = 0; i < size; i += 2) { for (int i = 0; i < Constants::TRACK_COUNT; i++) { sig_out += tracks[i].nextsample(); } sig_out *= 0.1; out[i] = sig_out; out[i + 1] = sig_out; } } void midi_realtime_handler(ld::MidiEvent& msg) { event_log.PushBack(msg); switch (msg.srt_type) { case daisy::TimingClock: 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; case daisy::Continue: break; } } 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].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].decay(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); 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() { hw.PrintLine("Entering MainLoop"); u32 systick_now{}; u32 midi_log_systick_last{}; bool heartbeat_led_state{ false }; u32 heartbeat_systick_last{}; bool but_record_new{ false }; bool but_record_current{ false }; bool but_clear_new{ false }; bool but_clear_current{ false }; 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.tick_infinite(); if (clock_time_current != clock_time_new) { clock_time_current = clock_time_new; } clock_bar_new = clock.count_bar(); if (clock_bar_new != clock_bar_current) { clock_bar_current = clock_bar_new; if (Constants::Developer::LOG_CLOCK_BAR) { 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; if (Constants::Developer::LOG_CLOCK_16N) { hw.PrintLine("16n: %i", clock_16n_current); } sequencer.next_step(); } sequencer_midi_handler(); midi_async_handler(); // REC but_rec.Debounce(); but_record_new = but_rec.Pressed(); if (but_record_current != but_record_new) { but_record_current = but_record_new; if (but_record_current) { hw.PrintLine("BUTTON RECORD ON"); } else { hw.PrintLine("BUTTON RECORD OFF"); } sequencer.recording = but_record_current; } // CLEAR but_clear.Debounce(); but_clear_new = but_clear.Pressed(); if (but_clear_current != but_clear_new) { but_clear_current = but_clear_new; if (but_clear_current) { hw.PrintLine("BUTTON CLEAR ON"); } else { hw.PrintLine("BUTTON CLEAR OFF"); sequencer.clear_sequence(); } } if (systick_now - midi_log_systick_last > 5) { midi_log_systick_last = systick_now; if (!event_log.IsEmpty()) { auto msg = event_log.PopFront(); switch (msg.type) { case ld::MidiMessageType::SystemRealTime: { if (Constants::Developer::LOG_MIDI_REALTIME) { char outstr[256]; char rttype_str[32]; GetMidiRTTypeAsString(msg, rttype_str); sprintf(outstr, "RT: type: %s\n", rttype_str); if (msg.srt_type != daisy::TimingClock) { hw.PrintLine("%s", outstr); } } } break; case ld::NoteOn: case ld::NoteOff: case ld::MidiMessageType::ControlChange: { if (Constants::Developer::LOG_MIDI_NOTESANDCC) { char outstr[256]; char type_str[32]; GetMidiTypeAsString(msg, type_str); sprintf( outstr, "time-last:\t%ld\ttype: %s\tChannel: %d\tData MSB: " "%d\tData LSB: %d\n", systick_now, type_str, msg.channel, msg.data[0], msg.data[1]); hw.PrintLine("%s", outstr); } } break; } } } if (systick_now - heartbeat_systick_last > 500) { heartbeat_systick_last = systick_now; heartbeat_led_state = !heartbeat_led_state; hw.SetLed(heartbeat_led_state); } } } } // namespace Heck int main() { Heck::init(); Heck::mainloop(); }