Chromium Code Reviews| 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_ |