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.
404 lines
12 KiB
404 lines
12 KiB
#include <array>
|
|
#include <memory>
|
|
|
|
#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<Track, Constants::TRACK_COUNT> 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;
|
|
}
|
|
}
|
|
|
|
|
|
enum class MidiSource : u8 {
|
|
UART = 0,
|
|
SEQUENCER = 1
|
|
};
|
|
|
|
|
|
void midi_dispatch_note(ld::MidiEvent msg, MidiSource source)
|
|
{
|
|
ld::DaisySeed::PrintLine(
|
|
"[%i] MIDI from seq=%i : NOTE - ch: %i, nr: %i, val: %i",
|
|
ld::System::GetNow(),
|
|
source,
|
|
msg.channel,
|
|
msg.AsNoteOn().note,
|
|
msg.AsNoteOn().velocity);
|
|
if (msg.channel >= 0 && msg.channel < Constants::TRACK_COUNT) {
|
|
tracks[msg.channel].trigger();
|
|
}
|
|
}
|
|
|
|
void midi_dispatch_cc(ld::MidiEvent msg, MidiSource source)
|
|
{
|
|
const ld::ControlChangeEvent msg_cc = msg.AsControlChange();
|
|
|
|
ld::DaisySeed::PrintLine(
|
|
"[%i] MIDI from seq=%i : CC - ch: %i, nr: %i, val: %i",
|
|
ld::System::GetNow(),
|
|
source,
|
|
msg_cc.channel,
|
|
msg_cc.control_number,
|
|
msg_cc.value);
|
|
|
|
if (source == MidiSource::UART) {
|
|
if (!State::clear_mode) {
|
|
sequencer.midi_in(msg);
|
|
} else {
|
|
ld::DaisySeed::PrintLine("Clear: ch: %i, cc: %i", msg_cc.channel, msg_cc.control_number);
|
|
sequencer.clear_track_cc(msg_cc.channel, msg_cc.control_number);
|
|
}
|
|
}
|
|
|
|
const float cc_val_normalized = float(msg_cc.value) / 128.F;
|
|
switch (msg_cc.control_number) {
|
|
// Pots
|
|
case Constants::MIDI_Mapping::TRACK_PITCH:
|
|
tracks[msg_cc.channel].instrument->ctl(0, cc_val_normalized);
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_DECAY:
|
|
tracks[msg_cc.channel].decay(cc_val_normalized);
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_PARAM1:
|
|
tracks[msg_cc.channel].instrument->ctl(2, cc_val_normalized);
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_PARAM2:
|
|
tracks[msg_cc.channel].instrument->ctl(3, cc_val_normalized);
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_FILTER:
|
|
tracks[msg_cc.channel].filter(cc_val_normalized);
|
|
delaytime = cc_val_normalized;
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_DRIVE:
|
|
tracks[msg_cc.channel].drive(cc_val_normalized);
|
|
delayfb = cc_val_normalized;
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_VOLUME:
|
|
tracks[msg_cc.channel].volume(cc_val_normalized);
|
|
break;
|
|
// Switches
|
|
case Constants::MIDI_Mapping::TRACK_ALGO:
|
|
tracks[msg_cc.channel].instrument->switch_algo(int(cc_val_normalized * 2.));
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_MODE:
|
|
tracks[msg_cc.channel].instrument->switch_mode(int(cc_val_normalized * 2.));
|
|
break;
|
|
case Constants::MIDI_Mapping::TRACK_FILTERMODE:
|
|
tracks[msg_cc.channel].filtermode(cc_val_normalized);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void midi_dispatch(ld::MidiEvent msg, MidiSource source)
|
|
{
|
|
switch (msg.type) {
|
|
case ld::MidiMessageType::NoteOn:
|
|
midi_dispatch_note(msg, source);
|
|
break;
|
|
case ld::MidiMessageType::ControlChange:
|
|
midi_dispatch_cc(msg, source);
|
|
break;
|
|
default:
|
|
// Other MIDI message
|
|
// ld::DaisySeed::PrintLine(
|
|
// "[%i] UNHANDLED MIDI UNHANDLED MIDI from seq=%i : CC - ch: %i",
|
|
// ld::System::GetNow(),
|
|
// from_seq,
|
|
// msg.channel);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void midi_from_uart()
|
|
{
|
|
while (midi.HasEvents()) {
|
|
ld::MidiEvent msg = midi.PopEvent();
|
|
midi_dispatch(msg, MidiSource::UART);
|
|
}
|
|
}
|
|
|
|
void midi_from_sequencer()
|
|
{
|
|
sequencer.midi_out([](ld::MidiEvent msg) { midi_dispatch(msg, MidiSource::SEQUENCER); });
|
|
}
|
|
|
|
//-------------------------------------------------
|
|
// 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_func, 100> task_heartbeat{};
|
|
|
|
|
|
//-------------------------------------------------
|
|
// MIDI LOGGER
|
|
//-------------------------------------------------
|
|
void task_logger_midi_print_func(u32)
|
|
{
|
|
logger_midi.print();
|
|
}
|
|
|
|
dz::PeriodicTaskCT<task_logger_midi_print_func, 5> task_logger_midi_print{};
|
|
|
|
|
|
//-------------------------------------------------
|
|
// REC BUTTON
|
|
//-------------------------------------------------
|
|
bool but_rec_update()
|
|
{
|
|
but_rec.Debounce();
|
|
return but_rec.Pressed();
|
|
}
|
|
|
|
void but_rec_changed(dz::Cache<bool>& 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<bool> c_but_rec{ but_rec_update, but_rec_changed };
|
|
|
|
//-------------------------------------------------
|
|
// CLEAR BUTTON
|
|
//-------------------------------------------------
|
|
bool but_clear_update()
|
|
{
|
|
but_clear.Debounce();
|
|
return but_clear.Pressed();
|
|
}
|
|
|
|
void but_clear_changed(dz::Cache<bool>& 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<bool> c_but_clear{ but_clear_update, but_clear_changed };
|
|
|
|
|
|
//==========================================================================================
|
|
// 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();
|
|
|
|
|
|
c_but_clear.update_and_notify_change();
|
|
c_but_rec.update_and_notify_change();
|
|
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();
|
|
}
|