Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2023 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 : */
22 :
23 : #pragma once
24 :
25 : #include <cstring>
26 : #include <algorithm>
27 : #include <cassert>
28 :
29 : #include "vectorops.h"
30 :
31 : /*
32 : * Utility buffer class for delay-based effects
33 : * Allocates a 2^n size buffer for branchless write and read using a bitmask
34 : * It can read and write to past and future samples, but does not check for overlaps
35 : */
36 :
37 : namespace muse::audio::fx {
38 : template<typename SampleT>
39 : class CircularSampleBuffer
40 : {
41 : public:
42 0 : CircularSampleBuffer()
43 0 : {
44 0 : }
45 :
46 0 : ~CircularSampleBuffer()
47 : {
48 0 : if (m_buffer) {
49 0 : free(m_buffer);
50 : }
51 0 : }
52 :
53 0 : void setSize(int n)
54 : {
55 0 : if (n > m_allocatedSize) {
56 0 : auto oldSize = m_allocatedSize;
57 :
58 0 : auto findLargerPowerOfTwo = [](int32_t number) {
59 0 : int32_t powerOf2 = 1;
60 0 : while (powerOf2 < number) {
61 0 : powerOf2 *= 2;
62 : }
63 0 : return powerOf2;
64 : };
65 :
66 0 : m_allocatedSize = findLargerPowerOfTwo(n);
67 0 : m_bufferSizeMask = m_allocatedSize - 1;
68 0 : m_buffer = (SampleT*)std::realloc(m_buffer, m_allocatedSize * sizeof(SampleT));
69 :
70 : // Reset the new memory region
71 0 : assert(m_buffer);
72 0 : std::fill(m_buffer + oldSize, m_buffer + m_allocatedSize, 0.f);
73 : }
74 0 : }
75 :
76 0 : int getAllocatedSize() const
77 : {
78 0 : return m_allocatedSize;
79 : }
80 :
81 0 : void reset()
82 : {
83 0 : if (m_buffer && m_allocatedSize > 0) {
84 0 : std::memset(m_buffer, 0, sizeof(SampleT) * m_allocatedSize);
85 : }
86 0 : m_positionIndex = 0;
87 0 : }
88 :
89 : void write(int offset, const SampleT& sample)
90 : {
91 : m_buffer[(m_positionIndex + offset) & m_bufferSizeMask] = sample;
92 : }
93 :
94 0 : void writeOffset0(const SampleT& sample)
95 : {
96 0 : m_buffer[m_positionIndex] = sample;
97 0 : }
98 :
99 0 : const SampleT& read(int offset) const
100 : {
101 0 : return m_buffer[(m_positionIndex + offset) & m_bufferSizeMask];
102 : }
103 :
104 : /// change the 0 position by n
105 0 : void advance(int n)
106 : {
107 0 : m_positionIndex += n;
108 0 : m_positionIndex &= m_bufferSizeMask;
109 0 : }
110 :
111 : private:
112 : template<typename fnc>
113 0 : void splitBlockOffsetFunction(int startOffset, int n, fnc f) const
114 : {
115 0 : assert(n <= m_allocatedSize);
116 0 : int firstIndex = (m_positionIndex + startOffset) & m_bufferSizeMask;
117 0 : int n_to_end = m_allocatedSize - firstIndex;
118 0 : if (n_to_end > n) {
119 0 : f(firstIndex, 0, n);
120 : } else {
121 0 : f(firstIndex, 0, n_to_end);
122 0 : f(0, n_to_end, n - n_to_end);
123 : }
124 0 : }
125 :
126 : public:
127 0 : void writeBlock(int startOffset, int n, const SampleT* sourceBlock)
128 : {
129 0 : splitBlockOffsetFunction(startOffset, n, [this, sourceBlock](int bufferOff, int sampleOff, int n) {
130 0 : vo::copy(&sourceBlock[sampleOff], &m_buffer[bufferOff], n);
131 : });
132 0 : }
133 :
134 0 : void readBlockWithGain(int startOffset, int n, SampleT* targetBlock, float gainFactor) const
135 : {
136 0 : splitBlockOffsetFunction(startOffset, n, [this, targetBlock, gainFactor](int bufferOff, int sampleOff, int n) {
137 0 : vo::constantMultiply(&m_buffer[bufferOff], gainFactor, &targetBlock[sampleOff], n);
138 : });
139 0 : }
140 :
141 0 : void readAddBlockWithGain(int startOffset, int n, SampleT* targetBlock, float gainFactor) const
142 : {
143 0 : splitBlockOffsetFunction(startOffset, n, [this, targetBlock, gainFactor](int bufferOff, int sampleOff, int n) {
144 0 : vo::constantMultiplyAndAdd(&m_buffer[bufferOff], gainFactor, &targetBlock[sampleOff], n);
145 : });
146 0 : }
147 :
148 : private:
149 : SampleT* m_buffer = nullptr;
150 :
151 : int m_positionIndex = 0; // position of sample index 0 inside _buffer. This is where the next sample will be written to
152 : int m_allocatedSize = 0; // 2^n buffer size
153 : int m_bufferSizeMask = 0; // 2^n-1 buffer mask
154 : };
155 : } // namespace muse::audio::fx
|