Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(889)

Unified Diff: media/base/audio_sample_conversion.h

Issue 1854433002: Clamp AudioBuffer float to int{16,32} conversion (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix handling of 32-bit, fix asymmetric scale Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/base/audio_bus_unittest.cc ('k') | media/media.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/base/audio_sample_conversion.h
diff --git a/media/base/audio_sample_conversion.h b/media/base/audio_sample_conversion.h
new file mode 100644
index 0000000000000000000000000000000000000000..9776be399f71121e3df8979d5a5779387daf3870
--- /dev/null
+++ b/media/base/audio_sample_conversion.h
@@ -0,0 +1,212 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_
+#define MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_
+
+#include <cstdint>
+
+namespace media {
+
+namespace internal {
+
+// The bias function specifies a bias required for conversion to or from offset
+// types. The only offset data type handled by AudioBus and AudioBuffer is
+// uint8_t so this function is specialized for uint8_t and otherwise returns 0.
+template <typename Integral>
+inline Integral bias() {
+ return 0;
+}
+template <>
+inline uint8_t bias() {
+ return 128;
+}
+
+// TODO(cleichner): Remove and use std::lrint(f) when C++11 library features
DaleCurtis 2016/04/01 00:24:01 This function is now available.
+// are approved for use in Chromium.
+template <typename Integral, typename Float>
+inline Integral doRound(Float f) {
+ const Float point_five = 0.5;
+ return f < 0 ? static_cast<Integral>(f - point_five)
+ : static_cast<Integral>(f + point_five);
+}
+
+// Special-case conversion from uint8_t to use an intermediate type that
+// won't overflow after removing the offset.
+template <typename Integral>
+struct Expanded {
+ typedef Integral type;
+};
+template <>
+struct Expanded<uint8_t> {
+ typedef int16_t type;
+};
+
+// After removing the offset, the extremal values for uint8_t should come from
+// int8_t.
+template <typename Integral>
+struct Fixed {
+ typedef Integral type;
+};
+template <>
+struct Fixed<uint8_t> {
+ typedef int8_t type;
+};
+
+// int32_t requires double precision for safe handling.
+template <bool NeedsDouble>
+struct SafeFloatHelper;
+
+template <>
+struct SafeFloatHelper<false> {
+ typedef float type;
+};
+
+template <>
+struct SafeFloatHelper<true> {
+ typedef double type;
DaleCurtis 2016/04/01 00:24:01 Why do we need a double? This will hurt performanc
+};
+
+template <typename Integral>
+struct SafeFloat : SafeFloatHelper<sizeof(Integral) >= sizeof(int32_t)> {};
DaleCurtis 2016/04/01 00:24:01 We don't support anything higher than int32_t.
+
+// The SampleConverter struct exists to partially specialize the ConvertSample()
+// functions. The functions can then capture general strategies for converting
+// between floating-point to integral types with the specifics such as extremal
+// values and offsets being derived from the types rather than passed in as
+// arguments.
+template <typename Source, typename Dest>
+struct SampleConverter {
+ static inline Dest Convert(Source sample);
+};
+
+// Any type can be converted to itself by returning the same values that are
+// supplied.
+template <typename S>
+struct SampleConverter<S, S> {
+ static inline S Convert(S sample) { return sample; }
+};
+
+// Integral to floating type conversion.
+template <typename Integral>
+struct SampleConverter<Integral, float> {
+ static inline float Convert(Integral sample) {
+ using Fixed = typename Fixed<Integral>::type;
+ using Expanded = typename Expanded<Integral>::type;
+ const Expanded internal = static_cast<Expanded>(sample) - bias<Integral>();
+ return internal * (internal < 0 ? -1.0f / std::numeric_limits<Fixed>::min()
+ : 1.0f / std::numeric_limits<Fixed>::max());
+ }
+};
+
+// Floating-point to integral type conversion.
+template <typename Integral>
+struct SampleConverter<float, Integral> {
+ static inline Integral Convert(float sample) {
+ using Fixed = typename Fixed<Integral>::type;
+ using SafeFloat = typename SafeFloat<Integral>::type;
+ Fixed min = std::numeric_limits<Fixed>::min();
+ Fixed max = std::numeric_limits<Fixed>::max();
+ // Floating point samples are not guaranteed to be in [-1.0, 1.0] so they
+ // need to be clamped as a part of conversion to an integral type.
+ sample = std::min(std::max(sample, -1.0f), 1.0f);
DaleCurtis 2016/04/01 00:24:01 Does the benchmark improve if you write: if (samp
+ SafeFloat scale =
+ sample < 0 ? -static_cast<SafeFloat>(min) : static_cast<SafeFloat>(max);
+ SafeFloat safe_sample = static_cast<SafeFloat>(sample) * scale;
+ return doRound<Integral>(safe_sample) + bias<Integral>();
+ }
+};
+
+// Integral -> Integral conversion can be done directly for signed integers
+// through multiplication and division.
+template <>
+struct SampleConverter<int32_t, int16_t> {
+ static inline int16_t Convert(int32_t sample) { return sample >> 16; }
+};
+
+template <>
+struct SampleConverter<int16_t, int32_t> {
+ static inline int32_t Convert(int16_t sample) {
+ return static_cast<int32_t>(sample) << 16;
+ }
+};
+
+} // namespace internal
+
+// ConvertSample converts a value interpreted as an audio sample from the
+// Source type to the Dest type assuming the normal conventions for audio
+// samples of a given type.
+template <typename Source, typename Dest>
+inline Dest ConvertSample(Source sample) {
+ return internal::SampleConverter<Source, Dest>::Convert(sample);
+}
+
+// InterleavedToPlanar takes a pointer to a contiguous block of memory with
+// interleaved samples of type Source, and starting |trim_start| frames into the
+// memory it copies the next |frames_to_copy| frames into each of the channels
+// in |channel_data| such that each channel is copied into the memory pointed to
+// by one entry in the |channel_data| vector. In addition to deinterleaving,
+// this function will convert the samples from the Source type to the Dest type.
+// The memory should already be allocated and there should be enough allocated
+// for each channel entry such that all of the frames in one channel can fit
+// contiguously after conversion to the Dest type.
+//
+// InterleavedToPlanar determines how many channels to deinterleave from the
+// source memory based on the dimension of the |channel_data| vector.
+//
+// The internal type allows this to accept vectors with many different
+// representations such as void* or char* as long as the memory pointed to by
+// the channel pointer can be reinterpreted as containing Source-typed elements.
+//
+// Source and Dest usually need to be explicitly specified, but Internal is
+// normally inferred.
+template <typename Source, typename Dest, typename Internal>
+void InterleavedToPlanar(const Source* source,
+ size_t frames_to_copy,
+ size_t trim_start,
+ const std::vector<Internal*>& channel_data) {
+ const size_t channels = channel_data.size();
+ for (size_t ch = 0; ch < channels; ++ch) {
+ Dest* single_channel_data = reinterpret_cast<Dest*>(channel_data[ch]);
+ for (size_t i = trim_start, offset = ch; i < trim_start + frames_to_copy;
+ ++i, offset += channels) {
+ single_channel_data[i] = ConvertSample<Source, Dest>(source[offset]);
+ }
+ }
+}
+
+// PlanarToInterleaved takes a vector of pointers to contiguous memory blocks
+// containing samples of type Source and interleaves them into the contiguous
+// memory pointed to by the dest_data pointer and converts them to type Dest if
+// Source and Dest differ. It trims |trim_start| samples from the beginning of
+// each sample and copies |frames_to_copy| samples from each of the channels
+// specified in |channel_data| into frames in the |dest_data| memory.
+//
+// PlanarToInterleaved determines how many channels to interleave into the
+// dest_data memory based on the dimension of the |channel_data| vector.
+//
+// The internal type allows this to accept vectors with many different
+// representations such as void* or char* as long as the memory pointed to by
+// the channel pointer can be reinterpreted as containing Source-typed elements.
+//
+// Source and Dest usually need to be explicitly specified, but Internal is
+// normally inferred.
+template <typename Source, typename Dest, typename Internal>
+void PlanarToInterleaved(const std::vector<Internal*>& channel_data,
+ size_t frames_to_copy,
+ size_t trim_start,
+ Dest* dest_data) {
+ for (size_t ch = 0; ch < channel_data.size(); ++ch) {
+ const Source* source_data =
+ reinterpret_cast<const Source*>(channel_data[ch]) + trim_start;
+ for (size_t i = 0, offset = ch; i < frames_to_copy;
+ ++i, offset += channel_data.size()) {
+ dest_data[offset] = ConvertSample<Source, Dest>(source_data[i]);
+ }
+ }
+}
+
+} // namespace media
+
+#endif // MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_
« no previous file with comments | « media/base/audio_bus_unittest.cc ('k') | media/media.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698