Deluge Firmware 1.3.0
Build date: 2026.04.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 {
137using VoicePool = deluge::memory::ObjectPool<Voice, deluge::memory::fast_allocator>;
138using VoiceSamplePool = deluge::memory::ObjectPool<VoiceSample, deluge::memory::fast_allocator>;
139using TimeStretcherPool = deluge::memory::ObjectPool<TimeStretcher, deluge::memory::fast_allocator>;
140void routine();
141void routine_task();
142void routineWithClusterLoading(bool mayProcessUserActionsBetween = false);
143void runRoutine();
144
145void init();
146void previewSample(String* path, FilePointer* filePointer, bool shouldActuallySound);
147void stopAnyPreviewing();
148
149void songSwapAboutToHappen();
150void killAllVoices(bool deletingSong = false);
151void logAction(char const* string);
152void logAction(int32_t number);
153
154void getReverbParamsFromSong(Song* song);
155
156VoiceSample* solicitVoiceSample();
157void voiceSampleUnassigned(VoiceSample* voiceSample);
158
159TimeStretcher* solicitTimeStretcher();
160void timeStretcherUnassigned(TimeStretcher* timeStretcher);
161
162LiveInputBuffer* getOrCreateLiveInputBuffer(OscType inputType, bool mayCreate);
163void slowRoutine();
164void doRecorderCardRoutines();
165
166int32_t getNumSamplesLeftToOutputFromPreviousRender();
167
168void registerSideChainHit(int32_t strength);
169
170SampleRecorder* getNewRecorder(int32_t numChannels, AudioRecordingFolder folderID, AudioInputChannel mode,
171 bool keepFirstReasons, bool writeLoopPoints, int32_t buttonPressLatency,
172 bool shouldNormalize, Output* outputRecordingFrom);
173void discardRecorder(SampleRecorder* recorder);
174bool isAnyInternalRecordingHappening();
175
176#ifdef FLIGHTDATA
177char log[32][64];
178uint8_t logNextIndex = 0;
179uint32_t logAbsoluteIndex = 0;
180char* getEmptyLogEntry();
181void printLog();
182#endif
183int32_t getNumAudio();
184int32_t getNumVoices();
185bool doSomeOutputting();
186void updateReverbParams();
187
188extern bool headphonesPluggedIn;
189extern bool micPluggedIn;
190extern bool lineInPluggedIn;
191extern bool renderInStereo;
192extern uint32_t audioSampleTimer;
193extern bool mustUpdateReverbParamsBeforeNextRender;
194extern bool bypassCulling;
195extern uint32_t i2sTXBufferPos;
196extern uint32_t i2sRXBufferPos;
197extern int32_t cpuDireness;
198extern InputMonitoringMode inputMonitoringMode;
199extern bool audioRoutineLocked;
200extern bool routineBeenCalled;
201extern uint8_t numHopsEndedThisRoutineCall;
202extern SideChain reverbSidechain;
203extern uint32_t timeThereWasLastSomeReverb;
204extern deluge::fast_vector<Sound*> sounds;
205extern deluge::dsp::Reverb reverb;
206extern uint32_t nextVoiceState;
207extern SoundDrum* sampleForPreview;
208extern int32_t reverbSidechainVolume;
209extern int32_t reverbSidechainShape;
210extern int32_t reverbPan;
211extern SampleRecorder* firstRecorder;
212extern Metronome metronome;
213extern RMSFeedbackCompressor mastercompressor;
214extern uint32_t timeLastSideChainHit;
215extern int32_t sizeLastSideChainHit;
216extern StereoFloatSample approxRMSLevel;
217extern AbsValueFollower envelopeFollower;
218extern TaskID routine_task_id;
219void feedReverbBackdoorForGrain(int index, q31_t value);
220
222bool allowedToStartVoice();
223} // 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:49
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