tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_AudioUtilities.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14using SampleCount = int64_t;
15using SampleRange = juce::Range<SampleCount>;
16
19{
20 lagrange,
21 sincFast,
24};
25
26float dbToGain (float db) noexcept;
27float gainToDb (float gain) noexcept;
28
29// returns "n dB" and handles -INF
30juce::String gainToDbString (float gain, float infLevel = -96.0f, int decPlaces = 2);
31float dbStringToDb (const juce::String& dbStr);
32float dbStringToGain (const juce::String& dbStr);
33
34juce::String getPanString (float pan);
35
36juce::String getSemitonesAsString (double semitones);
37
38template<typename FloatType>
39FloatType midiNoteToFrequency (FloatType midiNote)
40{
41 return static_cast<FloatType> (440)
42 * std::pow (static_cast<FloatType> (2), (midiNote - static_cast<FloatType> (69)) / static_cast<FloatType> (12));
43}
44
45template<typename FloatType>
46FloatType frequencyToMidiNote (FloatType freq)
47{
48 return static_cast<FloatType> (12)
49 * std::log2 (freq / static_cast<FloatType> (440)) + static_cast<FloatType> (69);
50}
51
52void sanitiseValues (juce::AudioBuffer<float>&,
53 int startSample, int numSamples,
54 float maxAbsValue,
55 float minAbsThreshold = 1.0f / 262144.0f);
56
57void addAntiDenormalisationNoise (juce::AudioBuffer<float>&, int start, int numSamples) noexcept;
58
59void resetFP() noexcept;
60bool hasFloatingPointDenormaliseOccurred() noexcept;
61void zeroDenormalisedValuesIfNeeded (juce::AudioBuffer<float>&) noexcept;
62
63bool isAudioDataAlmostSilent (const float* data, int num);
64float getAudioDataMagnitude (const float* data, int num);
65
66void convertIntsToFloats (juce::AudioBuffer<float>&);
67void convertFloatsToInts (juce::AudioBuffer<float>&);
68
69inline void yieldGUIThread() noexcept
70{
71 #if JUCE_WINDOWS
73 #endif
74}
75
76//==============================================================================
78template<typename SampleType>
79inline choc::buffer::BufferView<SampleType, choc::buffer::SeparateChannelLayout> toBufferView (juce::AudioBuffer<SampleType>& buffer)
80{
81 return choc::buffer::createChannelArrayView (buffer.getArrayOfWritePointers(),
82 (choc::buffer::ChannelCount) buffer.getNumChannels(),
83 (choc::buffer::FrameCount) buffer.getNumSamples());
84}
85
86//==============================================================================
89{
90 PanLawDefault = -1,
91 PanLawLinear = 0,
92 PanLaw2point5dBCenter = 1, // NB: don't change these numbers, they're stored in the edit.
93 PanLaw3dBCenter = 2,
94 PanLaw4point5dBCenter = 3,
95 PanLaw6dBCenter = 4
96};
97
98PanLaw getDefaultPanLaw() noexcept;
99void setDefaultPanLaw (PanLaw);
100juce::StringArray getPanLawChoices (bool includeDefault) noexcept;
101
102//==============================================================================
103float decibelsToVolumeFaderPosition (float dB) noexcept;
104float volumeFaderPositionToDB (float position) noexcept;
105float volumeFaderPositionToGain (float position) noexcept;
106float gainToVolumeFaderPosition (float gain) noexcept;
107
108void getGainsFromVolumeFaderPositionAndPan (float volSliderPos, float pan, PanLaw lawToUse,
109 float& leftGain, float& rightGain) noexcept;
110
111//================================================================================================
113{
114public:
115 AudioMidiFifo (int channels = 2, int maxSize = 1024)
116 {
117 setSize (channels, maxSize);
118 }
119
120 void setSize (int channels, int maxSize)
121 {
122 fifo.setTotalSize (maxSize + 1);
123 audioBuffer.setSize (channels, maxSize + 1);
124
125 clear();
126 }
127
128 void clear()
129 {
130 fifo.reset();
131 audioBuffer.clear();
132 midiBuffer.clear();
133 }
134
135 int getNumSamplesAvailable() { return fifo.getNumReady(); }
136 int getNumSamplesFree() { return fifo.getFreeSpace(); }
137
138 void writeSilence (int numSamples)
139 {
140 jassert (getNumSamplesFree() >= numSamples);
141
142 int start1, size1, start2, size2;
143 fifo.prepareToWrite (numSamples, start1, size1, start2, size2);
144
145 if (size1 > 0)
146 audioBuffer.clear (start1, size1);
147 if (size2 > 0)
148 audioBuffer.clear (start2, size2);
149
150 fifo.finishedWrite (size1 + size2);
151 }
152
153 void writeAudioAndMidi (const juce::AudioBuffer<float>& audioSrc, const juce::MidiBuffer& midiSrc)
154 {
155 jassert (getNumSamplesFree() >= audioSrc.getNumSamples());
156 jassert (audioSrc.getNumChannels() == audioBuffer.getNumChannels());
157
158 midiBuffer.addEvents (midiSrc, 0, audioSrc.getNumSamples(), fifo.getNumReady());
159
160 int start1, size1, start2, size2;
161 fifo.prepareToWrite (audioSrc.getNumSamples(), start1, size1, start2, size2);
162
163 int channels = juce::jmin (audioBuffer.getNumChannels(), audioSrc.getNumChannels());
164 for (int ch = 0; ch < channels; ch++)
165 {
166 if (size1 > 0)
167 audioBuffer.copyFrom (ch, start1, audioSrc, ch, 0, size1);
168 if (size2 > 0)
169 audioBuffer.copyFrom (ch, start2, audioSrc, ch, size1, size2);
170 }
171
172 fifo.finishedWrite (size1 + size2);
173 }
174
175 void readAudioAndMidi (juce::AudioBuffer<float>& audioDst, juce::MidiBuffer& midiDst)
176 {
177 jassert (getNumSamplesAvailable() >= audioDst.getNumSamples());
178 jassert (audioDst.getNumChannels() == audioBuffer.getNumChannels());
179
180 midiDst.addEvents (midiBuffer, 0, audioDst.getNumSamples(), 0);
181
182 // Move all the remaining midi events forward by the number of samples removed
183 juce::MidiBuffer temp;
184 temp.addEvents (midiBuffer, audioDst.getNumSamples(), fifo.getNumReady(), -audioDst.getNumSamples());
185 midiBuffer = temp;
186
187 int start1, size1, start2, size2;
188 fifo.prepareToRead (audioDst.getNumSamples(), start1, size1, start2, size2);
189
190 int numCh = juce::jmin (audioBuffer.getNumChannels(), audioDst.getNumChannels());
191 for (int ch = 0; ch < numCh; ch++)
192 {
193 if (size1 > 0)
194 audioDst.copyFrom (ch, 0, audioBuffer, ch, start1, size1);
195 if (size2 > 0)
196 audioDst.copyFrom (ch, size1, audioBuffer, ch, start2, size2);
197 }
198
199 fifo.finishedRead (size1 + size2);
200 }
201
202 private:
203 juce::AbstractFifo fifo {1};
204 juce::AudioBuffer<float> audioBuffer;
205 juce::MidiBuffer midiBuffer;
206
208};
209
210//================================================================================================
211// Takes a snapshot of a buffer state, and then case be used later
212// to tell if the buffer has had it's channels reallocated
214{
215public:
217 : buffer (b)
218 {
219 numChannels = buffer.getNumChannels();
220 numSamples = buffer.getNumSamples();
221
222 auto readPointers = buffer.getArrayOfReadPointers();
223 for (int i = 0; i < std::min (10, numChannels); i++)
224 channels[i] = readPointers[i];
225 }
226
227 bool hasBufferBeenReallocated()
228 {
229 if (numChannels != buffer.getNumChannels()
230 || numSamples != buffer.getNumSamples())
231 return true;
232
233 auto readPointers = buffer.getArrayOfReadPointers();
234 for (int i = 0; i < std::min (10, numChannels); i++)
235 if (channels[i] != readPointers[i])
236 return true;
237
238 return false;
239 }
240
241private:
243 int numChannels = 0, numSamples = 0;
244 const float* channels[10] = { nullptr }; // assume buffers have no more than 10 channels
245};
246
247inline void clearChannels (juce::AudioBuffer<float>& buffer, int startChannel, int endChannel = -1, int startSample = 0, int endSample = -1)
248{
249 if (endChannel == -1)
250 endChannel = buffer.getNumChannels();
251 if (endSample == -1)
252 endSample = buffer.getNumSamples();
253
254 for (int ch = startChannel; ch < endChannel; ch++)
255 buffer.clear (ch, startSample, endSample);
256}
257
258}} // namespace tracktion { inline namespace engine
259
260namespace juce
261{
262 template <>
263 struct VariantConverter<tracktion::engine::ResamplingQuality>
264 {
265 static tracktion::engine::ResamplingQuality fromVar (const var& v)
266 {
267 const auto s = v.toString();
268
269 if (s == "lagrange") return tracktion::engine::ResamplingQuality::lagrange;
270 if (s == "sincFast") return tracktion::engine::ResamplingQuality::sincFast;
271 if (s == "sincMedium") return tracktion::engine::ResamplingQuality::sincMedium;
272 if (s == "sincBest") return tracktion::engine::ResamplingQuality::sincBest;
273
275 }
276
277 static var toVar (tracktion::engine::ResamplingQuality v)
278 {
279 if (v == tracktion::engine::ResamplingQuality::sincFast) return "sincFast";
280 if (v == tracktion::engine::ResamplingQuality::sincMedium) return "sincMedium";
281 if (v == tracktion::engine::ResamplingQuality::sincBest) return "sincBest";
282
283 return "lagrange";
284 }
285 };
286}
287
288#ifndef DOXYGEN
289template<>
290struct std::hash<tracktion::engine::ResamplingQuality>
291{
292 size_t operator() (const tracktion::engine::ResamplingQuality& q) const noexcept
293 {
294 return static_cast<size_t> (q);
295 }
296};
297#endif
int getNumChannels() const noexcept
int getNumSamples() const noexcept
void clear() noexcept
void copyFrom(int destChannel, int destStartSample, const AudioBuffer &source, int sourceChannel, int sourceStartSample, int numSamples) noexcept
const Type *const * getArrayOfReadPointers() const noexcept
Type *const * getArrayOfWritePointers() noexcept
const String & toString() const noexcept
void addEvents(const MidiBuffer &otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd)
static void JUCE_CALLTYPE yield()
T is_pointer_v
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
T log2(T... args)
T min(T... args)
constexpr Type jmin(Type a, Type b)
choc::buffer::BufferView< SampleType, choc::buffer::SeparateChannelLayout > toBufferView(juce::AudioBuffer< SampleType > &buffer)
Converts a juce::AudioBuffer<SampleType> to a choc::buffer::BufferView.
PanLaw
All laws have been designed to be equal-power, excluding linear respectively.
ResamplingQuality
Specifies a number of resampling qualities that can be used.
@ lagrange
Lagrange interpolation.
@ sincBest
Best quality sinc interpolation provided by libsamplerate.
@ sincFast
Fast sinc interpolation provided by libsamplerate.
@ sincMedium
Medium quality sinc interpolation provided by libsamplerate.
T operator()(T... args)
T pow(T... args)
typedef int64_t