Mercurial > foo_out_sdl
comparison foosdk/sdk/foobar2000/SDK/audio_chunk.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:e9bb126753e7 | 1:20d02a178406 |
|---|---|
| 1 #include "foobar2000-sdk-pch.h" | |
| 2 #include "mem_block_container.h" | |
| 3 #include "audio_chunk.h" | |
| 4 | |
| 5 void audio_chunk::allocate(size_t size, bool bQuicker) { | |
| 6 if (bQuicker) { | |
| 7 const size_t before = this->get_data_size(); | |
| 8 const size_t allow_waste = pfc::max_t<size_t>(size, 4096); | |
| 9 const size_t upper = (size + allow_waste > size) ? size + allow_waste : SIZE_MAX; | |
| 10 if (before >= size && before <= upper) return; | |
| 11 } | |
| 12 this->set_data_size(size); | |
| 13 } | |
| 14 | |
| 15 void audio_chunk::set_data(const audio_sample* src, size_t samples, spec_t const & spec, bool bQuicker) { | |
| 16 t_size size = samples * spec.chanCount; | |
| 17 allocate(size, bQuicker); | |
| 18 if (src) | |
| 19 pfc::memcpy_t(get_data(), src, size); | |
| 20 else | |
| 21 pfc::memset_t(get_data(), (audio_sample)0, size); | |
| 22 set_sample_count(samples); | |
| 23 set_spec(spec); | |
| 24 } | |
| 25 | |
| 26 void audio_chunk::set_data(const audio_sample* src, size_t samples, unsigned nch, unsigned srate, unsigned channel_config) | |
| 27 { | |
| 28 set_data(src, samples, makeSpec(srate, nch, channel_config)); | |
| 29 } | |
| 30 | |
| 31 void audio_chunk::set_data(const audio_sample* src, size_t samples, unsigned nch, unsigned srate) { | |
| 32 | |
| 33 set_data(src, samples, makeSpec(srate, nch)); | |
| 34 } | |
| 35 | |
| 36 inline bool check_exclusive(unsigned val, unsigned mask) | |
| 37 { | |
| 38 return (val&mask)!=0 && (val&mask)!=mask; | |
| 39 } | |
| 40 | |
| 41 static void _import8u(uint8_t const * in, audio_sample * out, size_t count) { | |
| 42 for(size_t walk = 0; walk < count; ++walk) { | |
| 43 uint32_t i = *(in++); | |
| 44 i -= 0x80; // to signed | |
| 45 *(out++) = (audio_sample) (int32_t) i / (float) 0x80; | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 static void _import8s(uint8_t const * in, audio_sample * out, size_t count) { | |
| 50 for(size_t walk = 0; walk < count; ++walk) { | |
| 51 int32_t i = (int8_t) *(in++); | |
| 52 *(out++) = (audio_sample) i / (float) 0x80; | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 static audio_sample _import24s(uint32_t i) { | |
| 57 i ^= 0x800000; // to unsigned | |
| 58 i -= 0x800000; // and back to signed / fill MSBs proper | |
| 59 return (audio_sample) (int32_t) i / (audio_sample) 0x800000; | |
| 60 } | |
| 61 | |
| 62 static void _import24(const void * in_, audio_sample * out, size_t count) { | |
| 63 const uint8_t * in = (const uint8_t*) in_; | |
| 64 #if 1 | |
| 65 while(count > 0 && !pfc::is_ptr_aligned_t<4>(in)) { | |
| 66 uint32_t i = *(in++); | |
| 67 i |= (uint32_t) *(in++) << 8; | |
| 68 i |= (uint32_t) *(in++) << 16; | |
| 69 *(out++) = _import24s(i); | |
| 70 --count; | |
| 71 } | |
| 72 { | |
| 73 for(size_t loop = count >> 2; loop; --loop) { | |
| 74 uint32_t i1 = * (uint32_t*) in; in += 4; | |
| 75 uint32_t i2 = * (uint32_t*) in; in += 4; | |
| 76 uint32_t i3 = * (uint32_t*) in; in += 4; | |
| 77 *out++ = _import24s( i1 & 0xFFFFFF ); | |
| 78 *out++ = _import24s( (i1 >> 24) | ((i2 & 0xFFFF) << 8) ); | |
| 79 *out++ = _import24s( (i2 >> 16) | ((i3 & 0xFF) << 16) ); | |
| 80 *out++ = _import24s( i3 >> 8 ); | |
| 81 } | |
| 82 count &= 3; | |
| 83 } | |
| 84 for( ; count ; --count) { | |
| 85 uint32_t i = *(in++); | |
| 86 i |= (uint32_t) *(in++) << 8; | |
| 87 i |= (uint32_t) *(in++) << 16; | |
| 88 *(out++) = _import24s(i); | |
| 89 } | |
| 90 #else | |
| 91 if (count > 0) { | |
| 92 int32_t i = *(in++); | |
| 93 i |= (int32_t) *(in++) << 8; | |
| 94 i |= (int32_t) (int8_t) *in << 16; | |
| 95 *out++ = (audio_sample) i / (audio_sample) 0x800000; | |
| 96 --count; | |
| 97 | |
| 98 // Now we have in ptr at offset_of_next - 1 and we can read as int32 then discard the LSBs | |
| 99 for(;count;--count) { | |
| 100 int32_t i = *( int32_t*) in; in += 3; | |
| 101 *out++ = (audio_sample) (i >> 8) / (audio_sample) 0x800000; | |
| 102 } | |
| 103 } | |
| 104 #endif | |
| 105 } | |
| 106 | |
| 107 template<bool byteSwap, bool isSigned> static void _import16any(const void * in, audio_sample * out, size_t count) { | |
| 108 uint16_t const * inPtr = (uint16_t const*) in; | |
| 109 const audio_sample factor = 1.0f / (audio_sample) 0x8000; | |
| 110 for(size_t walk = 0; walk < count; ++walk) { | |
| 111 uint16_t v = *inPtr++; | |
| 112 if (byteSwap) v = pfc::byteswap_t(v); | |
| 113 if (!isSigned) v ^= 0x8000; // to signed | |
| 114 *out++ = (audio_sample) (int16_t) v * factor; | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 template<bool byteSwap, bool isSigned> static void _import32any(const void * in, audio_sample * out, size_t count) { | |
| 119 uint32_t const * inPtr = (uint32_t const*) in; | |
| 120 const audio_sample factor = 1.0f / (audio_sample) 0x80000000ul; | |
| 121 for(size_t walk = 0; walk < count; ++walk) { | |
| 122 uint32_t v = *inPtr++; | |
| 123 if (byteSwap) v = pfc::byteswap_t(v); | |
| 124 if (!isSigned) v ^= 0x80000000u; // to signed | |
| 125 *out++ = (audio_sample) (int32_t) v * factor; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 template<bool byteSwap, bool isSigned> static void _import24any(const void * in, audio_sample * out, size_t count) { | |
| 130 uint8_t const * inPtr = (uint8_t const*) in; | |
| 131 const audio_sample factor = 1.0f / (audio_sample) 0x800000; | |
| 132 for(size_t walk = 0; walk < count; ++walk) { | |
| 133 uint32_t v; | |
| 134 if (byteSwap) v = (uint32_t) inPtr[2] | ( (uint32_t) inPtr[1] << 8 ) | ( (uint32_t) inPtr[0] << 16 ); | |
| 135 else v = (uint32_t) inPtr[0] | ( (uint32_t) inPtr[1] << 8 ) | ( (uint32_t) inPtr[2] << 16 ); | |
| 136 inPtr += 3; | |
| 137 if (isSigned) v ^= 0x800000; // to unsigned | |
| 138 v -= 0x800000; // then subtract to get proper MSBs | |
| 139 *out++ = (audio_sample) (int32_t) v * factor; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 void audio_chunk::set_data_fixedpoint_ex(const void * source,t_size size,unsigned srate,unsigned nch,unsigned bps,unsigned flags,unsigned p_channel_config) | |
| 144 { | |
| 145 PFC_ASSERT( check_exclusive(flags,FLAG_SIGNED|FLAG_UNSIGNED) ); | |
| 146 PFC_ASSERT( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) ); | |
| 147 | |
| 148 bool byteSwap = !!(flags & FLAG_BIG_ENDIAN); | |
| 149 if (pfc::byte_order_is_big_endian) byteSwap = !byteSwap; | |
| 150 | |
| 151 t_size count = size / (bps/8); | |
| 152 set_data_size(count); | |
| 153 audio_sample * buffer = get_data(); | |
| 154 bool isSigned = !!(flags & FLAG_SIGNED); | |
| 155 | |
| 156 switch(bps) | |
| 157 { | |
| 158 case 8: | |
| 159 // byte order irrelevant | |
| 160 if (isSigned) _import8s( (const uint8_t*) source , buffer, count); | |
| 161 else _import8u( (const uint8_t*) source , buffer, count); | |
| 162 break; | |
| 163 case 16: | |
| 164 if (byteSwap) { | |
| 165 if (isSigned) { | |
| 166 _import16any<true, true>( source, buffer, count ); | |
| 167 } else { | |
| 168 _import16any<true, false>( source, buffer, count ); | |
| 169 } | |
| 170 } else { | |
| 171 if (isSigned) { | |
| 172 //_import16any<false, true>( source, buffer, count ); | |
| 173 audio_math::convert_from_int16((const int16_t*)source,count,buffer,1.0); | |
| 174 } else { | |
| 175 _import16any<false, false>( source, buffer, count); | |
| 176 } | |
| 177 } | |
| 178 break; | |
| 179 case 24: | |
| 180 if (byteSwap) { | |
| 181 if (isSigned) { | |
| 182 _import24any<true, true>( source, buffer, count ); | |
| 183 } else { | |
| 184 _import24any<true, false>( source, buffer, count ); | |
| 185 } | |
| 186 } else { | |
| 187 if (isSigned) { | |
| 188 //_import24any<false, true>( source, buffer, count); | |
| 189 _import24( source, buffer, count); | |
| 190 } else { | |
| 191 _import24any<false, false>( source, buffer, count); | |
| 192 } | |
| 193 } | |
| 194 break; | |
| 195 case 32: | |
| 196 if (byteSwap) { | |
| 197 if (isSigned) { | |
| 198 _import32any<true, true>( source, buffer, count ); | |
| 199 } else { | |
| 200 _import32any<true, false>( source, buffer, count ); | |
| 201 } | |
| 202 } else { | |
| 203 if (isSigned) { | |
| 204 audio_math::convert_from_int32((const int32_t*)source,count,buffer,1.0); | |
| 205 } else { | |
| 206 _import32any<false, false>( source, buffer, count); | |
| 207 } | |
| 208 } | |
| 209 break; | |
| 210 default: | |
| 211 //unknown size, cant convert | |
| 212 pfc::memset_t(buffer,(audio_sample)0,count); | |
| 213 break; | |
| 214 } | |
| 215 set_sample_count(count/nch); | |
| 216 set_srate(srate); | |
| 217 set_channels(nch,p_channel_config); | |
| 218 } | |
| 219 | |
| 220 void audio_chunk::set_data_fixedpoint_ms(const void * ptr, size_t bytes, unsigned sampleRate, unsigned channels, unsigned bps, unsigned channelConfig) { | |
| 221 //set_data_fixedpoint_ex(ptr,bytes,sampleRate,channels,bps,(bps==8 ? FLAG_UNSIGNED : FLAG_SIGNED) | flags_autoendian(), channelConfig); | |
| 222 PFC_ASSERT( bps != 0 ); | |
| 223 size_t count = bytes / (bps/8); | |
| 224 this->set_data_size( count ); | |
| 225 audio_sample * buffer = this->get_data(); | |
| 226 switch(bps) { | |
| 227 case 8: | |
| 228 _import8u((const uint8_t*)ptr, buffer, count); | |
| 229 break; | |
| 230 case 16: | |
| 231 audio_math::convert_from_int16((const int16_t*) ptr, count, buffer, 1.0); | |
| 232 break; | |
| 233 case 24: | |
| 234 _import24( ptr, buffer, count); | |
| 235 break; | |
| 236 case 32: | |
| 237 audio_math::convert_from_int32((const int32_t*) ptr, count, buffer, 1.0); | |
| 238 break; | |
| 239 default: | |
| 240 PFC_ASSERT(!"Unknown bit depth!"); | |
| 241 memset(buffer, 0, sizeof(audio_sample) * count); | |
| 242 break; | |
| 243 } | |
| 244 set_sample_count(count/channels); | |
| 245 set_srate(sampleRate); | |
| 246 set_channels(channels,channelConfig); | |
| 247 } | |
| 248 | |
| 249 void audio_chunk::set_data_fixedpoint_signed(const void * ptr,t_size bytes,unsigned sampleRate,unsigned channels,unsigned bps,unsigned channelConfig) { | |
| 250 PFC_ASSERT( bps != 0 ); | |
| 251 size_t count = bytes / (bps/8); | |
| 252 this->set_data_size( count ); | |
| 253 audio_sample * buffer = this->get_data(); | |
| 254 switch(bps) { | |
| 255 case 8: | |
| 256 _import8s((const uint8_t*)ptr, buffer, count); | |
| 257 break; | |
| 258 case 16: | |
| 259 audio_math::convert_from_int16((const int16_t*) ptr, count, buffer, 1.0); | |
| 260 break; | |
| 261 case 24: | |
| 262 _import24( ptr, buffer, count); | |
| 263 break; | |
| 264 case 32: | |
| 265 audio_math::convert_from_int32((const int32_t*) ptr, count, buffer, 1.0); | |
| 266 break; | |
| 267 default: | |
| 268 PFC_ASSERT(!"Unknown bit depth!"); | |
| 269 memset(buffer, 0, sizeof(audio_sample) * count); | |
| 270 break; | |
| 271 } | |
| 272 set_sample_count(count/channels); | |
| 273 set_srate(sampleRate); | |
| 274 set_channels(channels,channelConfig); | |
| 275 } | |
| 276 | |
| 277 void audio_chunk::set_data_int16(const int16_t * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config) { | |
| 278 const size_t count = samples * nch; | |
| 279 this->set_data_size( count ); | |
| 280 audio_sample * buffer = this->get_data(); | |
| 281 audio_math::convert_from_int16(src, count, buffer, 1.0); | |
| 282 set_sample_count(samples); | |
| 283 set_srate(srate); | |
| 284 set_channels(nch,channel_config); | |
| 285 } | |
| 286 | |
| 287 template<class t_float> | |
| 288 static void process_float_multi(audio_sample * p_out,const t_float * p_in,const t_size p_count) | |
| 289 { | |
| 290 audio_math::convert(p_in, p_out, p_count); | |
| 291 } | |
| 292 | |
| 293 template<class t_float> | |
| 294 static void process_float_multi_swap(audio_sample * p_out,const t_float * p_in,const t_size p_count) | |
| 295 { | |
| 296 for(size_t n=0;n<p_count;n++) { | |
| 297 p_out[n] = (audio_sample) pfc::byteswap_t(p_in[n]); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 void audio_chunk::set_data_32(const float* src, size_t samples, spec_t const& spec) { | |
| 302 #if audio_sample_size == 32 | |
| 303 set_data(src, samples, spec); | |
| 304 #else | |
| 305 t_size size = samples * spec.chanCount; | |
| 306 set_data_size(size); | |
| 307 if (src) | |
| 308 audio_math::convert(src, get_data(), size); | |
| 309 else | |
| 310 pfc::memset_t(get_data(), (audio_sample)0, size); | |
| 311 set_sample_count(samples); | |
| 312 set_spec(spec); | |
| 313 #endif | |
| 314 } | |
| 315 void audio_chunk::set_data_32(const float* src, size_t samples, unsigned nch, unsigned srate) { | |
| 316 set_data_32(src, samples, makeSpec(srate, nch) ); | |
| 317 } | |
| 318 | |
| 319 void audio_chunk::set_data_floatingpoint_ex(const void * ptr,t_size size,unsigned srate,unsigned nch,unsigned bps,unsigned flags,unsigned p_channel_config) | |
| 320 { | |
| 321 PFC_ASSERT(is_supported_floatingpoint(bps)); | |
| 322 PFC_ASSERT( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) ); | |
| 323 PFC_ASSERT( ! (flags & (FLAG_SIGNED|FLAG_UNSIGNED) ) ); | |
| 324 | |
| 325 bool use_swap = pfc::byte_order_is_big_endian ? !!(flags & FLAG_LITTLE_ENDIAN) : !!(flags & FLAG_BIG_ENDIAN); | |
| 326 | |
| 327 const t_size count = size / (bps/8); | |
| 328 set_data_size(count); | |
| 329 audio_sample * out = get_data(); | |
| 330 | |
| 331 if (bps == 32) | |
| 332 { | |
| 333 if (use_swap) | |
| 334 process_float_multi_swap(out,reinterpret_cast<const float*>(ptr),count); | |
| 335 else | |
| 336 process_float_multi(out,reinterpret_cast<const float*>(ptr),count); | |
| 337 } | |
| 338 else if (bps == 64) | |
| 339 { | |
| 340 if (use_swap) | |
| 341 process_float_multi_swap(out,reinterpret_cast<const double*>(ptr),count); | |
| 342 else | |
| 343 process_float_multi(out,reinterpret_cast<const double*>(ptr),count); | |
| 344 } else if (bps == 16) { | |
| 345 const uint16_t * in = reinterpret_cast<const uint16_t*>(ptr); | |
| 346 if (use_swap) { | |
| 347 for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat16(pfc::byteswap_t(in[walk])); | |
| 348 } else { | |
| 349 for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat16(in[walk]); | |
| 350 } | |
| 351 } else if (bps == 24) { | |
| 352 const uint8_t * in = reinterpret_cast<const uint8_t*>(ptr); | |
| 353 if (use_swap) { | |
| 354 for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat24ptrbs(&in[walk*3]); | |
| 355 } else { | |
| 356 for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat24ptr(&in[walk*3]); | |
| 357 } | |
| 358 } else pfc::throw_exception_with_message< exception_io_data >("invalid bit depth"); | |
| 359 | |
| 360 set_sample_count(count/nch); | |
| 361 set_srate(srate); | |
| 362 set_channels(nch,p_channel_config); | |
| 363 } | |
| 364 | |
| 365 pfc::string8 audio_chunk::formatChunkSpec() const { | |
| 366 pfc::string8 msg; | |
| 367 msg << get_sample_rate() << " Hz, " << get_channels() << ":0x" << pfc::format_hex(get_channel_config(), 2) << " channels, " << get_sample_count() << " samples"; | |
| 368 return msg; | |
| 369 } | |
| 370 | |
| 371 void audio_chunk::debugChunkSpec() const { | |
| 372 FB2K_DebugLog() << "Chunk: " << this->formatChunkSpec(); | |
| 373 } | |
| 374 | |
| 375 #if PFC_DEBUG | |
| 376 void audio_chunk::assert_valid(const char * ctx) const { | |
| 377 if (!is_valid()) { | |
| 378 FB2K_DebugLog() << "audio_chunk::assert_valid failure in " << ctx; | |
| 379 debugChunkSpec(); | |
| 380 uBugCheck(); | |
| 381 } | |
| 382 } | |
| 383 #endif | |
| 384 bool audio_chunk::is_valid() const | |
| 385 { | |
| 386 unsigned nch = get_channels(); | |
| 387 if (nch == 0 || nch > 32) return false; | |
| 388 if (!g_is_valid_sample_rate(get_srate())) return false; | |
| 389 t_size samples = get_sample_count(); | |
| 390 if (samples==0 || samples >= 0x80000000ul / (sizeof(audio_sample) * nch) ) return false; | |
| 391 t_size size = get_data_size(); | |
| 392 if (samples * nch > size) return false; | |
| 393 if (!get_data()) return false; | |
| 394 return true; | |
| 395 } | |
| 396 | |
| 397 bool audio_chunk::is_spec_valid() const { | |
| 398 return this->get_spec().is_valid(); | |
| 399 } | |
| 400 | |
| 401 void audio_chunk::pad_with_silence_ex(t_size samples,unsigned hint_nch,unsigned hint_srate) { | |
| 402 if (is_empty()) | |
| 403 { | |
| 404 if (hint_srate && hint_nch) { | |
| 405 return set_data(0,samples,hint_nch,hint_srate); | |
| 406 } else throw exception_io_data(); | |
| 407 } | |
| 408 else | |
| 409 { | |
| 410 if (hint_srate && hint_srate != get_srate()) samples = MulDiv_Size(samples,get_srate(),hint_srate); | |
| 411 if (samples > get_sample_count()) | |
| 412 { | |
| 413 t_size old_size = get_sample_count() * get_channels(); | |
| 414 t_size new_size = samples * get_channels(); | |
| 415 set_data_size(new_size); | |
| 416 pfc::memset_t(get_data() + old_size,(audio_sample)0,new_size - old_size); | |
| 417 set_sample_count(samples); | |
| 418 } | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 void audio_chunk::pad_with_silence(t_size samples) { | |
| 423 if (samples > get_sample_count()) | |
| 424 { | |
| 425 t_size old_size = get_sample_count() * get_channels(); | |
| 426 t_size new_size = pfc::multiply_guarded(samples,(size_t)get_channels()); | |
| 427 set_data_size(new_size); | |
| 428 pfc::memset_t(get_data() + old_size,(audio_sample)0,new_size - old_size); | |
| 429 set_sample_count(samples); | |
| 430 } | |
| 431 } | |
| 432 | |
| 433 void audio_chunk::set_silence(t_size samples) { | |
| 434 t_size items = samples * get_channels(); | |
| 435 set_data_size(items); | |
| 436 pfc::memset_null_t(get_data(), items); | |
| 437 set_sample_count(samples); | |
| 438 } | |
| 439 | |
| 440 void audio_chunk::set_silence_seconds( double seconds ) { | |
| 441 set_silence( (size_t) audio_math::time_to_samples( seconds, this->get_sample_rate() ) ); | |
| 442 } | |
| 443 | |
| 444 void audio_chunk::insert_silence_fromstart(t_size samples) { | |
| 445 t_size old_size = get_sample_count() * get_channels(); | |
| 446 t_size delta = samples * get_channels(); | |
| 447 t_size new_size = old_size + delta; | |
| 448 set_data_size(new_size); | |
| 449 audio_sample * ptr = get_data(); | |
| 450 pfc::memmove_t(ptr+delta,ptr,old_size); | |
| 451 pfc::memset_t(ptr,(audio_sample)0,delta); | |
| 452 set_sample_count(get_sample_count() + samples); | |
| 453 } | |
| 454 | |
| 455 bool audio_chunk::process_skip(double & skipDuration) { | |
| 456 t_uint64 skipSamples = audio_math::time_to_samples(skipDuration, get_sample_rate()); | |
| 457 if (skipSamples == 0) {skipDuration = 0; return true;} | |
| 458 const t_size mySamples = get_sample_count(); | |
| 459 if (skipSamples < mySamples) { | |
| 460 skip_first_samples((t_size)skipSamples); | |
| 461 skipDuration = 0; | |
| 462 return true; | |
| 463 } | |
| 464 if (skipSamples == mySamples) { | |
| 465 skipDuration = 0; | |
| 466 return false; | |
| 467 } | |
| 468 skipDuration -= audio_math::samples_to_time(mySamples, get_sample_rate()); | |
| 469 return false; | |
| 470 } | |
| 471 | |
| 472 t_size audio_chunk::skip_first_samples(t_size samples_delta) | |
| 473 { | |
| 474 t_size samples_old = get_sample_count(); | |
| 475 if (samples_delta >= samples_old) | |
| 476 { | |
| 477 set_sample_count(0); | |
| 478 set_data_size(0); | |
| 479 return samples_old; | |
| 480 } | |
| 481 else | |
| 482 { | |
| 483 t_size samples_new = samples_old - samples_delta; | |
| 484 unsigned nch = get_channels(); | |
| 485 audio_sample * ptr = get_data(); | |
| 486 pfc::memmove_t(ptr,ptr+nch*samples_delta,nch*samples_new); | |
| 487 set_sample_count(samples_new); | |
| 488 set_data_size(nch*samples_new); | |
| 489 return samples_delta; | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 audio_sample audio_chunk::get_peak(audio_sample p_peak) const { | |
| 494 return pfc::max_t(p_peak, get_peak()); | |
| 495 } | |
| 496 | |
| 497 audio_sample audio_chunk::get_peak() const { | |
| 498 return audio_math::calculate_peak(get_data(),get_sample_count() * get_channels()); | |
| 499 } | |
| 500 | |
| 501 void audio_chunk::scale(audio_sample p_value) | |
| 502 { | |
| 503 audio_sample * ptr = get_data(); | |
| 504 audio_math::scale(ptr,get_sample_count() * get_channels(),ptr,p_value); | |
| 505 } | |
| 506 | |
| 507 | |
| 508 namespace { | |
| 509 | |
| 510 struct sampleToIntDesc { | |
| 511 unsigned bps, bpsValid; | |
| 512 bool useUpperBits; | |
| 513 audio_sample scale; | |
| 514 }; | |
| 515 template<typename int_t> class sampleToInt { | |
| 516 public: | |
| 517 sampleToInt(sampleToIntDesc const & d) { | |
| 518 clipLo = - ( (int_t) 1 << (d.bpsValid-1)); | |
| 519 clipHi = ( (int_t) 1 << (d.bpsValid-1)) - 1; | |
| 520 scale = (float) ( (int64_t) 1 << (d.bpsValid - 1) ) * d.scale; | |
| 521 if (d.useUpperBits) { | |
| 522 shift = (int8_t)( d.bps - d.bpsValid ); | |
| 523 } else { | |
| 524 shift = 0; | |
| 525 } | |
| 526 } | |
| 527 inline int_t operator() (audio_sample s) const { | |
| 528 int_t v; | |
| 529 if constexpr (sizeof(int_t) > 4) v = (int_t) audio_math::rint64( s * scale ); | |
| 530 else v = (int_t)audio_math::rint32( s * scale ); | |
| 531 return pfc::clip_t<int_t>( v, clipLo, clipHi) << shift; | |
| 532 } | |
| 533 private: | |
| 534 int_t clipLo, clipHi; | |
| 535 int8_t shift; | |
| 536 audio_sample scale; | |
| 537 }; | |
| 538 } | |
| 539 static void render_24bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { | |
| 540 t_uint8 * outWalk = reinterpret_cast<t_uint8*>(out); | |
| 541 sampleToInt<int32_t> gen(d); | |
| 542 for(t_size walk = 0; walk < inLen; ++walk) { | |
| 543 int32_t v = gen(in[walk]); | |
| 544 *(outWalk ++) = (t_uint8) (v & 0xFF); | |
| 545 *(outWalk ++) = (t_uint8) ((v >> 8) & 0xFF); | |
| 546 *(outWalk ++) = (t_uint8) ((v >> 16) & 0xFF); | |
| 547 } | |
| 548 } | |
| 549 static void render_8bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { | |
| 550 sampleToInt<int32_t> gen(d); | |
| 551 t_int8 * outWalk = reinterpret_cast<t_int8*>(out); | |
| 552 for(t_size walk = 0; walk < inLen; ++walk) { | |
| 553 *outWalk++ = (t_int8)gen(in[walk]); | |
| 554 } | |
| 555 } | |
| 556 static void render_16bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { | |
| 557 sampleToInt<int32_t> gen(d); | |
| 558 int16_t * outWalk = reinterpret_cast<int16_t*>(out); | |
| 559 for(t_size walk = 0; walk < inLen; ++walk) { | |
| 560 *outWalk++ = (int16_t)gen(in[walk]); | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 template<typename internal_t> | |
| 565 static void render_32bit_(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { | |
| 566 sampleToInt<internal_t> gen(d); // must use int64 for clipping | |
| 567 int32_t * outWalk = reinterpret_cast<int32_t*>(out); | |
| 568 for(t_size walk = 0; walk < inLen; ++walk) { | |
| 569 *outWalk++ = (int32_t)gen(in[walk]); | |
| 570 } | |
| 571 } | |
| 572 | |
| 573 bool audio_chunk::g_toFixedPoint(const audio_sample * in, void * out, size_t count, uint32_t bps, uint32_t bpsValid, bool useUpperBits, audio_sample scale) { | |
| 574 const sampleToIntDesc d = {bps, bpsValid, useUpperBits, scale}; | |
| 575 if (bps == 0) { | |
| 576 PFC_ASSERT(!"How did we get here?"); | |
| 577 return false; | |
| 578 } else if (bps <= 8) { | |
| 579 render_8bit(in, count, out, d); | |
| 580 } else if (bps <= 16) { | |
| 581 render_16bit(in, count, out, d); | |
| 582 } else if (bps <= 24) { | |
| 583 render_24bit(in, count, out, d); | |
| 584 } else if (bps <= 32) { | |
| 585 if (bpsValid <= 28) { // for speed | |
| 586 render_32bit_<int32_t>(in, count, out, d); | |
| 587 } else { | |
| 588 render_32bit_<int64_t>(in, count, out, d); | |
| 589 } | |
| 590 } else { | |
| 591 PFC_ASSERT(!"How did we get here?"); | |
| 592 return false; | |
| 593 } | |
| 594 | |
| 595 return true; | |
| 596 } | |
| 597 | |
| 598 bool audio_chunk::toFixedPoint(class mem_block_container & out, uint32_t bps, uint32_t bpsValid, bool useUpperBits, audio_sample scale) const { | |
| 599 bps = (bps + 7) & ~7; | |
| 600 if (bps < bpsValid) return false; | |
| 601 const size_t count = get_sample_count() * get_channel_count(); | |
| 602 out.set_size( count * (bps/8) ); | |
| 603 return g_toFixedPoint(get_data(), out.get_ptr(), count, bps, bpsValid, useUpperBits, scale); | |
| 604 } | |
| 605 | |
| 606 bool audio_chunk::to_raw_data(mem_block_container & out, t_uint32 bps, bool useUpperBits, audio_sample scale) const { | |
| 607 uint32_t bpsValid = bps; | |
| 608 bps = (bps + 7) & ~7; | |
| 609 const size_t count = get_sample_count() * get_channel_count(); | |
| 610 out.set_size( count * (bps/8) ); | |
| 611 void * outPtr = out.get_ptr(); | |
| 612 audio_sample const * inPtr = get_data(); | |
| 613 if (bps == 32) { | |
| 614 float * f = (float*) outPtr; | |
| 615 audio_math::convert(inPtr, f, count, scale); | |
| 616 return true; | |
| 617 } else { | |
| 618 return g_toFixedPoint(inPtr, outPtr, count, bps, bpsValid, useUpperBits, scale); | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels) { | |
| 623 return makeSpec( rate, channels, g_guess_channel_config(channels) ); | |
| 624 } | |
| 625 | |
| 626 audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels, uint32_t mask) { | |
| 627 PFC_ASSERT(mask == 0 || pfc::countBits32(mask) == channels); | |
| 628 spec_t spec = {}; | |
| 629 spec.sampleRate = rate; spec.chanCount = channels; spec.chanMask = mask; | |
| 630 return spec; | |
| 631 } | |
| 632 | |
| 633 bool audio_chunk::spec_t::equals( const spec_t & v1, const spec_t & v2 ) { | |
| 634 return v1.sampleRate == v2.sampleRate && v1.chanCount == v2.chanCount && v1.chanMask == v2.chanMask; | |
| 635 } | |
| 636 | |
| 637 pfc::string8 audio_chunk::spec_t::toString(const char * delim) const { | |
| 638 pfc::string_formatter temp; | |
| 639 if ( sampleRate > 0 ) temp << sampleRate << "Hz"; | |
| 640 if (chanCount > 0) { | |
| 641 if ( temp.length() > 0 ) temp << delim; | |
| 642 temp << chanCount << "ch"; | |
| 643 } | |
| 644 | |
| 645 if ( chanMask != audio_chunk::channel_config_mono && chanMask != audio_chunk::channel_config_stereo ) { | |
| 646 pfc::string8 strMask; | |
| 647 audio_chunk::g_formatChannelMaskDesc( chanMask, strMask ); | |
| 648 if ( temp.length() > 0) temp << delim; | |
| 649 temp << strMask; | |
| 650 } | |
| 651 return temp; | |
| 652 } | |
| 653 | |
| 654 audio_chunk::spec_t audio_chunk::get_spec() const { | |
| 655 spec_t spec = {}; | |
| 656 spec.sampleRate = this->get_sample_rate(); | |
| 657 spec.chanCount = this->get_channel_count(); | |
| 658 spec.chanMask = this->get_channel_config(); | |
| 659 return spec; | |
| 660 } | |
| 661 void audio_chunk::set_spec(const spec_t & spec) { | |
| 662 set_sample_rate(spec.sampleRate); | |
| 663 set_channels( spec.chanCount, spec.chanMask ); | |
| 664 } | |
| 665 | |
| 666 bool audio_chunk::spec_t::is_valid() const { | |
| 667 if (this->chanCount==0 || this->chanCount>256) return false; | |
| 668 if (!audio_chunk::g_is_valid_sample_rate(this->sampleRate)) return false; | |
| 669 return true; | |
| 670 } | |
| 671 | |
| 672 #ifdef _WIN32 | |
| 673 | |
| 674 WAVEFORMATEX audio_chunk::spec_t::toWFX() const { | |
| 675 const uint32_t sampleWidth = sizeof(audio_sample); | |
| 676 | |
| 677 WAVEFORMATEX wfx = {}; | |
| 678 wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; | |
| 679 wfx.nChannels = (WORD) chanCount; | |
| 680 wfx.nSamplesPerSec = sampleRate; | |
| 681 wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth; | |
| 682 wfx.nBlockAlign = (WORD)( chanCount * sampleWidth ); | |
| 683 wfx.wBitsPerSample = sampleWidth * 8; | |
| 684 return wfx; | |
| 685 } | |
| 686 | |
| 687 WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEX() const { | |
| 688 const uint32_t sampleWidth = sizeof(audio_sample); | |
| 689 const bool isFloat = true; | |
| 690 | |
| 691 WAVEFORMATEXTENSIBLE wfxe; | |
| 692 wfxe.Format = toWFX(); | |
| 693 wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | |
| 694 wfxe.Format.cbSize = sizeof(wfxe) - sizeof(wfxe.Format); | |
| 695 wfxe.Samples.wValidBitsPerSample = sampleWidth * 8; | |
| 696 wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(this->chanMask); | |
| 697 wfxe.SubFormat = isFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; | |
| 698 | |
| 699 return wfxe; | |
| 700 } | |
| 701 | |
| 702 WAVEFORMATEX audio_chunk::spec_t::toWFXWithBPS(uint32_t bps) const { | |
| 703 const uint32_t sampleWidth = (bps+7)/8; | |
| 704 | |
| 705 WAVEFORMATEX wfx = {}; | |
| 706 wfx.wFormatTag = WAVE_FORMAT_PCM; | |
| 707 wfx.nChannels = (WORD)chanCount; | |
| 708 wfx.nSamplesPerSec = sampleRate; | |
| 709 wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth; | |
| 710 wfx.nBlockAlign = (WORD)( chanCount * sampleWidth ); | |
| 711 wfx.wBitsPerSample = (WORD)( sampleWidth * 8 ); | |
| 712 return wfx; | |
| 713 } | |
| 714 | |
| 715 WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEXWithBPS(uint32_t bps) const { | |
| 716 const uint32_t sampleWidth = (bps + 7) / 8; | |
| 717 const bool isFloat = false; | |
| 718 | |
| 719 WAVEFORMATEXTENSIBLE wfxe; | |
| 720 wfxe.Format = toWFXWithBPS(bps); | |
| 721 wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | |
| 722 wfxe.Format.cbSize = sizeof(wfxe) - sizeof(wfxe.Format); | |
| 723 wfxe.Samples.wValidBitsPerSample = (WORD)( sampleWidth * 8 ); | |
| 724 wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(this->chanMask); | |
| 725 wfxe.SubFormat = isFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; | |
| 726 | |
| 727 return wfxe; | |
| 728 } | |
| 729 #endif // _WIN32 | |
| 730 | |
| 731 void audio_chunk::append(const audio_chunk& other) { | |
| 732 if (other.get_spec() != this->get_spec()) { | |
| 733 throw pfc::exception_invalid_params(); | |
| 734 } | |
| 735 | |
| 736 this->grow_data_size(get_used_size() + other.get_used_size()); | |
| 737 audio_sample* p = this->get_data() + get_used_size(); | |
| 738 memcpy(p, other.get_data(), other.get_used_size() * sizeof(audio_sample)); | |
| 739 set_sample_count(get_sample_count() + other.get_sample_count()); | |
| 740 } |
