Compare commits

...

49 Commits
main ... master

Author SHA1 Message Date
heck 28c16ecc82 Fix: Clear mode inhibited instrument control 8 months ago
heck f1ed52c332 Only fetch sequencer output after next_step() 8 months ago
heck 3292cba8cd split midi_dispatch into subfunctions 8 months ago
heck ccb82648c0 stop using task per button for processing. just do it every main loop for now. 8 months ago
heck 6b5c1a4186 Fix: record button state inverted 8 months ago
heck ae95e03792 main_osp.cc - Log MIDI msg in midi_dispatch directly. stop using buffered MidiLoggerBuffered for that. 8 months ago
heck a3e3f5b6f0 Sequencer - ditch std::vector replace with cb. use i8 as seqdata type. take init out of the ctor. 8 months ago
heck c5e06d05ea main_osp.hh - REC = button 3, CLEAR = button 4 8 months ago
heck 2002c50870 tools - just add a c++ playground file 8 months ago
heck 99b283dabc remove debugs and rename LOG* Constants 8 months ago
heck 16de858404 libDaisy use static methods statically always 8 months ago
heck 3c40f99020 Move: MidiLogger into osp_utils 8 months ago
heck 942003449e Buttons (Rec,Clear) mnigrate to using libdizzy 'Cache' and 'PeriodicTaskCT' 8 months ago
heck 8207184595 main_osp.cc - Replace SWTimer with libdizzy::PeriodicTaskCT 8 months ago
heck 53e5b8388d remove redundancies with libdizzy 8 months ago
heck ffecf54f90 update for libdizzy moved into own namespace 8 months ago
heck 51bb0adae1 Start using 'libDizzy' as a new dependency 8 months ago
heck 52e300d9e2 .clang-format 8 months ago
heck e480ff163c DSP: add Ringbuffer, Delay101, Delta 8 months ago
heck a992e9194b Build: enable float printing (printf) 8 months ago
heck 35ee7364a4 Move everything in namespace Heck::OSP 8 months ago
heck 5d7c323416 just formatting and banalities 8 months ago
heck 784e6d793f rename 'mode1' to 'algo' 8 months ago
heck 4b483c4243 Build: Use the new heck_libdaisy build system 8 months ago
heck ba2ce38432 seq_model_perkons.py - correction, odds and prob are mutually exclusive 8 months ago
heck d5b2a999b6 route audio inputs [1,2] to dual mono outputs [1,2] 8 months ago
heck 417567f778 data: add perkons sample data for reverse engineering 8 months ago
heck 3e54a8fce2 tools - add sequencer size calculator - hypothetical perkons model 8 months ago
heck e376b2d71e tools - add sequencer size calculator - general midi model 8 months ago
heck 8bc7d1c424 Fix: bug - audio trashy as fuck 9 months ago
heck 9ec1fc2ed8 Update button pins 9 months ago
heck df8a24709b Utils: update and fix 9 months ago
heck 63ed5834b1 Fix: bug - Clear button clears everything 9 months ago
heck 78a52d8107 utils - update from template repo 9 months ago
heck f5fabe7041 rename 'hw' to 'seed' 9 months ago
heck 505911021a types.hh - move type 'Samplerate' from globals 9 months ago
heck 0ba8e189d2 heartbeat rate faster, i am impatient 9 months ago
heck bfac86d8a4 use non-interleaving audio callback 9 months ago
heck 8e63cdc858 Build: build.conf.example - formatting 9 months ago
heck 3f343f2b59 SWTimer (periodic_task) - callback add arg time_now 9 months ago
heck d191fd7c56 Implement async_log_tx using SWTimer 9 months ago
heck 904a8c644f Implement heartbeat using SWTimer 9 months ago
heck a0a3f4383e Utils - Add SWTimer 9 months ago
heck 792be4fb76 Const correctness (also to be chased in libDaisy a lot) 9 months ago
heck cf227919a9 sort includes 9 months ago
heck 044143e44f the globals file is the projects global file and not abstractable 9 months ago
heck 83cc242614 Rename: Its not a perkons, its a Heck Machines OSP 9 months ago
heck 22f118051f Build: libDaisy core/Makefile is now really sweet and customizable, use that. 9 months ago
heck bfa9393c7c constexpr what possible 9 months ago
  1. 1
      .clang-format
  2. 1
      .gitignore
  3. 39
      Makefile.conf
  4. 18
      build.conf.example
  5. BIN
      data/perkons_sample_data/BANKS/00/00.PAT
  6. BIN
      data/perkons_sample_data/BANKS/00/KITS/00.KIT
  7. BIN
      data/perkons_sample_data/BANKS/00/state.dat
  8. 4
      data/perkons_sample_data/INFO.TXT
  9. 37
      data/perkons_sample_data/System/device.json
  10. 54
      data/perkons_sample_data/System/multi-midi-cc.json
  11. 54
      data/perkons_sample_data/System/single-midi-cc.json
  12. 22
      src/Makefile
  13. 16
      src/instr_bd2.cc
  14. 14
      src/instr_bd2.hh
  15. 10
      src/instr_fm.cc
  16. 14
      src/instr_fm.hh
  17. 14
      src/instr_grainlet.cc
  18. 14
      src/instr_grainlet.hh
  19. 14
      src/instr_hihat.cc
  20. 14
      src/instr_hihat.hh
  21. 14
      src/instr_interface.hh
  22. 16
      src/instr_kick.cc
  23. 14
      src/instr_kick.hh
  24. 6
      src/instr_noise.cc
  25. 14
      src/instr_noise.hh
  26. 20
      src/instr_zosc.cc
  27. 14
      src/instr_zosc.hh
  28. 404
      src/main_osp.cc
  29. 34
      src/main_osp.hh
  30. 359
      src/main_perkons.cc
  31. 2
      src/midiclock.cc
  32. 6
      src/midiclock.hh
  33. 2
      src/osp_utils.cc
  34. 129
      src/osp_utils.hh
  35. 76
      src/sequencer.cc
  36. 33
      src/sequencer.hh
  37. 27
      src/track.hh
  38. 30
      src/types.hh
  39. 94
      src/utils.cc
  40. 15
      src/utils.hh
  41. 22
      tools/seq_model_midi.py
  42. 32
      tools/seq_model_perkons.py
  43. 29
      tools/test_cxx.cc

