Deluge Firmware 1.3.0
Build date: 2026.07.15
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
31extern "C" {
32#include "fatfs/ff.h"
33}
34
35class Song;
36class StereoSample;
37class Instrument;
38class Sound;
40class LiveInputBuffer;
41class SampleRecorder;
42class Voice;
43class VoiceSample;
44class TimeStretcher;
45class String;
46class SideChain;
47class VoiceVector;
48class Freeverb;
49class Metronome;
52class SoundDrum;
54
55namespace deluge::dsp {
56class Reverb;
57}
58
59/*
60 * ================== Audio rendering ==================
61 *
62 * The Deluge renders its audio in “windows” (or you could more or less say “buffers”),
63 * a certain number of samples long. In fact, the Deluge’s audio output buffer is a circular,
64 * 128-sample one, whose contents are continuously output to the DAC / codec via I2S
65 * (Renesas calls this SSI) at 44,100hz. That buffer is an array called ssiTxBuffer.
66 *
67 * Each time the audio routine, AudioEngine::routine(), is called, the code checks where the
68 * DMA outputting has gotten up to in ssiTxBuffer, considers where its rendering of the previous
69 * “window” had ended at, and so determines how many new samples it may now render and write into
70 * ssiTxBuffer without “getting ahead of itself”.
71 *
72 * This scheme is primarily useful because it regulates CPU load somewhat, for a slight tradeoff
73 * in audio rendering quality (more on that below) by using longer windows for each render. For instance,
74 * if the CPU load is very light (as in, not many sounds playing), rendering a window will happen very fast,
75 * so when the audio routine is finished and is then called again the next time, only say one or two
76 * samples will have been output via DMA to the DAC. This means the next window length is set at just
77 * one or two samples. I.e. lighter CPU load means shorter windows.
78 *
79 * Often the window length will be rounded to a multiple of 4, because various parts of the rendering code
80 * (e.g. oscillator table lookup / interpolation) use Arm NEON optimizations, which are implemented in
81 * a way that happens to perform best working on chunks of 4 samples. Also, and taking precedence over that,
82 * windows will be shortened to always end at the precise audio sample where an event (e.g. note-on)
83 * is to occur on the Deluge’s sequencer.
84 *
85 * The window length has strictly speaking no effect on the output or quality of oscillators
86 * (including sync, FM, ringmod and wavetable), filters, most effects, sample playback, or timings/jitter
87 * from the Deluge’s sequencer. Where it primarily does have a (barely audible) effect is on envelope shapes.
88 * The current “stage” of the envelope (i.e. A, D, S or R) is only recalculated at the beginning of each window,
89 * so the minimum attack, decay or release time is the length of that window, *unless* that parameter
90 * is in fact set to 0, in which case that stage is skipped. So, you can get a 0ms (well, 1 sample) attack time,
91 * but depending on the window length (which depends on CPU load), your 0.1ms attack time could theoretically
92 * get as long as 2.9ms (128 samples), though it would normally end up quite a bit shorter.
93 *
94 * With envelopes (and LFO position actually) only being recalculated at the start of each “window”,
95 * you might be wondering whether you’ll get an audible “zipper” or stepped effect as the output of these
96 * changes sporadically. The answer is, usually not, because most parameters for which this would be audible
97 * (e.g. definitely anything level/volume related) will linearly interpolate their value change throughout
98 * the window, usually using a variable named something to do with “increment”. This is done per parameter,
99 * rather than per modulation source, essentially for ease / performance. Wavetable position and FM amounts
100 * are other important parameters to do this to.
101 *
102 * But for many parameters, the stepping is not audible in the first place, so there is no interpolation.
103 * This includes filter resonance and oscillator / sample-playback pitch. Especially sample-playback
104 * pitch would be very difficult to interpolate this way because the (unchanging) pitch for the window
105 * is used at the start to calculate how many raw audio-samples (i.e. how many bytes) will need to be
106 * read from memory to render the (repitched) audio-sample window.
107 */
108
109/*
110 * ====================== Audio / CPU performance ======================
111 * A huge number of factors influence the performance of a particular Deluge firmware build.
112 * Subpar performance will usually be noticed in the form of sounds dropping out in songs which
113 * performed better in other firmware versions. As an open source contributer, ensuring optimal performance of any code
114 * modifications you make, and subsequently their builds, will be a big challenge. But don’t sweat it too much - if
115 * you’ve added some cool features which are useful to you or others, maybe lowered audio performance is a reasonable
116 * tradeoff?
117 *
118 * The Deluge codebase, since 2021, has been built with GCC 9.2. I (Rohan) compared this with the other 9.x GCC
119 * versions, some 10.x ones, and the 6.x version(s) that the Deluge had used earlier. Performance differences were
120 * negligible in most cases, and ultimately I settled on GCC 9.2 because it resulted in a built binary which was
121 * smaller by a number of kilobytes compared to other versions. GCC 10.x was particularly bad in this regard.
122 *
123 * The build process includes LTO - this helps performance a fair bit. And normal O2 optimization.
124 * These are both disabled in the HardwareDebug build configuration though, for faster build times and
125 * ease of code debugging. If you’re using live code uploading via a J-link and want to do some tests for
126 * real-world performance, you should enable these for this configuration, at least while doing your tests.
127 *
128 * A few other of the standard compiler optimizations are enabled, like –gc-sections to remove unused code from the
129 * build. Beyond that, I’ve experimented with enabling various of the most advanced GCC optimizations, but haven’t found
130 * any that improve overall performance.
131 */
132
134#define DO_AUDIO_LOG 0
135
136namespace AudioEngine {
137#define logAction(string) logAudioAction(string, __FILE__, __LINE__)
138
139using VoicePool = deluge::memory::ObjectPool<Voice, deluge::memory::fast_allocator>;
140using VoiceSamplePool = deluge::memory::ObjectPool<VoiceSample, deluge::memory::fast_allocator>;
141using TimeStretcherPool = deluge::memory::ObjectPool<TimeStretcher, deluge::memory::fast_allocator>;
142void routine();
143void routine_task();
144void routineWithClusterLoading(bool mayProcessUserActionsBetween = false);
145void runRoutine();
146
147void init();
148void previewSample(String* path, FilePointer* filePointer, bool shouldActuallySound);
149void stopAnyPreviewing();
150
151void songSwapAboutToHappen();
152void killAllVoices(bool deletingSong = false);
153void logAudioAction(char const* string, const char* file, int line);
154
155void getReverbParamsFromSong(Song* song);
156
157VoiceSample* solicitVoiceSample();
158void voiceSampleUnassigned(VoiceSample* voiceSample);
159
160TimeStretcher* solicitTimeStretcher();
161void timeStretcherUnassigned(TimeStretcher* timeStretcher);
162
163LiveInputBuffer* getOrCreateLiveInputBuffer(OscType inputType, bool mayCreate);
164void slowRoutine();
165void doRecorderCardRoutines();
166
167int32_t getNumSamplesLeftToOutputFromPreviousRender();
168
169void registerSideChainHit(int32_t strength);
170
171SampleRecorder* getNewRecorder(int32_t numChannels, AudioRecordingFolder folderID, AudioInputChannel mode,
172 bool keepFirstReasons, bool writeLoopPoints, int32_t buttonPressLatency,
173 bool shouldNormalize, Output* outputRecordingFrom, RecorderConfig config = {});
174void discardRecorder(SampleRecorder* recorder);
175bool isAnyInternalRecordingHappening();
176
177#ifdef FLIGHTDATA
178char log[32][64];
179uint8_t logNextIndex = 0;
180uint32_t logAbsoluteIndex = 0;
181char* getEmptyLogEntry();
182void printLog();
183#endif
184int32_t getNumAudio();
185int32_t getNumVoices();
186bool doSomeOutputting();
187void updateReverbParams();
188
189extern bool headphonesPluggedIn;
190extern bool micPluggedIn;
191extern bool lineInPluggedIn;
192extern bool renderInStereo;
193extern uint32_t audioSampleTimer;
194extern bool mustUpdateReverbParamsBeforeNextRender;
195extern bool bypassCulling;
196extern uint32_t i2sTXBufferPos;
197extern uint32_t i2sRXBufferPos;
198extern int32_t cpuDireness;
199extern InputMonitoringMode inputMonitoringMode;
200extern bool audioRoutineLocked;
201extern bool routineBeenCalled;
202extern uint8_t numHopsEndedThisRoutineCall;
203extern SideChain reverbSidechain;
204extern uint32_t timeThereWasLastSomeReverb;
205extern deluge::fast_vector<Sound*> sounds;
206extern deluge::dsp::Reverb reverb;
207extern uint32_t nextVoiceState;
208extern SoundDrum* sampleForPreview;
209extern int32_t reverbSidechainVolume;
210extern int32_t reverbSidechainShape;
211extern int32_t reverbPan;
212extern SampleRecorder* firstRecorder;
213extern Metronome metronome;
214extern RMSFeedbackCompressor mastercompressor;
215extern uint32_t timeLastSideChainHit;
216extern int32_t sizeLastSideChainHit;
217extern StereoFloatSample approxRMSLevel;
218extern AbsValueFollower envelopeFollower;
219extern TaskID routine_task_id;
220void feedReverbBackdoorForGrain(int index, q31_t value);
221
223bool allowedToStartVoice();
224} // 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 param_manager.h:174
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:71
Definition d_string.h:41
Definition time_stretcher.h:37
Definition voice_sample.h:36
Definition voice.h:35
Definition reverb.hpp:13
Definition stereo_sample.h:25