The OSP is a hackable, open source drum machine that accidentally got very, very close to the erica perkons hd-01, which is real beauty of instrument design that i truly love, except for its unhackable closed source nature. So, we new need gnu one.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

359 lines
12 KiB

// Hecks perkons extension
#include <array>
#include <memory>
#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<ld::MidiEvent, 128> event_log{};
Instrument::ZOsc instrument0{};
Instrument::FM instrument1{};
Instrument::Grainlet instrument2{};
Instrument::HiHat instrument3{};
std::array<Track, Constants::TRACK_COUNT> 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<ld::MidiEvent> 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();
}