Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/base/audio_bus.h" | 5 #include "media/base/audio_bus.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "media/audio/audio_parameters.h" | 10 #include "media/audio/audio_parameters.h" |
| 11 #include "media/base/limits.h" | 11 #include "media/base/limits.h" |
| 12 | 12 |
| 13 namespace media { | 13 namespace media { |
| 14 | 14 |
| 15 // Ensure each channel is 16-byte aligned for easy SSE optimizations. | 15 // Ensure each channel is 16-byte aligned for easy SSE optimizations. |
| 16 static const int kChannelAlignment = 16; | 16 static const int kChannelAlignment = 16; |
| 17 | 17 |
| 18 static bool IsAligned(void* ptr) { | 18 static bool IsAligned(void* ptr) { |
| 19 return (reinterpret_cast<uintptr_t>(ptr) & (kChannelAlignment - 1)) == 0U; | 19 return (reinterpret_cast<uintptr_t>(ptr) & (kChannelAlignment - 1)) == 0U; |
| 20 } | 20 } |
| 21 | 21 |
| 22 AudioBus::AudioBus(int channels, int frames) | 22 // Calculates the expected data_size() for an AudioBus with the given params, |
| 23 : frames_(frames) { | 23 // sets |aligned_frames| to the actual frame length of each channel array. |
| 24 CHECK_GT(frames, 0); | 24 static int ExpectedDataSizeInternal(int channels, int frames, |
| 25 CHECK_LE(frames, limits::kMaxSamplesPerPacket); | 25 int* aligned_frames) { |
|
Chris Rogers
2012/08/15 19:39:31
NULL-check aligned_frames
DaleCurtis
2012/08/16 01:54:14
Done.
| |
| 26 CHECK_GT(channels, 0); | 26 // Choose a size such that each channel is aligned by kChannelAlignment. |
| 27 CHECK_LE(channels, limits::kMaxChannels); | 27 *aligned_frames = |
| 28 DCHECK_LT(limits::kMaxSamplesPerPacket * limits::kMaxChannels, | 28 (frames + kChannelAlignment - 1) & ~(kChannelAlignment - 1); |
| 29 std::numeric_limits<int>::max()); | 29 // Include an extra kChannelAlignment bytes in case WrapBlock() needs to |
| 30 // manually align the start of the first channel. | |
| 31 return sizeof(float) * channels * *aligned_frames + kChannelAlignment; | |
| 32 } | |
| 30 | 33 |
| 31 // Choose a size such that each channel is aligned by kChannelAlignment. | 34 AudioBus::AudioBus(int channels, int frames, int sample_rate) |
| 32 int aligned_frames = | 35 : frames_(frames), |
| 33 (frames_ + kChannelAlignment - 1) & ~(kChannelAlignment - 1); | 36 sample_rate_(sample_rate) { |
| 34 data_size_ = sizeof(float) * channels * aligned_frames; | 37 ValidateConfig(channels, frames_, sample_rate_); |
| 38 | |
| 39 int aligned_frames = 0; | |
| 40 data_size_ = ExpectedDataSizeInternal(channels, frames, &aligned_frames); | |
| 35 | 41 |
| 36 data_.reset(static_cast<float*>(base::AlignedAlloc( | 42 data_.reset(static_cast<float*>(base::AlignedAlloc( |
| 37 data_size_, kChannelAlignment))); | 43 data_size_, kChannelAlignment))); |
|
Chris Rogers
2012/08/15 19:39:31
Are we now allocating more than we need to in orde
DaleCurtis
2012/08/15 20:12:54
We're allocating more than we need since we don't
| |
| 38 | 44 |
| 39 // Separate audio data out into channels for easy lookup later. | 45 BuildChannelData(channels, aligned_frames, data_.get()); |
| 40 channel_data_.reserve(channels); | 46 } |
| 41 for (int i = 0; i < channels; ++i) | 47 |
| 42 channel_data_.push_back(data_.get() + i * aligned_frames); | 48 AudioBus::AudioBus(int channels, int frames, int sample_rate, float* data) |
| 49 : data_size_(0), | |
| 50 frames_(frames), | |
| 51 sample_rate_(sample_rate) { | |
| 52 ValidateConfig(channels, frames_, sample_rate_); | |
| 53 | |
| 54 // Manually align the input pointer if it's not already aligned. We pad the | |
| 55 // amount returned by ExpectedDataSize() just for this case. | |
| 56 uintptr_t aligned_ptr = reinterpret_cast<uintptr_t>(data); | |
| 57 if (!IsAligned(data)) | |
| 58 aligned_ptr += kChannelAlignment - (aligned_ptr & (kChannelAlignment - 1)); | |
| 59 | |
| 60 // Don't set |data_size_| based on expected data size, since we do not own the | |
| 61 // block of memory provided for channel data. | |
| 62 int aligned_frames = 0; | |
| 63 ExpectedDataSizeInternal(channels, frames, &aligned_frames); | |
| 64 | |
| 65 BuildChannelData( | |
| 66 channels, aligned_frames, reinterpret_cast<float*>(aligned_ptr)); | |
| 43 } | 67 } |
| 44 | 68 |
| 45 AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data) | 69 AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data) |
| 46 : data_size_(0), | 70 : data_size_(0), |
| 47 channel_data_(channel_data), | 71 channel_data_(channel_data), |
| 48 frames_(frames) { | 72 frames_(frames), |
| 73 sample_rate_(0) { | |
| 74 ValidateConfig(channel_data_.size(), frames_, sample_rate_); | |
| 75 | |
| 49 // Sanity check wrapped vector for alignment and channel count. | 76 // Sanity check wrapped vector for alignment and channel count. |
| 50 for (size_t i = 0; i < channel_data_.size(); ++i) | 77 for (size_t i = 0; i < channel_data_.size(); ++i) |
| 51 DCHECK(IsAligned(channel_data_[i])); | 78 DCHECK(IsAligned(channel_data_[i])); |
| 52 } | 79 } |
| 53 | 80 |
| 54 AudioBus::~AudioBus() {} | 81 AudioBus::~AudioBus() {} |
| 55 | 82 |
| 56 scoped_ptr<AudioBus> AudioBus::Create(int channels, int frames) { | 83 scoped_ptr<AudioBus> AudioBus::Create(int channels, int frames) { |
| 57 return scoped_ptr<AudioBus>(new AudioBus(channels, frames)); | 84 return scoped_ptr<AudioBus>(new AudioBus(channels, frames, 0)); |
| 58 } | 85 } |
| 59 | 86 |
| 60 scoped_ptr<AudioBus> AudioBus::Create(const AudioParameters& params) { | 87 scoped_ptr<AudioBus> AudioBus::Create(const AudioParameters& params) { |
| 61 return scoped_ptr<AudioBus>(new AudioBus( | 88 return scoped_ptr<AudioBus>(new AudioBus( |
| 62 params.channels(), params.frames_per_buffer())); | 89 params.channels(), params.frames_per_buffer(), params.sample_rate())); |
| 63 } | 90 } |
| 64 | 91 |
| 65 scoped_ptr<AudioBus> AudioBus::WrapVector( | 92 scoped_ptr<AudioBus> AudioBus::WrapVector( |
| 66 int frames, const std::vector<float*>& channel_data) { | 93 int frames, const std::vector<float*>& channel_data) { |
| 67 return scoped_ptr<AudioBus>(new AudioBus(frames, channel_data)); | 94 return scoped_ptr<AudioBus>(new AudioBus(frames, channel_data)); |
| 68 } | 95 } |
| 69 | 96 |
| 97 scoped_ptr<AudioBus> AudioBus::WrapBlock(int channels, int frames, void* data) { | |
| 98 return scoped_ptr<AudioBus>(new AudioBus( | |
| 99 channels, frames, 0, static_cast<float*>(data))); | |
| 100 } | |
| 101 | |
| 102 scoped_ptr<AudioBus> AudioBus::WrapBlock(const AudioParameters& params, | |
| 103 void* data) { | |
| 104 return scoped_ptr<AudioBus>(new AudioBus( | |
| 105 params.channels(), params.frames_per_buffer(), params.sample_rate(), | |
| 106 static_cast<float*>(data))); | |
| 107 } | |
| 108 | |
| 70 void* AudioBus::data() { | 109 void* AudioBus::data() { |
| 71 DCHECK(data_.get()); | 110 DCHECK(data_.get()); |
| 72 return data_.get(); | 111 return data_.get(); |
| 73 } | 112 } |
| 74 | 113 |
| 75 int AudioBus::data_size() const { | 114 int AudioBus::data_size() const { |
| 76 DCHECK(data_.get()); | 115 DCHECK(data_.get()); |
| 77 return data_size_; | 116 return data_size_; |
| 78 } | 117 } |
| 79 | 118 |
| 80 void AudioBus::ZeroFrames(int frames) { | 119 void AudioBus::ZeroFrames(int frames) { |
| 81 DCHECK_LE(frames, frames_); | 120 DCHECK_LE(frames, frames_); |
| 82 for (size_t i = 0; i < channel_data_.size(); ++i) | 121 for (size_t i = 0; i < channel_data_.size(); ++i) |
| 83 memset(channel_data_[i], 0, frames * sizeof(*channel_data_[i])); | 122 memset(channel_data_[i], 0, frames * sizeof(*channel_data_[i])); |
| 84 } | 123 } |
| 85 | 124 |
| 86 void AudioBus::Zero() { | 125 void AudioBus::Zero() { |
| 87 ZeroFrames(frames_); | 126 ZeroFrames(frames_); |
| 88 } | 127 } |
| 89 | 128 |
| 129 int AudioBus::sample_rate() const { | |
| 130 DCHECK_NE(sample_rate_, 0); | |
| 131 return sample_rate_; | |
| 132 } | |
| 133 | |
| 134 int AudioBus::ExpectedDataSize(int channels, int frames) { | |
| 135 int aligned_frames = 0; | |
| 136 return ExpectedDataSizeInternal(channels, frames, &aligned_frames); | |
| 137 } | |
| 138 | |
| 139 int AudioBus::ExpectedDataSize(const AudioParameters& params) { | |
| 140 int aligned_frames = 0; | |
| 141 return ExpectedDataSizeInternal( | |
| 142 params.channels(), params.frames_per_buffer(), &aligned_frames); | |
| 143 } | |
| 144 | |
| 145 void AudioBus::BuildChannelData(int channels, int aligned_frames, float* data) { | |
| 146 DCHECK(IsAligned(data)); | |
| 147 DCHECK_EQ(channel_data_.size(), 0U); | |
| 148 // Separate audio data out into channels for easy lookup later. Figure out | |
| 149 channel_data_.reserve(channels); | |
| 150 for (int i = 0; i < channels; ++i) | |
| 151 channel_data_.push_back(data + i * aligned_frames); | |
| 152 } | |
| 153 | |
| 154 void AudioBus::ValidateConfig(int channels, int frames, int sample_rate) { | |
| 155 CHECK_GT(frames, 0); | |
| 156 CHECK_LE(frames, limits::kMaxSamplesPerPacket); | |
| 157 CHECK_GT(channels, 0); | |
| 158 CHECK_LE(channels, limits::kMaxChannels); | |
| 159 DCHECK_LT(limits::kMaxSamplesPerPacket * limits::kMaxChannels, | |
| 160 std::numeric_limits<int>::max()); | |
| 161 if (sample_rate != 0) | |
| 162 DCHECK_GT(sample_rate, limits::kMinSampleRate); | |
| 163 DCHECK_LT(sample_rate, limits::kMaxSampleRate); | |
| 164 } | |
| 165 | |
| 166 // TODO(dalecurtis): See if intrinsic optimizations help any here. | |
| 167 void AudioBus::FromInterleaved(const void* source, int frames, | |
|
henrika (OOO until Aug 14)
2012/08/15 09:59:37
Perhaps mention that these methods are related to
DaleCurtis
2012/08/16 01:54:14
Ideally those methods will go away and these will
| |
| 168 int bytes_per_sample) { | |
| 169 DCHECK_LE(frames, frames_); | |
| 170 | |
| 171 switch (bytes_per_sample) { | |
| 172 case 1: { | |
| 173 static const float kMinScale = 1.0f / kint8min; | |
| 174 static const float kMaxScale = 1.0f / kint8max; | |
| 175 const uint8* source8 = reinterpret_cast<const uint8*>(source); | |
| 176 for (int i = 0; i < frames; ++i) { | |
| 177 for (int ch = 0; ch < channels(); ++ch) { | |
| 178 int v = static_cast<int>(*source8++) - (-kint8min); | |
| 179 channel(ch)[i] = v * (v < 0 ? -kMinScale : kMaxScale); | |
|
Chris Rogers
2012/08/15 19:39:31
It seems like it would be more efficient to revers
DaleCurtis
2012/08/15 20:12:54
How about iterating over i=0..frames * channels an
DaleCurtis
2012/08/16 01:54:14
I did some basic benchmarking and the new template
| |
| 180 } | |
| 181 } | |
| 182 break; | |
| 183 } | |
| 184 | |
| 185 case 2: { | |
| 186 static const float kMinScale = 1.0f / kint16min; | |
| 187 static const float kMaxScale = 1.0f / kint16max; | |
| 188 const int16* source16 = reinterpret_cast<const int16*>(source); | |
| 189 for (int i = 0; i < frames; ++i) { | |
| 190 for (int ch = 0; ch < channels(); ++ch) { | |
| 191 float v = *source16++; | |
|
henrika (OOO until Aug 14)
2012/08/15 09:59:37
Is it not more clear to use static_cast<> here?
DaleCurtis
2012/08/16 01:54:14
I think that would be over-verbose.
| |
| 192 channel(ch)[i] = v * (v < 0 ? -kMinScale : kMaxScale); | |
| 193 } | |
| 194 } | |
| 195 break; | |
| 196 } | |
| 197 | |
| 198 case 4: { | |
| 199 static const float kMinScale = 1.0f / kint32min; | |
| 200 static const float kMaxScale = 1.0f / kint32max; | |
| 201 const int32* source32 = reinterpret_cast<const int32*>(source); | |
| 202 for (int i = 0; i < frames; ++i) { | |
| 203 for (int ch = 0; ch < channels(); ++ch) { | |
| 204 float v = *source32++; | |
| 205 channel(ch)[i] = v * (v < 0 ? -kMinScale : kMaxScale); | |
| 206 } | |
| 207 } | |
| 208 break; | |
| 209 } | |
| 210 | |
| 211 default: | |
| 212 CHECK(false) << "Unsupported bytes per sample encountered."; | |
| 213 } | |
| 214 | |
| 215 // Zero any remaining frames. | |
| 216 int remaining_frames = (frames_ - frames); | |
| 217 if (remaining_frames) { | |
| 218 for (int ch = 0; ch < channels(); ++ch) | |
| 219 memset(channel(ch) + frames, 0, sizeof(*channel(ch)) * remaining_frames); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 // |Format| is the destination type, |Fixed| is a type larger than |Format| | |
| 224 // such that operations can be made without overflowing. | |
| 225 template<class Format, class Fixed> | |
| 226 static void ToInterleavedInternal(const AudioBus* source, void* dest_bytes, | |
| 227 int frames) { | |
| 228 Format* destination = reinterpret_cast<Format*>(dest_bytes); | |
| 229 Fixed max_value = std::numeric_limits<Format>::max(); | |
| 230 Fixed min_value = std::numeric_limits<Format>::min(); | |
| 231 | |
| 232 Format bias = 0; | |
| 233 if (!std::numeric_limits<Format>::is_signed) { | |
| 234 bias = (max_value / 2) + 1; | |
| 235 max_value = bias - 1; | |
| 236 min_value = -bias; | |
| 237 } | |
| 238 | |
| 239 int channels = source->channels(); | |
| 240 for (int i = 0; i < channels; ++i) { | |
| 241 const float* channel_data = source->channel(i); | |
| 242 for (int j = 0; j < frames; ++j) { | |
| 243 Fixed sample = | |
| 244 (channel_data[j] < 0 ? -min_value : max_value) * channel_data[j]; | |
| 245 if (sample > max_value) | |
| 246 sample = max_value; | |
| 247 else if (sample < min_value) | |
| 248 sample = min_value; | |
| 249 | |
| 250 destination[j * channels + i] = static_cast<Format>(sample) + bias; | |
| 251 } | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 // TODO(dalecurtis): See if intrinsic optimizations help any here. | |
| 256 void AudioBus::ToInterleaved(void* dest, int frames, int bytes_per_sample) { | |
| 257 DCHECK_LE(frames, frames_); | |
| 258 switch (bytes_per_sample) { | |
| 259 case 1: | |
| 260 ToInterleavedInternal<uint8, int32>(this, dest, frames); | |
| 261 break; | |
| 262 case 2: | |
| 263 ToInterleavedInternal<int16, int32>(this, dest, frames); | |
| 264 break; | |
| 265 case 4: | |
| 266 ToInterleavedInternal<int32, int64>(this, dest, frames); | |
| 267 break; | |
| 268 default: | |
| 269 CHECK(false) << "Unsupported bytes per sample encountered."; | |
| 270 break; | |
| 271 } | |
| 272 } | |
| 273 | |
| 90 } // namespace media | 274 } // namespace media |
| OLD | NEW |