1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
media / base / audio_bus.h [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#ifndef MEDIA_BASE_AUDIO_BUS_H_
#define MEDIA_BASE_AUDIO_BUS_H_
#include <stdint.h>
#include <memory>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/aligned_memory.h"
#include "base/memory/raw_ptr.h"
#include "media/base/audio_sample_types.h"
#include "media/base/media_shmem_export.h"
namespace media {
class AudioParameters;
// Represents a sequence of audio frames containing frames() audio samples for
// each of channels() channels. The data is stored as a set of contiguous
// float arrays with one array per channel. The memory for the arrays is either
// allocated and owned by the AudioBus or it is provided to one of the factory
// methods. AudioBus guarantees that it allocates memory such that float array
// for each channel is aligned by AudioBus::kChannelAlignment bytes and it
// requires the same for memory passed to its Wrap...() factory methods.
class MEDIA_SHMEM_EXPORT AudioBus {
public:
using Channel = base::span<float>;
using ConstChannel = base::span<const float>;
using ChannelVector = std::vector<Channel>;
// Guaranteed alignment of each channel's data; use 16-byte alignment for easy
// SSE optimizations.
static constexpr size_t kChannelAlignment = 16;
// Creates a new AudioBus and allocates |channels| of length |frames|. Uses
// channels() and frames_per_buffer() from AudioParameters if given.
static std::unique_ptr<AudioBus> Create(int channels, int frames);
static std::unique_ptr<AudioBus> Create(const AudioParameters& params);
// Creates a new AudioBus with the given number of channels, but zero length.
// Clients are expected to subsequently call SetChannelData() and set_frames()
// to wrap externally allocated memory.
static std::unique_ptr<AudioBus> CreateWrapper(int channels);
// Creates a new AudioBus from an existing channel vector. Does not transfer
// ownership of |channel_data| to AudioBus; i.e., |channel_data| must outlive
// the returned AudioBus. Each channel must be aligned by kChannelAlignment.
static std::unique_ptr<AudioBus> WrapVector(
int frames,
const std::vector<float*>& channel_data);
// Creates a new AudioBus by wrapping an existing block of memory. Block must
// be at least CalculateMemorySize() bytes in size. |data| must outlive the
// returned AudioBus. |data| must be aligned by kChannelAlignment.
static std::unique_ptr<AudioBus> WrapMemory(int channels,
int frames,
void* data);
static std::unique_ptr<AudioBus> WrapMemory(const AudioParameters& params,
void* data);
static std::unique_ptr<const AudioBus> WrapReadOnlyMemory(int channels,
int frames,
const void* data);
static std::unique_ptr<const AudioBus> WrapReadOnlyMemory(
const AudioParameters& params,
const void* data);
// Based on the given number of channels and frames, calculates the minimum
// required size in bytes of a contiguous block of memory to be passed to
// AudioBus for storage of the audio data.
// Uses channels() and frames_per_buffer() from AudioParameters if given.
static int CalculateMemorySize(int channels, int frames);
static int CalculateMemorySize(const AudioParameters& params);
// Checks if buffer is properly aligned to be used in `SetChannelData()`
static bool IsAligned(void* ptr);
static bool IsAligned(base::span<float> span);
// Methods that are expected to be called after AudioBus::CreateWrapper() in
// order to wrap externally allocated memory.
// To avoid cases where channel sizes and number of frames don't match,
// `set_frames()` must be called before setting channel data.
// Note: It is illegal to call these methods when using a factory method other
// than CreateWrapper().
void set_frames(int frames);
void SetChannelData(int channel, Channel data);
void SetAllChannels(const ChannelVector& channel_data);
// Method optionally called after AudioBus::CreateWrapper().
// Runs |deleter| when on |this|' destruction, freeing external data
// referenced by SetChannelData().
// Note: It is illegal to call this method when using a factory method other
// than CreateWrapper().
void SetWrappedDataDeleter(base::OnceClosure deleter);
// Methods for compressed bitstream formats. The data size may not be equal to
// the capacity of the AudioBus. Also, the frame count may not be equal to the
// capacity of the AudioBus. Thus, we need extra methods to access the real
// data size and frame count for bitstream formats.
bool is_bitstream_format() const { return is_bitstream_format_; }
void set_is_bitstream_format(bool is_bitstream_format) {
is_bitstream_format_ = is_bitstream_format;
}
size_t GetBitstreamDataSize() const;
void SetBitstreamDataSize(size_t data_size);
int GetBitstreamFrames() const;
void SetBitstreamFrames(int frames);
// Overwrites the sample values stored in this AudioBus instance with values
// from a given interleaved |source_buffer| with expected layout
// [ch0, ch1, ..., chN, ch0, ch1, ...] and sample values in the format
// corresponding to the given SourceSampleTypeTraits.
// The sample values are converted to float values by means of the method
// convert_to_float32() provided by the SourceSampleTypeTraits. For a list of
// ready-to-use SampleTypeTraits, see file audio_sample_types.h.
// If |num_frames_to_write| is less than frames(), the remaining frames are
// zeroed out. If |num_frames_to_write| is more than frames(), this results in
// undefined behavior.
template <class SourceSampleTypeTraits>
void FromInterleaved(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int num_frames_to_write);
// Similar to FromInterleaved...(), but overwrites the frames starting at a
// given offset |write_offset_in_frames| and does not zero out frames that are
// not overwritten.
template <class SourceSampleTypeTraits>
void FromInterleavedPartial(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int write_offset_in_frames,
int num_frames_to_write);
// Reads the sample values stored in this AudioBus instance and places them
// into the given |dest_buffer| in interleaved format using the sample format
// specified by TargetSampleTypeTraits. For a list of ready-to-use
// SampleTypeTraits, see file audio_sample_types.h. If |num_frames_to_read| is
// larger than frames(), this results in undefined behavior.
template <class TargetSampleTypeTraits>
void ToInterleaved(
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest_buffer) const;
// Similar to ToInterleaved(), but reads the frames starting at a given
// offset |read_offset_in_frames|.
template <class TargetSampleTypeTraits>
void ToInterleavedPartial(
int read_offset_in_frames,
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest_buffer) const;
// Helper method for copying channel data from one AudioBus to another. Both
// AudioBus object must have the same frames() and channels().
void CopyTo(AudioBus* dest) const;
// Similar to above, but clips values to [-1, 1] during the copy process.
void CopyAndClipTo(AudioBus* dest) const;
// Helper method to copy frames from one AudioBus to another. Both AudioBus
// objects must have the same number of channels(). |source_start_frame| is
// the starting offset. |dest_start_frame| is the starting offset in |dest|.
// |frame_count| is the number of frames to copy.
void CopyPartialFramesTo(int source_start_frame,
int frame_count,
int dest_start_frame,
AudioBus* dest) const;
// Returns a raw pointer to the requested channel. Pointer is guaranteed to
// have a 16-byte alignment. Warning: Do not rely on having sane (i.e. not
// inf, nan, or between [-1.0, 1.0]) values in the channel data.
// TODO(crbug.com/373960632): Remove these methods, and rename `channel_span`
// to `channel`.
float* channel(int channel) { return channel_data_[channel].data(); }
const float* channel(int channel) const {
return channel_data_[channel].data();
}
Channel channel_span(int channel) { return channel_data_[channel]; }
ConstChannel channel_span(int channel) const {
return channel_data_[channel];
}
// Convenience function to allow range-based for-loops.
const ChannelVector& AllChannels() const;
// Returns a copy of `channels_`, with `subspan()` applied to each channel.
// Note: The returned channels might not be aligned, depending on `offset`.
ChannelVector AllChannelsSubspan(size_t offset, size_t count) const;
// Returns the number of channels.
int channels() const { return static_cast<int>(channel_data_.size()); }
// Returns the number of frames.
int frames() const { return frames_; }
// Helper method for zeroing out all channels of audio data.
void Zero();
void ZeroFrames(int frames);
void ZeroFramesPartial(int start_frame, int frames);
// Checks if all frames are zero.
bool AreFramesZero() const;
// Scale internal channel values by |volume| >= 0. If an invalid value
// is provided, no adjustment is done.
void Scale(float volume);
// Swaps channels identified by |a| and |b|. The caller needs to make sure
// the channels are valid.
void SwapChannels(int a, int b);
AudioBus(const AudioBus&) = delete;
AudioBus& operator=(const AudioBus&) = delete;
virtual ~AudioBus();
protected:
AudioBus(int channels, int frames);
AudioBus(int channels, int frames, float* data);
AudioBus(int frames, const std::vector<float*>& channel_data);
explicit AudioBus(int channels);
private:
// Helper method for building |channel_data_| from a block of memory. |data|
// must be at least CalculateMemorySize(...) bytes in size.
void BuildChannelData(int channels, base::span<float> data);
static void CheckOverflow(int start_frame, int frames, int total_frames);
template <class SourceSampleTypeTraits>
static void CopyConvertFromInterleavedSourceToAudioBus(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int write_offset_in_frames,
int num_frames_to_write,
AudioBus* dest);
template <class TargetSampleTypeTraits>
static void CopyConvertFromAudioBusToInterleavedTarget(
const AudioBus* source,
int read_offset_in_frames,
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest_buffer);
// Contiguous block of channel memory.
base::AlignedHeapArray<float> data_;
// Whether the data is compressed bitstream or not.
bool is_bitstream_format_ = false;
// The data size for a compressed bitstream.
size_t bitstream_data_size_ = 0;
// The PCM frame count for a compressed bitstream.
int bitstream_frames_ = 0;
// One float pointer per channel pointing to a contiguous block of memory for
// that channel. If the memory is owned by this instance, this will
// point to the memory in |data_|. Otherwise, it may point to memory provided
// by the client.
// TODO(crbug.com/385028986): Convert to `base::raw_span`
RAW_PTR_EXCLUSION ChannelVector channel_data_;
size_t frames_;
// Protect SetChannelData(), set_frames() and SetWrappedDataDeleter() for use
// by CreateWrapper().
bool is_wrapper_;
// Run on destruction. Frees memory to the data set via SetChannelData().
// Only used with CreateWrapper().
base::OnceClosure wrapped_data_deleter_cb_;
};
// Delegates to FromInterleavedPartial()
template <class SourceSampleTypeTraits>
void AudioBus::FromInterleaved(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int num_frames_to_write) {
FromInterleavedPartial<SourceSampleTypeTraits>(source_buffer, 0,
num_frames_to_write);
// Zero any remaining frames.
ZeroFramesPartial(num_frames_to_write, frames_ - num_frames_to_write);
}
template <class SourceSampleTypeTraits>
void AudioBus::FromInterleavedPartial(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int write_offset_in_frames,
int num_frames_to_write) {
CheckOverflow(write_offset_in_frames, num_frames_to_write, frames_);
CopyConvertFromInterleavedSourceToAudioBus<SourceSampleTypeTraits>(
source_buffer, write_offset_in_frames, num_frames_to_write, this);
}
// Delegates to ToInterleavedPartial()
template <class TargetSampleTypeTraits>
void AudioBus::ToInterleaved(
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest_buffer) const {
ToInterleavedPartial<TargetSampleTypeTraits>(0, num_frames_to_read,
dest_buffer);
}
template <class TargetSampleTypeTraits>
void AudioBus::ToInterleavedPartial(
int read_offset_in_frames,
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest) const {
CheckOverflow(read_offset_in_frames, num_frames_to_read, frames_);
CopyConvertFromAudioBusToInterleavedTarget<TargetSampleTypeTraits>(
this, read_offset_in_frames, num_frames_to_read, dest);
}
// TODO(chfremer): Consider using vector instructions to speed this up,
// https://crbug.com/619628
template <class SourceSampleTypeTraits>
void AudioBus::CopyConvertFromInterleavedSourceToAudioBus(
const typename SourceSampleTypeTraits::ValueType* source_buffer,
int write_offset_in_frames,
int num_frames_to_write,
AudioBus* dest) {
const int channels = dest->channels();
for (int ch = 0; ch < channels; ++ch) {
float* channel_data = dest->channel(ch);
for (int target_frame_index = write_offset_in_frames,
read_pos_in_source = ch;
target_frame_index < write_offset_in_frames + num_frames_to_write;
++target_frame_index, read_pos_in_source += channels) {
auto source_value = source_buffer[read_pos_in_source];
channel_data[target_frame_index] =
SourceSampleTypeTraits::ToFloat(source_value);
}
}
}
// TODO(chfremer): Consider using vector instructions to speed this up,
// https://crbug.com/619628
template <class TargetSampleTypeTraits>
void AudioBus::CopyConvertFromAudioBusToInterleavedTarget(
const AudioBus* source,
int read_offset_in_frames,
int num_frames_to_read,
typename TargetSampleTypeTraits::ValueType* dest_buffer) {
const int channels = source->channels();
for (int ch = 0; ch < channels; ++ch) {
const float* channel_data = source->channel(ch);
for (int source_frame_index = read_offset_in_frames, write_pos_in_dest = ch;
source_frame_index < read_offset_in_frames + num_frames_to_read;
++source_frame_index, write_pos_in_dest += channels) {
float sourceSampleValue = channel_data[source_frame_index];
dest_buffer[write_pos_in_dest] =
TargetSampleTypeTraits::FromFloat(sourceSampleValue);
}
}
}
} // namespace media
#endif // MEDIA_BASE_AUDIO_BUS_H_