Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_ | |
| 6 #define MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_ | |
| 7 | |
| 8 #include <cstdint> | |
| 9 | |
| 10 namespace media { | |
| 11 | |
| 12 namespace internal { | |
| 13 | |
| 14 // The bias function specifies a bias required for conversion to or from offset | |
| 15 // types. The only offset data type handled by AudioBus and AudioBuffer is | |
| 16 // uint8_t so this function is specialized for uint8_t and otherwise returns 0. | |
| 17 template <typename Integral> | |
| 18 inline Integral bias() { | |
| 19 return 0; | |
| 20 } | |
| 21 template <> | |
| 22 inline uint8_t bias() { | |
| 23 return 128; | |
| 24 } | |
| 25 | |
| 26 // 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.
| |
| 27 // are approved for use in Chromium. | |
| 28 template <typename Integral, typename Float> | |
| 29 inline Integral doRound(Float f) { | |
| 30 const Float point_five = 0.5; | |
| 31 return f < 0 ? static_cast<Integral>(f - point_five) | |
| 32 : static_cast<Integral>(f + point_five); | |
| 33 } | |
| 34 | |
| 35 // Special-case conversion from uint8_t to use an intermediate type that | |
| 36 // won't overflow after removing the offset. | |
| 37 template <typename Integral> | |
| 38 struct Expanded { | |
| 39 typedef Integral type; | |
| 40 }; | |
| 41 template <> | |
| 42 struct Expanded<uint8_t> { | |
| 43 typedef int16_t type; | |
| 44 }; | |
| 45 | |
| 46 // After removing the offset, the extremal values for uint8_t should come from | |
| 47 // int8_t. | |
| 48 template <typename Integral> | |
| 49 struct Fixed { | |
| 50 typedef Integral type; | |
| 51 }; | |
| 52 template <> | |
| 53 struct Fixed<uint8_t> { | |
| 54 typedef int8_t type; | |
| 55 }; | |
| 56 | |
| 57 // int32_t requires double precision for safe handling. | |
| 58 template <bool NeedsDouble> | |
| 59 struct SafeFloatHelper; | |
| 60 | |
| 61 template <> | |
| 62 struct SafeFloatHelper<false> { | |
| 63 typedef float type; | |
| 64 }; | |
| 65 | |
| 66 template <> | |
| 67 struct SafeFloatHelper<true> { | |
| 68 typedef double type; | |
|
DaleCurtis
2016/04/01 00:24:01
Why do we need a double? This will hurt performanc
| |
| 69 }; | |
| 70 | |
| 71 template <typename Integral> | |
| 72 struct SafeFloat : SafeFloatHelper<sizeof(Integral) >= sizeof(int32_t)> {}; | |
|
DaleCurtis
2016/04/01 00:24:01
We don't support anything higher than int32_t.
| |
| 73 | |
| 74 // The SampleConverter struct exists to partially specialize the ConvertSample() | |
| 75 // functions. The functions can then capture general strategies for converting | |
| 76 // between floating-point to integral types with the specifics such as extremal | |
| 77 // values and offsets being derived from the types rather than passed in as | |
| 78 // arguments. | |
| 79 template <typename Source, typename Dest> | |
| 80 struct SampleConverter { | |
| 81 static inline Dest Convert(Source sample); | |
| 82 }; | |
| 83 | |
| 84 // Any type can be converted to itself by returning the same values that are | |
| 85 // supplied. | |
| 86 template <typename S> | |
| 87 struct SampleConverter<S, S> { | |
| 88 static inline S Convert(S sample) { return sample; } | |
| 89 }; | |
| 90 | |
| 91 // Integral to floating type conversion. | |
| 92 template <typename Integral> | |
| 93 struct SampleConverter<Integral, float> { | |
| 94 static inline float Convert(Integral sample) { | |
| 95 using Fixed = typename Fixed<Integral>::type; | |
| 96 using Expanded = typename Expanded<Integral>::type; | |
| 97 const Expanded internal = static_cast<Expanded>(sample) - bias<Integral>(); | |
| 98 return internal * (internal < 0 ? -1.0f / std::numeric_limits<Fixed>::min() | |
| 99 : 1.0f / std::numeric_limits<Fixed>::max()); | |
| 100 } | |
| 101 }; | |
| 102 | |
| 103 // Floating-point to integral type conversion. | |
| 104 template <typename Integral> | |
| 105 struct SampleConverter<float, Integral> { | |
| 106 static inline Integral Convert(float sample) { | |
| 107 using Fixed = typename Fixed<Integral>::type; | |
| 108 using SafeFloat = typename SafeFloat<Integral>::type; | |
| 109 Fixed min = std::numeric_limits<Fixed>::min(); | |
| 110 Fixed max = std::numeric_limits<Fixed>::max(); | |
| 111 // Floating point samples are not guaranteed to be in [-1.0, 1.0] so they | |
| 112 // need to be clamped as a part of conversion to an integral type. | |
| 113 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
| |
| 114 SafeFloat scale = | |
| 115 sample < 0 ? -static_cast<SafeFloat>(min) : static_cast<SafeFloat>(max); | |
| 116 SafeFloat safe_sample = static_cast<SafeFloat>(sample) * scale; | |
| 117 return doRound<Integral>(safe_sample) + bias<Integral>(); | |
| 118 } | |
| 119 }; | |
| 120 | |
| 121 // Integral -> Integral conversion can be done directly for signed integers | |
| 122 // through multiplication and division. | |
| 123 template <> | |
| 124 struct SampleConverter<int32_t, int16_t> { | |
| 125 static inline int16_t Convert(int32_t sample) { return sample >> 16; } | |
| 126 }; | |
| 127 | |
| 128 template <> | |
| 129 struct SampleConverter<int16_t, int32_t> { | |
| 130 static inline int32_t Convert(int16_t sample) { | |
| 131 return static_cast<int32_t>(sample) << 16; | |
| 132 } | |
| 133 }; | |
| 134 | |
| 135 } // namespace internal | |
| 136 | |
| 137 // ConvertSample converts a value interpreted as an audio sample from the | |
| 138 // Source type to the Dest type assuming the normal conventions for audio | |
| 139 // samples of a given type. | |
| 140 template <typename Source, typename Dest> | |
| 141 inline Dest ConvertSample(Source sample) { | |
| 142 return internal::SampleConverter<Source, Dest>::Convert(sample); | |
| 143 } | |
| 144 | |
| 145 // InterleavedToPlanar takes a pointer to a contiguous block of memory with | |
| 146 // interleaved samples of type Source, and starting |trim_start| frames into the | |
| 147 // memory it copies the next |frames_to_copy| frames into each of the channels | |
| 148 // in |channel_data| such that each channel is copied into the memory pointed to | |
| 149 // by one entry in the |channel_data| vector. In addition to deinterleaving, | |
| 150 // this function will convert the samples from the Source type to the Dest type. | |
| 151 // The memory should already be allocated and there should be enough allocated | |
| 152 // for each channel entry such that all of the frames in one channel can fit | |
| 153 // contiguously after conversion to the Dest type. | |
| 154 // | |
| 155 // InterleavedToPlanar determines how many channels to deinterleave from the | |
| 156 // source memory based on the dimension of the |channel_data| vector. | |
| 157 // | |
| 158 // The internal type allows this to accept vectors with many different | |
| 159 // representations such as void* or char* as long as the memory pointed to by | |
| 160 // the channel pointer can be reinterpreted as containing Source-typed elements. | |
| 161 // | |
| 162 // Source and Dest usually need to be explicitly specified, but Internal is | |
| 163 // normally inferred. | |
| 164 template <typename Source, typename Dest, typename Internal> | |
| 165 void InterleavedToPlanar(const Source* source, | |
| 166 size_t frames_to_copy, | |
| 167 size_t trim_start, | |
| 168 const std::vector<Internal*>& channel_data) { | |
| 169 const size_t channels = channel_data.size(); | |
| 170 for (size_t ch = 0; ch < channels; ++ch) { | |
| 171 Dest* single_channel_data = reinterpret_cast<Dest*>(channel_data[ch]); | |
| 172 for (size_t i = trim_start, offset = ch; i < trim_start + frames_to_copy; | |
| 173 ++i, offset += channels) { | |
| 174 single_channel_data[i] = ConvertSample<Source, Dest>(source[offset]); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 // PlanarToInterleaved takes a vector of pointers to contiguous memory blocks | |
| 180 // containing samples of type Source and interleaves them into the contiguous | |
| 181 // memory pointed to by the dest_data pointer and converts them to type Dest if | |
| 182 // Source and Dest differ. It trims |trim_start| samples from the beginning of | |
| 183 // each sample and copies |frames_to_copy| samples from each of the channels | |
| 184 // specified in |channel_data| into frames in the |dest_data| memory. | |
| 185 // | |
| 186 // PlanarToInterleaved determines how many channels to interleave into the | |
| 187 // dest_data memory based on the dimension of the |channel_data| vector. | |
| 188 // | |
| 189 // The internal type allows this to accept vectors with many different | |
| 190 // representations such as void* or char* as long as the memory pointed to by | |
| 191 // the channel pointer can be reinterpreted as containing Source-typed elements. | |
| 192 // | |
| 193 // Source and Dest usually need to be explicitly specified, but Internal is | |
| 194 // normally inferred. | |
| 195 template <typename Source, typename Dest, typename Internal> | |
| 196 void PlanarToInterleaved(const std::vector<Internal*>& channel_data, | |
| 197 size_t frames_to_copy, | |
| 198 size_t trim_start, | |
| 199 Dest* dest_data) { | |
| 200 for (size_t ch = 0; ch < channel_data.size(); ++ch) { | |
| 201 const Source* source_data = | |
| 202 reinterpret_cast<const Source*>(channel_data[ch]) + trim_start; | |
| 203 for (size_t i = 0, offset = ch; i < frames_to_copy; | |
| 204 ++i, offset += channel_data.size()) { | |
| 205 dest_data[offset] = ConvertSample<Source, Dest>(source_data[i]); | |
| 206 } | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 } // namespace media | |
| 211 | |
| 212 #endif // MEDIA_BASE_AUDIO_SAMPLE_CONVERSION_H_ | |
| OLD | NEW |