diff --git a/.vscode/launch.json b/.vscode/launch.json index 384c819..ea063e5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -344,6 +344,36 @@ "svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd", "type": "cortex-debug" }, + { + "name": "Remote DPT triangles", + "configFiles": [ + "interface/stlink.cfg", + "target/stm32h7x.cfg" + ], + "cwd": "${workspaceFolder}/hosts/daisy/examples/dpt/triangles", + "debuggerArgs": [ + "-d", + "${workspaceRoot}/hosts/daisy/examples/dpt/triangles" + ], + "executable": "${workspaceRoot}/hosts/daisy/examples/dpt/triangles/build/signaletic-dpt-triangles.elf", + "interface": "swd", + "openOCDLaunchCommands": [ + "init", + "reset init" + ], + "preLaunchTask": "Debug Build DPT Examples", + "preRestartCommands": [ + "load", + "enable breakpoint", + "monitor reset" + ], + "request": "launch", + "runToEntryPoint": "main", + "servertype": "openocd", + "showDevDebugOutput": "parsed", + "svdFile": "${workspaceRoot}/.vscode/STM32H750x.svd", + "type": "cortex-debug" + }, { "name": "Remote DPT lfos", "configFiles": [ diff --git a/hosts/daisy/examples/dpt/triangles/Makefile b/hosts/daisy/examples/dpt/triangles/Makefile new file mode 100644 index 0000000..0a95e8a --- /dev/null +++ b/hosts/daisy/examples/dpt/triangles/Makefile @@ -0,0 +1,21 @@ +# Project Name +TARGET ?= signaletic-dpt-triangles + +DEBUG = 1 +OPT = -O0 + +# Sources +C_SOURCES += ../../../../../libsignaletic/vendor/tlsf/tlsf.c ../../../../../libsignaletic/src/libsignaletic.c ../../../src/signaletic-host.c +C_INCLUDES += -I../../../../../libsignaletic/vendor/tlsf -I../../../../../libsignaletic/include + +CPP_INCLUDES += -I../vendor/lib -I../../../vendor/lib/dev -I../../../include +CPP_SOURCES = ../../../vendor/dpt/lib/dev/DAC7554.cpp ../../../src/signaletic-daisy-host.cpp ../../../src/sig-daisy-patch-sm.cpp src/${TARGET}.cpp + +USE_FATFS = 0 + +# Library Locations +LIBDAISY_DIR = ../../../vendor/libDaisy + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/hosts/daisy/examples/dpt/triangles/src/signaletic-dpt-triangles.cpp b/hosts/daisy/examples/dpt/triangles/src/signaletic-dpt-triangles.cpp new file mode 100644 index 0000000..a5b9207 --- /dev/null +++ b/hosts/daisy/examples/dpt/triangles/src/signaletic-dpt-triangles.cpp @@ -0,0 +1,93 @@ +#include "../../../../include/dspcoffee-dpt-device.hpp" + +#define SAMPLERATE 96000 +#define HEAP_SIZE 1024 * 256 // 256KB +#define MAX_NUM_SIGNALS 128 + +uint8_t memory[HEAP_SIZE]; +struct sig_AllocatorHeap heap = { + .length = HEAP_SIZE, + .memory = (void*) memory +}; + +struct sig_Allocator allocator = { + .impl = &sig_TLSFAllocatorImpl, + .heap = &heap +}; + +DaisyHost host; + +struct sig_dsp_Signal* listStorage[MAX_NUM_SIGNALS]; +struct sig_List signals = { + .items = (void**) &listStorage, + .capacity = MAX_NUM_SIGNALS, + .length = 0 +}; + +struct sig_dsp_SignalListEvaluator* evaluator; +struct sig_host_CVIn* frequency; +struct sig_host_CVIn* triangleGain; +struct sig_dsp_Oscillator* triangle; +struct sig_dsp_BinaryOp* scaledTriangle; +struct sig_host_CVOut* cv1Out; +struct sig_host_CVOut* cv3Out; +float cv1Value; +float cv2Value; + +void buildSignalGraph(struct sig_SignalContext* context, + struct sig_Status* status) { + frequency = sig_host_CVIn_new(&allocator, context); + frequency->hardware = &host.device.hardware; + sig_List_append(&signals, frequency, status); + frequency->parameters.control = sig_host_CV_IN_1; + frequency->parameters.scale = 110.0f; + + triangle = sig_dsp_LFTriangle_new(&allocator, context); + sig_List_append(&signals, triangle, status); + triangle->inputs.freq = frequency->outputs.main; + + triangleGain =sig_host_CVIn_new(&allocator, context); triangleGain->hardware = &host.device.hardware; + sig_List_append(&signals, triangleGain, status); + triangleGain->parameters.control = sig_host_CV_IN_2; + + scaledTriangle= sig_dsp_Mul_new(&allocator, context); + sig_List_append(&signals, scaledTriangle, status); + scaledTriangle->inputs.left = triangle->outputs.main; + scaledTriangle->inputs.right = triangleGain->outputs.main; + + cv1Out = sig_host_CVOut_new(&allocator, context); + cv1Out->hardware = &host.device.hardware; + sig_List_append(&signals, cv1Out, status); + cv1Out->inputs.source = scaledTriangle->outputs.main; + cv1Out->parameters.control = sig_host_CV_OUT_1; + + cv3Out = sig_host_CVOut_new(&allocator, context); + cv3Out->hardware = &host.device.hardware; + sig_List_append(&signals, cv3Out, status); + cv3Out->inputs.source = scaledTriangle->outputs.main; + cv3Out->parameters.control = sig_host_CV_OUT_3; +} + +int main(void) { + allocator.impl->init(&allocator); + + struct sig_AudioSettings audioSettings = { + .sampleRate = SAMPLERATE, + .numChannels = 2, + .blockSize = 1 + }; + + struct sig_Status status; + sig_Status_init(&status); + sig_List_init(&signals, (void**) &listStorage, MAX_NUM_SIGNALS); + + evaluator = sig_dsp_SignalListEvaluator_new(&allocator, &signals); + host.Init(&audioSettings, (struct sig_dsp_SignalEvaluator*) evaluator); + + struct sig_SignalContext* context = sig_SignalContext_new( + &allocator, &audioSettings); + buildSignalGraph(context, &status); + host.Start(); + + while (1) {} +} diff --git a/hosts/daisy/include/dspcoffee-dpt-device.hpp b/hosts/daisy/include/dspcoffee-dpt-device.hpp index 01cdbc4..217a376 100644 --- a/hosts/daisy/include/dspcoffee-dpt-device.hpp +++ b/hosts/daisy/include/dspcoffee-dpt-device.hpp @@ -9,6 +9,11 @@ using namespace sig::libdaisy; +struct Normalization DPT_INTERNAL_DAC_NORMALIZATION = { + .scale = 0.651f, + .offset = -0.348f +}; + enum { sig_host_CV_IN_1 = 0, sig_host_CV_IN_2, @@ -95,8 +100,9 @@ namespace dpt { InputBank gateInputBank; GPIOOutput gateOutputs[NUM_GATES]; OutputBank gateOutputBank; - DMAAnalogOutput dacChannels[NUM_DAC_CHANNELS]; - OutputBank dacOutputBank; + BipolarInvertedBufferedAnalogOutput dacChannels[NUM_DAC_CHANNELS]; + OutputBank + dacOutputBank; struct sig_host_HardwareInterface hardware; static void onEvaluateSignals(size_t size, @@ -177,14 +183,6 @@ namespace dpt { } void InitADCController() { - // TODO: Scale DPT CV outputs so that they're all in - // a normalized range (e.g. -4V to +4V). - // Currently, I think, they're outputting -4.67V to 7.96V) - //static constexpr struct Normalization DPT_TO_8VPP = { - // .scale = ??f, - // .offset = ??f - // }; - adcController.Init(&board.adc, ADC_CHANNEL_SPECS); } @@ -193,12 +191,13 @@ namespace dpt { // here. A better architecture would allow for // multiple DAC (and ADC) instances. for (size_t i = 0; i < NUM_INTERNAL_DAC_CHANNELS; i++) { - dacChannels[i].Init(board.dacOutputValues, i); + dacChannels[i].Init(board.dacOutputValues, i, + DPT_INTERNAL_DAC_NORMALIZATION); } for (size_t i = 0; i < NUM_EXTERNAL_DAC_CHANNELS; i++) { size_t j = NUM_INTERNAL_DAC_CHANNELS + i; - dacChannels[j].Init(externalDACBuffer, i); + dacChannels[j].Init(externalDACBuffer, i, NO_NORMALIZATION); } dacOutputBank.outputs = dacChannels; diff --git a/hosts/daisy/include/lichen-bifocals-device.hpp b/hosts/daisy/include/lichen-bifocals-device.hpp index f2f9d34..ea34379 100644 --- a/hosts/daisy/include/lichen-bifocals-device.hpp +++ b/hosts/daisy/include/lichen-bifocals-device.hpp @@ -79,8 +79,8 @@ namespace bifocals { ADCController adcController; Toggle buttons[NUM_BUTTONS]; InputBank buttonBank; - DMAAnalogOutput dacChannels[NUM_DAC_CHANNELS]; - OutputBank dacOutputBank; + BufferedAnalogOutput dacChannels[NUM_DAC_CHANNELS]; + OutputBank dacOutputBank; struct sig_host_HardwareInterface hardware; static void onEvaluateSignals(size_t size, diff --git a/hosts/daisy/include/lichen-medium-device.hpp b/hosts/daisy/include/lichen-medium-device.hpp index b9820a1..0d9a082 100644 --- a/hosts/daisy/include/lichen-medium-device.hpp +++ b/hosts/daisy/include/lichen-medium-device.hpp @@ -91,8 +91,8 @@ namespace medium { InputBank switchBank; Toggle buttons[NUM_BUTTONS]; InputBank buttonBank; - DMAAnalogOutput dacChannels[NUM_DAC_CHANNELS]; - OutputBank dacOutputBank; + BufferedAnalogOutput dacChannels[NUM_DAC_CHANNELS]; + OutputBank dacOutputBank; struct sig_host_HardwareInterface hardware; static void onEvaluateSignals(size_t size, diff --git a/hosts/daisy/include/signaletic-daisy-host.hpp b/hosts/daisy/include/signaletic-daisy-host.hpp index f316961..9913d77 100644 --- a/hosts/daisy/include/signaletic-daisy-host.hpp +++ b/hosts/daisy/include/signaletic-daisy-host.hpp @@ -161,33 +161,58 @@ template class ADCController { } }; -class DMAAnalogOutput { +class BaseAnalogOutput { + public: + float scale = 1.0f; + float offset = 0.0f; +}; + +class BufferedAnalogOutput : public BaseAnalogOutput{ public: uint16_t* dacOutputs; size_t channel; - void Init(uint16_t* inDACOutputs, size_t inChannel) { + void Init(uint16_t* inDACOutputs, size_t inChannel, + struct Normalization normalization = NO_NORMALIZATION) { dacOutputs = inDACOutputs; channel = inChannel; + scale = normalization.scale; + offset = normalization.offset; } inline void Write(float value) { - dacOutputs[channel] = sig_unipolarToUint12(value); + float normalizedValue = value * scale + offset; + dacOutputs[channel] = sig_unipolarToUint12(normalizedValue); + } +}; + +// TODO: Probably worth parameterizing the write strategy, +// though it will make for quite long templated definitions, +// e.g. OutputBank> dacOutputBank; +class BipolarInvertedBufferedAnalogOutput : public BufferedAnalogOutput{ + public: + inline void Write(float value) { + float normalizedValue = value * scale + offset; + dacOutputs[channel] = sig_bipolarToInvUint12(normalizedValue); } }; -class PollingAnalogOutput { +class PollingAnalogOutput : public BaseAnalogOutput { public: daisy::DacHandle* dac; size_t channel; - void Init(daisy::DacHandle* inDAC, size_t inChannel) { + void Init(daisy::DacHandle* inDAC, size_t inChannel, + struct Normalization normalization = NO_NORMALIZATION) { dac = inDAC; channel = inChannel; + scale = normalization.scale; + offset = normalization.offset; } inline void Write(float value) { - uint16_t convertedValue = sig_unipolarToUint12(value); + float normalizedValue = value * scale + offset; + uint16_t convertedValue = sig_unipolarToUint12(normalizedValue); dac->WriteValue(static_cast(channel), convertedValue); }