audio_bus.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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.
4
5#include "media/base/audio_bus.h"
6
7#include "base/logging.h"
8#include "media/audio/audio_parameters.h"
9#include "media/base/limits.h"
10#include "media/base/vector_math.h"
11
12namespace media {
13
14static const uint8 kUint8Bias = 128;
15
16static bool IsAligned(void* ptr) {
17  return (reinterpret_cast<uintptr_t>(ptr) &
18          (AudioBus::kChannelAlignment - 1)) == 0U;
19}
20
21// Calculates the required size for an AudioBus with the given params, sets
22// |aligned_frames| to the actual frame length of each channel array.
23static int CalculateMemorySizeInternal(int channels, int frames,
24                                       int* out_aligned_frames) {
25  // Choose a size such that each channel will be aligned by
26  // kChannelAlignment when stored in a contiguous block.
27  int aligned_frames =
28      ((frames * sizeof(float) + AudioBus::kChannelAlignment - 1) &
29       ~(AudioBus::kChannelAlignment - 1)) / sizeof(float);
30
31  if (out_aligned_frames)
32    *out_aligned_frames = aligned_frames;
33
34  return sizeof(float) * channels * aligned_frames;
35}
36
37// |Format| is the destination type.  If a bias is present, |Fixed| must be a
38// type larger than |Format| such that operations can be made without
39// overflowing.  Without a bias |Fixed| must be the same as |Format|.
40template<class Format, class Fixed, Format Bias>
41static void FromInterleavedInternal(const void* src, int start_frame,
42                                    int frames, AudioBus* dest,
43                                    float min, float max) {
44  COMPILE_ASSERT((Bias == 0 && sizeof(Fixed) == sizeof(Format)) ||
45                 sizeof(Fixed) > sizeof(Format), invalid_deinterleave_types);
46  const Format* source = static_cast<const Format*>(src);
47  const int channels = dest->channels();
48  for (int ch = 0; ch < channels; ++ch) {
49    float* channel_data = dest->channel(ch);
50    for (int i = start_frame, offset = ch; i < start_frame + frames;
51         ++i, offset += channels) {
52      const Fixed v = static_cast<Fixed>(source[offset]) - Bias;
53      channel_data[i] = v * (v < 0 ? -min : max);
54    }
55  }
56}
57
58// |Format| is the destination type.  If a bias is present, |Fixed| must be a
59// type larger than |Format| such that operations can be made without
60// overflowing.  Without a bias |Fixed| must be the same as |Format|.
61template<class Format, class Fixed, Format Bias>
62static void ToInterleavedInternal(const AudioBus* source, int start_frame,
63                                  int frames, void* dst, Fixed min, Fixed max) {
64  COMPILE_ASSERT((Bias == 0 && sizeof(Fixed) == sizeof(Format)) ||
65                 sizeof(Fixed) > sizeof(Format), invalid_interleave_types);
66  Format* dest = static_cast<Format*>(dst);
67  const int channels = source->channels();
68  for (int ch = 0; ch < channels; ++ch) {
69    const float* channel_data = source->channel(ch);
70    for (int i = start_frame, offset = ch; i < start_frame + frames;
71         ++i, offset += channels) {
72      const float v = channel_data[i];
73
74      Fixed sample;
75      if (v < 0)
76        sample = v <= -1 ? min : static_cast<Fixed>(-v * min);
77      else
78        sample = v >= 1 ? max : static_cast<Fixed>(v * max);
79
80      dest[offset] = static_cast<Format>(sample) + Bias;
81    }
82  }
83}
84
85static void ValidateConfig(size_t channels, int frames) {
86  CHECK_GT(frames, 0);
87  CHECK_LE(channels, static_cast<size_t>(limits::kMaxChannels));
88}
89
90static void CheckOverflow(int start_frame, int frames, int total_frames) {
91  CHECK_GE(start_frame, 0);
92  CHECK_GE(frames, 0);
93  CHECK_GT(total_frames, 0);
94  int sum = start_frame + frames;
95  CHECK_LE(sum, total_frames);
96  CHECK_GE(sum, 0);
97}
98
99AudioBus::AudioBus(int channels, int frames)
100    : frames_(frames),
101      can_set_channel_data_(false) {
102  ValidateConfig(channels, frames_);
103
104  int aligned_frames = 0;
105  int size = CalculateMemorySizeInternal(channels, frames, &aligned_frames);
106
107  data_.reset(static_cast<float*>(base::AlignedAlloc(
108      size, AudioBus::kChannelAlignment)));
109
110  BuildChannelData(channels, aligned_frames, data_.get());
111}
112
113AudioBus::AudioBus(int channels, int frames, float* data)
114    : frames_(frames),
115      can_set_channel_data_(false) {
116  // Since |data| may have come from an external source, ensure it's valid.
117  CHECK(data);
118  ValidateConfig(channels, frames_);
119
120  int aligned_frames = 0;
121  CalculateMemorySizeInternal(channels, frames, &aligned_frames);
122
123  BuildChannelData(channels, aligned_frames, data);
124}
125
126AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data)
127    : channel_data_(channel_data),
128      frames_(frames),
129      can_set_channel_data_(false) {
130  ValidateConfig(channel_data_.size(), frames_);
131
132  // Sanity check wrapped vector for alignment and channel count.
133  for (size_t i = 0; i < channel_data_.size(); ++i)
134    DCHECK(IsAligned(channel_data_[i]));
135}
136
137AudioBus::AudioBus(int channels)
138    : channel_data_(channels),
139      frames_(0),
140      can_set_channel_data_(true) {
141  for (size_t i = 0; i < channel_data_.size(); ++i)
142    channel_data_[i] = NULL;
143}
144
145AudioBus::~AudioBus() {}
146
147scoped_ptr<AudioBus> AudioBus::Create(int channels, int frames) {
148  return scoped_ptr<AudioBus>(new AudioBus(channels, frames));
149}
150
151scoped_ptr<AudioBus> AudioBus::Create(const AudioParameters& params) {
152  return scoped_ptr<AudioBus>(new AudioBus(
153      params.channels(), params.frames_per_buffer()));
154}
155
156scoped_ptr<AudioBus> AudioBus::CreateWrapper(int channels) {
157  return scoped_ptr<AudioBus>(new AudioBus(channels));
158}
159
160scoped_ptr<AudioBus> AudioBus::WrapVector(
161    int frames, const std::vector<float*>& channel_data) {
162  return scoped_ptr<AudioBus>(new AudioBus(frames, channel_data));
163}
164
165scoped_ptr<AudioBus> AudioBus::WrapMemory(int channels, int frames,
166                                          void* data) {
167  // |data| must be aligned by AudioBus::kChannelAlignment.
168  CHECK(IsAligned(data));
169  return scoped_ptr<AudioBus>(new AudioBus(
170      channels, frames, static_cast<float*>(data)));
171}
172
173scoped_ptr<AudioBus> AudioBus::WrapMemory(const AudioParameters& params,
174                                          void* data) {
175  // |data| must be aligned by AudioBus::kChannelAlignment.
176  CHECK(IsAligned(data));
177  return scoped_ptr<AudioBus>(new AudioBus(
178      params.channels(), params.frames_per_buffer(),
179      static_cast<float*>(data)));
180}
181
182void AudioBus::SetChannelData(int channel, float* data) {
183  CHECK(can_set_channel_data_);
184  CHECK(data);
185  CHECK_GE(channel, 0);
186  CHECK_LT(static_cast<size_t>(channel), channel_data_.size());
187  DCHECK(IsAligned(data));
188  channel_data_[channel] = data;
189}
190
191void AudioBus::set_frames(int frames) {
192  CHECK(can_set_channel_data_);
193  frames_ = frames;
194}
195
196void AudioBus::ZeroFramesPartial(int start_frame, int frames) {
197  CheckOverflow(start_frame, frames, frames_);
198
199  if (frames <= 0)
200    return;
201
202  for (size_t i = 0; i < channel_data_.size(); ++i) {
203    memset(channel_data_[i] + start_frame, 0,
204           frames * sizeof(*channel_data_[i]));
205  }
206}
207
208void AudioBus::ZeroFrames(int frames) {
209  ZeroFramesPartial(0, frames);
210}
211
212void AudioBus::Zero() {
213  ZeroFrames(frames_);
214}
215
216int AudioBus::CalculateMemorySize(const AudioParameters& params) {
217  return CalculateMemorySizeInternal(
218      params.channels(), params.frames_per_buffer(), NULL);
219}
220
221int AudioBus::CalculateMemorySize(int channels, int frames) {
222  return CalculateMemorySizeInternal(channels, frames, NULL);
223}
224
225void AudioBus::BuildChannelData(int channels, int aligned_frames, float* data) {
226  DCHECK(IsAligned(data));
227  DCHECK_EQ(channel_data_.size(), 0U);
228  // Separate audio data out into channels for easy lookup later.  Figure out
229  channel_data_.reserve(channels);
230  for (int i = 0; i < channels; ++i)
231    channel_data_.push_back(data + i * aligned_frames);
232}
233
234// TODO(dalecurtis): See if intrinsic optimizations help any here.
235void AudioBus::FromInterleavedPartial(const void* source, int start_frame,
236                                      int frames, int bytes_per_sample) {
237  CheckOverflow(start_frame, frames, frames_);
238  switch (bytes_per_sample) {
239    case 1:
240      FromInterleavedInternal<uint8, int16, kUint8Bias>(
241          source, start_frame, frames, this,
242          1.0f / kint8min, 1.0f / kint8max);
243      break;
244    case 2:
245      FromInterleavedInternal<int16, int16, 0>(
246          source, start_frame, frames, this,
247          1.0f / kint16min, 1.0f / kint16max);
248      break;
249    case 4:
250      FromInterleavedInternal<int32, int32, 0>(
251          source, start_frame, frames, this,
252          1.0f / kint32min, 1.0f / kint32max);
253      break;
254    default:
255      NOTREACHED() << "Unsupported bytes per sample encountered.";
256      ZeroFramesPartial(start_frame, frames);
257      return;
258  }
259
260  // Don't clear remaining frames if this is a partial deinterleave.
261  if (!start_frame) {
262    // Zero any remaining frames.
263    ZeroFramesPartial(frames, frames_ - frames);
264  }
265}
266
267void AudioBus::FromInterleaved(const void* source, int frames,
268                               int bytes_per_sample) {
269  FromInterleavedPartial(source, 0, frames, bytes_per_sample);
270}
271
272void AudioBus::ToInterleaved(int frames, int bytes_per_sample,
273                             void* dest) const {
274  ToInterleavedPartial(0, frames, bytes_per_sample, dest);
275}
276
277// TODO(dalecurtis): See if intrinsic optimizations help any here.
278void AudioBus::ToInterleavedPartial(int start_frame, int frames,
279                                    int bytes_per_sample, void* dest) const {
280  CheckOverflow(start_frame, frames, frames_);
281  switch (bytes_per_sample) {
282    case 1:
283      ToInterleavedInternal<uint8, int16, kUint8Bias>(
284          this, start_frame, frames, dest, kint8min, kint8max);
285      break;
286    case 2:
287      ToInterleavedInternal<int16, int16, 0>(
288          this, start_frame, frames, dest, kint16min, kint16max);
289      break;
290    case 4:
291      ToInterleavedInternal<int32, int32, 0>(
292          this, start_frame, frames, dest, kint32min, kint32max);
293      break;
294    default:
295      NOTREACHED() << "Unsupported bytes per sample encountered.";
296      memset(dest, 0, frames * bytes_per_sample);
297      return;
298  }
299}
300
301void AudioBus::CopyTo(AudioBus* dest) const {
302  CopyPartialFramesTo(0, frames(), 0, dest);
303}
304
305void AudioBus::CopyPartialFramesTo(int source_start_frame,
306                                   int frame_count,
307                                   int dest_start_frame,
308                                   AudioBus* dest) const {
309  CHECK_EQ(channels(), dest->channels());
310  CHECK_LE(source_start_frame + frame_count, frames());
311  CHECK_LE(dest_start_frame + frame_count, dest->frames());
312
313  // Since we don't know if the other AudioBus is wrapped or not (and we don't
314  // want to care), just copy using the public channel() accessors.
315  for (int i = 0; i < channels(); ++i) {
316    memcpy(dest->channel(i) + dest_start_frame,
317           channel(i) + source_start_frame,
318           sizeof(*channel(i)) * frame_count);
319  }
320}
321
322void AudioBus::Scale(float volume) {
323  if (volume > 0 && volume != 1) {
324    for (int i = 0; i < channels(); ++i)
325      vector_math::FMUL(channel(i), volume, frames(), channel(i));
326  } else if (volume == 0) {
327    Zero();
328  }
329}
330
331}  // namespace media
332