Skip to content

Commit

Permalink
Add audio applets and template-based applet defs
Browse files Browse the repository at this point in the history
  • Loading branch information
qiemem committed Jul 18, 2024
1 parent 5253e16 commit 7c708dd
Show file tree
Hide file tree
Showing 15 changed files with 836 additions and 574 deletions.
8 changes: 4 additions & 4 deletions software/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ build_flags =
-DENABLE_APP_SCENES
; -DENABLE_APP_ENIGMA
-DENABLE_APP_MIDI
-DENABLE_APP_PONG
-DENABLE_APP_PIQUED
-DENABLE_APP_POLYLFO
-DENABLE_APP_H1200
; -DENABLE_APP_PONG
; -DENABLE_APP_PIQUED
; -DENABLE_APP_POLYLFO
; -DENABLE_APP_H1200
-DENABLE_APP_BYTEBEATGEN
; -DENABLE_APP_NEURAL_NETWORK
; -DENABLE_APP_DARKEST_TIMELINE
Expand Down
3 changes: 3 additions & 0 deletions software/res/progname.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@
extras += "_flipped"

env.Replace(PROGNAME=f"o_C-phazerville-{version}{extras}-{git_rev}")

# include toolchain paths in compiledb for clangd lsp
env.Replace(COMPILATIONDB_INCLUDE_TOOLCHAIN=True)
143 changes: 119 additions & 24 deletions software/src/APP_HEMISPHERE.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
#endif

#include "hemisphere_config.h"
#include <optional>
#ifdef ARDUINO_TEENSY41
#include "AudioSetup.h"
#include "AudioPassthrough.h"
#endif

