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_ |