diff --git a/src/instr_kick.cc b/src/instr_kick.cc new file mode 100644 index 0000000..fc539ba --- /dev/null +++ b/src/instr_kick.cc @@ -0,0 +1,55 @@ +#include "instr_kick.hh" +#include "daisysp.h" + +using namespace daisysp; + + +namespace Heck { + namespace Instrument { + + void Kick::init(float samplerate) + { + osc.Init(samplerate); + + osc.SetWaveform(Oscillator::WAVE_TRI); + osc.SetAmp(1); + + pitchEnv.Init(samplerate); + pitchEnv.SetTime(ADENV_SEG_ATTACK, .001); + pitchEnv.SetTime(ADENV_SEG_DECAY, .01); + pitchEnv.SetMax(250); + pitchEnv.SetMin(20); + + volEnv.Init(samplerate); + volEnv.SetTime(ADENV_SEG_ATTACK, .0001); + volEnv.SetTime(ADENV_SEG_DECAY, 0.1); + volEnv.SetMax(1); + volEnv.SetMin(0); + } + + + void Kick::trigger() + { + volEnv.Trigger(); + pitchEnv.Trigger(); + return; + } + + void Kick::ctl(unsigned int ctl_nr, float val) noexcept {} + void Kick::switch_mode(unsigned int pos) noexcept {} + void Kick::swtich_variation(unsigned int pos) noexcept {} + void Kick::switch_filter(unsigned int pos) noexcept {} + + float Kick::nextsample() noexcept + { + float kck_env_out = volEnv.Process(); + + osc.SetFreq(pitchEnv.Process()); + osc.SetAmp(kck_env_out); + float osc_out = osc.Process(); + + return osc_out; + } + + } // namespace Instrument +} // namespace Heck \ No newline at end of file diff --git a/src/instr_kick.hh b/src/instr_kick.hh new file mode 100644 index 0000000..d41b2bb --- /dev/null +++ b/src/instr_kick.hh @@ -0,0 +1,34 @@ +#ifndef HECK_DAISY_INSTR_KICK_HH +#define HECK_DAISY_INSTR_KICK_HH + +#include "perkons_instrument_interface.hh" +#include "daisy_seed.h" +#include "daisysp.h" + +namespace ld = daisy; +namespace dsp = daisysp; + +namespace Heck { + namespace Instrument { + + class Kick : public PerkonsInstrumentInterface { + public: + void init(float samplerate); + + void trigger() noexcept override; + void ctl(unsigned int ctl_nr, float val) noexcept override; + void switch_mode(unsigned int pos) noexcept override; + void swtich_variation(unsigned int pos) noexcept override; + void switch_filter(unsigned int pos) noexcept override; + + float nextsample() noexcept override; + + private: + dsp::Oscillator osc; + dsp::AdEnv volEnv; + dsp::AdEnv pitchEnv; + }; + + } // namespace Instrument +} // namespace Heck +#endif \ No newline at end of file diff --git a/src/instr_noise.cc b/src/instr_noise.cc new file mode 100644 index 0000000..c5de580 --- /dev/null +++ b/src/instr_noise.cc @@ -0,0 +1,40 @@ +#include "instr_noise.hh" +#include "daisysp.h" + +using namespace daisysp; + +namespace Heck { + namespace Instrument { + + void Noise::init(float samplerate) + { + noise.Init(); + + env.Init(samplerate); + env.SetTime(ADENV_SEG_ATTACK, .0001); + env.SetTime(ADENV_SEG_DECAY, .01); + env.SetMax(1); + env.SetMin(0); + } + + void Noise::trigger() + { + env.Trigger(); + } + + void Noise::ctl(unsigned int ctl_nr, float val) noexcept {} + void Noise::switch_mode(unsigned int pos) noexcept {} + void Noise::swtich_variation(unsigned int pos) noexcept {} + void Noise::switch_filter(unsigned int pos) noexcept {} + + float Noise::nextsample() noexcept + { + float snr_env_out = env.Process(); + float sig = noise.Process(); + sig *= snr_env_out; + + return sig; + } + + } // namespace Instrument +} // namespace Heck \ No newline at end of file diff --git a/src/instr_noise.hh b/src/instr_noise.hh new file mode 100644 index 0000000..d21d68f --- /dev/null +++ b/src/instr_noise.hh @@ -0,0 +1,33 @@ +#ifndef HECK_DAISY_INSTR_NOISE_HH +#define HECK_DAISY_INSTR_NOISE_HH + +#include "perkons_instrument_interface.hh" +#include "daisy_seed.h" +#include "daisysp.h" + +namespace ld = daisy; +namespace dsp = daisysp; + +namespace Heck { + namespace Instrument { + + class Noise : public PerkonsInstrumentInterface { + public: + void init(float samplerate); + + void trigger() noexcept override; + void ctl(unsigned int ctl_nr, float val) noexcept override; + void switch_mode(unsigned int pos) noexcept override; + void swtich_variation(unsigned int pos) noexcept override; + void switch_filter(unsigned int pos) noexcept override; + + float nextsample() noexcept override; + + private: + dsp::WhiteNoise noise; + dsp::AdEnv env; + }; + + } // namespace Instrument +} // namespace Heck +#endif \ No newline at end of file diff --git a/src/main_perkons.cc b/src/main_perkons.cc new file mode 100644 index 0000000..df56283 --- /dev/null +++ b/src/main_perkons.cc @@ -0,0 +1,138 @@ +// Hecks perkons extension +#include "daisy_seed.h" +#include "daisysp.h" + +#include "types.hh" +#include "utils.hh" +#include "perkons_instrument_interface.hh" +#include "instr_kick.hh" +#include "instr_noise.hh" + +using namespace daisy; +using namespace daisysp; + +static DaisySeed hw{}; +static MidiUartHandler midi{}; +static FIFO event_log{}; +static float samplerate{}; + +static Heck::Instrument::Kick instr_kick{}; +static Heck::Instrument::Noise instr_noise{}; + +void AudioCallback( + AudioHandle::InterleavingInputBuffer in, + AudioHandle::InterleavingOutputBuffer out, + size_t size) +{ + float sig{}; + + for (size_t i = 0; i < size; i += 2) { + float kick_sig = instr_kick.nextsample(); + float noise_sig = instr_noise.nextsample(); + sig = .5 * noise_sig + .5 * kick_sig; + sig *= 0.4; + + out[i] = sig; + out[i + 1] = sig; + } +} + +int init_dsp_chain() +{ + instr_kick.init(samplerate); + instr_noise.init(samplerate); + return 0; +} + +int main(void) +{ + hw.Configure(); + hw.Init(); + hw.StartLog(); + + uint32_t now = System::GetNow(); + uint32_t log_time{}; + bool heartbeat_led_state{ false }; + uint32_t heartbeat_time{}; + + init_dsp_chain(); + + // Start Audio + hw.SetAudioBlockSize(4); + samplerate = hw.AudioSampleRate(); + hw.StartAudio(AudioCallback); + + // MIDI RX + MidiUartHandler::Config midi_config; + midi.Init(midi_config); + uint32_t systick_last_rt_msg{}; + midi.realtime_callback = [&systick_last_rt_msg](MidiEvent& msg) { + uint32_t systick_now = System::GetNow(); + uint32_t systick_since_last = systick_now - systick_last_rt_msg; + systick_last_rt_msg = systick_now; + + char outstr[128]; + char rttype_str[32]; + GetMidiRTTypeAsString(msg, rttype_str); + + // sprintf( + // outstr, + // "RT: systick: %i\ttimedelta: %i\ttype: %s\n", + // (unsigned int)systick_now, + // (unsigned int)systick_since_last, + // rttype_str); + // hw.PrintLine("%s", outstr); + }; + + midi.StartReceive(); + midi.Listen(); + + while (1) { + now = System::GetNow(); + while (midi.HasEvents()) { + MidiEvent msg = midi.PopEvent(); + event_log.PushBack(msg); + if (msg.type == MidiMessageType::NoteOn) { + if (msg.channel == 0) { + instr_kick.trigger(); + } + if (msg.channel == 1) { + instr_noise.trigger(); + } + + std::array bytes = { 0x90, 0x00, 0x00 }; + bytes[1] = msg.data[0]; + bytes[2] = msg.data[1]; + midi.SendMessage(bytes.data(), 3); + } else if (msg.type == MidiMessageType::ControlChange) { + } else { + } + } + + if (now - log_time > 5) { + log_time = now; + if (!event_log.IsEmpty()) { + auto msg = event_log.PopFront(); + char outstr[128]; + 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", + now, + type_str, + msg.channel, + msg.data[0], + msg.data[1]); + hw.PrintLine(outstr); + } + } + + if (now - heartbeat_time > 500) { + heartbeat_time = now; + hw.SetLed(heartbeat_led_state); + heartbeat_led_state = !heartbeat_led_state; + } + } +} \ No newline at end of file diff --git a/src/perkons_instrument_interface.hh b/src/perkons_instrument_interface.hh new file mode 100644 index 0000000..7f8114a --- /dev/null +++ b/src/perkons_instrument_interface.hh @@ -0,0 +1,21 @@ +#ifndef HECK_DAISY_PERKONS_INSTRUMENT_INTERFACE +#define HECK_DAISY_PERKONS_INSTRUMENT_INTERFACE + +class PerkonsInstrumentInterface { +public: + virtual void trigger() noexcept = 0; + + // ctl-nr must be 0-5 + // val must be 0-1 + virtual void ctl(unsigned int ctl_nr, float val) noexcept = 0; + + // pos must be either 0,1,2 + virtual void switch_mode(unsigned int pos) noexcept = 0; + virtual void swtich_variation(unsigned int pos) noexcept = 0; + virtual void switch_filter(unsigned int pos) noexcept = 0; + virtual float nextsample() noexcept = 0; + +private: +}; + +#endif // HECK_DAISY_PERKONS_INSTRUMENT_INTERFACE diff --git a/src/utils.cc b/src/utils.cc new file mode 100644 index 0000000..a910dcd --- /dev/null +++ b/src/utils.cc @@ -0,0 +1,78 @@ +#include "utils.hh" + +using namespace daisy; + +void GetMidiTypeAsString(MidiEvent& msg, char* str) +{ + switch (msg.type) { + case NoteOff: + strcpy(str, "NoteOff"); + break; + case NoteOn: + strcpy(str, "NoteOn"); + break; + case PolyphonicKeyPressure: + strcpy(str, "PolyKeyPres."); + break; + case ControlChange: + strcpy(str, "CC"); + break; + case ProgramChange: + strcpy(str, "Prog. Change"); + break; + case ChannelPressure: + strcpy(str, "Chn. Pressure"); + break; + case PitchBend: + strcpy(str, "PitchBend"); + break; + case SystemCommon: + strcpy(str, "Sys. Common"); + break; + case SystemRealTime: + strcpy(str, "Sys. Realtime"); + break; + case ChannelMode: + strcpy(str, "Chn. Mode"); + break; + default: + strcpy(str, "Unknown"); + break; + } +} + +void GetMidiRTTypeAsString(MidiEvent& msg, char* str) +{ + switch (msg.srt_type) { + case TimingClock: + strcpy(str, "TimingClock"); + break; + case SRTUndefined0: + strcpy(str, "SRTUndefined0"); + break; + case Start: + strcpy(str, "Start"); + break; + case Continue: + strcpy(str, "Continue"); + break; + case Stop: + strcpy(str, "Stop"); + break; + case SRTUndefined1: + strcpy(str, "SRTUndefined1"); + break; + case ActiveSensing: + strcpy(str, "ActiveSensing"); + break; + case Reset: + strcpy(str, "Reset"); + break; + case SystemRealTimeLast: + strcpy(str, "SystemRealTimeLast"); + break; + default: + strcpy(str, "Unknown"); + break; + } +} diff --git a/src/utils.hh b/src/utils.hh new file mode 100644 index 0000000..121a671 --- /dev/null +++ b/src/utils.hh @@ -0,0 +1,11 @@ +#ifndef HECK_DAISY_UTILS_HH +#define HECK_DAISY_UTILS_HH + +#include "daisy_seed.h" +#include "types.hh" + +void GetMidiTypeAsString(daisy::MidiEvent& msg, char* str); +void GetMidiRTTypeAsString(daisy::MidiEvent& msg, char* str); + + +#endif // HECK_DAISY_UTILS_HH \ No newline at end of file