summaryrefslogtreecommitdiffstats
path: root/media/base/audio_bus.cc
diff options
context:
space:
mode:
authordalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-21 02:49:49 +0000
committerdalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-21 02:49:49 +0000
commit23ff5a176c299bd3b498efbee249891eb58f3b74 (patch)
tree0d0d6f2e33436575a996e6ac37df820bf3f81b98 /media/base/audio_bus.cc
parente8c4612dd7454464fac243645d1f6d01ca9596ff (diff)
downloadchromium_src-23ff5a176c299bd3b498efbee249891eb58f3b74.zip
chromium_src-23ff5a176c299bd3b498efbee249891eb58f3b74.tar.gz
chromium_src-23ff5a176c299bd3b498efbee249891eb58f3b74.tar.bz2
Upgrade AudioBus to support wrapping, interleaving.
Introduces AudioBus::WrapMemory(...) for constructing an AudioBus object via an existing block of memory. Introduces AudioBus::CalculateMemorySize(...) for sizing the shared memory without having to create an empty AudioBus. Introduces AudioBus::FromInterleaved(...) and AudioBus:: ToInterleaved() for converting between formats. Fixes off by one errors in our existing audio_util implmentations. Removes InterleaveFloatToInt from audio_util. Removes data(), data_size() methods in favor of CopyTo() helper method combined with AudioBus::WrapMemory(). And of course adds new tests for all of the above. This CL is an extraction of all AudioBus functionality required to land the WIP CL which converts browser side to use AudioBus: http://codereview.chromium.org/10832285/ BUG=114700, 120319 TEST=unit tests under ASAN. Review URL: https://chromiumcodereview.appspot.com/10824304 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152494 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base/audio_bus.cc')
-rw-r--r--media/base/audio_bus.cc203
1 files changed, 180 insertions, 23 deletions
diff --git a/media/base/audio_bus.cc b/media/base/audio_bus.cc
index 4c43af7..a7d9ad0 100644
--- a/media/base/audio_bus.cc
+++ b/media/base/audio_bus.cc
@@ -12,40 +12,116 @@
namespace media {
-// Ensure each channel is 16-byte aligned for easy SSE optimizations.
-static const int kChannelAlignment = 16;
-
static bool IsAligned(void* ptr) {
- return (reinterpret_cast<uintptr_t>(ptr) & (kChannelAlignment - 1)) == 0U;
+ return (reinterpret_cast<uintptr_t>(ptr) &
+ (AudioBus::kChannelAlignment - 1)) == 0U;
}
-AudioBus::AudioBus(int channels, int frames)
- : frames_(frames) {
+// Calculates the required size for an AudioBus with the given params, sets
+// |aligned_frames| to the actual frame length of each channel array.
+static int CalculateMemorySizeInternal(int channels, int frames,
+ int* aligned_frames) {
+ CHECK(aligned_frames);
+ // Choose a size such that each channel will be aligned by kChannelAlignment
+ // when stored in a contiguous block.
+ *aligned_frames =
+ ((frames * sizeof(float) + AudioBus::kChannelAlignment - 1) &
+ ~(AudioBus::kChannelAlignment - 1)) / sizeof(float);
+ return sizeof(float) * channels * (*aligned_frames);
+}
+
+// |Format| is the destination type, |Fixed| is a type larger than |Format|
+// such that operations can be made without overflowing.
+template<class Format, class Fixed>
+static void FromInterleavedInternal(const void* src, int frames,
+ AudioBus* dest) {
+ const Format* source = static_cast<const Format*>(src);
+
+ static const Fixed kBias = std::numeric_limits<Format>::is_signed ? 0 :
+ std::numeric_limits<Format>::max() / 2 + 1;
+ static const float kMaxScale = 1.0f / (kBias ? kBias - 1 :
+ std::numeric_limits<Format>::max());
+ static const float kMinScale = 1.0f / (kBias ? kBias :
+ -static_cast<Fixed>(std::numeric_limits<Format>::min()));
+
+ int channels = dest->channels();
+ for (int ch = 0; ch < channels; ++ch) {
+ float* channel_data = dest->channel(ch);
+ for (int i = 0, offset = ch; i < frames; ++i, offset += channels) {
+ Fixed v = static_cast<Fixed>(source[offset]) - kBias;
+ channel_data[i] = v * (v < 0 ? kMinScale : kMaxScale);
+ }
+ }
+}
+
+// |Format| is the destination type, |Fixed| is a type larger than |Format|
+// such that operations can be made without overflowing.
+template<class Format, class Fixed>
+static void ToInterleavedInternal(const AudioBus* source, int frames,
+ void* dst) {
+ Format* dest = static_cast<Format*>(dst);
+
+ static const Format kBias = std::numeric_limits<Format>::is_signed ? 0 :
+ std::numeric_limits<Format>::max() / 2 + 1;
+ static const Fixed kMaxValue = kBias ? kBias - 1 :
+ std::numeric_limits<Format>::max();
+ static const Fixed kMinValue = kBias ? -kBias :
+ std::numeric_limits<Format>::min();
+
+ int channels = source->channels();
+ for (int ch = 0; ch < channels; ++ch) {
+ const float* channel_data = source->channel(ch);
+ for (int i = 0, offset = ch; i < frames; ++i, offset += channels) {
+ float v = channel_data[i];
+ Fixed sample = v * (v < 0 ? -kMinValue : kMaxValue);
+
+ if (sample > kMaxValue)
+ sample = kMaxValue;
+ else if (sample < kMinValue)
+ sample = kMinValue;
+
+ dest[offset] = static_cast<Format>(sample) + kBias;
+ }
+ }
+}
+
+static void ValidateConfig(int channels, int frames) {
CHECK_GT(frames, 0);
CHECK_LE(frames, limits::kMaxSamplesPerPacket);
CHECK_GT(channels, 0);
CHECK_LE(channels, limits::kMaxChannels);
DCHECK_LT(limits::kMaxSamplesPerPacket * limits::kMaxChannels,
std::numeric_limits<int>::max());
+}
- // Choose a size such that each channel is aligned by kChannelAlignment.
- int aligned_frames =
- (frames_ + kChannelAlignment - 1) & ~(kChannelAlignment - 1);
- data_size_ = sizeof(float) * channels * aligned_frames;
+AudioBus::AudioBus(int channels, int frames)
+ : frames_(frames) {
+ ValidateConfig(channels, frames_);
+
+ int aligned_frames = 0;
+ int size = CalculateMemorySizeInternal(channels, frames, &aligned_frames);
data_.reset(static_cast<float*>(base::AlignedAlloc(
- data_size_, kChannelAlignment)));
+ size, AudioBus::kChannelAlignment)));
- // Separate audio data out into channels for easy lookup later.
- channel_data_.reserve(channels);
- for (int i = 0; i < channels; ++i)
- channel_data_.push_back(data_.get() + i * aligned_frames);
+ BuildChannelData(channels, aligned_frames, data_.get());
+}
+
+AudioBus::AudioBus(int channels, int frames, float* data)
+ : frames_(frames) {
+ ValidateConfig(channels, frames_);
+
+ int aligned_frames = 0;
+ CalculateMemorySizeInternal(channels, frames, &aligned_frames);
+
+ BuildChannelData(channels, aligned_frames, data);
}
AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data)
- : data_size_(0),
- channel_data_(channel_data),
+ : channel_data_(channel_data),
frames_(frames) {
+ ValidateConfig(channel_data_.size(), frames_);
+
// Sanity check wrapped vector for alignment and channel count.
for (size_t i = 0; i < channel_data_.size(); ++i)
DCHECK(IsAligned(channel_data_[i]));
@@ -67,14 +143,21 @@ scoped_ptr<AudioBus> AudioBus::WrapVector(
return scoped_ptr<AudioBus>(new AudioBus(frames, channel_data));
}
-void* AudioBus::data() {
- DCHECK(data_.get());
- return data_.get();
+scoped_ptr<AudioBus> AudioBus::WrapMemory(int channels, int frames,
+ void* data) {
+ // |data| must be aligned by AudioBus::kChannelAlignment.
+ CHECK(IsAligned(data));
+ return scoped_ptr<AudioBus>(new AudioBus(
+ channels, frames, static_cast<float*>(data)));
}
-int AudioBus::data_size() const {
- DCHECK(data_.get());
- return data_size_;
+scoped_ptr<AudioBus> AudioBus::WrapMemory(const AudioParameters& params,
+ void* data) {
+ // |data| must be aligned by AudioBus::kChannelAlignment.
+ CHECK(IsAligned(data));
+ return scoped_ptr<AudioBus>(new AudioBus(
+ params.channels(), params.frames_per_buffer(),
+ static_cast<float*>(data)));
}
void AudioBus::ZeroFrames(int frames) {
@@ -87,4 +170,78 @@ void AudioBus::Zero() {
ZeroFrames(frames_);
}
+int AudioBus::CalculateMemorySize(const AudioParameters& params) {
+ int aligned_frames = 0;
+ return CalculateMemorySizeInternal(
+ params.channels(), params.frames_per_buffer(), &aligned_frames);
+}
+
+void AudioBus::BuildChannelData(int channels, int aligned_frames, float* data) {
+ DCHECK(IsAligned(data));
+ DCHECK_EQ(channel_data_.size(), 0U);
+ // Separate audio data out into channels for easy lookup later. Figure out
+ channel_data_.reserve(channels);
+ for (int i = 0; i < channels; ++i)
+ channel_data_.push_back(data + i * aligned_frames);
+}
+
+// TODO(dalecurtis): See if intrinsic optimizations help any here.
+void AudioBus::FromInterleaved(const void* source, int frames,
+ int bytes_per_sample) {
+ DCHECK_LE(frames, frames_);
+ switch (bytes_per_sample) {
+ case 1:
+ FromInterleavedInternal<uint8, int16>(source, frames, this);
+ break;
+ case 2:
+ FromInterleavedInternal<int16, int32>(source, frames, this);
+ break;
+ case 4:
+ FromInterleavedInternal<int32, int64>(source, frames, this);
+ break;
+ default:
+ NOTREACHED() << "Unsupported bytes per sample encountered.";
+ Zero();
+ return;
+ }
+
+ // Zero any remaining frames.
+ int remaining_frames = (frames_ - frames);
+ if (remaining_frames) {
+ for (int ch = 0; ch < channels(); ++ch)
+ memset(channel(ch) + frames, 0, sizeof(*channel(ch)) * remaining_frames);
+ }
+}
+
+// TODO(dalecurtis): See if intrinsic optimizations help any here.
+void AudioBus::ToInterleaved(int frames, int bytes_per_sample,
+ void* dest) const {
+ DCHECK_LE(frames, frames_);
+ switch (bytes_per_sample) {
+ case 1:
+ ToInterleavedInternal<uint8, int16>(this, frames, dest);
+ break;
+ case 2:
+ ToInterleavedInternal<int16, int32>(this, frames, dest);
+ break;
+ case 4:
+ ToInterleavedInternal<int32, int64>(this, frames, dest);
+ break;
+ default:
+ NOTREACHED() << "Unsupported bytes per sample encountered.";
+ memset(dest, 0, frames * bytes_per_sample);
+ return;
+ }
+}
+
+void AudioBus::CopyTo(AudioBus* dest) const {
+ DCHECK_EQ(channels(), dest->channels());
+ DCHECK_EQ(frames(), dest->frames());
+
+ // Since we don't know if the other AudioBus is wrapped or not (and we don't
+ // want to care), just copy using the public channel() accessors.
+ for (int i = 0; i < channels(); ++i)
+ memcpy(dest->channel(i), channel(i), sizeof(*channel(i)) * frames());
+}
+
} // namespace media