// The settings specify the selected applets, and 64 bits of data for each applet,
// plus 64 bits of data for the ClockSetup applet (which includes some misc config).
Expand Down Expand Up @@ -85,9 +90,9 @@ class HemispherePreset : public SystemExclusiveHandler,
return (h == LEFT_HEMISPHERE) ? values_[HEMISPHERE_SELECTED_LEFT_ID]
: values_[HEMISPHERE_SELECTED_RIGHT_ID];
}
HemisphereApplet* GetApplet(int h) {
HemisphereApplet& GetApplet(int h) {
int idx = HS::get_applet_index_by_id( GetAppletId(h) );
return HS::available_applets[idx].instance[h];
return HS::available_applets[idx]->GetInstance(h);
}
void SetAppletId(int h, int id) {
apply_value(h, id);
Expand Down Expand Up @@ -242,6 +247,9 @@ class HemisphereManager : public HSApplication {

SetApplet(LEFT_HEMISPHERE, HS::get_applet_index_by_id(18)); // DualTM
SetApplet(RIGHT_HEMISPHERE, HS::get_applet_index_by_id(15)); // EuclidX
#ifdef ARDUINO_TEENSY41
UpdateAudioPipeline();
#endif
}

void Resume() {
Expand All @@ -267,11 +275,11 @@ class HemisphereManager : public HSApplication {
for (int h = 0; h < 2; h++)
{
int index = my_applet[h];
if (hem_active_preset->GetAppletId(HEM_SIDE(h)) != HS::available_applets[index].id)
if (hem_active_preset->GetAppletId(HEM_SIDE(h)) != HS::available_applets[index]->id)
doSave = 1;
hem_active_preset->SetAppletId(HEM_SIDE(h), HS::available_applets[index].id);
hem_active_preset->SetAppletId(HEM_SIDE(h), HS::available_applets[index]->id);

uint64_t data = HS::available_applets[index].instance[h]->OnDataRequest();
uint64_t data = HS::available_applets[index]->GetInstance(h).OnDataRequest();
if (data != applet_data[h]) doSave = 1;
applet_data[h] = data;
hem_active_preset->SetData(HEM_SIDE(h), data);
Expand Down Expand Up @@ -319,7 +327,7 @@ class HemisphereManager : public HSApplication {
int index = HS::get_applet_index_by_id( hem_active_preset->GetAppletId(h) );
applet_data[h] = hem_active_preset->GetData(HEM_SIDE(h));
SetApplet(HEM_SIDE(h), index);
HS::available_applets[index].instance[h]->OnDataReceive(applet_data[h]);
HS::available_applets[index]->GetInstance(h).OnDataReceive(applet_data[h]);
}


Expand All @@ -331,13 +339,88 @@ class HemisphereManager : public HSApplication {
LoadFromPreset(queued_preset);
}

HemisphereApplet& GetApplet(HEM_SIDE hemisphere) {
return HS::available_applets[my_applet[hemisphere]]->GetInstance(hemisphere);
}

#ifdef ARDUINO_TEENSY41
void UpdateAudioPipeline() {
AudioNoInterrupts();
HemisphereApplet& left_applet = GetApplet(HEM_SIDE::LEFT_HEMISPHERE);
HemisphereApplet& right_applet = GetApplet(HEM_SIDE::RIGHT_HEMISPHERE);

for (int i=0; i < 3; i++) for (int j=0; j < 2; j++) conns[i][j].disconnect();

AudioStream& in = OC::AudioDSP::input_stream();
AudioStream& out = OC::AudioDSP::output_stream();

auto lin = left_applet.InputStream();
auto lout = left_applet.OutputStream();

auto rin = right_applet.InputStream();
auto rout = right_applet.OutputStream();

// Rules:
// - If both sides are mono, left applet controls left pipeline, right controls right
// - If one side is mono and the other stereo, the stereo effectively becomes mono
// - If one side is stereo and the other none, the stereo applet controls both channels
// - If both are stereo, left pipeline feeds into right

// Wire up left channel
if (left_applet.NumAudioChannels() > 0) {
conns[0][0].connect(in, 0, lin.value(), 0);
if (right_applet.NumAudioChannels() == 2 && left_applet.NumAudioChannels() == 2) {
conns[1][0].connect(lout.value(), 0, rin.value(), 0);
conns[2][0].connect(rout.value(), 0, out, 0);
} else {
conns[1][0].connect(lout.value(), 0, out, 0);
}
} else if (right_applet.NumAudioChannels() == 2) {
conns[0][0].connect(in, 0, rin.value(), 0);
conns[1][0].connect(rout.value(), 0, out, 0);
} else {
conns[0][0]. connect(in, 0, out, 0);
}

// Wire up right channel
if (right_applet.NumAudioChannels() == 1) {
conns[0][1].connect(in, 1, rin.value(), 0);
conns[1][1].connect(rout.value(), 0, out, 1);
} else if (left_applet.NumAudioChannels() == 2) {
conns[0][1].connect(in, 1, lin.value(), 1);
if (right_applet.NumAudioChannels() == 2) {
conns[1][1].connect(lout.value(), 1, rin.value(), 1);
conns[2][1].connect(rout.value(), 1, out, 1);
} else {
conns[1][1].connect(lout.value(), 1, out, 1);
}
} else if (right_applet.NumAudioChannels() == 2) {
conns[0][1].connect(in, 1, rin.value(), 1);
conns[1][1].connect(rout.value(), 1, out, 1);
} else {
conns[0][1].connect(in, 1, out, 1);
}
AudioInterrupts();
}
#endif

// does not modify the preset, only the manager
void SetApplet(HEM_SIDE hemisphere, int index) {
//if (my_applet[hemisphere]) // TODO: special case for first load?
HS::available_applets[my_applet[hemisphere]].instance[hemisphere]->Unload();
GetApplet(hemisphere).Unload();
#ifdef ARDUINO_TEENSY41
const uint8_t last_channels = GetApplet(hemisphere).NumAudioChannels();
#endif
next_applet[hemisphere] = my_applet[hemisphere] = index;
HS::available_applets[index].instance[hemisphere]->BaseStart(hemisphere);
HemisphereApplet& selected_applet = HS::available_applets[index]->GetInstance(hemisphere);
selected_applet.BaseStart(hemisphere);

#ifdef ARDUINO_TEENSY41
if (selected_applet.NumAudioChannels() > 0 || selected_applet.NumAudioChannels() != last_channels)
UpdateAudioPipeline();
#endif
}

void ChangeApplet(HEM_SIDE h, int dir) {
int index = HS::get_next_applet_index(next_applet[h], dir);
next_applet[h] = index;
Expand Down Expand Up @@ -423,7 +506,7 @@ class HemisphereManager : public HSApplication {
int index = my_applet[h];

// MIDI signals mixed with inputs to applets
if (HS::available_applets[index].id != 150) // not MIDI In
if (HS::available_applets[index]->id != 150) // not MIDI In
{
ForEachChannel(ch) {
int chan = h*2 + ch;
Expand All @@ -448,7 +531,7 @@ class HemisphereManager : public HSApplication {
}
}
}
HS::available_applets[index].instance[h]->BaseController();
HS::available_applets[index]->GetInstance(h).BaseController();
}

#ifdef ARDUINO_TEENSY41
Expand Down Expand Up @@ -500,7 +583,7 @@ class HemisphereManager : public HSApplication {
#ifdef ARDUINO_TEENSY41
if (view_state == AUDIO_SETUP) {
gfxHeader("Audio DSP Setup");
OC::AudioDSP::DrawAudioSetup();
// OC::AudioDSP::DrawAudioSetup();
draw_applets = false;
}
#endif
Expand All @@ -514,8 +597,7 @@ class HemisphereManager : public HSApplication {
draw_applets = false;
}
else if (help_hemisphere > -1) {
int index = my_applet[help_hemisphere];
HS::available_applets[index].instance[help_hemisphere]->BaseView(true);
GetApplet(static_cast<HEM_SIDE>(help_hemisphere)).BaseView(true);
draw_applets = false;
}
}
Expand All @@ -524,7 +606,7 @@ class HemisphereManager : public HSApplication {
for (int h = 0; h < 2; h++)
{
int index = my_applet[h];
HS::available_applets[index].instance[h]->BaseView();
HS::available_applets[index]->GetInstance(h).BaseView();
}

if (HS::clock_m.IsRunning()) {
Expand Down Expand Up @@ -558,7 +640,7 @@ class HemisphereManager : public HSApplication {
}
#ifdef ARDUINO_TEENSY41
if (view_state == AUDIO_SETUP) {
if (!down) OC::AudioDSP::AudioSetupButtonAction(h);
// if (!down) OC::AudioDSP::AudioSetupButtonAction(h);
return;
}
#endif
Expand All @@ -577,7 +659,7 @@ class HemisphereManager : public HSApplication {
} else if (!clock_setup) {
// regular applets get button release
int index = my_applet[h];
HS::available_applets[index].instance[h]->OnButtonPress();
HS::available_applets[index]->GetInstance(h).OnButtonPress();
}
}

Expand Down Expand Up @@ -679,11 +761,11 @@ class HemisphereManager : public HSApplication {
// -- button release
if (!clock_setup) {
const int index = my_applet[hemisphere];
HemisphereApplet* applet = HS::available_applets[index].instance[hemisphere];
HemisphereApplet& applet = HS::available_applets[index]->GetInstance(hemisphere);

if (applet->EditMode()) {
if (applet.EditMode()) {
// select button becomes aux button while editing a param
applet->AuxButton();
applet.AuxButton();
} else {
// Select Mode
if (hemisphere == select_mode) select_mode = -1; // Exit Select Mode if same button is pressed
Expand Down Expand Up @@ -711,7 +793,7 @@ class HemisphereManager : public HSApplication {
}
#ifdef ARDUINO_TEENSY41
if (view_state == AUDIO_SETUP) {
OC::AudioDSP::AudioMenuAdjust(h, event.value);
// OC::AudioDSP::AudioMenuAdjust(h, event.value);
return;
}
#endif
Expand All @@ -725,7 +807,7 @@ class HemisphereManager : public HSApplication {
ChangeApplet(HEM_SIDE(h), event.value);
} else {
int index = my_applet[h];
HS::available_applets[index].instance[h]->OnEncoderMove(event.value);
HS::available_applets[index]->GetInstance(h).OnEncoderMove(event.value);
}
}

Expand Down Expand Up @@ -803,6 +885,13 @@ class HemisphereManager : public HSApplication {
int config_page = 0;
int dummy_count = 0;

#ifdef ARDUINO_TEENSY41
// default audio streams for applets so non-audio applets don't need their
// own.
AudioPassthrough<2> passthroughs[2];
AudioConnection conns[3][2]; // input_to_left, left_to_right, right_to_output
#endif

OC::menu::ScreenCursor<5> showhide_cursor;

int select_mode = -1;
Expand Down Expand Up @@ -1088,7 +1177,7 @@ class HemisphereManager : public HSApplication {
++current, y += LineH) {

gfxIcon(1, y + 1, HS::applet_is_hidden(current) ? CHECK_OFF_ICON : CHECK_ON_ICON);
gfxPrint( 11, y + 2, HS::available_applets[current].instance[0]->applet_name());
gfxPrint( 11, y + 2, HS::available_applets[current]->GetInstance(0).applet_name());

if (current == showhide_cursor.cursor_pos())
gfxInvert(0, y, 127, LineH - 1);
Expand Down Expand Up @@ -1166,9 +1255,9 @@ class HemisphereManager : public HSApplication {
if (!hem_presets[i].is_valid())
gfxPrint(18, y, "(empty)");
else {
gfxPrint(18, y, hem_presets[i].GetApplet(0)->applet_name());
gfxPrint(18, y, hem_presets[i].GetApplet(0).applet_name());
gfxPrint(", ");
gfxPrint(hem_presets[i].GetApplet(1)->applet_name());
gfxPrint(hem_presets[i].GetApplet(1).applet_name());
}

y += 10;
Expand Down Expand Up @@ -1217,6 +1306,12 @@ void HEMISPHERE_init() {
manager.BaseStart();
}

// #ifdef ARDUINO_TEENSY41
// void HEMISPHERE_initAudio(AudioStream &input, AudioStream &output) {
// manager.SetIOStreams(input, output);
// }
// #endif

static constexpr size_t HEMISPHERE_storageSize() {
return HemispherePreset::storageSize() * HEM_NR_OF_PRESETS;
}
Expand Down
Loading

0 comments on commit 7c708dd

Please sign in to comment.