ohmtech-rdi / eurorack-blocks Goto Github PK
View Code? Open in Web Editor NEWSoftware to Hardware Prototyping for Eurorack using C++, Max/Gen~ or Faust
License: Other
Software to Hardware Prototyping for Eurorack using C++, Max/Gen~ or Faust
License: Other
A number of issues were present while testing kivu12
board revision 2.
Tasks:
libDaisy
(needed by ADC fix)kivu12
daisy_field
Add SD support to ERB.
erbui
DSLkicad-libs
submodule and front PCB generatormicropatch
boardfield
boardfield
boardNote: eMMC is not supported yet.
Generated gyp
file from erbb
are missing some actions inputs:
erbb
file itselfHow to reproduce:
configure
doesn't help either.We have a problem with SDRAM in general. When soft or hard resetting the board, the SDRAM will return a different value than the one written for some portion of memories. Turning off the system and on again "correct" this problem. We don't yet whether this is caused by:
For now, we consider that we can merge this PR, as this SDRAM problem doesn't seem to be related to the SDRAM implementation. What was tried and failed:
ValidateSDRAM
function from Daisy to give it a test. It seems it always find failures, somehow of a magnitude of 1 million words.Most likely a faulty board. Progress is paused until the manufacturer gets back to us.
The board was faulty. The new board works perfectly.
patch_sm/HardwareTest
patch_sm/HardwareTest
after a power cycle and with a serial monitor and reportkivu12
Add .DS_Store
files in .gitignore
file.
Steps to reproduce:
configure.py
and build.py
the test/vcvrack
integration tests,configure.py
and build.py
the samples/drop
example code,VCV Rack will crash when trying to add the model for the vcvrack
integration test plugin.
Analysis:
VcvRack
plugin is called Ahh
and therefore is instantiated before Drop
, Drop
will crash VCV Rack,extern
global value plugin instance and model names (and so linkage and how dynamic libraries are loaded on Unix), doesn't have any beneficial effects.Workaround:
The crash is completely similar to #92 and we believe they are related somehow.
Currently, when using mono samples, we have an extra channels
member that doesn't make so much sense.
Currently also, FAUST integration would require a planar representation of samples. For now this is not completely known how much performance impact has reading interleaved samples linearly, compared to reading them from 2 different planes.
Nevertheless, we can add an option
to the data
in erbb
that accounts for that, when the data
type is AudioSample
mode mono
: if the file is mono, skip the channels
membermode planar
: start with channels
followed by samples
mode interleaved
: what we have nowFor now, running configure.py
and then build.py
will compile for the daisy target, not the vcvrack target. This can be confusing for the first minutes of evaluation, as people would prefer to build for vcvrack.
Though, building for the simulator should be done in the IDE.
To mitigate that, build.py
should show which erb target and build configuration it is using.
dsp/
shouldn't be in samples/
, because it is not a sample, and that creates confusion in the first minutes of project discovery and associated mental representation.
The declaration and definition of data
is done in the *Data.h
module artifacts file. Since this one is included from the main module file, it gets recompiled every time there is a change to the main module file, which starts to be quite long with big pieces of data.
Move the definition to a new compilation unit to avoid that problem.
After the first kivu12
board is assembled, make a first round of hardware test, before we test the software as in #180.
Tasks:
kivu12/CI1
and kivu12/CI2
to 0V, 2.5V, -2.5V, 5V, -5V, Jumpers on position ±5VDPSM/C4
and DPSM/C5
are 0V, 2.5V, -2.5V, 5V, -5Vkivu12/CI1
and kivu12/CI2
to 0V, 4V, 8V, Jumpers on position 0..10VDPSM/C4
and DPSM/C5
are -5V, -1V, 3VDPSM/A2/A3/A9
to GND
, kivu12/B2
to xV, DPSM/B9
is xVDPSM/A2/A3/A9
to GND
, kivu12/B10
to xV, DPSM/B10
is xVTasks:
kivu12/3V3
Using a docker image with everything already installed would remove almost all sorts of installations on the user side, would be cross-platform, and would ensure a specific version of all the tools to ensure a perfect reproducible environment. Testing would be possible with GitHub actions workflow as well.
Make an interactive kivu12
board test suite by using USB CDC.
On the firmware side, the USB receives data to change scenarios, and to transmit data relevant to a specific scenario.
Scenarios:
\b
Our users expressed the need to have different language integration, and FAUST is one of them.
Faust (Functional Audio Stream) is a functional programming language for sound synthesis and audio processing with a strong focus on the design of synthesizers, musical instruments, audio effects, etc. Faust targets high-performance signal processing applications.
Avery has an idea of a new Eurorack module. They want to develop it in FAUST, because they are familiar with it, and they can leverage quickly the big standard library of dsp blocks available at their fingertips.
They started to draw the layout of the module on a piece of paper and a quick schematic of the audio flow. They use the GUI editor to make a first sketch of their idea and write the dsp code.
They iterate over the sketch by usually first modify the GUI to add or remove a control, and then update the dsp file accordingly.
Brooklyn has been working for 10 years with FAUST and as a result has a big collection of dsp modules, some of which they would want to have as real Eurorack modules.
For each dsp code, They use the GUI editor to make a first sketch, that they will iterate on.
FAUST code have presentation and model mixed into the source code. The faust
program can be used to generate C++ code as well as declarative structured data representing some static properties of the DSP code (such as number of input/outputs, or parameters' description), for example in JSON.
faust -json -o plugin_faust.cpp ../Kick.dsp
Will generate:
Like the erbui
/C++ bindings, we need to be able to map a erbui
name to a corresponding C++ name.
This is done in C++ by making sure that a mismatched mapping will fail at build time rather than at runtime.
Since we can extract a static representation of the FAUST dsp code, we could apply the same principle as in C++, that is an unused erbui
name in C++ would be silently ignored, but a name in C++ not defined on the erbui
side would trigger an error.
However, using C++ allows a strict control about parameters of the DSP.
In the case of FAUST, including libraries may add widgets primitives to the generated DSP code, that the user wouldn't want to have visible in the erbui
UI.
For this reason:
erbui
will be either silently ignored or at most emit a warning,erbui
code that wouldn't match its counterpart in FAUST code will be silently ignored.ERB has the concept of compound types, for example:
FAUST doesn't have this concept for its widgets. Furthermore, widgets are classified as passive or active whether they respectively take or provide a scalar value.
To remedy this, since the label is a string, we will use dot notation to bind a scalar to a compound element.
For example, considering the following erbui
code:
module MyModule {
control led LedBi { ... }
}
To access red and green components of the physical LED, we could use the same notation
as in C++, that is led.r
and led.g
, but from within the primitive label:
... : vbargraph("led.r",min,max);
... : vbargraph("led.g",min,max);
On one side, erbui
defines a name of a control as an identifier. Such an identifier matches the regex ([a-zA-Z]\w*)
, so that it needs to start with a letter, and then be followed a letter, number of underscore.
On the other side, labels are arbitrary. They can start with a number, contain spaces, and all sorts of other different characters.
Also, due the recursive nested structure with *group
primitives, the same label may appear multiple times, but with a different group.
Given that OSC paths must be unique, "fully qualified labels" are unique for a given FAUST top-level source code.
Instead of breaking the isomorphism to match names between erbui
and FAUST by applying some sort of normalisation of the name, which would also be very ugly for the user in some case, we will allow to create a string literal that aliases the erbui
control name.
This string literal will be the "address" of the widget, as defined by FAUST and available in the JSON description of the dsp code:
module Guitar {
control attack Pot {
faust { bind { address "/Distortion Effect/0x00/Compressor/Attack" } }
}
}
When the faust address is not present, it will be crafted by taking the control name prefixed with a /
:
module Guitar {
control attack Pot {
// would be implicitely: faust { bind { address "/attack" } }
}
}
In the case of compound types, the control property identifier needs to be specified in the binding:
module Guitar {
control led LedBi {
faust {
bind { property r address "/Distortion Effect/0x00/Compressor/Level X" }
bind { property g address "/Distortion Effect/0x00/Compressor/Level Y" }
}
}
}
When the faust address is not present, it will be crafted with dot notation by taking the control and property names prefixed with a /
:
module Guitar {
control led LedBi {
/* would be implicitely:
faust {
bind { property r address "/led.r" }
bind { property g address "/led.g" }
}
*/
}
}
We consider that the order in which widgets are defined in the FAUST JSON generated file follows the same order in which widgets functions are called in the C++ generated buildUserInterface
function: this allows for a very straightforward mapping.
It is worth noting that a non-erbui
-mapped name is valid, but the underlying parameter will stay to the FAUST defined default "init" value, which is probably not always good.
Also in this case, the generator issues a warning:
warning: non mapped hslider /PHASER2/0x00/Notch Depth (Intensity)
Those warnings are nice, because it allows to fetch easily the address of a FAUST widget, when using already made dsp, and when the widgget is buried deep into the dsp hierarchy.
But leaving warnings can be unsatisfactory, so we need a way to solve that nicely.
To solve those 2 problems (silenting a warning, and providing an initial value different from the FAUST defined one),
we can simply add a syntax for this, for non-mapped active components:
module Guitar {
faust {
init { address "/PHASER2/0x00/Notch Depth (Intensity)" value 0 }
init { address "/PHASER2/0x00/Vibrato Mode" value 0 }
}
}
Finally for completion, we can do the same for the equivalent "passive widgets" (in FAUST terms) for an erbui
control like a LED:
module Guitar {
control led_bi LedBi {
faust {
bind { property r address "/Distortion/0x00/Output Gain" }
init { property g value 0.5 }
}
}
}
Sample data binding is done through the UI adapter, rather than being an adapter on its own.
Thought our adapter declaration and definition can be splitted, so that we have 2 definition files (one for the UI, one for the sample data).
This allows the erbui
generator to only generate UI adapter functions, and the erbb
generator to only generate audio sample adapter functions.
All parameters (inputs or outputs), are represented as floating-point numbers.
Input parameters like sliders are associated to a function defining the minimum and maximum value as well as a mapping function defined for example as a meta/scale
function. Since ERB considers only normalized or bipolar numbers, some sort of adaptation needs to be done there.
Conversely, output parameters are associated to a function defining the minimum and maximum value, and a mapping function. Since ERB LEDs are normalized, the inverse function will need to be done there as well.
The mapping functions are implemented in C++, but we will generate a flattened version of this C++ code from python.
The numerical entries widget nentry
doesn't have an equivalent in the hardware world, but it is considered as a scalar value.
We don't have labels for audio inputs and outputs, so we will rely on the order in which they are declared in the erbui
file to bind them with the FAUST dsp code.
Since we don't have the concept of a filesystem, we need a system to match the erbb
AudioSample
definition to the corresponding FAUST soundfile
expression.
To reuse the same concepts, we can use the name mapping defined for the erbui
Language, but in the erbb
language, since Data is generated from erbb
, not erbui
:
resources {
data sample AudioSample {
file "sample.wav"
faust { bind { address "/Drum/0x00/Kick/sound" } }
}
for the corresponding FAUST dsp code (in a library Kick.dsp
):
process = 0,_~+(1):soundfile("sound[url:{'kick.wav'}]",2):!,!,_,_;
In particular, should there be a url
option like "foo[url:{'foo.wav'}]"
, then the url
information is either ignored or an error or warning is emitted if the file names do not match. At first, we will just ignore it.
As for name mappings with erbui
, we will keep the same nice features, for example:
module Kick {
resources {
data sample AudioSample {
file "sample.wav"
// implicitely:
// faust { bind { address "/Kick/sample" } }
}
}
FAUST only supports for now planar (ie non-interleaved) sample formats.
Eurorack-blocks support this stream mode, and will use mode planar
automatically for every multi-channel samples when using FAUST.
Planar representation most probably suffers from cache cohenrency on embedded platforms, as well as using SIMD instructions more difficult and less efficient, but we will leave this for now until we have collected execution performance data, and a interleaved mode would be supported in FAUST.
FAUST has some sort of convenience system which allows to assemble multiple audio files into one consolidated file.
We don't have such a convenience system, and don't plan to use it. Rather we could use standard markers (such as "cue"/"splice" markers for WAV format), to detect parts in an audio sample file.
The current latest brew
/apt
release of FAUST doesn't include the number of parts in the SoundFile
structure, so that the later structure is not closed (ie the information it provides are not sufficient to use it). But work has been already done to include this number in the SoundFile
, and should be therefore available after version 2.39.1
.
Ideally the generated code should be portable for both the simulator and the embedded daisy platform.
However "allocating" big portions of memory, such as delay lines, are going to be handled differently on both platforms:
Such "allocations" (the amount of memory is actually known at build time) appear when a @
time expression is used, for example.
For reference, FAUST has a custom memory manager, but it is only meant for rdtable
primitives.
It is still unclear how to handle that, but it might be out of scope for this integration. The generated FAUST code will need somehow to place large amount of memories in the SDRAM section, and ERB should just make sure that enough space is available in SDRAM by decreasing the erb_SDRAM_MEM_POOL_SIZE
to just what is needed for ERB (which is currently nothing).
In the worst case, provided the compiled code would need to be different for each platforms, we could wrap the code underlying target specific implementations:
// faust.cpp
#if defined (erb_TARGET_SIMULATOR)
#include "faust_simulator.cpp"
#else if defined (erb_TARGET_DAISY)
#include "faust_daisy.cpp"
#endif
Layout informations, as this is already done by erbui
, are ignored.
This includes all "box" primitives, that is hgroup
, vgroup
and tgroup
UI primitives.
While layout informations are ignored, the label of a group is used for name mapping as described above.
We don't have sample code to show the usage of data foo AudioSample
construct.
Related to that, the use of audio samples will almost immediately go past the 128K Flash memory limit, so QSPI support is needed in this case.
Make a Kick, which illustrates both of those technics:
Add a "background thread" abstraction, for tasks that are not suited to the audio realtime task, like loading a sample from SD.
The abstraction would allow to run on both the Daisy and simulator ERB targets.
std::thread
Note: any kind of synchronisation is out of the scope of this feature.
Add asampler
sample, to illustrate the use of the SD filesystem abstraction, and synchronisation primitives with the audio realtime thread.
sampler
mimics Simpler built-in from Ableton Live but without time-stretching and globally less options (no enveloppe, no LFOs, etc.)
Because of the current limitation in libDaisy
to detect SD, only one sample would be supported for now, and loaded when inserting a SD card and pressing the LOAD
button. This should be enough to demonstrate how to walk the filesystem and filter the files to load.
Front panel:
SD
cardLOAD
buttonLOAD
LED loading fast blinking progressPLAY
buttonPLAY
gatePLAY
feedback LEDMODE
(one-shot, loop)TRIGGER
/GATE
switchSTART
sample start Pot, CV, AttenuverterSIZE
sample length Pot, CV, AttenuverterFADE IN
PotFADE OUT
PotLP
low pass filter Pot, CV, AttenuverterOUT L/R
audio outputsTasks:
Steps to reproduce:
Analysis:
ninja
fails, but we don't understand why.Workaround:
Hardware
job fails because KiCad package for chocolatey
seems to be broken. The download URL doesn't existSoftware
job fails for the same reasons as before: for some reason libdaisy.a
is not produced. This might be because of cross compiling.Unit Tests
job fails because of gyp
software erosion. Visual Studio can't be found, but do we need to support this IDE? Is our VS Code generator good enough?Erbb tests
job fails, most likely because of a wrong python3
path (the one used now seems to come from Travis-ci)Our future integrations use "fast math" to replace parts of the standard library with a "faster" equivalent.
Sometimes for example, std::sin
is replaced with the CMSIS equivalent, but the implementation doesn't seem to be faster than the one in glibc
, unless we are looking at the wrong implementation.
To make sure those are relevant, benchmark them:
The generated data header file can't be included twice, because it is missing a guarding clause.
The AudioSample*
ignore sample type template parameter:
For example:
template <class T, size_t Length, size_t NbrChannels>
struct AudioSampleInterleaved
{
struct Frame
{
std::array <float, NbrChannels> channels;
};
float sample_rate;
std::array <Frame, Length>
frames;
};
T
is not used in the declaration of AudioSampleInterleaved
Our users expressed the need to have different language integrations, and Max is the most used of them.
Max, also known as Max/MSP/Jitter, is a visual programming language for music and multimedia developed and maintained by the software company Cycling '74.
Avery has an idea of a new Eurorack module. They want to develop it in Max gen~
, because they are familiar with it, and they can leverage quickly the big standard library of dsp blocks available at their mouse clicks.
They started to draw the layout of the module on a piece of paper and a quick schematic of the audio flow. They use the GUI editor to make a first sketch of their idea and visually create the dsp code.
They iterate over the sketch by modifying the GUI to add or remove a control, and then update the Max patch accordingly, or the other way around.
An implementation exists already for the Daisy platform, in the form of oopsy.
However this implementation does not fit our needs:
However, oopsy
is a working implementation of gen~
targetted at the Daisy platform, and the learnings from it are important for our project, and have been collected in this paper.
While our project has its own very permissive open-source license, the code exported by gen~
has a different license, and this needs to be very clear for the end-user, since the Eurorack-blocks abstracts so many things away, a user might not see they are violating a license, especially if they intend to sell their Eurorack-blocks module, which we will facilitate in the future. For enthousiast hobbyists, that shouldn't be a problem though.
At least we should:
One maybe conceptual issue is that Max users probably expect their patch to be the "center" of what they do. In that sense, they would expect some template patches as starting points for their module.
The only problem with this approach is that we need the erbb and erbui file to get started, and the erbb references the Max patch (rather than the other way around). Changing the way this work would probably increase the amount of work needed, as everything would need to be done the "reversed way", and this could have severe impact on the ecosystem of tools we are trying to bring (such as the GUI editor).
So for now, we will consider the erbb meta-build-system file to be the "center". Only when we will know in practice which impact would be to have the Max patch at the "center" of everything, and a proper long-term strategy for it, then we would revise the current strategy.
Users will first call erbb max init
which will create a skeleton of a max patch to start with, with:
gen~
patch,gen~
patch,
gen~
code generation, using a savebang
(see error early), and the generation (configure + build) of the simulatorerbb
command.The Max package will contain:
bpatcher
gendsp
functions which mimics what we have in the C++ API (button pressed, held, released, LED animation system, etc.)Using a Max package has the advantage to be a search path for the javascript code we have, so that the ERB project stays clean and upgrading the repository updates as well all the user patches accordingly.
Thanks to its recursive structure, we should only consider a flat name space, so that name mapping should be direct.
Since param names should contains only characters that matches C++ identifiers, we can't use dot notation to refer to a property in the compound, so we will use a _
instead.
We can't use an attribute for the history
, as this would give the same value.
Not sure if there are really other nice solutions. It seems possible to use buffer
or data
for those types, or the value can be a vec
, but that doesn't seem to be convenient at all. We need to assess this. A vector could be used as well for some reason, and that doesn't interfere with our "underscore notation".
Some controls have additional computed properties. For example a button value is the raw signal value, including electrical bouncing. But we have algos to filter that, and convenient interface like pressed ()
or held ()
on the C++ side.
Made this accessible to Max with the same technic than the one above: replace dot notation with "underscore notation".
Make a gendsp function for that.
Some controls like LED can animate the value they own. For example a LED has some functions like blink
, pulse
etc. with some arguments about timings.
Do history led_blink
(using again "underscore notation"). Not sure how to pass arguments though.
Make a gendsp function for that.
Internal data
is still accessible in generated code, with a nice setter to do the work. We just need to implement genlib_data_setbuffer
The gen_dsp
library doesn't compile without modifications. We have our own copy of gen_dsp
and use it instead of the provided one.
Oopsy removed a lot of thing, and it seems that ARM C7 DSP functions are not used, and it's unclear why.
Ideally the generated code should be portable for both the simulator and the embedded daisy platform.
However "allocating" big portions of memory, such as delay lines, are going to be handled differently on both platforms:
Oopsy implemented an heuristic which is similar to the one we implemented on the allocate-ram-auto
branch. We used this allocation strategy for FAUST, and we will reuse it for Max.
Also Oopsy is putting the module state in the (most likely) SRAM. Since they already parse the C++ code, it's unclear why they didn't go a bit further and allow to have the State on the stack to put it in DTCMRAM
. After all this memory is twice as fast as the AXI SRAM
, and all filters history are stored in it.
DTCMRAM
for State
out
for output parameters rather than the more practical history
(as seen in their paper)For now, the UI part of a module must be constructed before its DSP, to ensure correct initialization sequence.
For example:
struct Reverb {
ReverbDsp dsp;
ReverbUi ui;
};
Is most likely to crash as soon as the dsp needs to allocate in the SDRAM.
This is because the Daisy platform is inited in the board, which is done during the UI construction.
This is very error prone.
To remedy this, since we are generating a main
per platform, and in particular one for Daisy, we could move the critical part of the board setup to early main
.
Tackle the "allocating into SDRAM" problem, by making it explicit to the project user, while not punishing them when they come from a desktop software experience, because of lack of knowledge, as the learning curve can be quite steep.
This "allocation system", that would still help people to learn, while keeping good embedded practices, has the following properties:
sdram_ptr
template that allocates into SDRAM with a semantic similar to std::unique_ptr
,sdram_bss
section attribute for the whole memory pool.It is still unclear if destroying a owning sdram_ptr
should break into the debugger (as it is leaking memory), but that could be an option.
The simulator could help to:
sdram_ptr
is destroyed,Finally for learning, breaking into the debugger should have a link next to it to redirect to a page that explains memory "allocation" on embedded platforms, and how this differs from desktop software development.
struct ReverbSc {
struct DelayLines {...};
ReverbSc ();
erb::sdram_ptr <DelayLines> delays;
}
void init () {
ReverbSc reverb_sc1;
ReverbSc reverb_sc2;
// jobs done!
}
The IDE generated project contains both the simulator target and daisy target. The latter won't be compiled into the IDE and is therefore some noise for the user of the framework.
However it may be important for the framework developer, to navigate the framework files conveniently.
Add an option to segregate between those two personas.
Example for contributor/maintainer:
$ ./configure.py --dev
Example for user:
$ ./configure.py
Still, the daisy ninja files are created in this case. It's just that the erb-daisy targets are hidden.
Ultimately, compiling target All
shouldn't show any error.
We were assuming incorrectly that the stack was 512K, when it is only 128K.
In the simulator version of the module, we make a check for the size of the module structure.
Adapt the check so that if the structure is more than 128K, then we know that it is too much:
Data elements should be define in erbb
, not erbui
. Also be more consistent with the language:
module Kick {
sources {
file "Kick.cpp"
file "Kick.h"
}
resources {
data kick AudioSample { file "kick.wav" }
}
}
Keep resources a separate keyword, so we can group resources logically, and more specifically when we will introduce the concept of destination section (ie qspi, flash, program, etc.)
The current way to edit the UI in erbui
is far from optimal. This is the feedback we have for the moment and how to address it:
erbui
A GUI editor for erbui
appears to be a good solution:
Ideally, the editor could be bootstrapped immediately, so an implementation in Python would be a good idea.
Currently, the erbb install
command is not great:
openocd
is installed, it is used instead of dfu-util
which might be unsetting, if the user doesn't have a stlink programmer,erbb install
should probably use dfu-util
by default,openocd
is probably a bad command option, since it mentions the tooling rather than was system is used. Maybe jtag
or stlink
would be bettererbb install bootloader
would feel more natural than erbb bootloader
Additionally, the DFU util error is a bit misleading, so something should be done about that, to report success.
Test kivu12
rev3
Kivu12Max
module?kivu12
PCBdrop
PCBkivu12
drop
Steps to reproduce:
configure.py
for samples/drop
,drop.xcodeproj
in the drop/artifacts
folder,drop-vcvrack
target in the IDE to launch the Rack
application,Launch
option to be Automatically
(the default one),Document Versions
in Option
Tab (to prevent unrecognized launch argument options for VCV Rack).When running the target from Xcode, Rack
will crash.
init
is called 2 times, and will assert
when adding the model a second time (because it was already added).
Workarounds:
Rack
without the Xcode debugger fixes that problem,Launch
option Wait for executable to be launched
fixes that problem as well.The Daisy QSPI 8MB Flash is going to be used for programs that can't fit into the small STM32 flash.
This is even more important with the addition of the data
language feature, which stores small resources directly into the program, making the 128K most likely very tight when using samples like transients.
Check to see if this can be only done with USB, and generally how to manage JTAG debugging after.
Take time to see how to enhance the workflow, in particular for the first hour, when the bootloader is most likely not present.
Add the section
keyword in erbb
:
module Kick {
section qspi // or flash (default)
sources {
file "Kick.cpp"
file "Kick.h"
}
resources {
// section sdmmc (in the future resources could have an associated section too)
// for now "same as module" is assumed.
data body AudioSample { file "body.wav" }
data transient01 AudioSample { file "transient01.wav" }
}
}
The path to build system is wrong in the generate_vcvrack.py
script.
Therefore, one cannot make changes to UI from within the IDE.
python3 artifacts/generate_vcvrack.py "KickCut24.erbui"
Traceback (most recent call last):
File "artifacts/generate_vcvrack.py", line 20, in <module>
import erbui
ModuleNotFoundError: No module named 'erbui'
make: *** [artifacts/deploy_vcvrack.py] Error 1
This is because the path is relative and hardcoded.
Workaround: call configure.py
for each UI changes instead.
Remove the erbb_flash_lds
variable from modules gyp
files.
This variable was introduced to specify which linker script to use at build time, most likely because the path for the linker was a bit different, and we couldn't find a way back then to integrate it into the erb-daisy
dependency.
tl1105
button style
generates an error when generating front PCB.
This because the move component function is not implemented for this button.
unsupported block tl1105
Traceback (most recent call last):
File "./build.py", line 74, in <module>
erbui.generate_front_pcb (PATH_ARTIFACTS, ast)
File "/Users/raf/dev/eurorack-blocks/build-system/erbui/__init__.py", line 209, in generate_front_pcb
generate_front_pcb_kicad_pcb (path, ast)
File "/Users/raf/dev/eurorack-blocks/build-system/erbui/__init__.py", line 221, in generate_front_pcb_kicad_pcb
generator.generate (path, ast)
File "/Users/raf/dev/eurorack-blocks/build-system/erbui/generators/front_pcb/kicad_pcb.py", line 27, in generate
self.generate_module (path, module)
File "/Users/raf/dev/eurorack-blocks/build-system/erbui/generators/front_pcb/kicad_pcb.py", line 44, in generate_module
self.generate_control (control)
File "/Users/raf/dev/eurorack-blocks/build-system/erbui/generators/front_pcb/kicad_pcb.py", line 211, in generate_control
component = self.move (component, control.position)
UnboundLocalError: local variable 'component' referenced before assignment
UI and data headers are missing in Xcode project artifacts.
So add them in at least in the erbb
vcvrack
generator.
Don't include the data source file, as its size makes a lot of text editors either crash or get stuck.
Currently erbb setup
infers that bash
is the current shell, and will append its path to the .bash_profile
.
If the current shell is not bash
, we need to mitigate this:
zsh
which became the default shell on a fresh installation of the latest macOS operating systems,The project provides SdramPtr
which allows to allocate in the external SDRAM chip, but doesn't have the facility to allocate into the internal SRAM.
Add a SramPtr
template class on the same model as SdramPtr
which provides this feature.
Tasks:
The current build system is far from optimal. This is the feedback we have for the moment and how to address it:
gyp
is not famous compared to cmake
SdRam
depends on erb_TARGET_XXX
data
feature is in the erbui
language but should be part of build system, because it doesn't represent a UI elementconfigure.py
and build.py
scripts in each moduleerbb
Languagecmake
coud be something, but we have lots of very specific things for cross-compiling that would make the syntax most likely cryptic.
Instead of that, we will roll out a small language that takes into account everything upper.
module Kick {
location flash // or qspi
import "../dsp/dsp.erbb"
define erb_SDRAM_MEM_POOL_SIZE=1234
define erb_SDRAM_MEM_POOL_SIZE_SIMULATOR_CHECK=0
sources {
file "Kick.cpp"
file "Kick.h"
}
resources {
// for now location is the same as module
data body AudioSample { file "body.wav" }
data transient01 AudioSample { file "transient01.wav" }
}
}
dsp.erbb
define SOMETHING=1
// not sure yet which term for header search path
search ".."
base ".."
origin ".."
root ".."
include ".."
directory ".."
sources {
file "Filter2Poles.cpp"
file "Filter2Poles.h"
}
resources {
// resources for an oscillator for example
}
erbb
Command Line InterfaceWe would have a command line erbb
.
Create everything to be able to build (IDE project, and all genrated code/graphic resources):
$ erbb configure
This would also install all binary dependencies if needed. erbb setup
could also force it.
Build the firmware:
$ erbb build
Install the bootloader:
$ erbb install bootloader
Press RESET + BOOT and hit any key to start the bootloader install
...
Install the firmware:
$ erbb install
Press RESET and hit any key to start the bootloader install
...
Somehow, erbb
needs to be in the PATH
.
Either:
source
iterbb
to the PATH
source
it from .profile
or .bash_profile
gyp
sense) in a projecterbb
languageerbb
Xcode syntax highlightererbb
simulator gyp
generatorerbb
Daisy gyp
generatorerbb
VSCode gyp
generatorerbb
command line and env init sourcing scriptAutoroute the front PCB to save time.
For now, the front PCB exposes pads with name, and the user needs to air wire all pads with the name name. We made just a tad simpler by having planes for both GND and 3V3, which reduces the number of things to solder.
The freerouting project is the defacto autorouteur for KiCad 5, since the included autorouteur was removed from KiCad 4.
Fortunately, the freerouting autorouteur:
On branch front-pcb-autorouter.
PAUSED
KiCad 5.1 doesn't include in the python bindings the functions we need. We decided to wait for the release of KiCad 6.0 before continuing on this issue. This release should be quite imminent, has rc1 has been around since some time already.
Because all audio samples are concrete types with template parameters already configured, it is hard to manipulate them as some sort of abstract type, for example:
std::array
, even though the concrete types are different types (because the samples length are different for example)This problem is quite apparent in the Kick
sample, for example on how we handle transients. This leads to this weird design, where all transients need to have exactly the same length, just so that they happen to be the same type.
Fix this by making some sort of AudioSample
polymorphic type. Since we are representing a 2d array, we need to acknowledge that and represents the offset to next frame, and the offset to next channel.
As for grouping samples by meaning in an std::array
, we could make erbb
resources
have an optional name that would group elements in resources with that name and find the best class that keeps resources homogeneity (for example AudioSample
if it contains only AudioSample
types, at worst a Data
polymorphic type). For example:
AudioSampleMono_ <float> // all are mono samples with different lengths
AudioSampleInterleaved_ <float, 2> // all are interleaved stereo samples with different lengths
AudioSample <float> // any kind of samples
Usage:
module Kick {
resources {
data body AudioSample { file "body.wav" }
}
resources transients {
data t01 AudioSample { file "transient01.wav" }
data t02 AudioSample { file "transient02.wav" }
...
data t18 AudioSample { file "transient18.wav" }
}
}
Generated code:
struct KickData
{
static const erb::AudioSampleMono <float, 38320> body;
struct ResourcesType_transients {
static const erb::AudioSampleMono <float, 38400> t01;
static const erb::AudioSampleMono <float, 38400> t02;
static const erb::AudioSampleMono <float, 38400> t03;
static const erb::AudioSampleMono <float, 38400> t04;
static const erb::AudioSampleMono <float, 38400> t05;
static const erb::AudioSampleMono <float, 38400> t06;
static const erb::AudioSampleMono <float, 38400> t07;
static const erb::AudioSampleMono <float, 38400> t08;
static const erb::AudioSampleMono <float, 20100> t09; // different length
static const erb::AudioSampleMono <float, 38400> t10;
static const erb::AudioSampleMono <float, 38400> t11;
static const erb::AudioSampleMono <float, 38400> t12;
static const erb::AudioSampleMono <float, 38400> t13;
static const erb::AudioSampleMono <float, 38400> t14;
static const erb::AudioSampleMono <float, 38400> t15;
static const erb::AudioSampleMono <float, 38400> t16;
static const erb::AudioSampleMono <float, 38400> t17;
static const erb::AudioSampleMono <float, 38400> t18;
using value_type = erb::AudioSampleMono_ <float>; // AudioSampleMono_ is the best common type
constexpr size_t size () const { return 18; }
const value_type & operator [] (size_t index);
} transients;
};
Usage:
// static
do_something (data.transients.t10);
// dynamic
do_something (data.transients [9]); // can throw on simulator, clamp bounds on daisy, or break if DEBUG is activated
Reverb documentation still references the old make_sdram_object
method, and doesn't explain how to use erb::SdramPtr
instead.
Some files from VCV Rack and erb have errors, for example:
submodules/vcv-rack-sdk/include/midi.hpp:152:2: '~Input' overrides a destructor but is not marked 'override'
When a configure, build or deploy script python import is failing, this most likely indicates an install problem, which in most cases could be related to the user not finding the documentation about setting up the project.
If we detect an import error, then we should display at least the link to the setup documentation.
This is very important, as this most likely will appear in the first minute of the user's evaluation.
Add a erbb
command to create a new project, to get started quickly.
Make sure erbb init
generates valid projects, by adding it to our GitHub Actions workflows.
Add SD and MMC support for data
location in memory.
data
represents an immutable piece of data, available as soon as the module is started.
For now the data itself was stored as static data in the program itself, but realistically, we need QSPI support (#219) to support this feature fully.
Alternatively, we can use the SD feature to provide basically the same thing, except the data is loaded from SD to SDRAM when the module is started.
For now we have the following scheme, where the data is stored into the program.
data sample {
file "bla.wav"
type AudioSample
}
We can extend the data
attributes with location
which dictates where data is stored:
data sample {
file "foo.wav"
type AudioSample
location program // default if not specified
}
data sample2 {
file "bar.wav"
type AudioSample
location sdmmc // in SD or "back MMC" on board
}
For the sdmmc
location, no program is generated. Instead we create a .bin
file which is a byte per byte representation of what the program
location does.
When starting the module, the data location in SDRAM is filled with the content of the .bin
file. The data is either truncated or complemented with zeros if the file size doesn't match.
For this feature to work, the current types (like AudioSample
) must have at most one dynamic length. As a result for AudioSample
for example, we need to go from a planar representation to an interleaved one.
Finally, because the data is stored in SDRAM, we need to play nice with the system we already have in place, by either:
SdRamPtr
system,SdramPtr
system.AudioSample
type interleavedSdramPtr
systemlocation
attribute to languagelocation
attribute documentationlocation
generator supportThe current state of the project doesn't include a call-to-action that would lead to a successful feeling in the 5-minutes and first-hour of evaluation milestones.
As for strategy, first start by writing the guide and getting feedback about it, before we get into shooting the video, as it will be easier to integrate feedback for some text and pictures rather than a video edit.
kivu12
Provide a way to access conveniently and in a portable way (for both the Daisy and simulator platform), user-defined and immutable pieces of data, typically small samples for oscillators, transients or wave tables.
module Sampler {
board kivu12
data sample01 {
file "data/sample01.wav"
type AudioSample
}
control audio_out AudioOut {
pin AO1
}
}
struct Sampler {
SamplerDsp dsp { erb_SAMPLE_RATE };
SamplerUi ui;
SamplerData data;
void process () {
auto pos = ...;
ui.audio_out [pos] = data.sample01.samples [pos];
}
}
Based on the type, a dispatcher is used to generate a C++ file in the artifacts
folder.
Typically the script will take an input file path, the data name, the type used with its arguments, and an output file path.
It will populate the output file with C++ so that it can be used directly. For example it would generate:
// artifacts/SamplerData.h
#include "erb/erb.h"
struct SamplerData {
static constexpr erb::AudioSample <float, 14938> sample01 = {
.sample_rate = 48000,
.samples = { 0x123456789p0f, ... }
};
};
This will be done by implementing a new code generator. For now, only framework provided type will be supported. ie., it won't be possible to provide custom user types.
When type
is not specified, the raw data is used instead, using an std::array <uint8_t, size_of_the_file>
.
The implementation relies on the fact that we will transition our programs to the QSPI flash soon.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.