[Interview] Stéphane Letz, France (co-author of the Faust programming language, designer of JACK2, head of research at GRAME-CNCM)
Stéphane Letz is a French software engineer and head of research at GRAME-CNCM in Lyon, working in the Emeraude team with Inria. He is a co-author of Faust, the functional programming language for digital signal processing created by Yann Orlarey in 2002, and a core member of the Faust development team. He designed JACK2, the multi-processor version of the low latency JACK audio server for Linux, macOS and Windows. His other work includes libaudiostream, a C++ framework for manipulating audio as streams, faust-sampler, and faust-mcp, which connects Faust with large language models.
Table of Contents
You are a co-author of Faust, which Yann Orlarey created in 2002. How did you two start working together, and what did you contribute to the language?
It’s a long story. We have been working together at GRAME since the 1990s. Faust itself is the outcome of several earlier projects, starting with a first paper in 1994, Lambda Calculus and Music Calculi (hal.science/hal-02158947v1), which laid the foundations for using the lambda calculus, the essence of functional languages, in particular with GCalc, a demonstration language invented by Yann Orlarey (github.com/grame-cncm/gcalc). Then came Elody, a functional language for musical composition, also built on the lambda calculus (see hal.science/hal-02158948 and github.com/grame-cncm/elody).
So Faust is, in a way, the result of all these earlier attempts, carried over to digital audio signals, which was a new field for GRAME at the time, in the early 2000s. Yann was also inspired by existing languages such as Max/MSP, but he turned the view of this dataflow language around: he read a Max/MSP patch backwards, as an algebraic expression. Faust’s block-diagram algebra also came out of this thinking.
For several years Yann developed the Faust language and its compiler fairly independently, and I did not work on the language right away. In the early 2000s I was working on the Elody environment, and then on Jack2. I really started working on the Faust ecosystem at the end of the 2000s, first by developing architectures, for instance to use Faust building blocks in Max/MSP. In 2010 I wrote a paper on a parallelization scheme based on a work-stealing scheduler, which allows Faust DSP code to be parallelized automatically. My main contributions really begin in the 2010s: I worked a lot on the gradual extension of the Faust ecosystem, that is, on making the same Faust program run on a whole range of different targets: standalone applications, plugins, and so on. Then there is all the work around LLVM, which lets the Faust compiler be embedded as a library in many projects. Faust code can then be compiled dynamically, on the fly, and run in the environment the library has been integrated into. In the early 2010s, we became interested in the Web, with the Web Audio API that was beginning to appear at the time. I contributed to the software layers that make the Faust compilation chain usable on the Web, first with asm.js, and later, from 2017, with WebAssembly.
The history of the Faust project is also one of encounters. Around 2005, with Albert Gräf, from the University of Mainz, who worked with Yann on the pattern matching added to the compiler, which came from his own work on functional languages based on term rewriting, first Q, then Pure (github.com/agraef). Albert also contributed several architectures to the Faust ecosystem: the Pure Data externals and the faust2pd tool that generates their user interfaces, pd-faust to load Faust programs dynamically in Pd, the use of Faust from Pure, and the LV2 and VST plugin architectures.
Then with Julius Smith, a world-renowned audio DSP guru and professor at CCRMA, Stanford, who has gradually contributed a huge part of the Faust libraries; and of course, from the 2010s on, with Romain Michon, who did his PhD at CCRMA with Julius. Romain has contributed a lot to the Faust libraries and to their organization, in particular with the physical modeling library (physmodels.lib), and he developed a large part of the architectures for embedded systems: audio boards based on the Teensy or the ESP32, a whole new use case for the Faust ecosystem, which is still growing today. With GRAME, he also developed the Gramophone (ccrma.stanford.edu/~rmichon/gramophone), a speaker and musical instrument built around an ESP32 board, programmable in Faust and fitted with sensors to interact with the sound. It is at the heart of Amstramgrame, an educational project that uses it to teach science and programming in schools through music.
Can you explain to the rest of us what Faust actually is, and what problem it solves that C++ does not?
Faust (Functional AUdio STream) is a domain-specific language (DSL) dedicated to real-time audio signal processing and synthesis. A Faust program does not describe a sequence of instructions, as an imperative language like C++ does, but a signal processor: a mathematical function (think of it as describing a “circuit”) that turns input signals into output signals. Programs are built by composing blocks with a small set of operators (sequential, parallel, split, merge and recursive composition), which is the block-diagram algebra (BDA) I mentioned earlier. A filter, a reverb or a whole synthesizer can be written in a few lines, very close to the equations or to the diagram a DSP engineer would draw on paper.
The Faust compiler then translates this specification into efficient code: C++ at first, and today also C, LLVM, WebAssembly, Rust, Cmajor, Julia and others. So Faust does not compete with C++, it produces it. What it saves the developer is everything in hand-written C++ that is not the algorithm itself: the sample loop, the delay lines and filter states, memory allocation, sorting out what has to be computed once, at control rate or at audio rate… The compiler does this analysis automatically, and it can apply optimizations that are tedious and error-prone by hand, such as vectorization (which makes use of SIMD instructions) or parallelization.
The other problem it solves is separating the DSP from everything around it. A Faust program says nothing about the audio driver, the user interface or the plugin format: these are provided separately, so the same code can become a plugin, a standalone application or a web page, and its user interface is generated from the sliders and buttons declared in the program.
Finally, because the language has a precise mathematical semantics, a Faust program is something one can reason about: the compiler simplifies and normalizes expressions, programs can be documented as block diagrams, and it opens the way to formal verification. And the standard libraries, developed with the community, provide hundreds of ready-to-use filters, oscillators, effects and physical models, so a newcomer can have a working instrument very quickly.
Faust compiles the same code to plugins, Android apps, Bela boards, SuperCollider units and the web. How is one source made to run on so many targets?
The key idea is what we call architecture files, a part of the project I have worked on a lot. The Faust compiler only produces the signal processing part: a self-contained class (in C++, a subclass of a generic dsp class) with a small interface that is always the same: init(sample_rate) prepares its state, compute(count, inputs, outputs) processes a block of samples, buildUserInterface(UI*) describes its controls, and metadata(Meta*) describes the program itself. Everything that connects it to the outside world is written once, separately, in an architecture file: a text file with a marker where the compiler inserts the generated code, as in faust -a jack-gtk.cpp foo.dsp.
An architecture file assembles two kinds of building blocks, each written against an abstract interface. On the audio side, a driver derived from an audio class (init, start, stop) calls compute for every buffer: JACK, CoreAudio, ALSA, PortAudio, the audio callback of a plugin format, or that of a board such as Bela, Teensy or ESP32. On the control side, buildUserInterface walks through the program’s sliders, buttons and bargraphs, grouped in boxes, and lets any UI class build what it needs from them: a Qt or GTK window, but also an OSC or HTTP remote control, a MIDI mapping, or simply a JSON description that a web page turns into a GUI. Each control is a memory cell shared between the DSP and the controllers, so several of them can drive the same program at once, a window and OSC for instance. Metadata written in the Faust source, such as [unit:Hz], [style:knob] or [midi:ctrl 7], refines this mapping without touching the DSP code.
Since the DSP and its architecture only meet through these few methods, the combinations multiply: every audio driver with every kind of controller, and every plugin format. The faust2xx scripts (faust2jaqt, faust2android, faust2bela, faust2supercollider, faust2juce, faust2wasm…) wrap the compilation with the right architecture files and the platform’s own build tools, so a single command turns the same .dsp file into a JACK application, an Android app or a SuperCollider unit generator. Reusable layers sit on top of this: polyphony, where a voice is duplicated and MIDI notes are mapped to its freq, gain and gate controls; effect chaining; parameter smoothing; sample-accurate control. And because the code compiled on the fly by the LLVM or interpreter backends is wrapped in the same dsp interface, the same audio and UI classes drive it too.
Most architecture files are written in C++, but the principle is the same for the other languages the compiler generates, C, Rust, Julia, or WebAssembly with its JavaScript layer. Supporting a new target usually means writing a new architecture file, not changing the compiler, and much of the community’s work over the years has been exactly that. That is why the list of targets keeps growing.
Which target was the hardest to support, and why?
Each target has its own difficulty. On desktop computers, it is the variety of libraries: audio APIs, GUI toolkits, plugin SDKs, each with its own versions and build systems, which the architecture files have to keep up with over the years. On embedded platforms, it is fine-grained memory management and the size of the generated code: a microcontroller like the Teensy or the ESP32 has very little RAM and hardly any operating system, so the compiler has to be able to place large delay lines and tables in external memory, to generate code without a user interface layer, and to keep the binary small.
But the hardest target is clearly the FPGA. There, a Faust program does not become software running on a processor, but a hardware circuit: the generated code goes through high-level synthesis tools that turn it into a design, which is then placed and routed on the chip. This requires a complex and heavy compilation chain to set up: large vendor tools that change from one version to the next, synthesis runs that take tens of minutes, careful handling of memory and timing constraints, and the coordination between the circuit and the processor that handles the control part. This is the Syfala project, developed with our colleagues of the Emeraude team (github.com/inria-emeraude/syfala). The reward is audio processing computed sample by sample with extremely low latency, which opens new applications such as active control of sound or spatial audio with a large number of loudspeakers.
You designed JACK2. What was the main limitation of the original JACK that Jack2 set out to fix?
The original version of JACK was developed by Paul Davis in the early 2000s, together with Ardour, the digital audio workstation that was one of the first applications to support this client/server audio model. JACK lets users combine several audio applications and make them work together through a system of virtual audio and MIDI cables, all synchronized very precisely by the server: every application processes the same buffer, at the same time, with no added latency.
The original code was written in C and only ran on Linux. One of the first things I did on JACK was to port this C code to macOS, in particular by using very low-level primitives of the Mach kernel for inter-process synchronization. So one of the first motivations of the Jack2 project, first called jackdmp, was to fix a few bugs and to modernize the code, rewritten in C++, to make it easier to port the system to other operating systems: macOS first, then Windows a few years later (and even Android and iOS later on, thanks to external partners).
The other motivation was to extend the computation model of the JACK server so that it makes better use of multi-core machines, which were just appearing at the time. The original JACK used only one core at a time to run the graph. The idea was to compute the different nodes of the JACK audio graph on different cores, in parallel, according to the topology of the connection graph between the applications: two applications that do not depend on each other can run at the same time.
Along the way, we also worked on robustness. When audio flows through several processes, a single application that crashes or misses its deadline should not block all the others, and connecting or disconnecting applications should not interrupt the sound: in Jack2 the graph can be changed while it runs, without glitches, using lock-free techniques to switch from one state of the graph to the next. Jack2 kept the same API as the original JACK, so existing applications could use it without any change, and it became the version shipped by many Linux distributions, and the one used on macOS and Windows.
JACK2 added multi-processor support for audio processing. How do you split real-time audio work across cores without adding latency?
In the original JACK, written in C, each new connection graph goes through a step called topological sorting, which determines an activation order from the inputs to the outputs that respects the data dependencies between the nodes of the graph, here the audio applications. The applications are then run in this order, one after the other.
Jack2 follows what is called a dataflow model: the dependency graph is walked through on the fly, at every execution cycle, and there is no need for this topological sorting step beforehand. Starting from the inputs, a token system models the activations of the clients and circulates between them. When a client with two inputs, for example, has received the two tokens coming from these inputs, which have just been computed, it knows it can be activated. Once its own computation is done, it passes its token on to the clients connected to its outputs. This progressive activation takes place at every buffer, from the inputs to the outputs, until all the clients of the audio graph have been computed. The whole point of this model is that the parallelism expressed in the graph is naturally spread over the cores available on the machine.
In practice, each client has an activation counter in shared memory, reset at each cycle to the number of its inputs and decremented atomically, without locks, by each client that feeds it; the client whose counter reaches zero is woken up. Since each client runs its own real-time thread, it is the operating system’s scheduler that places the woken threads on the free cores, with no central scheduler adding its own overhead. And no latency is added, because the parallelism stays within the cycle: all the clients still process the same buffer in the same period, and the graph simply finishes earlier when independent branches run at the same time. A graph that is a single chain, of course, gains nothing. Jack2 also offers an asynchronous mode, where the server does not wait for the end of the graph and outputs the result of the previous cycle: it costs one buffer of latency, but a late client no longer stalls the whole system.
The difficulty is then to implement this activation system efficiently with the synchronization primitives of each operating system.
JACK started on Linux and now runs on Windows and macOS too. Was it difficult to keep the same behavior on all three systems?
The first step, the rewrite in C++, was precisely meant to better separate the generic parts from those that depend on the operating system: for example the synchronization primitives, shared memory management, and some lock-free primitives, which depend on the operating system or on the processor. So for each port we had to find the most efficient synchronization primitives to implement the process activation system: futexes or FIFOs on Linux, Mach semaphores on macOS, events on Windows. Thanks to this decoupling in the code itself, the questions related to each operating system were greatly simplified and isolated in the source code.
Concretely, the server and the client library are written against a small set of abstract classes (threads, semaphores, shared memory, communication channels between clients and server) with one implementation per system: POSIX on Linux, Mach on macOS, Win32 on Windows. The same goes for the audio drivers: ALSA, or FFADO for FireWire interfaces, on Linux; CoreAudio on macOS; PortAudio on Windows, which gives access to ASIO and the other Windows audio APIs. All the logic of the graph, of the activation and of the timing is shared.
Keeping exactly the same behavior is harder, because the systems do not offer the same real-time guarantees. On macOS, the “time-constraint” threads of the Mach kernel give very good results without any configuration. On Linux, the user has to be allowed to use real-time priorities, and the kernel configuration matters a lot, which explains the many guides on setting up a Linux machine for audio. On Windows, real-time priorities are less strict, and the quality of the audio drivers varies a lot from one interface to another. So the code is the same, but the latency that can be reached reliably depends on the platform.
The systems also change under your feet. On macOS in particular, the security rules introduced over the years (application sandboxing, code signing, restrictions on communication between processes) made it harder to deploy a server shared between applications, and some components, such as JackRouter, the virtual audio device that let applications which do not support JACK send their audio to it, became hard to maintain. Conversely, what has lasted best is the JACK API itself: on Linux today, PipeWire reimplements it, and JACK applications keep working without any change.
At GRAME you work in a research institution, not a company. How does that change which problems you pick and how long you can work on them?
GRAME is a non-profit association under French law, mainly funded by public money: the French Ministry of Culture, the City of Lyon and the Auvergne-Rhône-Alpes Region. GRAME’s research lab decided early on to release its work as open source, so all the projects we have developed over the years have been made available to the computer music community, and more broadly. Bringing together in the same structure research in computer music and the activities of creation and outreach in contemporary music makes it possible to build close working relationships between the technical side and the artistic and outreach sides. This structure also allows research and development over the long term, and indeed we have been able to work for many years on Jack2 and Faust.
It changes which problems we choose in two ways. First, many of them come from artistic needs: a composer who wants a piece to run on many loudspeakers, an installation that has to run on a small board, an educational project for schools. These concrete needs, often with a fixed date, a concert or a premiere at the Biennale Musiques en Scène in past years, the festival GRAME organizes, keep the research grounded. Second, we are not tied to a product roadmap or to a market: we can work on questions that have no immediate commercial interest, such as the semantics of a language, compilation techniques or, more recently, formal verification, which only pay off years later.
As for how long, Faust is now more than twenty years old, and Jack2 almost as old: few companies would keep a small team on the same tools for so long without expecting a return. The counterpart is that the team and the resources are small, and that much of the funding comes from national research projects (ANR) and European projects, which have to be won and come with their own agenda. Our partnerships with academic labs matter a lot here: the Emeraude team, shared between Inria, INSA Lyon and GRAME, works precisely on Faust and on programmable embedded audio systems.
Open source is also what makes this model work. Since the code generated by the Faust compiler can be used freely, including in commercial products, companies use Faust, and many of them, along with the wider community, contribute back. So being a research institution does not mean being cut off from industry: the transfer happens through the tools themselves rather than through products we would sell.
Faust now has web tooling that runs in the browser. What did it take to compile and run DSP code in a web page?
We became interested in the Web platform in the 2010s, when the Web Audio API appeared and made it possible to imagine that custom DSP code, such as the code generated automatically by a compiler like Faust, could be deployed in web pages, and more broadly in web applications. The first thing we explored was asm.js and the Emscripten compiler, originally developed by engineers at Mozilla. This technology made it very easy to bring large C and C++ code bases to the Web, by compiling them directly to the language of the Web, JavaScript, or more precisely to asm.js, a highly optimizable subset of JavaScript.
So by 2014 we already had a first version of the Faust compilation chain fully available on the Web. We had first added a backend to the C++ compiler to generate asm.js code, and the C++ compiler itself could be compiled to JavaScript with Emscripten. This low-level code model for the Web was then standardized with the WebAssembly initiative, supported by the four major Web players: Mozilla, Google, Microsoft and Apple. In 2017 we developed a new backend for this new language, and with the chain already in place, we had a modernized dynamic compilation chain for Faust on the Web.
The second step was to develop a set of specific architecture files to wrap the code generated by the compiler and make it available as a node of the Web Audio API. This code was first written in JavaScript, and more recently it was completely rewritten, more cleanly, in TypeScript. This project, faustwasm (github.com/grame-cncm/faustwasm), is now distributed as a separate component that many community projects have been able to integrate and use.
The hardest part was not so much the compilation as real time. In the early Web Audio API, custom processing ran in a ScriptProcessorNode, on the page’s main thread, shared with the display and the user interface, so the slightest activity caused glitches. AudioWorklet, which arrived around 2018, finally made it possible to run the generated WebAssembly code in the browser’s audio rendering thread. We also had to adapt code generation to the constraints of WebAssembly: the whole state of the DSP and the audio buffers live in the module’s linear memory, and the mathematical functions that WebAssembly does not provide are imported from JavaScript. Around this, the same principles as on the other platforms apply: the JSON description produced by the compiler is used to build the user interface automatically, polyphony and MIDI go through the Web MIDI API, and a Faust program can also be exported as a Web Audio Module, the plugin format for web audio applications.
So in the current model, either the Faust code is precompiled to WebAssembly and JavaScript and deployed in pages or applications, or the compilation chain itself is embedded, for all kinds of applications where the user writes Faust DSP code that is compiled and run on the fly. This is the case of the Faust IDE (faustide.grame.fr), where a Faust program can be written, tested and exported with nothing but a browser, and of many educational and creative tools built by the community.
libaudiostream treats audio as streams that applications can combine. What problem was it built for, and is it still in use?
It was developed as part of a research project, with the aim of handling audio streams through an algebraic approach, somewhat in the same vein as everything we have done with languages over the years.
The problem was that of applications that need to play, transform and combine sound material, such as audio players, interactive music applications or offline processing tools. Instead of managing buffers by hand, the application describes what it wants to hear as an expression built from a few operators: read a sound file or a live input, cut a region, put two streams in sequence, mix them, loop them, apply a fade, change the pitch or the duration, or apply an effect. These expressions can be composed like algebraic terms, and the library renders them in real time on top of the usual audio drivers, or writes them to a file. Since Faust was being developed at the same time, the effects could themselves be written in Faust and compiled on the fly, which made the two projects meet. It was used, for instance, to build interactive scores of audio processing together with the i-score sequencer developed in Bordeaux.
The project was not really developed beyond this research work of the 2010s, and it is no longer maintained, although the code is still available (github.com/sletz/libaudiostream). But its central idea, describing audio processing as the composition of a few simple operators rather than as code that manipulates buffers, is also at the heart of Faust, which is where most of our effort has gone since.
You built faust-mcp to connect Faust with large language models. What can an LLM do with Faust that it cannot do with C++?
The arrival of LLMs in 2022 with ChatGPT, and fairly quickly the agentic model, with LLMs more powerful at each generation, is completely transforming software development, mathematics and, in the end, all fields of science in the broad sense.
A question we quickly asked ourselves was whether it still made sense to keep working on specialized audio languages, Faust in our case, since LLMs can code the same things in more generic and more standard languages like C++. With hindsight, our impression is that very specialized tools are extremely relevant. A specialized language makes it possible to express a program idea in an extremely compact way, with a lot of semantics and few tokens, and recent LLMs are becoming incredibly good at working with code of this kind.
Concretely, this is what faust-mcp provides (github.com/sletz/faust-mcp): a set of MCP servers through which a model can compile the Faust code it writes, render it offline and get back an analysis of the sound, or play it in real time, read and change its parameters, and connect a MIDI keyboard to it. The loop between writing and listening, or at least measuring, is closed. Doing the same in C++ would mean asking the model to also write the buffers, the audio callback and the plugin glue, and to avoid the memory errors that can crash the audio thread. In Faust, all this is handled by the compiler and the architecture files: a program has no pointers and no allocation, and its memory size is known at compile time. The model can concentrate on the signal processing itself, and its mistakes show up as compilation errors or as sounds that can be measured, not as crashes. The documentation of the libraries is also exported in a machine-readable form, so an agent can look up the exact function it needs instead of reinventing it.
This year, I have explored different approaches, including MCP, but not only: the Faust ecosystem can just as well be used by agents through specialized command-line (CLI) tools.
We also see the value of combining LLMs and tools in the revolution happening in mathematics, with the Lean formalization language for example. It is this language that has allowed the most powerful models to produce verified mathematical proofs, because the LLMs themselves understand and write the formalization as Lean code.
In a way, one can imagine that languages like Faust, very specialized DSLs, take on this particular role among audio languages: offering a platform and an ecosystem to describe, specify, preserve and document the know-how of the audio DSP community. We have really decided to explore this path, using modern LLMs to strengthen the Faust ecosystem itself. This means continuing to work on the compiler to make it more efficient and safer, able to generate faster and certified code. Another part of the work consists of reworking the Faust libraries: documenting them better, testing them, and possibly certifying them for much broader use cases.
And we have seen this relevance of a specialized language start to show in a whole set of tools and applications where LLMs are embedded in the application itself to generate Faust DSP code on the fly: faustdoc.grame.fr/manual/llm/#projects-using-llms-with-faust.
Another piece of work we have started is rewriting the Faust compiler itself in Rust, entirely with LLMs, in order to explore extensions of the language more quickly, for example automatic differentiation. See the faust-rs project: github.com/grame-cncm/faust-rs.
Faust and JACK are both open source tools that many people depend on. How do you keep them maintained and who helps?
I worked on JACK between 2004 and 2013, and gradually handed it over to a developer from the Linux audio community, Filipe Coelho, better known as falkTX, the author of the Carla plugin host and of the KXStudio project, who took over the maintenance of Jack2 in 2016. So I no longer take care of JACK today: he makes the releases, with the help of other contributors.
As for Faust, Yann Orlarey, its designer, is now retired, but he still works very actively on the compiler and the language, in particular with the help of modern LLMs, as well as on other tools and applications built around Faust. On my side, I take care of a large part of the ecosystem and of its maintenance, helped by an ever wider community and by many contributions, some of which are now also made with LLMs.
The libraries are a good example. Julius Smith has contributed a large part of the filters, oscillators and reverbs for many years, Romain Michon the physical models, and in recent years David Braun, Dario Sanfilippo, Bart Brouns, Oleg Nesterov and others have added new functions, fixed bugs and reviewed changes. Everything happens in the open on GitHub (github.com/grame-cncm), through issues and pull requests, and the discussions take place on the community’s channels.
Maintaining tools that many people depend on mostly means not breaking anything. With LLM agents, we have been able to set up a much more systematic set of tests for the libraries: every function is documented with an example that is compiled and run, and its output is compared with a reference, in single and double precision, at several sample rates and on the different backends. A change that alters the sound has to be justified. This kind of meticulous, repetitive work used to be out of reach for a small team; it is now part of our daily routine.
What do you tell developers who want to start writing DSP code for the first time?
I think it really depends on how much time you are ready to put into it. Audio DSP is an extremely broad field. You can already have fun with existing tools, environments where the work is pre-digested, but it takes years to become a real expert in all the mathematical formalism and the knowledge of acoustics and physics needed to truly master the field.
There are now a huge number of resources, both to start practicing and to learn DSP. Of course, Julius Smith’s online courses and the books he has written are a reference known to the whole community, from Mathematics of the DFT and Introduction to Digital Filters to Physical Audio Signal Processing and Spectral Audio Signal Processing, all freely available online (ccrma.stanford.edu/~jos/). But many other authors have written content in the form of blogs, articles or books available on the Web, such as Miller Puckette’s The Theory and Technique of Electronic Music, also freely available. And then there is the whole set of tools to be found, plugins, platforms, finished applications: it is an endless world to explore.
And we think that the Faust approach is an extremely efficient way to approach the field, because you can really concentrate on the essence of audio DSP without being bothered too much by less interesting questions. In the Faust IDE (faustide.grame.fr), you can write a few lines in a browser, hear the result immediately, look at the block diagram, and export the same program as a plugin or an application. And the Faust libraries are a course in themselves: each filter, oscillator or reverb is written in a few readable lines, with its documentation and its references, so you can see how it is built and modify it.
My practical advice would be to start small, with an oscillator, a filter or a delay, and to always check what you do by listening and by measuring: an impulse response, a spectrum, a level. Many basic notions, sampling, aliasing, the stability of a recursive filter, are best understood by hearing what happens when they go wrong. LLMs can now explain a concept or propose some code, which is a great help for learning, provided you check the result in the same way: by listening, by measuring and by understanding why it works.
Where can people follow your latest work, releases and publications?
On social networks, I am only on LinkedIn (www.linkedin.com/in/stéphane-letz-96ba4424b/). Otherwise, most of the projects I work on are on GitHub, either on my own account (github.com/sletz) or on GRAME’s (github.com/grame-cncm), where the Faust releases are published (github.com/grame-cncm/faust/releases).
Everything about Faust, its documentation, tutorials and news, is on the Faust website (faust.grame.fr). GRAME’s research activity, with its projects and publications, is presented at www.grame.fr/recherche, and most of our papers are available on HAL, the French open archive (hal.science).
