Deluge Firmware 1.3.0
Build date: 2026.10.14
Loading...
Searching...
No Matches
audio_engine.h
1/*
2 * Copyright © 2014-2023 Synthstrom Audible Limited
3 *
4 * This file is part of The Synthstrom Audible Deluge Firmware.
5 *
6 * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software Foundation,
8 * either version 3 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with this program.
15 * If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include "OSLikeStuff/scheduler_api.h"
21#include "definitions_cxx.hpp"
22#include "dsp/compressor/rms_feedback.h"
23#include "dsp/envelope_follower/absolute_value.h"
24#include "memory/fast_allocator.h"
25#include "memory/object_pool.h"
26#include "model/output.h"
27#include "util/containers.h"
28#include <cstdint>
29#include <memory>
30
31struct InterruptsDisabled;
32
33extern "C" {
34#include "fatfs/ff.h"
35}
36
37class Song;
38class StereoSample;
39class Instrument;
40class Sound;
42class LiveInputBuffer;
43class SampleRecorder;
44class Voice;
45class VoiceSample;
46class TimeStretcher;
47class String;
48class SideChain;
49class VoiceVector;
50class Freeverb;
51class Metronome;
54class SoundDrum;
56enum class Polarity : uint8_t;
57
58namespace deluge::dsp {
59class Reverb;
60}
61
62/*
63 * ================== Audio rendering ==================
64 *
65 * The Deluge renders its audio in “windows” (or you could more or less say “buffers”),
66 * a certain number of samples long. In fact, the Deluge’s audio output buffer is a circular,
67 * 128-sample one, whose contents are continuously output to the DAC / codec via I2S
68 * (Renesas calls this SSI) at 44,100hz. That buffer is an array called ssiTxBuffer.
69 *
70 * Each time the audio routine, AudioEngine::routine(), is called, the code checks where the
71 * DMA outputting has gotten up to in ssiTxBuffer, considers where its rendering of the previous
72 * “window” had ended at, and so determines how many new samples it may now render and write into
73 * ssiTxBuffer without “getting ahead of itself”.
74 *
75 * This scheme is primarily useful because it regulates CPU load somewhat, for a slight tradeoff
76 * in audio rendering quality (more on that below) by using longer windows for each render. For instance,
77 * if the CPU load is very light (as in, not many sounds playing), rendering a window will happen very fast,
78 * so when the audio routine is finished and is then called again the next time, only say one or two
79 * samples will have been output via DMA to the DAC. This means the next window length is set at just
80 * one or two samples. I.e. lighter CPU load means shorter windows.
81 *
82 * Often the window length will be rounded to a multiple of 4, because various parts of the rendering code
83 * (e.g. oscillator table lookup / interpolation) use Arm NEON optimizations, which are implemented in
84 * a way that happens to perform best working on chunks of 4 samples. Also, and taking precedence over that,
85 * windows will be shortened to always end at the precise audio sample where an event (e.g. note-on)
86 * is to occur on the Deluge’s sequencer.
87 *
88 * The window length has strictly speaking no effect on the output or quality of oscillators
89 * (including sync, FM, ringmod and wavetable), filters, most effects, sample playback, or timings/jitter
90 * from the Deluge’s sequencer. Where it primarily does have a (barely audible) effect is on envelope shapes.
91 * The current “stage” of the envelope (i.e. A, D, S or R) is only recalculated at the beginning of each window,
92 * so the minimum attack, decay or release time is the length of that window, *unless* that parameter
93 * is in fact set to 0, in which case that stage is skipped. So, you can get a 0ms (well, 1 sample) attack time,
94 * but depending on the window length (which depends on CPU load), your 0.1ms attack time could theoretically
95 * get as long as 2.9ms (128 samples), though it would normally end up quite a bit shorter.
96 *
97 * With envelopes (and LFO position actually) only being recalculated at the start of each “window”,
98 * you might be wondering whether you’ll get an audible “zipper” or stepped effect as the output of these
99 * changes sporadically. The answer is, usually not, because most parameters for which this would be audible
100 * (e.g. definitely anything level/volume related) will linearly interpolate their value change throughout
101 * the window, usually using a variable named something to do with “increment”. This is done per parameter,
102 * rather than per modulation source, essentially for ease / performance. Wavetable position and FM amounts
103 * are other important parameters to do this to.
104 *
105 * But for many parameters, the stepping is not audible in the first place, so there is no interpolation.
106 * This includes filter resonance and oscillator / sample-playback pitch. Especially sample-playback
107 * pitch would be very difficult to interpolate this way because the (unchanging) pitch for the window
108 * is used at the start to calculate how many raw audio-samples (i.e. how many bytes) will need to be
109 * read from memory to render the (repitched) audio-sample window.
110 */
111
112/*
113 * ====================== Audio / CPU performance ======================
114 * A huge number of factors influence the performance of a particular Deluge firmware build.
115 * Subpar performance will usually be noticed in the form of sounds dropping out in songs which
116 * performed better in other firmware versions. As an open source contributer, ensuring optimal performance of any code
117 * modifications you make, and subsequently their builds, will be a big challenge. But don’t sweat it too much - if
118 * you’ve added some cool features which are useful to you or others, maybe lowered audio performance is a reasonable
119 * tradeoff?
120 *
121 * The Deluge codebase, since 2021, has been built with GCC 9.2. I (Rohan) compared this with the other 9.x GCC
122 * versions, some 10.x ones, and the 6.x version(s) that the Deluge had used earlier. Performance differences were
123 * negligible in most cases, and ultimately I settled on GCC 9.2 because it resulted in a built binary which was
124 * smaller by a number of kilobytes compared to other versions. GCC 10.x was particularly bad in this regard.
125 *
126 * The build process includes LTO - this helps performance a fair bit. And normal O2 optimization.
127 * These are both disabled in the HardwareDebug build configuration though, for faster build times and
128 * ease of code debugging. If you’re using live code uploading via a J-link and want to do some tests for
129 * real-world performance, you should enable these for this configuration, at least while doing your tests.
130 *
131 * A few other of the standard compiler optimizations are enabled, like –gc-sections to remove unused code from the
132 * build. Beyond that, I’ve experimented with enabling various of the most advanced GCC optimizations, but haven’t found
133 * any that improve overall performance.
134 */
135
137#define DO_AUDIO_LOG 0
138
139namespace AudioEngine {
140#define logAction(string) logAudioAction(string, __FILE__, __LINE__)
141
142using VoicePool = deluge::memory::ObjectPool<Voice, deluge::memory::fast_allocator>;
143using VoiceSamplePool = deluge::memory::ObjectPool<VoiceSample, deluge::memory::fast_allocator>;
144using TimeStretcherPool = deluge::memory::ObjectPool<TimeStretcher, deluge::memory::fast_allocator>;
145void routine();
146void routine_task();
147void routineWithClusterLoading(bool mayProcessUserActionsBetween = false);
148void runRoutine();
149
150void init();
155 int32_t volumePostFX;
156 int32_t localVolume;
159
161 static PreviewVolume neutral();
162};
163
164void previewSample(String* path, FilePointer* filePointer, bool shouldActuallySound, PreviewVolume const& volume);
165void stopAnyPreviewing();
166
167void songSwapAboutToHappen();
168void killAllVoices(bool deletingSong = false);
169void logAudioAction(char const* string, const char* file, int line);
170
171void getReverbParamsFromSong(Song* song);
172
173VoiceSample* solicitVoiceSample();
174void voiceSampleUnassigned(VoiceSample* voiceSample);
175
176TimeStretcher* solicitTimeStretcher();
177void timeStretcherUnassigned(TimeStretcher* timeStretcher);
178
179LiveInputBuffer* getOrCreateLiveInputBuffer(OscType inputType, bool mayCreate);
180void slowRoutine();
181void doRecorderCardRoutines();
182
183int32_t getNumSamplesLeftToOutputFromPreviousRender();
184
185void registerSideChainHit(int32_t strength);
186
187SampleRecorder* getNewRecorder(int32_t numChannels, AudioRecordingFolder folderID, AudioInputChannel mode,
188 bool keepFirstReasons, bool writeLoopPoints, int32_t buttonPressLatency,
189 bool shouldNormalize, Output* outputRecordingFrom, RecorderConfig config = {});
190void discardRecorder(SampleRecorder* recorder);
191bool isAnyInternalRecordingHappening();
192
193#ifdef FLIGHTDATA
194char log[32][64];
195uint8_t logNextIndex = 0;
196uint32_t logAbsoluteIndex = 0;
197char* getEmptyLogEntry();
198void printLog();
199#endif
200int32_t getNumAudio();
201int32_t getNumVoices();
202bool doSomeOutputting();
203void updateReverbParams();
204
205extern bool headphonesPluggedIn;
206extern bool micPluggedIn;
207extern bool lineInPluggedIn;
208extern bool renderInStereo;
209extern uint32_t audioSampleTimer;
210// Latches true once enough samples have elapsed since power-on for the codec ADC to settle and input monitoring to
211// fade in. Until then, input monitoring (Looper/Sampler) is suppressed and then faded in, so the codec's power-on
212// transient isn't passed through to the output. See getInputMonitoringGain().
213extern bool inputMonitoringWarmedUp;
214extern bool mustUpdateReverbParamsBeforeNextRender;
215extern bool bypassCulling;
216extern uint32_t i2sTXBufferPos;
217extern uint32_t i2sRXBufferPos;
218extern int32_t cpuDireness;
219extern InputMonitoringMode inputMonitoringMode;
220extern bool audioRoutineLocked;
221extern bool routineBeenCalled;
222extern uint8_t numHopsEndedThisRoutineCall;
223extern SideChain reverbSidechain;
224extern uint32_t timeThereWasLastSomeReverb;
225extern deluge::fast_vector<Sound*> sounds;
226extern deluge::dsp::Reverb reverb;
227extern uint32_t nextVoiceState;
228extern SoundDrum* sampleForPreview;
229extern int32_t reverbSidechainVolume;
230extern int32_t reverbSidechainShape;
231extern int32_t reverbPan;
232extern SampleRecorder* firstRecorder;
233extern Metronome metronome;
234extern RMSFeedbackCompressor mastercompressor;
235extern uint32_t timeLastSideChainHit;
236extern int32_t sizeLastSideChainHit;
237extern StereoFloatSample approxRMSLevel;
238extern AbsValueFollower envelopeFollower;
239extern TaskID routine_task_id;
240void feedReverbBackdoorForGrain(int index, q31_t value);
243q31_t getInputMonitoringGain(uint32_t offset);
244
246bool allowedToStartVoice();
247
250void cancelScheduledGates(uint8_t gateMask, const InterruptsDisabled&);
252void midiGateOutputTimerFired();
253} // namespace AudioEngine
Definition absolute_value.h:24
Definition instrument.h:45
Definition live_input_buffer.h:23
Definition metronome.h:26
Definition model_stack.h:287
Definition output.h:81
Definition param_manager.h:175
Definition rms_feedback.h:26
Definition sample_recorder.h:53
Definition sidechain.h:27
Definition song.h:103
Definition sound_drum.h:28
Definition sound.h:76
Definition d_string.h:41
Definition time_stretcher.h:37
Definition voice_sample.h:36
Definition voice.h:35
Definition reverb.hpp:13
Definition audio_engine.h:154
int32_t velocityCableDepth
depth of the VELOCITY -> LOCAL_VOLUME patch cable
Definition audio_engine.h:157
int32_t volumePostFX
params::GLOBAL_VOLUME_POST_FX
Definition audio_engine.h:155
int32_t localVolume
params::LOCAL_VOLUME
Definition audio_engine.h:156
Polarity velocityCablePolarity
polarity of the VELOCITY -> LOCAL_VOLUME patch cable
Definition audio_engine.h:158
static PreviewVolume neutral()
The defaults the preview drum is built with, from Sound::initParams() and Sound::setupAsSample().
Definition audio_engine.cpp:1552
Definition sample_recorder.h:49
Definition functions.h:444
Definition stereo_sample.h:25