1 // Copyright 2014 Emilie Gillet. 2 // 3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // Circular buffer storing audio samples. 28 29 #ifndef CLOUDS_DSP_AUDIO_BUFFER_H_ 30 #define CLOUDS_DSP_AUDIO_BUFFER_H_ 31 32 #include <algorithm> 33 34 #include "stmlib/stmlib.h" 35 36 #include "stmlib/dsp/dsp.h" 37 #include "stmlib/utils/dsp.h" 38 39 #include "clouds/dsp/mu_law.h" 40 41 const int32_t kCrossFadeSize = 256; 42 const int32_t kInterpolationTail = 8; 43 44 namespace clouds { 45 46 enum Resolution { 47 RESOLUTION_16_BIT, 48 RESOLUTION_8_BIT, 49 RESOLUTION_8_BIT_DITHERED, 50 RESOLUTION_8_BIT_MU_LAW, 51 }; 52 53 enum InterpolationMethod { 54 INTERPOLATION_ZOH, 55 INTERPOLATION_LINEAR, 56 INTERPOLATION_HERMITE 57 }; 58 59 template<Resolution resolution> 60 class AudioBuffer { 61 public: AudioBuffer()62 AudioBuffer() { } ~AudioBuffer()63 ~AudioBuffer() { } 64 Init(void * buffer,int32_t size,int16_t * tail_buffer)65 void Init( 66 void* buffer, 67 int32_t size, 68 int16_t* tail_buffer) { 69 s16_ = static_cast<int16_t*>(buffer); 70 s8_ = static_cast<int8_t*>(buffer); 71 size_ = size - kInterpolationTail; 72 write_head_ = 0; 73 quantization_error_ = 0.0f; 74 crossfade_counter_ = 0; 75 if (resolution == RESOLUTION_16_BIT) { 76 std::fill(&s16_[0], &s16_[size], 0); 77 } else { 78 std::fill( 79 &s8_[0], 80 &s8_[size], 81 resolution == RESOLUTION_8_BIT_MU_LAW ? 127 : 0); 82 } 83 tail_ = tail_buffer; 84 } 85 Resync(int32_t head)86 inline void Resync(int32_t head) { 87 write_head_ = head; 88 crossfade_counter_ = 0; 89 } 90 Write(float in)91 inline void Write(float in) { 92 if (resolution == RESOLUTION_16_BIT) { 93 s16_[write_head_] = stmlib::Clip16( 94 static_cast<int32_t>(in * 32768.0f)); 95 } else if (resolution == RESOLUTION_8_BIT_DITHERED) { 96 float sample = in * 127.0f; 97 sample += quantization_error_; 98 int32_t quantized = static_cast<int32_t>(sample); 99 if (quantized < -127) quantized = -127; 100 else if (quantized > 127) quantized = 127; 101 quantization_error_ = sample - static_cast<float>(in); 102 s8_[write_head_] = quantized; 103 } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 104 int16_t sample = stmlib::Clip16(static_cast<int32_t>(in * 32768.0f)); 105 s8_[write_head_] = Lin2MuLaw(sample); 106 } else { 107 s8_[write_head_] = static_cast<int8_t>( 108 stmlib::Clip16(in * 32768.0f) >> 8); 109 } 110 111 if (resolution == RESOLUTION_16_BIT) { 112 if (write_head_ < kInterpolationTail) { 113 s16_[write_head_ + size_] = s16_[write_head_]; 114 } 115 } else { 116 if (write_head_ < kInterpolationTail) { 117 s8_[write_head_ + size_] = s8_[write_head_]; 118 } 119 } 120 ++write_head_; 121 if (write_head_ >= size_) { 122 write_head_ = 0; 123 } 124 } 125 WriteFade(const float * in,int32_t size,int32_t stride,bool write)126 inline void WriteFade( 127 const float* in, 128 int32_t size, 129 int32_t stride, 130 bool write) { 131 if (!write) { 132 // Continue recording samples to have something to crossfade with 133 // when recording resumes. 134 if (crossfade_counter_ < kCrossFadeSize) { 135 while (size--) { 136 if (crossfade_counter_ < kCrossFadeSize) { 137 tail_[crossfade_counter_++] = stmlib::Clip16( 138 static_cast<int32_t>(*in * 32767.0f)); 139 in += stride; 140 } 141 } 142 } 143 } else if (write && !crossfade_counter_ && 144 resolution == RESOLUTION_16_BIT && 145 write_head_ >= kInterpolationTail && write_head_ < (size_ - size)) { 146 // Fast write routine for the most common case. 147 while (size--) { 148 s16_[write_head_] = stmlib::Clip16( 149 static_cast<int32_t>(*in * 32767.0f)); 150 ++write_head_; 151 in += stride; 152 } 153 } else { 154 while (size--) { 155 float sample = *in; 156 if (crossfade_counter_) { 157 --crossfade_counter_; 158 float tail_sample = tail_[kCrossFadeSize - crossfade_counter_]; 159 float gain = crossfade_counter_ * (1.0f / float(kCrossFadeSize)); 160 sample += (tail_sample / 32768.0f - sample) * gain; 161 } 162 Write(sample); 163 in += stride; 164 } 165 } 166 } 167 Write(const float * in,int32_t size,int32_t stride)168 inline void Write(const float* in, int32_t size, int32_t stride) { 169 if (resolution == RESOLUTION_16_BIT 170 && write_head_ >= kInterpolationTail && write_head_ < (size_ - size)) { 171 // Fast write routine for the most common case. 172 while (size--) { 173 s16_[write_head_] = stmlib::Clip16( 174 static_cast<int32_t>(*in * 32768.0f)); 175 ++write_head_; 176 in += stride; 177 } 178 } else { 179 while (size--) { 180 Write(*in); 181 in += stride; 182 } 183 } 184 } 185 186 template<InterpolationMethod method> Read(int32_t integral,uint16_t fractional)187 inline float Read(int32_t integral, uint16_t fractional) const { 188 if (method == INTERPOLATION_ZOH) { 189 return ReadZOH(integral, fractional); 190 } else if (method == INTERPOLATION_LINEAR) { 191 return ReadLinear(integral, fractional); 192 } else if (method == INTERPOLATION_HERMITE) { 193 return ReadHermite(integral, fractional); 194 } 195 } 196 ReadZOH(int32_t integral,uint16_t fractional)197 inline float ReadZOH(int32_t integral, uint16_t fractional) const { 198 if (integral >= size_) { 199 integral -= size_; 200 } 201 202 float x0, scale; 203 if (resolution == RESOLUTION_16_BIT) { 204 x0 = s16_[integral]; 205 scale = 1.0f / 32768.0f; 206 } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 207 x0 = MuLaw2Lin(s8_[integral]); 208 scale = 1.0f / 32768.0f; 209 } else { 210 x0 = s8_[integral]; 211 scale = 1.0f / 128.0f; 212 } 213 return x0 * scale; 214 } 215 ReadLinear(int32_t integral,uint16_t fractional)216 inline float ReadLinear(int32_t integral, uint16_t fractional) const { 217 if (integral >= size_) { 218 integral -= size_; 219 } 220 221 // assert(integral >= 0 && integral < size_); 222 223 float x0, x1, scale; 224 float t = static_cast<float>(fractional) / 65536.0f; 225 if (resolution == RESOLUTION_16_BIT) { 226 x0 = s16_[integral]; 227 x1 = s16_[integral + 1]; 228 scale = 1.0f / 32768.0f; 229 } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 230 x0 = MuLaw2Lin(s8_[integral]); 231 x1 = MuLaw2Lin(s8_[integral + 1]); 232 scale = 1.0f / 32768.0f; 233 } else { 234 x0 = s8_[integral]; 235 x1 = s8_[integral + 1]; 236 scale = 1.0f / 128.0f; 237 } 238 return (x0 + (x1 - x0) * t) * scale; 239 } 240 ReadHermite(int32_t integral,uint16_t fractional)241 inline float ReadHermite(int32_t integral, uint16_t fractional) const { 242 if (integral >= size_) { 243 integral -= size_; 244 } 245 246 // assert(integral >= 0 && integral < size_); 247 248 float xm1, x0, x1, x2, scale; 249 float t = static_cast<float>(fractional) / 65536.0f; 250 251 if (resolution == RESOLUTION_16_BIT) { 252 xm1 = s16_[integral]; 253 x0 = s16_[integral + 1]; 254 x1 = s16_[integral + 2]; 255 x2 = s16_[integral + 3]; 256 scale = 1.0f / 32768.0f; 257 } else if (resolution == RESOLUTION_8_BIT_MU_LAW) { 258 xm1 = MuLaw2Lin(s8_[integral]); 259 x0 = MuLaw2Lin(s8_[integral + 1]); 260 x1 = MuLaw2Lin(s8_[integral + 2]); 261 x2 = MuLaw2Lin(s8_[integral + 3]); 262 scale = 1.0f / 32768.0f; 263 } else { 264 xm1 = s8_[integral]; 265 x0 = s8_[integral + 1]; 266 x1 = s8_[integral + 2]; 267 x2 = s8_[integral + 3]; 268 scale = 1.0f / 128.0f; 269 } 270 271 // Laurent de Soras's Hermite interpolator. 272 const float c = (x1 - xm1) * 0.5f; 273 const float v = x0 - x1; 274 const float w = c + v; 275 const float a = w + v + (x2 - x0) * 0.5f; 276 const float b_neg = w + a; 277 return ((((a * t) - b_neg) * t + c) * t + x0) * scale; 278 } 279 size()280 inline int32_t size() const { return size_; } head()281 inline int32_t head() const { return write_head_; } 282 283 private: 284 int16_t* s16_; 285 int8_t* s8_; 286 287 float quantization_error_; 288 289 int16_t tail_ptr_; 290 291 int32_t size_; 292 int32_t write_head_; 293 294 int16_t* tail_; 295 int32_t crossfade_counter_; 296 297 DISALLOW_COPY_AND_ASSIGN(AudioBuffer); 298 }; 299 300 } // namespace clouds 301 302 #endif // CLOUDS_DSP_AUDIO_BUFFER_H_ 303