1
.clang-format

@ -40,3 +40,4 @@ AllowShortBlocksOnASingleLine: Always
IndentPPDirectives: BeforeHash
IndentExternBlock: Indent
Cpp11BracedListStyle: false
BreakTemplateDeclarations: Leave

1
.gitignore

@ -2,3 +2,4 @@
/build.conf
build/
/tools/a.out

39
Makefile.conf

@ -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

18
build.conf.example

@ -1,12 +1,14 @@
# Build config
# also, infos for the build system about the dev environment
# Dev environment build config
# This is a template, make a local copy called 'build.conf',
# remove this line, and tweak the values as you develop
# The program to compile and flash
# the name of the main cxx file without the prefix 'main_'
TARGET?=perkons
# the name of the main cxx file without the suffix
DEBUG?=0
#FLASH_TARGET ?= main_osp
DEBUG ?= 0
# dependencies relative to dir 'src'
LIBDAISY_DIR ?= ../../heck_libDaisy
DAISYSP_DIR ?= ../../heck_DaisySP
# Daisy dependencies Relative to dir 'src'
LIBDAISY_DIR?=../../heck_libDaisy
DAISYSP_DIR?=../../heck_DaisySP

BIN
data/perkons_sample_data/BANKS/00/00.PAT

Binary file not shown.

BIN
data/perkons_sample_data/BANKS/00/KITS/00.KIT

Binary file not shown.

BIN
data/perkons_sample_data/BANKS/00/state.dat

Binary file not shown.

4
data/perkons_sample_data/INFO.TXT

@ -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

37
data/perkons_sample_data/System/device.json

@ -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
}
}

54
data/perkons_sample_data/System/multi-midi-cc.json

@ -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
}
}

54
data/perkons_sample_data/System/single-midi-cc.json

@ -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
}
}

22
src/Makefile

@ -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

16
src/instr_bd2.cc

