Compare commits
49 Commits
Author | SHA1 | Date |
---|---|---|
|
28c16ecc82 | 8 months ago |
|
f1ed52c332 | 8 months ago |
|
3292cba8cd | 8 months ago |
|
ccb82648c0 | 8 months ago |
|
6b5c1a4186 | 8 months ago |
|
ae95e03792 | 8 months ago |
|
a3e3f5b6f0 | 8 months ago |
|
c5e06d05ea | 8 months ago |
|
2002c50870 | 8 months ago |
|
99b283dabc | 8 months ago |
|
16de858404 | 8 months ago |
|
3c40f99020 | 8 months ago |
|
942003449e | 8 months ago |
|
8207184595 | 8 months ago |
|
53e5b8388d | 8 months ago |
|
ffecf54f90 | 8 months ago |
|
51bb0adae1 | 8 months ago |
|
52e300d9e2 | 8 months ago |
|
e480ff163c | 8 months ago |
|
a992e9194b | 8 months ago |
|
35ee7364a4 | 8 months ago |
|
5d7c323416 | 8 months ago |
|
784e6d793f | 8 months ago |
|
4b483c4243 | 8 months ago |
|
ba2ce38432 | 8 months ago |
|
d5b2a999b6 | 8 months ago |
|
417567f778 | 8 months ago |
|
3e54a8fce2 | 8 months ago |
|
e376b2d71e | 8 months ago |
|
8bc7d1c424 | 9 months ago |
|
9ec1fc2ed8 | 9 months ago |
|
df8a24709b | 9 months ago |
|
63ed5834b1 | 9 months ago |
|
78a52d8107 | 9 months ago |
|
f5fabe7041 | 9 months ago |
|
505911021a | 9 months ago |
|
0ba8e189d2 | 9 months ago |
|
bfac86d8a4 | 9 months ago |
|
8e63cdc858 | 9 months ago |
|
3f343f2b59 | 9 months ago |
|
d191fd7c56 | 9 months ago |
|
904a8c644f | 9 months ago |
|
a0a3f4383e | 9 months ago |
|
792be4fb76 | 9 months ago |
|
cf227919a9 | 9 months ago |
|
044143e44f | 9 months ago |
|
83cc242614 | 9 months ago |
|
22f118051f | 9 months ago |
|
bfa9393c7c | 9 months ago |
43 changed files with 994 additions and 738 deletions
@ -1,27 +1,22 @@ |
|||
HERE:=$(dir $(lastword $(MAKEFILE_LIST))) |
|||
-include $(HERE)build.conf |
|||
-include build.conf |
|||
|
|||
# Project Name
|
|||
TARGET?=perkons |
|||
FLASH_TARGET ?= main_osp |
|||
DEBUG ?= 0 |
|||
|
|||
# Configure for debugging
|
|||
# common configurations:
|
|||
# use DEBUG = 1 and OPT = -Og for debugging
|
|||
# or DEBUG = 0 and OPT = -O3 for performance
|
|||
DEBUG?=0 |
|||
OPT?=-O3 |
|||
LIBDAISY_DIR ?= ../../heck_libDaisy |
|||
DAISYSP_DIR ?= ../../heck_DaisySP |
|||
|
|||
# (optional) Includes FatFS source files within project.
|
|||
#USE_FATFS = 1
|
|||
LIBDIZZY_DIR ?= ../../heck_libdizzy/src/ |
|||
CXXFLAGS += -I$(LIBDIZZY_DIR) |
|||
LDFLAGS += -L$(LIBDIZZY_DIR) |
|||
LDFLAGS += -ldizzy |
|||
|
|||
# Relative to dir 'src'
|
|||
LIBDAISY_DIR?=../../heck_libDaisy |
|||
DAISYSP_DIR?=../../heck_DaisySP |
|||
CXX_STANDARD ?= -std=c++17 |
|||
CXXFLAGS += -Wall -Wno-unused -Wno-reorder-ctor -Wno-switch |
|||
CXXFLAGS += \
|
|||
-fdiagnostics-color=always \
|
|||
-fmessage-length=170 \
|
|||
-fdiagnostics-path-format=inline-events \
|
|||
-fdiagnostics-show-template-tree \
|
|||
|
|||
CXXFLAGS+=-Wall -Wno-unused -Wno-reorder-ctor -Wno-switch |
|||
|
|||
ifneq (,$(findstring g++,$(CXX))) |
|||
CXXFLAGS+=-fdiagnostics-color=always |
|||
else ifneq (,$(findstring clang,$(CXX))) |
|||
CXXFLAGS+=-fcolor-diagnostics |
|||
endif |
|||
LDFLAGS += -u_printf_float |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,4 @@ |
|||
Current Controller (CM4) version: 1.0.2 - g6aa8285 |
|||
Current Voice Engine (CM7) version: 1.0.2 - g6aa8285 |
|||
|
|||
Last firmware image used for update: perkons_both_v1.0.2-0-g6aa8285.img |
@ -0,0 +1,37 @@ |
|||
{ |
|||
"clock-source": "INT", |
|||
"clock-in-ppqn": 4, |
|||
"clock-out-ppqn": 4, |
|||
"kit-link": true, |
|||
"pot-catch": false, |
|||
"accent": false, |
|||
"smooth-param-rec": true, |
|||
"vintage-bbd": true, |
|||
"trig-key-behav": true, |
|||
"hard-filter-sw": true, |
|||
"trig-sensetivity": { |
|||
"ch1": 0, |
|||
"ch2": 6, |
|||
"ch3": 2, |
|||
"ch4": 0 |
|||
}, |
|||
"midi-clock-out": true, |
|||
"midi-cont-clock": false, |
|||
"midi-thru": false, |
|||
"midi-seq-out": false, |
|||
"midi-cc-out": false, |
|||
"midi-mode": "SINGLE", |
|||
"midi-channel": { |
|||
"voice1": 0, |
|||
"voice2": 1, |
|||
"voice3": 2, |
|||
"voice4": 3, |
|||
"single": 0 |
|||
}, |
|||
"midi-trig-note": { |
|||
"voice1": 36, |
|||
"voice2": 37, |
|||
"voice3": 38, |
|||
"voice4": 39 |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
{ |
|||
"voice1": { |
|||
"tune": 70, |
|||
"decay": 74, |
|||
"param1": 71, |
|||
"param2": 75, |
|||
"cutoff": 72, |
|||
"drive": 76, |
|||
"fx-send": 73, |
|||
"level": 77, |
|||
"algo": 78, |
|||
"mode": 79, |
|||
"filter": 80 |
|||
}, |
|||
"voice2": { |
|||
"tune": 70, |
|||
"decay": 74, |
|||
"param1": 71, |
|||
"param2": 75, |
|||
"cutoff": 72, |
|||
"drive": 76, |
|||
"fx-send": 73, |
|||
"level": 77, |
|||
"algo": 78, |
|||
"mode": 79, |
|||
"filter": 80 |
|||
}, |
|||
"voice3": { |
|||
"tune": 70, |
|||
"decay": 74, |
|||
"param1": 71, |
|||
"param2": 75, |
|||
"cutoff": 72, |
|||
"drive": 76, |
|||
"fx-send": 73, |
|||
"level": 77, |
|||
"algo": 78, |
|||
"mode": 79, |
|||
"filter": 80 |
|||
}, |
|||
"voice4": { |
|||
"tune": 70, |
|||
"decay": 74, |
|||
"param1": 71, |
|||
"param2": 75, |
|||
"cutoff": 72, |
|||
"drive": 76, |
|||
"fx-send": 73, |
|||
"level": 77, |
|||
"algo": 78, |
|||
"mode": 79, |
|||
"filter": 80 |
|||
} |
|||
} |
@ -0,0 +1,54 @@ |
|||
{ |
|||
"voice1": { |
|||
"tune": 70, |
|||
"decay": 74, |
|||
"param1": 71, |
|||
"param2": 75, |
|||
"cutoff": 72, |
|||
"drive": 76, |
|||
"fx-send": 73, |
|||
"level": 77, |
|||
"algo": 78, |
|||
"mode": 79, |
|||
"filter": 80 |
|||
}, |
|||
"voice2": { |
|||
"tune": 81, |
|||
"decay": 85, |
|||
"param1": 82, |
|||
"param2": 86, |
|||
"cutoff": 83, |
|||
"drive": 87, |
|||
"fx-send": 84, |
|||
"level": 88, |
|||
"algo": 89, |
|||
"mode": 90, |
|||
"filter": 91 |
|||
}, |
|||
"voice3": { |
|||
"tune": 92, |
|||
"decay": 96, |
|||
"param1": 93, |
|||
"param2": 97, |
|||
"cutoff": 94, |
|||
"drive": 98, |
|||
"fx-send": 95, |
|||
"level": 99, |
|||
"algo": 100, |
|||
"mode": 101, |
|||
"filter": 102 |
|||
}, |
|||
"voice4": { |
|||
"tune": 103, |
|||
"decay": 107, |
|||
"param1": 104, |
|||
"param2": 108, |
|||
"cutoff": 105, |
|||
"drive": 109, |
|||
"fx-send": 106, |
|||
"level": 110, |
|||
"algo": 111, |
|||
"mode": 112, |
|||
"filter": 113 |
|||
} |
|||
} |
@ -1,19 +1,7 @@ |
|||
include ../Makefile.conf |
|||
HERE:=$(dir $(lastword $(MAKEFILE_LIST))) |
|||
include $(HERE)../Makefile.conf |
|||
|
|||
ALL_SRC=$(wildcard *.cc) |
|||
TARGET_SRC=main_$(TARGET).cc |
|||
MODULES_SRC=$(filter-out main_%,$(ALL_SRC)) |
|||
CXX_SRC=$(MODULES_SRC) $(TARGET_SRC) |
|||
#TARGET_LIB =
|
|||
|
|||
$(info CXXFLAGS: $(CXXFLAGS)) |
|||
$(info SOURCES: $(ALL_SRC)) |
|||
$(info MODULES: $(MODULES_SRC)) |
|||
$(info TARGET_SRC: $(TARGET_SRC)) |
|||
$(info CXX_SRC: $(CXX_SRC)) |
|||
|
|||
# libDaisy Makefile variables
|
|||
CFLAGS=$(CXXFLAGS) |
|||
SYSTEM_FILES_DIR=$(LIBDAISY_DIR)/core |
|||
CPP_SOURCES=$(CXX_SRC) |
|||
|
|||
include $(SYSTEM_FILES_DIR)/Makefile |
|||
SOURCES := $(wildcard *) |
|||
include $(LIBDAISY_DIR)/core/Makefile |
|||
|
@ -0,0 +1,404 @@ |
|||
#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(); |
|||
} |
@ -1,359 +0,0 @@ |
|||
// 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(); |
|||
} |
@ -0,0 +1,2 @@ |
|||
#include "osp_utils.hh" |
|||
|
@ -0,0 +1,129 @@ |
|||
#ifndef HECK_OSP_UTILS_HH |
|||
#define HECK_OSP_UTILS_HH |
|||
|
|||
#include "dizzy.hh" |
|||
#include "daisy_seed.h" |
|||
#include "utils.hh" |
|||
|
|||
namespace Heck::OSP { |
|||
namespace Sig { |
|||
struct Delta { |
|||
float process(float in) |
|||
{ |
|||
float delta = in - history[0]; |
|||
if (abs(delta) < 0.4) { |
|||
delta == 0; |
|||
} |
|||
history[0] = in; |
|||
return delta; |
|||
} |
|||
|
|||
float history[1]; |
|||
}; |
|||
|
|||
template<u32 SIZE> |
|||
struct RingBuffer { |
|||
void init(float* buffer) |
|||
{ |
|||
buf_ = buffer; |
|||
} |
|||
|
|||
void push_front(float in) |
|||
{ |
|||
u32 index_new = index_first_ + 1; |
|||
if (index_new < SIZE) { |
|||
index_first_ = index_new; |
|||
} else { |
|||
index_first_ = 0; |
|||
} |
|||
|
|||
buf_[index_first_] = in; |
|||
} |
|||
|
|||
// index 0 reads the last sample written
|
|||
float get(u32 index) const |
|||
{ |
|||
u32 pos_read = index_first_ + index; |
|||
if (!(pos_read < SIZE)) { |
|||
pos_read = pos_read - (SIZE - 1); |
|||
} |
|||
return buf_[pos_read]; |
|||
} |
|||
|
|||
u32 index_first_{ SIZE - 1 }; |
|||
float* buf_; |
|||
u32 size = SIZE; |
|||
}; |
|||
|
|||
template<int SIZE> |
|||
struct Delay101 { |
|||
void init(Sig::RingBuffer<SIZE>* buff) |
|||
{ |
|||
buff_ = buff; |
|||
} |
|||
|
|||
float process(float sig_in) |
|||
{ |
|||
buff_->push_front(sig_in + (sig_tap1_ * delayfb_)); |
|||
sig_tap1_ = buff_->get(buff_->size - (u32)((delaytime_ * delaytime_) * buff_->size)); |
|||
return sig_in + sig_tap1_; |
|||
} |
|||
|
|||
float delayfb_; // 0-1
|
|||
float delaytime_; // 0-1
|
|||
float sig_tap1_; |
|||
Sig::RingBuffer<SIZE>* buff_; |
|||
}; |
|||
|
|||
|
|||
} // namespace Sig
|
|||
} // namespace Heck::OSP
|
|||
|
|||
namespace Heck::OSP { |
|||
struct MidiLoggerBuffered { |
|||
|
|||
void push(const ld::MidiEvent& msg) |
|||
{ |
|||
event_log_.PushBack(msg); |
|||
} |
|||
|
|||
void print() |
|||
{ |
|||
u32 time_now{ 0 }; |
|||
if (!event_log_.IsEmpty()) { |
|||
auto msg = event_log_.PopFront(); |
|||
switch (msg.type) { |
|||
case ld::MidiMessageType::SystemRealTime: { |
|||
char outstr[256]; |
|||
char rttype_str[32]; |
|||
dz::GetMidiRTTypeAsString(msg, rttype_str); |
|||
sprintf(outstr, "RT: type: %s\n", rttype_str); |
|||
if (msg.srt_type != ld::TimingClock) { |
|||
ld::DaisySeed::PrintLine("%s", outstr); |
|||
} |
|||
} break; |
|||
case ld::NoteOn: |
|||
case ld::NoteOff: |
|||
case ld::MidiMessageType::ControlChange: { |
|||
char outstr[256]; |
|||
char type_str[32]; |
|||
dz::GetMidiTypeAsString(msg, type_str); |
|||
sprintf( |
|||
outstr, |
|||
"time-last:\t%ld\ttype: %s\tChannel: %d\tData MSB: " |
|||
"%d\tData LSB: %d\n", |
|||
time_now, |
|||
type_str, |
|||
msg.channel, |
|||
msg.data[0], |
|||
msg.data[1]); |
|||
ld::DaisySeed::PrintLine("%s", outstr); |
|||
} break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
ld::FIFO<ld::MidiEvent, 128> event_log_{}; |
|||
}; |
|||
} // namespace Heck::OSP
|
|||
#endif // HECK_DAISY_UTILS_HH
|
@ -1,72 +1,88 @@ |
|||
#include "sequencer.hh" |
|||
|
|||
namespace Heck { |
|||
Sequencer::Sequencer() |
|||
namespace Heck::OSP { |
|||
constexpr i8 EMPTY = -1; |
|||
|
|||
Sequencer::Sequencer() {} |
|||
|
|||
void Sequencer::init() |
|||
{ |
|||
step_current_ = 0; |
|||
recording_ = false; |
|||
clear_sequence(); |
|||
} |
|||
|
|||
void Sequencer::reset() |
|||
{ |
|||
step_current = 0; |
|||
step_current_ = 0; |
|||
} |
|||
|
|||
bool Sequencer::recording(bool set_rec) |
|||
{ |
|||
recording_ = set_rec; |
|||
return recording_; |
|||
} |
|||
|
|||
void Sequencer::next_step() |
|||
{ |
|||
step_current++; |
|||
if (step_current >= STEP_COUNT) { |
|||
step_current = 0; |
|||
step_current_++; |
|||
if (step_current_ >= STEP_COUNT) { |
|||
step_current_ = 0; |
|||
} |
|||
} |
|||
|
|||
void Sequencer::clear_track(int track_nr) |
|||
void Sequencer::clear_track(i8 track_nr) |
|||
{ |
|||
for (int step = 0; step < STEP_COUNT; step++) { |
|||
for (int cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { |
|||
sequence[track_nr][step][cc_nr] = -1; |
|||
for (i8 step = 0; step < STEP_COUNT; step++) { |
|||
for (i8 cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { |
|||
sequence_[track_nr][step][cc_nr] = EMPTY; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void Sequencer::clear_track_cc(int track_nr, int cc_nr) |
|||
void Sequencer::clear_track_cc(i8 track_nr, i8 cc_nr) |
|||
{ |
|||
for (int step = 0; step < STEP_COUNT; step++) { |
|||
sequence[track_nr][step][cc_nr] = -1; |
|||
for (i8 step = 0; step < STEP_COUNT; step++) { |
|||
sequence_[track_nr][step][cc_nr] = EMPTY; |
|||
} |
|||
} |
|||
|
|||
void Sequencer::clear_sequence() |
|||
{ |
|||
for (int track = 0; track < TRACK_COUNT; track++) { |
|||
for (i8 track = 0; track < TRACK_COUNT; track++) { |
|||
clear_track(track); |
|||
} |
|||
} |
|||
|
|||
void Sequencer::midi_in(ld::MidiEvent& msg) |
|||
{ |
|||
if (recording) { |
|||
if (recording_) { |
|||
ld::ControlChangeEvent cc = msg.AsControlChange(); |
|||
sequence[cc.channel][step_current][cc.control_number] = cc.value; |
|||
sequence_[cc.channel][step_current_][cc.control_number] = cc.value; |
|||
} |
|||
} |
|||
|
|||
std::vector<ld::MidiEvent> Sequencer::midi_out() |
|||
void Sequencer::midi_out(Sequencer::MidiOutCallback cb) |
|||
{ |
|||
std::vector<ld::MidiEvent> ret{}; |
|||
for (int track = 0; track < TRACK_COUNT; track++) { |
|||
for (int cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { |
|||
int cc_val = sequence[track][step_current][cc_nr]; |
|||
if (cc_val != -1) { |
|||
ld::MidiEvent ev{}; |
|||
ev.channel = track; |
|||
ev.type = ld::ControlChange; |
|||
ev.data[0] = cc_nr; |
|||
ev.data[1] = cc_val; |
|||
ret.push_back(ev); |
|||
if (!cb) { |
|||
ld::DaisySeed::PrintLine("Sequencer: Error - no midi out callback registered"); |
|||
return; |
|||
} else { |
|||
for (i8 track = 0; track < TRACK_COUNT; track++) { |
|||
for (i8 cc_nr = 0; cc_nr < MIDI_MAX; cc_nr++) { |
|||
i8 cc_val = sequence_[track][step_current_][cc_nr]; |
|||
if (cc_val != EMPTY) { |
|||
ld::MidiEvent ev{}; |
|||
ev.channel = track; |
|||
ev.type = ld::ControlChange; |
|||
ev.data[0] = cc_nr; |
|||
ev.data[1] = cc_val; |
|||
cb(ev); |
|||
} |
|||
} |
|||
} |
|||
return; |
|||
} |
|||
return ret; |
|||
} |
|||
|
|||
} // namespace Heck
|
|||
} // namespace Heck::OSP
|
@ -1,34 +1,39 @@ |
|||
#ifndef HECK_PERKONS_SEQUENCER_HH |
|||
#define HECK_PERKONS_SEQUENCER_HH |
|||
#ifndef HECK_OSP_SEQUENCER_HH |
|||
#define HECK_OSP_SEQUENCER_HH |
|||
|
|||
#include <vector> |
|||
#include "daisy_seed.h" |
|||
#include "dizzy_types.hh" |
|||
|
|||
namespace ld = daisy; |
|||
|
|||
namespace Heck { |
|||
namespace Heck::OSP { |
|||
struct Sequencer { |
|||
// its a MIDI CC step sequencer
|
|||
constexpr static int MIDI_MAX = 127; |
|||
constexpr static int STEP_COUNT = 16; |
|||
constexpr static int TRACK_COUNT = 16; |
|||
constexpr static i8 MIDI_MAX = 127; |
|||
constexpr static i8 STEP_COUNT = 16; |
|||
constexpr static i8 TRACK_COUNT = 4; |
|||
|
|||
using Step = std::array<int, MIDI_MAX>; |
|||
using MidiOutCallback = std::function<void(ld::MidiEvent ev)>; |
|||
|
|||
using Step = std::array<i8, MIDI_MAX>; |
|||
using Track = std::array<Step, STEP_COUNT>; |
|||
using Sequence = std::array<Track, TRACK_COUNT>; |
|||
|
|||
Sequencer(); |
|||
void init(); |
|||
void reset(); |
|||
void next_step(); |
|||
void clear_track(int track_nr); |
|||
void clear_track_cc(int track_nr, int cc_nr); |
|||
bool recording(bool set_rec); |
|||
void clear_track(i8 track_nr); |
|||
void clear_track_cc(i8 track_nr, i8 cc_nr); |
|||
void clear_sequence(); |
|||
void midi_in(ld::MidiEvent& msg); |
|||
std::vector<ld::MidiEvent> midi_out(); |
|||
|
|||
int step_current{ 0 }; |
|||
bool recording{ false }; |
|||
Sequence sequence{}; |
|||
void midi_out(Sequencer::MidiOutCallback cb); |
|||
private: |
|||
i8 step_current_; |
|||
bool recording_; |
|||
Sequence sequence_; |
|||
}; |
|||
} // namespace Heck
|
|||
|
|||
|
@ -1,30 +0,0 @@ |
|||
#ifndef HECK_DAISY_TYPES_HH |
|||
#define HECK_DAISY_TYPES_HH |
|||
|
|||
#include <cstdint> |
|||
#include "daisy_seed.h" |
|||
#include "daisysp.h" |
|||
|
|||
namespace Heck { |
|||
|
|||
// Types
|
|||
using u8 = uint8_t; |
|||
using u16 = uint16_t; |
|||
using u32 = uint32_t; |
|||
using u64 = uint64_t; |
|||
|
|||
using i8 = int8_t; |
|||
using i16 = int16_t; |
|||
using i32 = int32_t; |
|||
using i64 = int64_t; |
|||
|
|||
using f32 = float; |
|||
using f64 = double; |
|||
|
|||
// namespace aliases
|
|||
namespace ld = daisy; |
|||
namespace dsp = daisysp; |
|||
} // namespace Heck
|
|||
|
|||
|
|||
#endif |
@ -1,94 +0,0 @@ |
|||
#include "utils.hh" |
|||
|
|||
namespace Heck { |
|||
|
|||
void GetMidiTypeAsString(ld::MidiEvent& msg, char* str) |
|||
{ |
|||
switch (msg.type) { |
|||
case ld::NoteOff: |
|||
strcpy(str, "NoteOff"); |
|||
break; |
|||
case ld::NoteOn: |
|||
strcpy(str, "NoteOn"); |
|||
break; |
|||
case ld::PolyphonicKeyPressure: |
|||
strcpy(str, "PolyKeyPres."); |
|||
break; |
|||
case ld::ControlChange: |
|||
strcpy(str, "CC"); |
|||
break; |
|||
case ld::ProgramChange: |
|||
strcpy(str, "Prog. Change"); |
|||
break; |
|||
case ld::ChannelPressure: |
|||
strcpy(str, "Chn. Pressure"); |
|||
break; |
|||
case ld::PitchBend: |
|||
strcpy(str, "PitchBend"); |
|||
break; |
|||
case ld::SystemCommon: |
|||
strcpy(str, "Sys. Common"); |
|||
break; |
|||
case ld::SystemRealTime: |
|||
strcpy(str, "Sys. Realtime"); |
|||
break; |
|||
case ld::ChannelMode: |
|||
strcpy(str, "Chn. Mode"); |
|||
break; |
|||
default: |
|||
strcpy(str, "Unknown"); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
void GetMidiRTTypeAsString(ld::MidiEvent& msg, char* str) |
|||
{ |
|||
switch (msg.srt_type) { |
|||
case ld::TimingClock: |
|||
strcpy(str, "TimingClock"); |
|||
break; |
|||
case ld::SRTUndefined0: |
|||
strcpy(str, "SRTUndefined0"); |
|||
break; |
|||
case ld::Start: |
|||
strcpy(str, "Start"); |
|||
break; |
|||
case ld::Continue: |
|||
strcpy(str, "Continue"); |
|||
break; |
|||
case ld::Stop: |
|||
strcpy(str, "Stop"); |
|||
break; |
|||
case ld::SRTUndefined1: |
|||
strcpy(str, "SRTUndefined1"); |
|||
break; |
|||
case ld::ActiveSensing: |
|||
strcpy(str, "ActiveSensing"); |
|||
break; |
|||
case ld::Reset: |
|||
strcpy(str, "Reset"); |
|||
break; |
|||
case ld::SystemRealTimeLast: |
|||
strcpy(str, "SystemRealTimeLast"); |
|||
break; |
|||
default: |
|||
strcpy(str, "Unknown"); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
|
|||
float scalen_min_max(float val, float min, float max) |
|||
{ |
|||
float range = max - min; |
|||
float ret = min + (val * range); |
|||
return ret; |
|||
} |
|||
|
|||
float scalen_center_range(float val, float center, float range) |
|||
{ |
|||
float min = center - (range / 2); |
|||
float ret = min + (val * range); |
|||
return ret; |
|||
} |
|||
} // namespace Heck
|
@ -1,15 +0,0 @@ |
|||
#ifndef HECK_DAISY_UTILS_HH |
|||
#define HECK_DAISY_UTILS_HH |
|||
|
|||
#include "daisy_seed.h" |
|||
#include "globals.hh" |
|||
|
|||
namespace Heck { |
|||
void GetMidiTypeAsString(ld::MidiEvent& msg, char* str); |
|||
void GetMidiRTTypeAsString(ld::MidiEvent& msg, char* str); |
|||
|
|||
float scalen_min_max(float val, float min, float max); |
|||
float scalen_center_range(float val, float center, float range); |
|||
} // namespace Heck
|
|||
|
|||
#endif // HECK_DAISY_UTILS_HH
|
@ -0,0 +1,22 @@ |
|||
#!/usr/bin/env python3 |
|||
|
|||
# Standard MIDI sequencer model |
|||
ppq = 24 |
|||
|
|||
cc_count = 128 |
|||
cc_bits = 7 |
|||
note_bits = 127 # 1 = trig , 7 = monophon, 127 = polyphon |
|||
|
|||
bars = 1 |
|||
channels = 16 |
|||
|
|||
|
|||
bits_per_pulse = (cc_count * cc_bits) + note_bits |
|||
bytes_per_quarter = bits_per_pulse / 8 * ppq |
|||
|
|||
print('quarter size: {0} bits {1}bytes'.format(bytes_per_quarter * 8, bytes_per_quarter)) |
|||
print('sequence size: {0} kb'.format((bytes_per_quarter * 4 * bars * channels) / 1024)) |
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,32 @@ |
|||
#!/usr/bin/env python3 |
|||
|
|||
# Perkons Sequencer Model |
|||
# (Hypothetical) |
|||
|
|||
ppq_cc = 16 |
|||
cc_count = 8 |
|||
cc_bits = 7 |
|||
|
|||
ppq_note = 4 |
|||
note_trigger_bits = 1 |
|||
note_threepos_count = 3 |
|||
note_threepos_bits = 2 |
|||
note_accent_bits = 1 |
|||
note_ratchet_bits = 3 |
|||
note_oddprob_bits = 4 |
|||
|
|||
bars = 1 |
|||
channels = 4 |
|||
|
|||
|
|||
cc_bits_per_pulse = cc_count * cc_bits |
|||
note_bits_per_pulse = (note_threepos_count * note_threepos_bits) + (note_trigger_bits) + note_ratchet_bits + note_oddprob_bits |
|||
|
|||
cc_bytes_per_quarter = cc_bits_per_pulse / 8 * ppq_cc |
|||
note_bytes_per_quarter = note_bits_per_pulse / 8 * ppq_note |
|||
|
|||
bytes_per_quarter = cc_bytes_per_quarter + note_bytes_per_quarter |
|||
#bytes_per_quarter = 128 |
|||
|
|||
print('quarter size: {0} bits {1}bytes'.format(bytes_per_quarter * 8, bytes_per_quarter)) |
|||
print('sequence size: {0} kb'.format((bytes_per_quarter * 4 * bars * channels) / 1024)) |
@ -0,0 +1,29 @@ |
|||
#include <iostream> |
|||
|
|||
template<unsigned int SIZE> struct Supplier { |
|||
char data[5]{ 'h', 'e', 'l', 'l', 'o' }; |
|||
uint8_t pos{ 0 }; |
|||
|
|||
char next() |
|||
{ |
|||
pos++; |
|||
if (pos >= SIZE) { |
|||
return -34; |
|||
} |
|||
|
|||
return data[pos]; |
|||
} |
|||
}; |
|||
|
|||
int main() |
|||
{ |
|||
std::cout << "BEGIN" << std::endl; |
|||
Supplier<5> sup{}; |
|||
|
|||
|
|||
while (char c = true) { |
|||
std::cout << c; |
|||
} |
|||
|
|||
std::cout << "END" << std::endl; |
|||
} |
Loading…
Reference in new issue