1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/base/audio_bus.h"
7 #include "base/logging.h"
8 #include "base/numerics/safe_conversions.h"
9 #include "media/audio/audio_parameters.h"
10 #include "media/base/limits.h"
11 #include "media/base/vector_math.h"
15 static const uint8 kUint8Bias
= 128;
17 static bool IsAligned(void* ptr
) {
18 return (reinterpret_cast<uintptr_t>(ptr
) &
19 (AudioBus::kChannelAlignment
- 1)) == 0U;
22 // Calculates the required size for an AudioBus with the given params, sets
23 // |aligned_frames| to the actual frame length of each channel array.
24 static int CalculateMemorySizeInternal(int channels
, int frames
,
25 int* out_aligned_frames
) {
26 // Choose a size such that each channel will be aligned by
27 // kChannelAlignment when stored in a contiguous block.
29 ((frames
* sizeof(float) + AudioBus::kChannelAlignment
- 1) &
30 ~(AudioBus::kChannelAlignment
- 1)) / sizeof(float);
32 if (out_aligned_frames
)
33 *out_aligned_frames
= aligned_frames
;
35 return sizeof(float) * channels
* aligned_frames
;
38 // |Format| is the destination type. If a bias is present, |Fixed| must be a
39 // type larger than |Format| such that operations can be made without
40 // overflowing. Without a bias |Fixed| must be the same as |Format|.
41 template<class Format
, class Fixed
, Format Bias
>
42 static void FromInterleavedInternal(const void* src
, int start_frame
,
43 int frames
, AudioBus
* dest
,
44 float min
, float max
) {
45 static_assert((Bias
== 0 && sizeof(Fixed
) == sizeof(Format
)) ||
46 sizeof(Fixed
) > sizeof(Format
), "invalid deinterleave types");
47 const Format
* source
= static_cast<const Format
*>(src
);
48 const int channels
= dest
->channels();
49 for (int ch
= 0; ch
< channels
; ++ch
) {
50 float* channel_data
= dest
->channel(ch
);
51 for (int i
= start_frame
, offset
= ch
; i
< start_frame
+ frames
;
52 ++i
, offset
+= channels
) {
53 const Fixed v
= static_cast<Fixed
>(source
[offset
]) - Bias
;
54 channel_data
[i
] = v
* (v
< 0 ? -min
: max
);
59 // |Format| is the destination type. If a bias is present, |Fixed| must be a
60 // type larger than |Format| such that operations can be made without
61 // overflowing. Without a bias |Fixed| must be the same as |Format|.
62 template<class Format
, class Fixed
, Format Bias
>
63 static void ToInterleavedInternal(const AudioBus
* source
, int start_frame
,
64 int frames
, void* dst
, Fixed min
, Fixed max
) {
65 static_assert((Bias
== 0 && sizeof(Fixed
) == sizeof(Format
)) ||
66 sizeof(Fixed
) > sizeof(Format
), "invalid interleave types");
67 Format
* dest
= static_cast<Format
*>(dst
);
68 const int channels
= source
->channels();
69 for (int ch
= 0; ch
< channels
; ++ch
) {
70 const float* channel_data
= source
->channel(ch
);
71 for (int i
= start_frame
, offset
= ch
; i
< start_frame
+ frames
;
72 ++i
, offset
+= channels
) {
73 const float v
= channel_data
[i
];
77 sample
= v
<= -1 ? min
: static_cast<Fixed
>(-v
* min
);
79 sample
= v
>= 1 ? max
: static_cast<Fixed
>(v
* max
);
81 dest
[offset
] = static_cast<Format
>(sample
) + Bias
;
86 static void ValidateConfig(int channels
, int frames
) {
88 CHECK_GT(channels
, 0);
89 CHECK_LE(channels
, static_cast<int>(limits::kMaxChannels
));
92 static void CheckOverflow(int start_frame
, int frames
, int total_frames
) {
93 CHECK_GE(start_frame
, 0);
95 CHECK_GT(total_frames
, 0);
96 int sum
= start_frame
+ frames
;
97 CHECK_LE(sum
, total_frames
);
101 AudioBus::AudioBus(int channels
, int frames
)
103 can_set_channel_data_(false) {
104 ValidateConfig(channels
, frames_
);
106 int aligned_frames
= 0;
107 int size
= CalculateMemorySizeInternal(channels
, frames
, &aligned_frames
);
109 data_
.reset(static_cast<float*>(base::AlignedAlloc(
110 size
, AudioBus::kChannelAlignment
)));
112 BuildChannelData(channels
, aligned_frames
, data_
.get());
115 AudioBus::AudioBus(int channels
, int frames
, float* data
)
117 can_set_channel_data_(false) {
118 // Since |data| may have come from an external source, ensure it's valid.
120 ValidateConfig(channels
, frames_
);
122 int aligned_frames
= 0;
123 CalculateMemorySizeInternal(channels
, frames
, &aligned_frames
);
125 BuildChannelData(channels
, aligned_frames
, data
);
128 AudioBus::AudioBus(int frames
, const std::vector
<float*>& channel_data
)
129 : channel_data_(channel_data
),
131 can_set_channel_data_(false) {
133 base::checked_cast
<int>(channel_data_
.size()), frames_
);
135 // Sanity check wrapped vector for alignment and channel count.
136 for (size_t i
= 0; i
< channel_data_
.size(); ++i
)
137 DCHECK(IsAligned(channel_data_
[i
]));
140 AudioBus::AudioBus(int channels
)
141 : channel_data_(channels
),
143 can_set_channel_data_(true) {
144 CHECK_GT(channels
, 0);
145 for (size_t i
= 0; i
< channel_data_
.size(); ++i
)
146 channel_data_
[i
] = NULL
;
149 AudioBus::~AudioBus() {}
151 scoped_ptr
<AudioBus
> AudioBus::Create(int channels
, int frames
) {
152 return scoped_ptr
<AudioBus
>(new AudioBus(channels
, frames
));
155 scoped_ptr
<AudioBus
> AudioBus::Create(const AudioParameters
& params
) {
156 return scoped_ptr
<AudioBus
>(new AudioBus(
157 params
.channels(), params
.frames_per_buffer()));
160 scoped_ptr
<AudioBus
> AudioBus::CreateWrapper(int channels
) {
161 return scoped_ptr
<AudioBus
>(new AudioBus(channels
));
164 scoped_ptr
<AudioBus
> AudioBus::WrapVector(
165 int frames
, const std::vector
<float*>& channel_data
) {
166 return scoped_ptr
<AudioBus
>(new AudioBus(frames
, channel_data
));
169 scoped_ptr
<AudioBus
> AudioBus::WrapMemory(int channels
, int frames
,
171 // |data| must be aligned by AudioBus::kChannelAlignment.
172 CHECK(IsAligned(data
));
173 return scoped_ptr
<AudioBus
>(new AudioBus(
174 channels
, frames
, static_cast<float*>(data
)));
177 scoped_ptr
<AudioBus
> AudioBus::WrapMemory(const AudioParameters
& params
,
179 // |data| must be aligned by AudioBus::kChannelAlignment.
180 CHECK(IsAligned(data
));
181 return scoped_ptr
<AudioBus
>(new AudioBus(
182 params
.channels(), params
.frames_per_buffer(),
183 static_cast<float*>(data
)));
186 void AudioBus::SetChannelData(int channel
, float* data
) {
187 CHECK(can_set_channel_data_
);
189 CHECK_GE(channel
, 0);
190 CHECK_LT(static_cast<size_t>(channel
), channel_data_
.size());
191 DCHECK(IsAligned(data
));
192 channel_data_
[channel
] = data
;
195 void AudioBus::set_frames(int frames
) {
196 CHECK(can_set_channel_data_
);
197 ValidateConfig(static_cast<int>(channel_data_
.size()), frames
);
201 void AudioBus::ZeroFramesPartial(int start_frame
, int frames
) {
202 CheckOverflow(start_frame
, frames
, frames_
);
207 for (size_t i
= 0; i
< channel_data_
.size(); ++i
) {
208 memset(channel_data_
[i
] + start_frame
, 0,
209 frames
* sizeof(*channel_data_
[i
]));
213 void AudioBus::ZeroFrames(int frames
) {
214 ZeroFramesPartial(0, frames
);
217 void AudioBus::Zero() {
221 int AudioBus::CalculateMemorySize(const AudioParameters
& params
) {
222 return CalculateMemorySizeInternal(
223 params
.channels(), params
.frames_per_buffer(), NULL
);
226 int AudioBus::CalculateMemorySize(int channels
, int frames
) {
227 return CalculateMemorySizeInternal(channels
, frames
, NULL
);
230 void AudioBus::BuildChannelData(int channels
, int aligned_frames
, float* data
) {
231 DCHECK(IsAligned(data
));
232 DCHECK_EQ(channel_data_
.size(), 0U);
233 // Separate audio data out into channels for easy lookup later. Figure out
234 channel_data_
.reserve(channels
);
235 for (int i
= 0; i
< channels
; ++i
)
236 channel_data_
.push_back(data
+ i
* aligned_frames
);
239 // TODO(dalecurtis): See if intrinsic optimizations help any here.
240 void AudioBus::FromInterleavedPartial(const void* source
, int start_frame
,
241 int frames
, int bytes_per_sample
) {
242 CheckOverflow(start_frame
, frames
, frames_
);
243 switch (bytes_per_sample
) {
245 FromInterleavedInternal
<uint8
, int16
, kUint8Bias
>(
246 source
, start_frame
, frames
, this,
247 1.0f
/ kint8min
, 1.0f
/ kint8max
);
250 FromInterleavedInternal
<int16
, int16
, 0>(
251 source
, start_frame
, frames
, this,
252 1.0f
/ kint16min
, 1.0f
/ kint16max
);
255 FromInterleavedInternal
<int32
, int32
, 0>(
256 source
, start_frame
, frames
, this,
257 1.0f
/ kint32min
, 1.0f
/ kint32max
);
260 NOTREACHED() << "Unsupported bytes per sample encountered.";
261 ZeroFramesPartial(start_frame
, frames
);
265 // Don't clear remaining frames if this is a partial deinterleave.
267 // Zero any remaining frames.
268 ZeroFramesPartial(frames
, frames_
- frames
);
272 void AudioBus::FromInterleaved(const void* source
, int frames
,
273 int bytes_per_sample
) {
274 FromInterleavedPartial(source
, 0, frames
, bytes_per_sample
);
277 void AudioBus::ToInterleaved(int frames
, int bytes_per_sample
,
279 ToInterleavedPartial(0, frames
, bytes_per_sample
, dest
);
282 // TODO(dalecurtis): See if intrinsic optimizations help any here.
283 void AudioBus::ToInterleavedPartial(int start_frame
, int frames
,
284 int bytes_per_sample
, void* dest
) const {
285 CheckOverflow(start_frame
, frames
, frames_
);
286 switch (bytes_per_sample
) {
288 ToInterleavedInternal
<uint8
, int16
, kUint8Bias
>(
289 this, start_frame
, frames
, dest
, kint8min
, kint8max
);
292 ToInterleavedInternal
<int16
, int16
, 0>(
293 this, start_frame
, frames
, dest
, kint16min
, kint16max
);
296 ToInterleavedInternal
<int32
, int32
, 0>(
297 this, start_frame
, frames
, dest
, kint32min
, kint32max
);
300 NOTREACHED() << "Unsupported bytes per sample encountered.";
301 memset(dest
, 0, frames
* bytes_per_sample
);
306 void AudioBus::CopyTo(AudioBus
* dest
) const {
307 CopyPartialFramesTo(0, frames(), 0, dest
);
310 void AudioBus::CopyPartialFramesTo(int source_start_frame
,
312 int dest_start_frame
,
313 AudioBus
* dest
) const {
314 CHECK_EQ(channels(), dest
->channels());
315 CHECK_LE(source_start_frame
+ frame_count
, frames());
316 CHECK_LE(dest_start_frame
+ frame_count
, dest
->frames());
318 // Since we don't know if the other AudioBus is wrapped or not (and we don't
319 // want to care), just copy using the public channel() accessors.
320 for (int i
= 0; i
< channels(); ++i
) {
321 memcpy(dest
->channel(i
) + dest_start_frame
,
322 channel(i
) + source_start_frame
,
323 sizeof(*channel(i
)) * frame_count
);
327 void AudioBus::Scale(float volume
) {
328 if (volume
> 0 && volume
!= 1) {
329 for (int i
= 0; i
< channels(); ++i
)
330 vector_math::FMUL(channel(i
), volume
, frames(), channel(i
));
331 } else if (volume
== 0) {
336 void AudioBus::SwapChannels(int a
, int b
) {
337 DCHECK(a
< channels() && a
>= 0);
338 DCHECK(b
< channels() && b
>= 0);
340 std::swap(channel_data_
[a
], channel_data_
[b
]);
343 scoped_refptr
<AudioBusRefCounted
> AudioBusRefCounted::Create(
344 int channels
, int frames
) {
345 return scoped_refptr
<AudioBusRefCounted
>(
346 new AudioBusRefCounted(channels
, frames
));
349 AudioBusRefCounted::AudioBusRefCounted(int channels
, int frames
)
350 : AudioBus(channels
, frames
) {}
352 AudioBusRefCounted::~AudioBusRefCounted() {}