
8 changed files with 410 additions and 0 deletions
@ -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
|
@ -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 |
@ -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
|
@ -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 |
@ -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<MidiEvent, 128> 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<uint8_t, 3> 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; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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
|
@ -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; |
||||
|
} |
||||
|
} |
@ -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
|
Loading…
Reference in new issue