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