@ -1,8 +1,8 @@
#include "instr_bd2.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
BD2::BD2()
@ -30,26 +30,26 @@ namespace Heck {
{
switch (ctl_nr) {
case 0: {
bd2.SetFreq(scalen_min_max(val, 40, 180));
bd2.SetFreq( dz::scalen_min_max(val, 40, 180));
} break;
case 1: {
bd2.SetDecay(scalen_min_max(val,0,1));
bd2.SetDecay( dz::scalen_min_max(val,0,1));
} break;
case 2: {
bd2.SetFmEnvelopeAmount(scalen_min_max(val,0,1));
bd2.SetFmEnvelopeAmount( dz::scalen_min_max(val,0,1));
} break;
case 3: {
bd2.SetFmEnvelopeDecay(scalen_min_max(val,0,1));
bd2.SetFmEnvelopeDecay( dz::scalen_min_max(val,0,1));
} break;
}
}
void BD2::switch_mode1(unsigned int pos)
void BD2::switch_algo(unsigned int pos)
{
mode1 = pos;
}
void BD2::switch_mode2(unsigned int pos)
void BD2::switch_mode(unsigned int pos)
{
mode2 = pos;
}

14
src/instr_bd2.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_BD2_HH
#define HECK_DAISY_INSTR_BD2_HH
#ifndef HECK_OSP_INSTR_BD2_HH
#define HECK_OSP_INSTR_BD2_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class BD2 : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:

10
src/instr_fm.cc

@ -1,8 +1,8 @@
#include "instr_fm.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
FM::FM()
@ -32,7 +32,7 @@ namespace Heck {
case 1:
break;
case 2: {
float ratio_val = scalen_min_max(val, 1., 3.);
float ratio_val = dz::scalen_min_max(val, 1., 3.);
osc.SetRatio(ratio_val);
} break;
case 3:
@ -41,8 +41,8 @@ namespace Heck {
}
}
void FM::switch_mode1(unsigned int pos) {}
void FM::switch_mode2(unsigned int pos) {}
void FM::switch_algo(unsigned int pos) {}
void FM::switch_mode(unsigned int pos) {}
float FM::nextsample()
{

14
src/instr_fm.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_FM_HH
#define HECK_DAISY_INSTR_FM_HH
#ifndef HECK_OSP_INSTR_FM_HH
#define HECK_OSP_INSTR_FM_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class FM : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:

14
src/instr_grainlet.cc

@ -1,8 +1,8 @@
#include "instr_Grainlet.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
Grainlet::Grainlet()
@ -23,26 +23,26 @@ namespace Heck {
{
switch (ctl_nr) {
case 0: {
grainlet.SetFreq(scalen_min_max(val, 40, 400));
grainlet.SetFreq( dz::scalen_min_max(val, 40, 400));
} break;
case 1: {
} break;
case 2: {
grainlet.SetFormantFreq(scalen_min_max(val, 400, 2000));
grainlet.SetFormantFreq( dz::scalen_min_max(val, 400, 2000));
} break;
case 3: {
grainlet.SetShape(scalen_min_max(val, 0, 1));
grainlet.SetShape( dz::scalen_min_max(val, 0, 1));
} break;
}
}
void Grainlet::switch_mode1(unsigned int pos)
void Grainlet::switch_algo(unsigned int pos)
{
mode1 = pos;
}
void Grainlet::switch_mode2(unsigned int pos)
void Grainlet::switch_mode(unsigned int pos)
{
mode2 = pos;
}

14
src/instr_grainlet.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_GRAINLET_HH
#define HECK_DAISY_INSTR_GRAINLET_HH
#ifndef HECK_OSP_INSTR_GRAINLET_HH
#define HECK_OSP_INSTR_GRAINLET_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class Grainlet : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:
dsp::GrainletOscillator grainlet{};

14
src/instr_hihat.cc

@ -1,8 +1,8 @@
#include "instr_hihat.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
HiHat::HiHat()
@ -25,26 +25,26 @@ namespace Heck {
{
switch (ctl_nr) {
case 0: {
hihat.SetFreq(scalen_min_max(val, 800, 2000));
hihat.SetFreq( dz::scalen_min_max(val, 800, 2000));
} break;
case 1: {
} break;
case 2: {
hihat.SetTone(scalen_min_max(val, 0, 1));
hihat.SetTone( dz::scalen_min_max(val, 0, 1));
} break;
case 3: {
hihat.SetNoisiness(scalen_min_max(val, 0, 1));
hihat.SetNoisiness( dz::scalen_min_max(val, 0, 1));
} break;
}
}
void HiHat::switch_mode1(unsigned int pos)
void HiHat::switch_algo(unsigned int pos)
{
mode1 = pos;
}
void HiHat::switch_mode2(unsigned int pos)
void HiHat::switch_mode(unsigned int pos)
{
mode2 = pos;
}

14
src/instr_hihat.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_HIHAT_HH
#define HECK_DAISY_INSTR_HIHAT_HH
#ifndef HECK_OSP_INSTR_HIHAT_HH
#define HECK_OSP_INSTR_HIHAT_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class HiHat : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:

14
src/instr_abstract.hh → src/instr_interface.hh

@ -1,9 +1,9 @@
#ifndef HECK_DAISY_PERKONS_INSTR_ABSTRACT
#define HECK_DAISY_PERKONS_INSTR_ABSTRACT
#ifndef HECK_OSP_INSTR_ABSTRACT
#define HECK_OSP_INSTR_ABSTRACT
#include "globals.hh"
#include "main_osp.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class AbstractInstrument {
public:
@ -14,8 +14,8 @@ namespace Heck {
virtual void ctl(unsigned int ctl_nr, float val) = 0;
// pos must be either 0,1,2
virtual void switch_mode1(unsigned int pos) = 0;
virtual void switch_mode2(unsigned int pos) = 0;
virtual void switch_algo(unsigned int pos) = 0;
virtual void switch_mode(unsigned int pos) = 0;
virtual float nextsample() = 0;
private:
};
@ -23,4 +23,4 @@ namespace Heck {
} // namespace Heck
#endif // HECK_DAISY_PERKONS_INSTR_ABSTRACT
#endif // HECK_OSP_INSTR_ABSTRACT

16
src/instr_kick.cc

@ -1,8 +1,8 @@
#include "instr_kick.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
Kick::Kick()
@ -39,28 +39,28 @@ namespace Heck {
{
switch (ctl_nr) {
case 0: {
float relpitch = scalen_min_max(val, 40, 400);
float relpitch = dz::scalen_min_max(val, 40, 400);
osc.SetFreq(relpitch);
pitchEnv.SetMax(relpitch * 0.03);
pitchEnv.SetMin(relpitch);
} break;
case 1: {
// pitchEnv.SetTime(dsp::ADSR_SEG_DECAY, scalen_min_max(val, 0.01, 0.4));
// pitchEnv.SetTime(dsp::ADSR_SEG_DECAY, dz::scalen_min_max(val, 0.01, 0.4));
} break;
case 2: {
osc.SetPw(scalen_min_max(val, 0, 1));
osc.SetPw(dz::scalen_min_max(val, 0, 1));
} break;
case 3: {
chorus.SetDelay(scalen_min_max(val, 0.1, 0.9));
chorus.SetDelay(dz::scalen_min_max(val, 0.1, 0.9));
} break;
}
}
void Kick::switch_mode1(unsigned int pos)
void Kick::switch_algo(unsigned int pos)
{
mode1 = pos;
}
void Kick::switch_mode2(unsigned int pos)
void Kick::switch_mode(unsigned int pos)
{
mode2 = pos;
}

14
src/instr_kick.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_KICK_HH
#define HECK_DAISY_INSTR_KICK_HH
#ifndef HECK_OSP_INSTR_KICK_HH
#define HECK_OSP_INSTR_KICK_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class Kick : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:
dsp::Oscillator osc;

6
src/instr_noise.cc

@ -1,7 +1,7 @@
#include "instr_noise.hh"
#include "daisysp.h"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
Noise::Noise() {
@ -25,8 +25,8 @@ namespace Heck {
}
void Noise::ctl(unsigned int ctl_nr, float val) {}
void Noise::switch_mode1(unsigned int pos) {}
void Noise::switch_mode2(unsigned int pos) {}
void Noise::switch_algo(unsigned int pos) {}
void Noise::switch_mode(unsigned int pos) {}
float Noise::nextsample()
{

14
src/instr_noise.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_NOISE_HH
#define HECK_DAISY_INSTR_NOISE_HH
#ifndef HECK_OSP_INSTR_NOISE_HH
#define HECK_OSP_INSTR_NOISE_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class Noise : public AbstractInstrument {
@ -15,8 +13,8 @@ namespace Heck {
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:

20
src/instr_zosc.cc

@ -1,8 +1,8 @@
#include "instr_zosc.hh"
#include "daisysp.h"
#include "utils.hh"
#include "osp_utils.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
ZOsc::ZOsc()
@ -24,31 +24,31 @@ namespace Heck {
{
switch (ctl_nr) {
case 0: {
zosc.SetFreq(scalen_min_max(val, 40, 180));
zosc.SetFreq(dz::scalen_min_max(val, 40, 180));
} break;
case 1: {
// zosc.SetDecay(scalen_min_max(val,0,1));
// zosc.SetDecay( dz::scalen_min_max(val,0,1));
} break;
case 2: {
zosc.SetFormantFreq(scalen_min_max(val, 200, 800));
zosc.SetFormantFreq(dz::scalen_min_max(val, 200, 800));
} break;
case 3: {
zosc.SetShape(1. - scalen_min_max(val, 0, 1));
zosc.SetShape(1. - dz::scalen_min_max(val, 0, 1));
} break;
}
}
void ZOsc::switch_mode1(unsigned int pos)
void ZOsc::switch_algo(unsigned int pos)
{
mode1 = pos;
zosc.SetMode(((float)pos) / 3.);
hw.PrintLine("");
ld::DaisySeed::PrintLine("");
}
void ZOsc::switch_mode2(unsigned int pos)
void ZOsc::switch_mode(unsigned int pos)
{
mode2 = pos;
hw.PrintLine("ZOSC MODE2: %i", mode2);
ld::DaisySeed::PrintLine("ZOSC MODE2: %i", mode2);
}
float ZOsc::nextsample()

14
src/instr_zosc.hh

@ -1,11 +1,9 @@
#ifndef HECK_DAISY_INSTR_ZOSC_HH
#define HECK_DAISY_INSTR_ZOSC_HH
#ifndef HECK_OSP_INSTR_ZOSC_HH
#define HECK_OSP_INSTR_ZOSC_HH
#include "instr_abstract.hh"
#include "daisy_seed.h"
#include "daisysp.h"
#include "instr_interface.hh"
namespace Heck {
namespace Heck::OSP {
namespace Instrument {
class ZOsc : public AbstractInstrument {
@ -14,8 +12,8 @@ namespace Heck {
void init();
void trigger() override;
void ctl(unsigned int ctl_nr, float val) override;
void switch_mode1(unsigned int pos) override;
void switch_mode2(unsigned int pos) override;
void switch_algo(unsigned int pos) override;
void switch_mode(unsigned int pos) override;
float nextsample() override;
private:

404
src/main_osp.cc

@ -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();
}

34
src/globals.hh → src/main_osp.hh

@ -1,23 +1,18 @@
#ifndef HECK_DAISY_GLOBALS_HH
#define HECK_DAISY_GLOBALS_HH
#ifndef HECK_OSP_GLOBALS_HH
#define HECK_OSP_GLOBALS_HH
#include <cstdint>
#include "daisy_seed.h"
#include "daisysp.h"
#include "types.hh"
namespace Heck {
using Samplerate = ld::SaiHandle::Config::SampleRate;
#include "dizzy.hh"
namespace Heck::OSP {
namespace Constants {
namespace Hardware {
constexpr int PIN_BUTTON_RECORD = 28;
constexpr int PIN_BUTTON_CLEAR = 27;
}
constexpr bool CPU_BOOST480MHZ = true;
constexpr bool CPU_BOOST480MHZ = false;
constexpr int AUDIO_BUFFERSIZE = 4;
constexpr Samplerate AUDIO_SAMPLERATE = Samplerate::SAI_48KHZ;
constexpr dz::Samplerate AUDIO_SAMPLERATE = dz::Samplerate::SAI_48KHZ;
constexpr int TRACK_COUNT = 4;
namespace MIDI_Mapping {
@ -28,24 +23,23 @@ namespace Heck {
constexpr int TRACK_FILTER = 74;
constexpr int TRACK_DRIVE = 75;
constexpr int TRACK_VOLUME = 76;
constexpr int TRACK_MODE1 = 78;
constexpr int TRACK_MODE2 = 79;
constexpr int TRACK_ALGO = 78;
constexpr int TRACK_MODE = 79;
constexpr int TRACK_FILTERMODE = 80;
} // namespace MIDI_Mapping
namespace Developer {
constexpr bool LOG_BLOCKS_BOOT = false;
constexpr bool LOG_MIDI_REALTIME = false;
constexpr bool LOG_MIDI_NOTESANDCC = false;
constexpr bool LOG_CLOCK_BAR = false;
constexpr bool LOG_BLOCKS_BOOT = true;
constexpr bool LOG_MIDI_UART_REALTIME = false;
constexpr bool LOG_MIDI_UART_POLLING = false;
constexpr bool LOG_CLOCK_16N = false;
}
} // namespace Constants
//Hardware
extern ld::DaisySeed hw;
extern ld::Switch but_rec;
extern ld::Switch but_clear;
extern ld::DaisySeed seed;
// extern ld::Switch but_rec;
// extern ld::Switch but_clear;
} // namespace Heck

359
src/main_perkons.cc

@ -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();
}

2
src/midiclock.cc

@ -1,7 +1,7 @@
#include "midiclock.hh"
#include <cmath>
namespace Heck {
namespace Heck::OSP {
void MidiClock::tick_advance()
{
if (enabled) {

6
src/midiclock.hh

@ -1,7 +1,7 @@
#ifndef HECK_PERKONS_MIDICLOCK_HH
#define HECK_PERKONS_MIDICLOCK_HH
#ifndef HECK_OSP_MIDICLOCK_HH
#define HECK_OSP_MIDICLOCK_HH
namespace Heck {
namespace Heck::OSP {
struct MidiClock {
public:
void tick_advance();

2
src/osp_utils.cc

@ -0,0 +1,2 @@
#include "osp_utils.hh"

129
src/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

76
src/sequencer.cc

@ -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

33
src/sequencer.hh

@ -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

27
src/track.hh

@ -1,13 +1,11 @@
#ifndef HECK_PERKONS_TRACK_HH
#define HECK_PERKONS_TRACK_HH
#ifndef HECK_OSP_TRACK_HH
#define HECK_OSP_TRACK_HH
#include "daisysp.h"
#include "instr_abstract.hh"
#include "utils.hh"
#include "globals.hh"
namespace dsp = daisysp;
#include "instr_interface.hh"
#include "dizzy.hh"
#include "main_osp.hh"
namespace Heck {
namespace Heck::OSP {
struct Track {
public:
void init(Instrument::AbstractInstrument& instr)
@ -66,13 +64,14 @@ namespace Heck {
void filter(float val)
{
float ladder_freq = 80 + (val * 9000);
ladder_.SetFreq(val * ladder_freq);
// val *= val;
float ladder_freq = 5. + ((Constants::AUDIO_SAMPLERATE / 2.F) * val);
ladder_.SetFreq(ladder_freq);
}
void drive(float amt)
{
od_.SetDrive(scalen_min_max(amt, 0.35, 0.5));
od_.SetDrive(dz::scalen_min_max(amt, 0.35, 0.5));
}
// range: 0-1
@ -82,17 +81,17 @@ namespace Heck {
// MID
if (ctl_filtermode_ < 0.33) {
hw.PrintLine("Track: Filter BP");
ld::DaisySeed::PrintLine("Track: Filter BP");
ladder_.SetFilterMode(daisysp::LadderFilter::FilterMode::BP24);
}
// HIGH
if (ctl_filtermode_ >= 0.33 && ctl_filtermode_ < 0.66) {
hw.PrintLine("Track: Filter HP");
ld::DaisySeed::PrintLine("Track: Filter HP");
ladder_.SetFilterMode(daisysp::LadderFilter::FilterMode::HP24);
}
// LOW
if (ctl_filtermode_ >= 0.66) {
hw.PrintLine("Track: Filter LP");
ld::DaisySeed::PrintLine("Track: Filter LP");
ladder_.SetFilterMode(daisysp::LadderFilter::FilterMode::LP24);
}
}

30
src/types.hh

@ -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

94
src/utils.cc

@ -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

15
src/utils.hh

@ -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

22
tools/seq_model_midi.py

@ -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))

32
tools/seq_model_perkons.py

@ -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))

29
tools/test_cxx.cc

@ -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…
Cancel
Save