Deluge Firmware 1.3.0
Build date: 2025.04.16
Loading...
Searching...
No Matches
render_wave.h
1/*
2 * Copyright © 2017-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#include "processing/vector_rendering_function.h"
20#include "util/fixedpoint.h"
21#include "util/waves.h"
22#include <argon.hpp>
23#include <arm_neon.h>
24
25[[gnu::always_inline]] static inline void //<
26renderOscSync(auto storageFunction, auto extraInstructionsForCrossoverSampleRedo,
27 // Params
28 uint32_t& phase, uint32_t phaseIncrement, uint32_t& resetterPhase, uint32_t resetterPhaseIncrement,
29 int32_t resetterDivideByPhaseIncrement, uint32_t retriggerPhase, int32_t& numSamplesThisOscSyncSession,
30 int32_t*& bufferStartThisSync) {
31
32 bool renderedASyncFromItsStartYet = false;
33 int32_t crossoverSampleBeforeSync;
34 int32_t fadeBetweenSyncs;
35
36 /* Do a bunch of samples until we get to the next crossover sample */
37 /* A starting value that'll be added to. It's 1 because we want to include the 1 extra sample at the end - the
38 * crossover sample. */
39 uint32_t samplesIncludingNextCrossoverSample = 1;
40startRenderingASync:
41 uint32_t distanceTilNextCrossoverSample = -resetterPhase - (resetterPhaseIncrement >> 1);
42 samplesIncludingNextCrossoverSample += (uint32_t)(distanceTilNextCrossoverSample - 1) / resetterPhaseIncrement;
43 bool shouldBeginNextSyncAfter = (numSamplesThisOscSyncSession >= samplesIncludingNextCrossoverSample);
44 size_t numSamplesThisSyncRender = shouldBeginNextSyncAfter
45 ? samplesIncludingNextCrossoverSample
46 : numSamplesThisOscSyncSession; /* Just limit it, basically. */
47
48 storageFunction(std::span{bufferStartThisSync, numSamplesThisSyncRender}, phase);
49
50 /* Sort out the crossover sample at the *start* of that window we just did, if there was one. */
51 if (renderedASyncFromItsStartYet) {
52 int32_t average = (*bufferStartThisSync >> 1) + (crossoverSampleBeforeSync >> 1);
53 int32_t halfDifference = (*bufferStartThisSync >> 1) - (crossoverSampleBeforeSync >> 1);
54 int32_t sineValue = getSine(fadeBetweenSyncs >> 1);
55 *bufferStartThisSync = average + (multiply_32x32_rshift32(halfDifference, sineValue) << 1);
56 }
57
58 if (shouldBeginNextSyncAfter) {
59 /* We've just done a crossover (i.e. hit a sync point) at the end of that window, so start thinking about that
60 * and planning the next window. */
61 bufferStartThisSync += samplesIncludingNextCrossoverSample - 1;
62 crossoverSampleBeforeSync = *bufferStartThisSync;
63 numSamplesThisOscSyncSession -= samplesIncludingNextCrossoverSample - 1;
64 extraInstructionsForCrossoverSampleRedo(samplesIncludingNextCrossoverSample);
65
66 /* We want this to always show one sample late at this point (why again?). */
67 resetterPhase += resetterPhaseIncrement * (samplesIncludingNextCrossoverSample - renderedASyncFromItsStartYet);
68 /* The first time we get here, it won't yet be, so make it so. */
69
70 /* The result of that comes out as between "-0.5 and 0.5", represented as +-(1<<14) */
71 fadeBetweenSyncs = multiply_32x32_rshift32((int32_t)resetterPhase, resetterDivideByPhaseIncrement)
72 << 17; /* And this makes it "full-scale", so "1" is 1<<32. */
73 phase = multiply_32x32_rshift32(fadeBetweenSyncs, phaseIncrement) + retriggerPhase;
74
75 phase -= phaseIncrement; /* Because we're going back and redoing the last sample. */
76 renderedASyncFromItsStartYet = true;
77 samplesIncludingNextCrossoverSample = 2; /* Make this 1 higher now, because resetterPhase's value is 1 sample
78 later than what it "is in reality". */
79 goto startRenderingASync;
80 }
81
82 /* We're not beginning a next sync, so are not going to reset phase, so need to update (increment) it to keep it
83 * valid. */
84 phase += phaseIncrement * numSamplesThisSyncRender;
85}
86
87auto renderWavetableLoop(auto bufferStartThisSync, auto firstCycleNumber, auto bandHere, auto phaseIncrement,
88 auto crossCycleStrength2, auto crossCycleStrength2Increment, auto kernel) {
89 return [&](int32_t const* const bufferEndThisSyncRender, uint32_t phaseTemp, int32_t* __restrict__ writePos) {
90 doRenderingLoop(bufferStartThisSync, bufferEndThisSyncRender, firstCycleNumber, bandHere, phaseTemp,
91 phaseIncrement, crossCycleStrength2, crossCycleStrength2Increment, kernel);
92 };
93}
94
96inline Argon<int32_t> createAmplitudeVector(int32_t amplitude, int32_t amplitude_increment) {
97 // amplitude + amplitudeIncrement * lane_n
98 auto amplitudeVector = Argon<int32_t>{amplitude}.MultiplyAdd(amplitude_increment, int32x4_t{1, 2, 3, 4});
99
100 // TODO(@stellar-aria): investigate where the doubling comes from (likely an unshifted smmul)
101 return amplitudeVector >> 1; // halve amplitude
102}