HART  0.1.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_audio_buffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // max_element()
4#include <vector>
5
7
8namespace hart {
9
10template <typename SampleType>
12{
13public:
14 AudioBuffer (size_t numChannels = 0, size_t numFrames = 0) :
15 m_numChannels (numChannels),
16 m_numFrames (numFrames),
17 m_frames (m_numChannels * m_numFrames),
18 m_channelPointers (m_numChannels)
19 {
20 updateChannelPointers();
21 }
22
23 AudioBuffer(const AudioBuffer& other) :
24 m_numChannels (other.m_numChannels),
25 m_numFrames (other.m_numFrames),
26 m_frames (other.m_frames),
27 m_channelPointers (m_numChannels)
28 {
29 updateChannelPointers();
30 }
31
33 m_numChannels (other.m_numChannels),
34 m_numFrames (other.m_numFrames),
35 m_frames (std::move (other.m_frames)),
36 m_channelPointers (std::move (other.m_channelPointers))
37 {
38 other.clear();
39 }
40
41 ~AudioBuffer() = default;
42
44 {
45 if (this == &other)
46 return *this;
47
48 if (m_numChannels != other.m_numChannels)
49 HART_THROW_OR_RETURN (hart::ChannelLayoutError, "Can't copy from a buffer with different number of channels", *this);
50
51 m_numFrames = other.m_numFrames;
52 m_frames = other.m_frames;
53 m_channelPointers.resize (m_numChannels);
54 updateChannelPointers();
55
56 return *this;
57 }
58
60 {
61 if (this == &other)
62 return *this;
63
64 if (m_numChannels != other.m_numChannels)
65 HART_THROW_OR_RETURN (hart::ChannelLayoutError, "Can't move from a buffer with different number of channels", *this);
66
67 m_numFrames = other.m_numFrames;
68 m_frames = std::move (other.m_frames);
69 m_channelPointers = std::move (other.m_channelPointers);
70 other.clear();
71
72 return *this;
73 }
74
75 const SampleType* const* getArrayOfReadPointers() const
76 {
77 return static_cast<const SampleType* const*> (m_channelPointers.data());
78 }
79
80 SampleType* const* getArrayOfWritePointers()
81 {
82 return m_channelPointers.data();
83 }
84
85 static AudioBuffer emptyLike (const AudioBuffer& other)
86 {
87 return AudioBuffer (other.getNumChannels(), other.getNumFrames());
88 }
89
90 size_t getNumChannels() const { return m_numChannels; }
91 size_t getNumFrames() const { return m_numFrames; }
92
93 SampleType* operator[] (size_t channel)
94 {
95 return m_channelPointers[channel];
96 }
97
98 const SampleType* operator[] (size_t channel) const
99 {
100 return m_channelPointers[channel];
101 }
102
103 void appendFrom (const AudioBuffer<SampleType>& otherBuffer)
104 {
105 if (otherBuffer.getNumChannels() != m_numChannels)
106 HART_THROW_OR_RETURN_VOID (hart::ChannelLayoutError, "Channel count mismatch");
107
108 const size_t thisNumFrames = m_numFrames;
109 const size_t otherNumFrames = otherBuffer.getNumFrames();
110
111 std::vector<SampleType> combinedFrames (m_numChannels * (thisNumFrames + otherNumFrames));
112
113 for (size_t channel = 0; channel < m_numChannels; ++channel)
114 {
115 SampleType* newChannelStart = &combinedFrames[channel * (thisNumFrames + otherNumFrames)];
116 std::copy (m_channelPointers[channel], m_channelPointers[channel] + thisNumFrames, newChannelStart);
117 std::copy (otherBuffer[channel], otherBuffer[channel] + otherNumFrames, newChannelStart + thisNumFrames);
118 }
119
120 m_frames = std::move (combinedFrames);
121 m_numFrames += otherNumFrames;
122
123 updateChannelPointers();
124 }
125
126 void clear()
127 {
128 m_numFrames = 0;
129 m_frames.clear();
130
131 // If m_channelPointers was std::move'd, its size will be zero
132 if (m_channelPointers.size() != m_numChannels)
133 m_channelPointers.resize (m_numChannels);
134
135 updateChannelPointers();
136 }
137
138 SampleType getMagnitude (size_t channel, size_t startFrame, size_t numFrames) const
139 {
140 if (channel >= m_numChannels)
141 HART_THROW_OR_RETURN (hart::IndexError, "Invalid channel", (SampleType) 0);
142
143 if (startFrame + numFrames > m_numFrames || numFrames == 0)
144 HART_THROW_OR_RETURN (hart::IndexError, "Invalid frame range", (SampleType) 0);
145
146 const SampleType* start = m_channelPointers[channel] + startFrame;
147 const SampleType* peakSample = std::max_element (
148 start,
149 start + numFrames,
150 [] (SampleType a, SampleType b) { return std::abs (a) < std::abs (b); }
151 );
152
153 return std::abs (*peakSample);
154 }
155
156 SampleType getMagnitude (size_t startFrame, size_t numFrames) const
157 {
158 if (startFrame + numFrames > m_numFrames || numFrames == 0)
159 HART_THROW_OR_RETURN (hart::IndexError, "Invalid frame range", (SampleType) 0);
160
161 SampleType peakSampleAcrossAllChannels = (SampleType) 0;
162
163 for (size_t channel = 0; channel < m_numChannels; ++channel)
164 {
165 const SampleType* start = m_channelPointers[channel] + startFrame;
166 const SampleType* peakSample = std::max_element (
167 start,
168 start + numFrames,
169 [] (SampleType a, SampleType b) { return std::abs (a) < std::abs (b); }
170 );
171 peakSampleAcrossAllChannels = std::max (peakSampleAcrossAllChannels, std::abs (*peakSample));
172 }
173
174 return peakSampleAcrossAllChannels;
175 }
176
177 // TODO: Implement resize() and copyFrom() to avoid repeated memory re-allocations caused by spamming appendFrom()
178
179private:
180 const size_t m_numChannels = 0;
181 size_t m_numFrames = 0;
182 std::vector<SampleType> m_frames;
183 std::vector<SampleType*> m_channelPointers;
184
185 void updateChannelPointers()
186 {
187 for (size_t channel = 0; channel < m_numChannels; ++channel)
188 m_channelPointers[channel] = m_numFrames > 0 ? &m_frames[channel * m_numFrames] : nullptr;
189 }
190};
191
192} // namespace hart
SampleType * operator[](size_t channel)
static AudioBuffer emptyLike(const AudioBuffer &other)
AudioBuffer & operator=(AudioBuffer &&other)
size_t getNumFrames() const
AudioBuffer & operator=(const AudioBuffer &other)
void appendFrom(const AudioBuffer< SampleType > &otherBuffer)
const SampleType *const * getArrayOfReadPointers() const
SampleType getMagnitude(size_t startFrame, size_t numFrames) const
SampleType *const * getArrayOfWritePointers()
AudioBuffer(const AudioBuffer &other)
~AudioBuffer()=default
const SampleType * operator[](size_t channel) const
AudioBuffer(AudioBuffer &&other)
SampleType getMagnitude(size_t channel, size_t startFrame, size_t numFrames) const
size_t getNumChannels() const
AudioBuffer(size_t numChannels=0, size_t numFrames=0)
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)