#include #include #include "main_osp.hh" #include "track.hh" #include "osp_utils.hh" #include "midiclock.hh" #include "sequencer.hh" #include "instr_interface.hh" #include "instr_kick.hh" #include "instr_noise.hh" #include "instr_fm.hh" #include "instr_grainlet.hh" #include "instr_hihat.hh" #include "instr_zosc.hh" // DTCM_MEM_SECTION // DMA_BUFFER_MEM_SECTION // DSY_SDRAM_DATA // DSY_SDRAM_BSS float DSY_SDRAM_BSS sdram_block1[4000000]; namespace Heck::OSP { namespace State { bool record_mode{ false }; bool clear_mode{ false }; } // namespace State // ============================================================================================= // INIT // ============================================================================================= ld::DaisySeed seed{}; ld::Switch but_rec{}; ld::Switch but_clear{}; static ld::MidiUartHandler midi{}; static MidiLoggerBuffered logger_midi{}; Instrument::ZOsc instrument0{}; Instrument::FM instrument1{}; Instrument::Grainlet instrument2{}; Instrument::HiHat instrument3{}; std::array tracks{}; MidiClock clock{}; Sequencer sequencer{}; // function prototypes void audio_callback(ld::AudioHandle::InputBuffer in, ld::AudioHandle::OutputBuffer out, size_t size); void midi_realtime_handler(const ld::MidiEvent& msg); void init_tracks() { sequencer.init(); tracks[0].init(instrument0); tracks[1].init(instrument1); tracks[2].init(instrument2); tracks[3].init(instrument3); } void init() { seed.Configure(); seed.Init(Constants::CPU_BOOST480MHZ); ld::DaisySeed::StartLog(Constants::Developer::LOG_BLOCKS_BOOT); but_rec.Init(ld::DaisySeed::GetPin(Constants::Hardware::PIN_BUTTON_RECORD), 0); but_clear.Init(ld::DaisySeed::GetPin(Constants::Hardware::PIN_BUTTON_CLEAR), 0); ld::DaisySeed::PrintLine("Setting Blocksize: %i", Constants::AUDIO_BUFFERSIZE); seed.SetAudioBlockSize(Constants::AUDIO_BUFFERSIZE); ld::DaisySeed::PrintLine("Setting Samplerate: %i", Constants::AUDIO_SAMPLERATE); seed.SetAudioSampleRate(Constants::AUDIO_SAMPLERATE); ld::DaisySeed::PrintLine("Initializing MIDI"); ld::MidiUartHandler::Config midi_config{}; midi.Init(midi_config); init_tracks(); ld::DaisySeed::PrintLine("Starting MIDI Receive"); midi.StartReceiveRt(&midi_realtime_handler); midi.Listen(); ld::DaisySeed::PrintLine("Starting Audio"); seed.StartAudio(audio_callback); } // ============================================================================================= // RUN // ============================================================================================= Sig::RingBuffer<30000> buff1{}; Sig::Delay101<30000> dly1{}; volatile float sig_in[Constants::AUDIO_BUFFERSIZE]; volatile float sig_out[Constants::AUDIO_BUFFERSIZE]; volatile float delaytime{}; volatile float delayfb{}; float sig_dly1{}; float sig_osp{}; float sig_bus{}; void process_signals() { for (size_t i = 0; i < Constants::AUDIO_BUFFERSIZE; i++) { sig_osp = tracks[0].nextsample(); sig_osp += tracks[1].nextsample(); sig_osp += tracks[2].nextsample(); sig_osp += tracks[3].nextsample(); sig_bus = sig_osp + sig_in[i]; dly1.delayfb_ = delayfb; dly1.delaytime_ = delaytime; sig_bus += dly1.process(sig_bus); sig_out[i] = sig_bus; } } void audio_callback(ld::AudioHandle::InputBuffer in, ld::AudioHandle::OutputBuffer out, size_t size) { for (size_t i = 0; i < size; i++) { sig_in[i] = in[0][i]; } process_signals(); for (size_t i = 0; i < size; i++) { out[0][i] = sig_out[i]; out[1][i] = sig_out[i]; } } void midi_realtime_handler(const ld::MidiEvent& msg) { if constexpr (Constants::Developer::LOG_MIDI_UART_REALTIME) { logger_midi.push(msg); } switch (msg.srt_type) { case ld::TimingClock: clock.tick_advance(); break; case ld::Start: { sequencer.reset(); clock.reset(); clock.enable(true); } break; case ld::Stop: { clock.enable(false); clock.reset(); sequencer.reset(); } break; case ld::Reset: break; case ld::Continue: break; } } void midi_dispatch(ld::MidiEvent msg, bool from_seq) { switch (msg.type) { case ld::MidiMessageType::NoteOn: { ld::DaisySeed::PrintLine( "MIDI from seq=%i : NOTE - ch: %i, nr: %i, val: %i", from_seq, msg.channel, msg.AsNoteOn().note, msg.AsNoteOn().velocity); if (msg.channel >= 0 && msg.channel < Constants::TRACK_COUNT) { tracks[msg.channel].trigger(); } } break; case ld::MidiMessageType::ControlChange: { ld::DaisySeed::PrintLine( "MIDI from seq=%i : CC - ch: %i, nr: %i, val: %i", from_seq, msg.channel, msg.AsControlChange().control_number, msg.AsControlChange().value); const ld::ControlChangeEvent cc = msg.AsControlChange(); if (State::clear_mode && !from_seq) { ld::DaisySeed::PrintLine("Clear: ch: %i, cc: %i", cc.channel, cc.control_number); sequencer.clear_track_cc(cc.channel, cc.control_number); } else { if (!from_seq) { sequencer.midi_in(msg); } const 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); delaytime = val_normalized; break; case Constants::MIDI_Mapping::TRACK_DRIVE: tracks[cc.channel].drive(val_normalized); delayfb = val_normalized; break; case Constants::MIDI_Mapping::TRACK_VOLUME: tracks[cc.channel].volume(val_normalized); break; // Switches case Constants::MIDI_Mapping::TRACK_ALGO: tracks[cc.channel].instrument->switch_algo(int(val_normalized * 2.)); break; case Constants::MIDI_Mapping::TRACK_MODE: tracks[cc.channel].instrument->switch_mode(int(val_normalized * 2.)); break; case Constants::MIDI_Mapping::TRACK_FILTERMODE: tracks[cc.channel].filtermode(val_normalized); break; default: break; } } } break; default: { ld::DaisySeed::PrintLine( "UNHANDLED MIDI UNHANDLED MIDI from seq=%i : CC - ch: %i", from_seq, msg.channel); // Other MIDI message } break; } } void midi_from_uart() { while (midi.HasEvents()) { ld::MidiEvent msg = midi.PopEvent(); midi_dispatch(msg, false); } } void midi_from_sequencer() { sequencer.midi_out([](ld::MidiEvent msg) { midi_dispatch(msg, true); }); } //------------------------------------------------- // HEARTBEAT //------------------------------------------------- void task_heartbeat_func(u32) { static bool heartbeat_led_state{ false }; heartbeat_led_state = !heartbeat_led_state; seed.SetLed(heartbeat_led_state); } dz::PeriodicTaskCT task_heartbeat{}; //------------------------------------------------- // MIDI LOGGER //------------------------------------------------- void task_logger_midi_print_func(u32) { logger_midi.print(); } dz::PeriodicTaskCT task_logger_midi_print{}; //------------------------------------------------- // REC BUTTON //------------------------------------------------- bool but_rec_update() { but_rec.Debounce(); return but_rec.Pressed(); } void but_rec_changed(dz::Cache& cache) { bool but_rec_current = cache.read(); if (but_rec_current) { if (State::clear_mode) { ld::DaisySeed::PrintLine("CLEAR SEQUENCE"); sequencer.clear_sequence(); } else { ld::DaisySeed::PrintLine("BUTTON RECORD ON"); State::record_mode = true; } } else { ld::DaisySeed::PrintLine("BUTTON RECORD OFF"); State::record_mode = false; } sequencer.recording(but_rec_current); } dz::Cache c_but_rec{ but_rec_update, but_rec_changed }; void task_but_rec_process_func(u32) { c_but_rec.update_and_notify_change(); } dz::PeriodicTaskCT task_but_rec_process{}; //------------------------------------------------- // CLEAR BUTTON //------------------------------------------------- bool but_clear_update() { but_clear.Debounce(); return but_clear.Pressed(); } void but_clear_changed(dz::Cache& cache) { bool but_clear_current = cache.read(); if (but_clear_current) { ld::DaisySeed::PrintLine("BUTTON CLEAR ON"); State::clear_mode = true; } else { ld::DaisySeed::PrintLine("BUTTON CLEAR OFF"); State::clear_mode = false; } } dz::Cache c_but_clear{ but_clear_update, but_clear_changed }; void task_but_clear_process_func(u32) { c_but_clear.update_and_notify_change(); } dz::PeriodicTaskCT task_but_clear_process{}; //========================================================================================== // MAINLOOP //========================================================================================== void mainloop() { ld::DaisySeed::PrintLine("Entering MainLoop"); u32 uptime_ms{}; int clock_16n_new{}; int clock_16n_current{}; buff1.init(sdram_block1); dly1.init(&buff1); while (1) { uptime_ms = ld::System::GetNow(); clock_16n_new = clock.count_16n(); if (clock_16n_new != clock_16n_current) { clock_16n_current = clock_16n_new; if constexpr (Constants::Developer::LOG_CLOCK_16N) { ld::DaisySeed::PrintLine("16n: %i", clock_16n_current); } sequencer.next_step(); } midi_from_sequencer(); midi_from_uart(); task_but_rec_process.run_pending(uptime_ms); task_but_clear_process.run_pending(uptime_ms); task_logger_midi_print.run_pending(uptime_ms); task_heartbeat.run_pending(uptime_ms); } } } // namespace Heck::OSP int main() { Heck::OSP::init(); Heck::OSP::mainloop(); }