UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
SampleBuffer.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
8
9namespace Audio
10{
12
13 /************************************************************************/
14 /* TSampleBuffer<class SampleType> */
15 /* This class owns an audio buffer. */
16 /* To convert between fixed Q15 buffers and float buffers, */
17 /* Use the assignment operator. Example: */
18 /* */
19 /* TSampleBuffer<float> AFloatBuffer; */
20 /* TSampleBuffer<int16> AnIntBuffer = AFloatBuffer; */
21 /************************************************************************/
22 template <class SampleType = DefaultUSoundWaveSampleType>
24 {
25 private:
26 // raw PCM data buffer
27 TArray<SampleType> RawPCMData;
28 // The number of samples in the buffer
29 int32 NumSamples;
30 // The number of frames in the buffer
31 int32 NumFrames;
32 // The number of channels in the buffer
33 int32 NumChannels;
34 // The sample rate of the buffer
35 int32 SampleRate;
36 // The duration of the buffer in seconds
37 float SampleDuration;
38
39 public:
40 // Ensure that we can trivially copy construct private members between templated TSampleBuffers:
41 template <class> friend class TSampleBuffer;
42
44 : NumSamples(0)
45 , NumFrames(0)
46 , NumChannels(0)
47 , SampleRate(0)
48 , SampleDuration(0.0f)
49 {}
50
52 {
53 NumSamples = Other.NumSamples;
54 NumFrames = Other.NumFrames;
55 NumChannels = Other.NumChannels;
56 SampleRate = Other.SampleRate;
57 SampleDuration = Other.SampleDuration;
58
59 RawPCMData.Reset(NumSamples);
60 RawPCMData.AddUninitialized(NumSamples);
61 FMemory::Memcpy(RawPCMData.GetData(), Other.RawPCMData.GetData(), NumSamples * sizeof(SampleType));
62 }
63
68
70 {
71 NumSamples = InNumSamples;
72 NumFrames = NumSamples / InNumChannels;
73 NumChannels = InNumChannels;
74 SampleRate = InSampleRate;
75 SampleDuration = ((float)NumFrames) / SampleRate;
76
77 RawPCMData.Reset(NumSamples);
78 RawPCMData.AddUninitialized(NumSamples);
79
80 if constexpr(std::is_same_v<SampleType, float>)
81 {
82 FMemory::Memcpy(RawPCMData.GetData(), InBufferPtr, NumSamples * sizeof(float));
83 }
84 else if constexpr(std::is_same_v<SampleType, int16>)
85 {
86 // Convert from float to int:
87 for (int32 SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
88 {
89 RawPCMData[SampleIndex] = (int16)(FMath::Clamp(InBufferPtr[SampleIndex], -1.0f, 1.0f) * 32767.0f);
90 }
91 }
92 else
93 {
94 // for any other types, we don't know how to explicitly convert, so we fall back to casts:
95 for (int32 SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
96 {
97 RawPCMData[SampleIndex] = (SampleType)(InBufferPtr[SampleIndex]);
98 }
99 }
100 }
101
103 {
104 NumSamples = InNumSamples;
105 NumFrames = NumSamples / InNumChannels;
106 NumChannels = InNumChannels;
107 SampleRate = InSampleRate;
108 SampleDuration = ((float)NumFrames) / SampleRate;
109
110 RawPCMData.Reset(NumSamples);
111 RawPCMData.AddUninitialized(NumSamples);
112
113 if constexpr(std::is_same_v<SampleType, int16>)
114 {
115 FMemory::Memcpy(RawPCMData.GetData(), InBufferPtr, NumSamples * sizeof(int16));
116 }
117 else if constexpr(std::is_same_v<SampleType, float>)
118 {
119 // Convert from int to float:
120 Audio::ArrayPcm16ToFloat(MakeArrayView(InBufferPtr, NumSamples), RawPCMData);
121 }
122 else
123 {
124 // for any other types, we don't know how to explicitly convert, so we fall back to casts:
125 for (int32 SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
126 {
127 RawPCMData[SampleIndex] = (SampleType)(InBufferPtr[SampleIndex]);
128 }
129 }
130 }
131
132 // Vanilla assignment operator:
134 {
135 NumSamples = Other.NumSamples;
136 NumFrames = Other.NumFrames;
137 NumChannels = Other.NumChannels;
138 SampleRate = Other.SampleRate;
139 SampleDuration = Other.SampleDuration;
140
141 RawPCMData.Reset(NumSamples);
142 RawPCMData.AddUninitialized(NumSamples);
143
144 FMemory::Memcpy(RawPCMData.GetData(), Other.RawPCMData.GetData(), NumSamples * sizeof(SampleType));
145
146 return *this;
147 }
148
149 //SampleType converting assignment operator:
150 template<class OtherSampleType>
152 {
153 NumSamples = Other.NumSamples;
154 NumFrames = Other.NumFrames;
155 NumChannels = Other.NumChannels;
156 SampleRate = Other.SampleRate;
157 SampleDuration = Other.SampleDuration;
158
159 RawPCMData.Reset(NumSamples);
160 RawPCMData.AddUninitialized(NumSamples);
161
162 if constexpr(std::is_same_v<SampleType, OtherSampleType>)
163 {
164 // If buffers are of the same type, copy over:
165 FMemory::Memcpy(RawPCMData.GetData(), Other.RawPCMData.GetData(), NumSamples * sizeof(SampleType));
166 }
167 else if constexpr(std::is_same_v<SampleType, int16> && std::is_same_v<OtherSampleType, float>)
168 {
169 // Convert from float to int:
171 }
172 else if constexpr(std::is_same_v<SampleType, float> && std::is_same_v<OtherSampleType, int16>)
173 {
174 // Convert from int to float:
176 }
177 else
178 {
179 // for any other types, we don't know how to explicitly convert, so we fall back to casts:
180 for (int32 SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
181 {
182 RawPCMData[SampleIndex] = (SampleType)Other.RawPCMData[SampleIndex];
183 }
184 }
185
186 return *this;
187 }
188
189 // copy from a container of the same element type
191 {
192 NumSamples = InArray.Num();
193 NumFrames = NumSamples / InNumChannels;
194 NumChannels = InNumChannels;
195 SampleRate = InSampleRate;
196 SampleDuration = ((float)NumFrames) / SampleRate;
197
198 RawPCMData.Reset(NumSamples);
199 RawPCMData.AddZeroed(NumSamples);
200
201 FMemory::Memcpy(RawPCMData.GetData(), InArray.GetData(), NumSamples * sizeof(SampleType));
202 }
203
204 // Append audio data to internal buffer of different sample type of this sample buffer
205 template<class OtherSampleType>
207 {
208 int32 StartIndex = RawPCMData.AddUninitialized(InNumSamples);
209
210 if constexpr(std::is_same_v<SampleType, OtherSampleType>)
211 {
212 FMemory::Memcpy(&RawPCMData[StartIndex], InputBuffer, InNumSamples * sizeof(SampleType));
213 }
214 else
215 {
216 if constexpr(std::is_same_v<SampleType, int16> && std::is_same_v<OtherSampleType, float>)
217 {
218 // Convert from float to int:
220 }
221 else if constexpr(std::is_same_v<SampleType, float> && std::is_same_v<OtherSampleType, int16>)
222 {
223 // Convert from int to float:
225 }
226 else
227 {
228 // for any other types, we don't know how to explicitly convert, so we fall back to casts:
229 for (int32 SampleIndex = 0; SampleIndex < InNumSamples; SampleIndex++)
230 {
231 RawPCMData[StartIndex + SampleIndex] = InputBuffer[SampleIndex];
232 }
233 }
234 }
235
236 // Update meta-data
237 NumSamples += InNumSamples;
238 NumFrames = NumSamples / NumChannels;
239 SampleDuration = (float)NumFrames / SampleRate;
240 }
241
242 // Overload of Append that also sets the number of channels and sample rate.
243 template<class OtherSampleType>
251
253
254 void Reset()
255 {
256 RawPCMData.Reset();
257 NumSamples = 0;
258 NumFrames = 0;
259 NumChannels = 0;
260 SampleRate = 0.0f;
261 SampleDuration = 0.0f;
262 }
263
264 // Gets the raw PCM data of the sound wave
265 inline const SampleType* GetData() const
266 {
267 return RawPCMData.GetData();
268 }
269
271 {
272 return MakeArrayView(RawPCMData);
273 }
274
276 {
277 return MakeArrayView(RawPCMData);
278 }
279
280 // Gets the number of samples of the sound wave
281 inline int32 GetNumSamples() const
282 {
283 return NumSamples;
284 }
285
286 // Gets the number of frames of the sound wave
287 inline int32 GetNumFrames() const
288 {
289 return NumFrames;
290 }
291
292 // Gets the number of channels of the sound wave
293 inline int32 GetNumChannels() const
294 {
295 return NumChannels;
296 }
297
298 // Gets the sample rate of the sound wave
299 inline int32 GetSampleRate() const
300 {
301 return SampleRate;
302 }
303
304 inline float GetSampleDuration() const
305 {
306 return SampleDuration;
307 }
308
310 {
311 if (!RawPCMData.Num() || InNumChannels <= 0)
312 {
313 return;
314 }
315
316 TUniquePtr<SampleType[]> TempBuffer;
317 TempBuffer.Reset(new SampleType[InNumChannels * NumFrames]);
318 FMemory::Memset(TempBuffer.Get(), 0, InNumChannels * NumFrames * sizeof(SampleType));
319
320 const SampleType* SrcBuffer = GetData();
321
322 // Downmixing using the channel modulo assumption:
323 // TODO: Use channel matrix for channel conversions.
324 for (int32 FrameIndex = 0; FrameIndex < NumFrames; FrameIndex++)
325 {
326 for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ChannelIndex++)
327 {
328 const int32 DstSampleIndex = FrameIndex * InNumChannels + (ChannelIndex % InNumChannels);
329 const int32 SrcSampleIndex = FrameIndex * NumChannels + ChannelIndex;
330
331 TempBuffer[DstSampleIndex] += SrcBuffer[SrcSampleIndex];
332 }
333 }
334
335 NumChannels = InNumChannels;
336 NumSamples = NumFrames * NumChannels;
337
338 // Resize our buffer and copy the result in:
339 RawPCMData.Reset(NumSamples);
340 RawPCMData.AddUninitialized(NumSamples);
341
342 FMemory::Memcpy(RawPCMData.GetData(), TempBuffer.Get(), NumSamples * sizeof(SampleType));
343 }
344
345 void Clamp(float Ceiling = 1.0f)
346 {
347 if constexpr(std::is_same_v<SampleType, float>)
348 {
349 // Float case:
350 float ClampMin = Ceiling * -1.0f;
351
352 for (int32 SampleIndex = 0; SampleIndex < RawPCMData.Num(); SampleIndex++)
353 {
354 RawPCMData[SampleIndex] = static_cast<SampleType>(FMath::Clamp<float>(RawPCMData[SampleIndex], ClampMin, Ceiling));
355 }
356 }
357 else if constexpr(std::is_same_v<SampleType, int16>)
358 {
359 // int16 case:
360 Ceiling = FMath::Clamp(Ceiling, 0.0f, 1.0f);
361
362 int16 ClampMax = static_cast<int16>(Ceiling * 32767.0f);
363 int16 ClampMin = static_cast<int16>(Ceiling * -32767.0f);
364
365 for (int32 SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
366 {
367 RawPCMData[SampleIndex] = FMath::Clamp<int16>(RawPCMData[SampleIndex], ClampMin, ClampMax);
368 }
369 }
370 else
371 {
372 // Unknown type case:
373 float ClampMin = Ceiling * -1.0f;
374
375 for (int32 SampleIndex = 0; SampleIndex < RawPCMData.Num(); SampleIndex++)
376 {
377 RawPCMData[SampleIndex] = static_cast<SampleType>(FMath::Clamp<SampleType>(RawPCMData[SampleIndex], ClampMin, Ceiling));
378 }
379 }
380 }
381
387 {
389 {
390 NumFramesToAppend = FMath::RoundUpToPowerOfTwo(NumFrames) - NumFrames;
391 }
392
393 RawPCMData.AddZeroed(NumFramesToAppend * NumChannels);
394 NumFrames += NumFramesToAppend;
395 NumSamples = NumFrames * NumChannels;
396 }
397
399 {
400 RawPCMData.SetNum(InNumFrames * NumChannels);
401 NumFrames = RawPCMData.Num() / NumChannels;
402 NumSamples = RawPCMData.Num();
403 }
404
405 // InIndex [0.0f, NumSamples - 1.0f]
406 // OutFrame is the multichannel output for one index value
407 // Returns InIndex wrapped between 0.0 and NumFrames
409 {
410 InIndex = FMath::Fmod(InIndex, static_cast<float>(NumFrames));
411
412 GetAudioFrameAtFractionalIndexInternal(InIndex, OutFrame);
413
414 return InIndex;
415 }
416
417 // InPhase [0, 1], wrapped, through duration of file (ignores sample rate)
418 // OutFrame is the multichannel output for one phase value
419 // Returns InPhase wrapped between 0.0 and 1.0
421 {
422 InPhase = FMath::Fmod(InPhase, 1.0f);
423
424 GetAudioFrameAtFractionalIndexInternal(InPhase * NumFrames, OutFrame);
425
426 return InPhase;
427 }
428
429
430 // InTimeSec, get the value of the buffer at the given time (uses sample rate)
431 // OutFrame is the multichannel output for one time value
432 // Returns InTimeSec wrapped between 0.0 and (NumSamples / SampleRate)
434 {
435 if (InTimeSec >= SampleDuration)
436 {
437 InTimeSec -= SampleDuration;
438 }
439
440 check(InTimeSec >= 0.0f && InTimeSec <= SampleDuration);
441
442 GetAudioFrameAtFractionalIndexInternal(NumSamples * (InTimeSec / SampleDuration), OutFrame);
443
444 return InTimeSec;
445 }
446
447 private:
448 // Internal implementation. Called by all public GetAudioFrameAt_ _ _ _() functions
449 // public functions do range checking/wrapping and then call this function
450 void GetAudioFrameAtFractionalIndexInternal(float InIndex, TArray<SampleType>& OutFrame) const
451 {
452 const float Alpha = FMath::Fmod(InIndex, 1.0f);
453 const int32 WholeThisIndex = FMath::FloorToInt(InIndex);
455
456 // check for interpolation between last and first frames
457 if (WholeNextIndex == NumFrames)
458 {
459 WholeNextIndex = 0;
460 }
461
462 // TODO: if(NumChannels < 4)... do the current (non vectorized) way
463 OutFrame.SetNumUninitialized(NumChannels);
464
465 for (int32 i = 0; i < NumChannels; ++i)
466 {
467 float SampleA, SampleB;
468
469 if constexpr(std::is_same_v<SampleType, float>)
470 {
471 SampleA = RawPCMData[(WholeThisIndex * NumChannels) + i];
472 SampleB = RawPCMData[(WholeNextIndex * NumChannels) + i];
474 }
475 else
476 {
477 SampleA = static_cast<float>(RawPCMData[(WholeThisIndex * NumChannels) + i]);
478 SampleB = static_cast<float>(RawPCMData[(WholeNextIndex * NumChannels) + i]);
479 OutFrame[i] = static_cast<SampleType>(FMath::Lerp(SampleA, SampleB, Alpha));
480 }
481 }
482
483 // TODO: else { do vectorized version }
484 // make new function in BufferVectorOperations.cpp
485 // (use FMath::Lerp() overload for VectorRegisters)
486 }
487 };
488
489 // FSampleBuffer is a strictly defined TSampleBuffer that uses the same sample format we use for USoundWaves.
491}
constexpr auto MakeArrayView(OtherRangeType &&Other)
Definition ArrayView.h:873
#define check(expr)
Definition AssertionMacros.h:314
FPlatformTypes::int16 int16
A 16-bit signed integer.
Definition Platform.h:1123
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
char const * InputBuffer
Definition binka_ue_decode_test.cpp:29
Definition SampleBuffer.h:24
float GetAudioFrameAtPhase(float InPhase, TArray< SampleType > &OutFrame) const
Definition SampleBuffer.h:420
friend class TSampleBuffer
Definition SampleBuffer.h:41
int32 GetNumChannels() const
Definition SampleBuffer.h:293
int32 GetNumFrames() const
Definition SampleBuffer.h:287
void Reset()
Definition SampleBuffer.h:254
TSampleBuffer(const FAlignedFloatBuffer &InData, int32 InNumChannels, int32 InSampleRate)
Definition SampleBuffer.h:64
TSampleBuffer & operator=(const TSampleBuffer< OtherSampleType > &Other)
Definition SampleBuffer.h:151
void ZeroPad(int32 NumFramesToAppend=0)
Definition SampleBuffer.h:386
~TSampleBuffer()
Definition SampleBuffer.h:252
void Append(const OtherSampleType *InputBuffer, int32 InNumSamples)
Definition SampleBuffer.h:206
void MixBufferToChannels(int32 InNumChannels)
Definition SampleBuffer.h:309
void CopyFrom(const TArray< SampleType > &InArray, int32 InNumChannels, int32 InSampleRate)
Definition SampleBuffer.h:190
float GetAudioFrameAtTime(float InTimeSec, TArray< SampleType > &OutFrame) const
Definition SampleBuffer.h:433
float GetAudioFrameAtFractionalIndex(float InIndex, TArray< SampleType > &OutFrame) const
Definition SampleBuffer.h:408
TSampleBuffer(const float *InBufferPtr, int32 InNumSamples, int32 InNumChannels, int32 InSampleRate)
Definition SampleBuffer.h:69
TArrayView< SampleType > GetArrayView()
Definition SampleBuffer.h:270
void Append(const OtherSampleType *InputBuffer, int32 InNumSamples, int32 InNumChannels, int32 InSampleRate)
Definition SampleBuffer.h:244
TSampleBuffer()
Definition SampleBuffer.h:43
int32 GetSampleRate() const
Definition SampleBuffer.h:299
const SampleType * GetData() const
Definition SampleBuffer.h:265
int32 GetNumSamples() const
Definition SampleBuffer.h:281
TSampleBuffer(const TSampleBuffer &Other)
Definition SampleBuffer.h:51
void Clamp(float Ceiling=1.0f)
Definition SampleBuffer.h:345
TArrayView< const SampleType > GetArrayView() const
Definition SampleBuffer.h:275
void SetNumFrames(int32 InNumFrames)
Definition SampleBuffer.h:398
TSampleBuffer(const int16 *InBufferPtr, int32 InNumSamples, int32 InNumChannels, int32 InSampleRate)
Definition SampleBuffer.h:102
TSampleBuffer & operator=(const TSampleBuffer &Other)
Definition SampleBuffer.h:133
float GetSampleDuration() const
Definition SampleBuffer.h:304
Definition ArrayView.h:139
Definition Array.h:670
UE_FORCEINLINE_HINT SizeType AddUninitialized()
Definition Array.h:1664
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void Reset(SizeType NewSize=0)
Definition Array.h:2246
UE_NODEBUG UE_FORCEINLINE_HINT ElementType * GetData() UE_LIFETIMEBOUND
Definition Array.h:1027
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
SizeType AddZeroed()
Definition Array.h:2755
Definition UniquePtr.h:107
void Reset(T *InPtr=nullptr)
Definition UniquePtr.h:346
UE_FORCEINLINE_HINT T * Get() const
Definition UniquePtr.h:324
NO_LOGGING.
Definition AudioMixerPlatformAndroid.cpp:53
void ArrayPcm16ToFloat(TArrayView< const int16 > InView, TArrayView< float > OutView)
Definition FloatArrayMath.cpp:2476
int16 DefaultUSoundWaveSampleType
Definition SampleBuffer.h:11
TSampleBuffer FSampleBuffer
Definition SampleBuffer.h:490
void ArrayFloatToPcm16(TArrayView< const float > InView, TArrayView< int16 > OutView)
Definition FloatArrayMath.cpp:2418
IMAGECORE_API int NumChannels(Type Format)
Definition ImageCore.cpp:1452
static constexpr UE_FORCEINLINE_HINT T Lerp(const T &A, const T &B, const U &Alpha)
Definition UnrealMathUtility.h:1116
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static UE_FORCEINLINE_HINT void * Memcpy(void *Dest, const void *Src, SIZE_T Count)
Definition UnrealMemory.h:160
static UE_FORCEINLINE_HINT void * Memset(void *Dest, uint8 Char, SIZE_T Count)
Definition UnrealMemory.h:119