// Hecks perkons extension #include #include #include "globals.hh" #include "track.hh" #include "utils.hh" #include "midiclock.hh" #include "sequencer.hh" #include "instr_abstract.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" namespace Heck { namespace State { bool record_mode{ false }; bool clear_mode{ false }; } // namespace State // ============================================================================================= // INIT // ============================================================================================= ld::DaisySeed hw{}; ld::Switch but_rec{}; ld::Switch but_clear{}; static ld::MidiUartHandler midi{}; static ld::FIFO event_log{}; Instrument::ZOsc instrument0{}; Instrument::FM instrument1{}; Instrument::Grainlet instrument2{}; Instrument::HiHat instrument3{}; std::array tracks; 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(Constants::CPU_BOOST480MHZ); 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::AUDIO_BUFFERSIZE); hw.SetAudioBlockSize(Constants::AUDIO_BUFFERSIZE); hw.PrintLine("Setting Samplerate: %i", Constants::AUDIO_SAMPLERATE); hw.SetAudioSampleRate(Constants::AUDIO_SAMPLERATE); hw.PrintLine("Initializing MIDI"); ld::MidiUartHandler::Config midi_config{}; midi.Init(midi_config); init_tracks(); hw.PrintLine("Starting MIDI Receive"); midi.StartReceiveRt(&midi_realtime_handler); 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 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_in_from_uart_polling(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: { ld::ControlChangeEvent cc = msg.AsControlChange(); if (State::clear_mode) { ld::ControlChangeEvent cc = msg.AsControlChange(); hw.PrintLine("Clear: ch: %i, cc: %i",cc.channel,cc.control_number); sequencer.clear_track_cc(cc.channel, cc.control_number); } else { sequencer.midi_in(msg); 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_in_from_uart_interrupt() { while (midi.HasEvents()) { ld::MidiEvent msg = midi.PopEvent(); event_log.PushBack(msg); midi_in_from_uart_polling(msg); } } void midi_in_from_sequencer() { std::vector queue = sequencer.midi_out(); for (ld::MidiEvent msg : queue) { midi_in_from_uart_polling(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(); } midi_in_from_sequencer(); midi_in_from_uart_interrupt(); // REC but_rec.Debounce(); but_record_new = but_rec.Pressed(); if (but_record_current != but_record_new) { // State changed but_record_current = but_record_new; if (but_record_current) { if (State::clear_mode) { hw.PrintLine("CLEAR SEQUENCE"); sequencer.clear_sequence(); } else { hw.PrintLine("BUTTON RECORD ON"); State::record_mode = true; } } else { hw.PrintLine("BUTTON RECORD OFF"); State::record_mode = false; } sequencer.recording = but_record_current; } // CLEAR but_clear.Debounce(); but_clear_new = but_clear.Pressed(); if (but_clear_current != but_clear_new) { // State changed but_clear_current = but_clear_new; if (but_clear_current) { hw.PrintLine("BUTTON CLEAR ON"); State::clear_mode = true; } else { hw.PrintLine("BUTTON CLEAR OFF"); State::clear_mode = false; } } 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 != ld::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(); }