#include "foobar2000.h" void audio_chunk::set_data(const audio_sample * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config) { t_size size = samples * nch; set_data_size(size); if (src) pfc::memcpy_t(get_data(),src,size); else pfc::memset_t(get_data(),(audio_sample)0,size); set_sample_count(samples); set_channels(nch,channel_config); set_srate(srate); } inline bool check_exclusive(unsigned val, unsigned mask) { return (val&mask)!=0 && (val&mask)!=mask; } static void _import8u(uint8_t const * in, audio_sample * out, size_t count) { for(size_t walk = 0; walk < count; ++walk) { uint32_t i = *(in++); i -= 0x80; // to signed *(out++) = (float) (int32_t) i / (float) 0x80; } } static void _import8s(uint8_t const * in, audio_sample * out, size_t count) { for(size_t walk = 0; walk < count; ++walk) { int32_t i = (int8_t) *(in++); *(out++) = (float) i / (float) 0x80; } } static audio_sample _import24s(uint32_t i) { i ^= 0x800000; // to unsigned i -= 0x800000; // and back to signed / fill MSBs proper return (float) (int32_t) i / (float) 0x800000; } static void _import24(const void * in_, audio_sample * out, size_t count) { const uint8_t * in = (const uint8_t*) in_; #if 1 while(count > 0 && !pfc::is_ptr_aligned_t<4>(in)) { uint32_t i = *(in++); i |= (uint32_t) *(in++) << 8; i |= (uint32_t) *(in++) << 16; *(out++) = _import24s(i); --count; } { for(size_t loop = count >> 2; loop; --loop) { uint32_t i1 = * (uint32_t*) in; in += 4; uint32_t i2 = * (uint32_t*) in; in += 4; uint32_t i3 = * (uint32_t*) in; in += 4; *out++ = _import24s( i1 & 0xFFFFFF ); *out++ = _import24s( (i1 >> 24) | ((i2 & 0xFFFF) << 8) ); *out++ = _import24s( (i2 >> 16) | ((i3 & 0xFF) << 16) ); *out++ = _import24s( i3 >> 8 ); } count &= 3; } for( ; count ; --count) { uint32_t i = *(in++); i |= (uint32_t) *(in++) << 8; i |= (uint32_t) *(in++) << 16; *(out++) = _import24s(i); } #else if (count > 0) { int32_t i = *(in++); i |= (int32_t) *(in++) << 8; i |= (int32_t) (int8_t) *in << 16; *out++ = (audio_sample) i / (audio_sample) 0x800000; --count; // Now we have in ptr at offset_of_next - 1 and we can read as int32 then discard the LSBs for(;count;--count) { int32_t i = *( int32_t*) in; in += 3; *out++ = (audio_sample) (i >> 8) / (audio_sample) 0x800000; } } #endif } template static void _import16any(const void * in, audio_sample * out, size_t count) { uint16_t const * inPtr = (uint16_t const*) in; const audio_sample factor = 1.0f / (audio_sample) 0x8000; for(size_t walk = 0; walk < count; ++walk) { uint16_t v = *inPtr++; if (byteSwap) v = pfc::byteswap_t(v); if (!isSigned) v ^= 0x8000; // to signed *out++ = (audio_sample) (int16_t) v * factor; } } template static void _import32any(const void * in, audio_sample * out, size_t count) { uint32_t const * inPtr = (uint32_t const*) in; const audio_sample factor = 1.0f / (audio_sample) 0x80000000ul; for(size_t walk = 0; walk < count; ++walk) { uint32_t v = *inPtr++; if (byteSwap) v = pfc::byteswap_t(v); if (!isSigned) v ^= 0x80000000u; // to signed *out++ = (audio_sample) (int32_t) v * factor; } } template static void _import24any(const void * in, audio_sample * out, size_t count) { uint8_t const * inPtr = (uint8_t const*) in; const audio_sample factor = 1.0f / (audio_sample) 0x800000; for(size_t walk = 0; walk < count; ++walk) { uint32_t v; if (byteSwap) v = (uint32_t) inPtr[2] | ( (uint32_t) inPtr[1] << 8 ) | ( (uint32_t) inPtr[0] << 16 ); else v = (uint32_t) inPtr[0] | ( (uint32_t) inPtr[1] << 8 ) | ( (uint32_t) inPtr[2] << 16 ); inPtr += 3; if (isSigned) v ^= 0x800000; // to unsigned v -= 0x800000; // then subtract to get proper MSBs *out++ = (audio_sample) (int32_t) v * factor; } } 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) { PFC_ASSERT( check_exclusive(flags,FLAG_SIGNED|FLAG_UNSIGNED) ); PFC_ASSERT( check_exclusive(flags,FLAG_LITTLE_ENDIAN|FLAG_BIG_ENDIAN) ); bool byteSwap = !!(flags & FLAG_BIG_ENDIAN); if (pfc::byte_order_is_big_endian) byteSwap = !byteSwap; t_size count = size / (bps/8); set_data_size(count); audio_sample * buffer = get_data(); bool isSigned = !!(flags & FLAG_SIGNED); switch(bps) { case 8: // byte order irrelevant if (isSigned) _import8s( (const uint8_t*) source , buffer, count); else _import8u( (const uint8_t*) source , buffer, count); break; case 16: if (byteSwap) { if (isSigned) { _import16any( source, buffer, count ); } else { _import16any( source, buffer, count ); } } else { if (isSigned) { //_import16any( source, buffer, count ); audio_math::convert_from_int16((const int16_t*)source,count,buffer,1.0); } else { _import16any( source, buffer, count); } } break; case 24: if (byteSwap) { if (isSigned) { _import24any( source, buffer, count ); } else { _import24any( source, buffer, count ); } } else { if (isSigned) { //_import24any( source, buffer, count); _import24( source, buffer, count); } else { _import24any( source, buffer, count); } } break; case 32: if (byteSwap) { if (isSigned) { _import32any( source, buffer, count ); } else { _import32any( source, buffer, count ); } } else { if (isSigned) { audio_math::convert_from_int32((const int32_t*)source,count,buffer,1.0); } else { _import32any( source, buffer, count); } } break; default: //unknown size, cant convert pfc::memset_t(buffer,(audio_sample)0,count); break; } set_sample_count(count/nch); set_srate(srate); set_channels(nch,p_channel_config); } void audio_chunk::set_data_fixedpoint_ms(const void * ptr, size_t bytes, unsigned sampleRate, unsigned channels, unsigned bps, unsigned channelConfig) { //set_data_fixedpoint_ex(ptr,bytes,sampleRate,channels,bps,(bps==8 ? FLAG_UNSIGNED : FLAG_SIGNED) | flags_autoendian(), channelConfig); PFC_ASSERT( bps != 0 ); size_t count = bytes / (bps/8); this->set_data_size( count ); audio_sample * buffer = this->get_data(); switch(bps) { case 8: _import8u((const uint8_t*)ptr, buffer, count); break; case 16: audio_math::convert_from_int16((const int16_t*) ptr, count, buffer, 1.0); break; case 24: _import24( ptr, buffer, count); break; case 32: audio_math::convert_from_int32((const int32_t*) ptr, count, buffer, 1.0); break; default: PFC_ASSERT(!"Unknown bit depth!"); memset(buffer, 0, sizeof(audio_sample) * count); break; } set_sample_count(count/channels); set_srate(sampleRate); set_channels(channels,channelConfig); } void audio_chunk::set_data_fixedpoint_signed(const void * ptr,t_size bytes,unsigned sampleRate,unsigned channels,unsigned bps,unsigned channelConfig) { PFC_ASSERT( bps != 0 ); size_t count = bytes / (bps/8); this->set_data_size( count ); audio_sample * buffer = this->get_data(); switch(bps) { case 8: _import8s((const uint8_t*)ptr, buffer, count); break; case 16: audio_math::convert_from_int16((const int16_t*) ptr, count, buffer, 1.0); break; case 24: _import24( ptr, buffer, count); break; case 32: audio_math::convert_from_int32((const int32_t*) ptr, count, buffer, 1.0); break; default: PFC_ASSERT(!"Unknown bit depth!"); memset(buffer, 0, sizeof(audio_sample) * count); break; } set_sample_count(count/channels); set_srate(sampleRate); set_channels(channels,channelConfig); } void audio_chunk::set_data_int16(const int16_t * src,t_size samples,unsigned nch,unsigned srate,unsigned channel_config) { const size_t count = samples * nch; this->set_data_size( count ); audio_sample * buffer = this->get_data(); audio_math::convert_from_int16(src, count, buffer, 1.0); set_sample_count(samples); set_srate(srate); set_channels(nch,channel_config); } template static void process_float_multi(audio_sample * p_out,const t_float * p_in,const t_size p_count) { for(size_t n=0;n static void process_float_multi_swap(audio_sample * p_out,const t_float * p_in,const t_size p_count) { for(size_t n=0;n(ptr),count); else process_float_multi(out,reinterpret_cast(ptr),count); } else if (bps == 64) { if (use_swap) process_float_multi_swap(out,reinterpret_cast(ptr),count); else process_float_multi(out,reinterpret_cast(ptr),count); } else if (bps == 16) { const uint16_t * in = reinterpret_cast(ptr); if (use_swap) { for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat16(pfc::byteswap_t(in[walk])); } else { for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat16(in[walk]); } } else if (bps == 24) { const uint8_t * in = reinterpret_cast(ptr); if (use_swap) { for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat24ptrbs(&in[walk*3]); } else { for(size_t walk = 0; walk < count; ++walk) out[walk] = audio_math::decodeFloat24ptr(&in[walk*3]); } } else pfc::throw_exception_with_message< exception_io_data >("invalid bit depth"); set_sample_count(count/nch); set_srate(srate); set_channels(nch,p_channel_config); } pfc::string8 audio_chunk::formatChunkSpec() const { pfc::string8 msg; msg << get_sample_rate() << " Hz, " << get_channels() << ":0x" << pfc::format_hex(get_channel_config(), 2) << " channels, " << get_sample_count() << " samples"; return msg; } void audio_chunk::debugChunkSpec() const { FB2K_DebugLog() << "Chunk: " << this->formatChunkSpec(); } #if PFC_DEBUG void audio_chunk::assert_valid(const char * ctx) const { if (!is_valid()) { FB2K_DebugLog() << "audio_chunk::assert_valid failure in " << ctx; debugChunkSpec(); uBugCheck(); } } #endif bool audio_chunk::is_valid() const { unsigned nch = get_channels(); if (nch == 0 || nch > 32) return false; if (!g_is_valid_sample_rate(get_srate())) return false; t_size samples = get_sample_count(); if (samples==0 || samples >= 0x80000000ul / (sizeof(audio_sample) * nch) ) return false; t_size size = get_data_size(); if (samples * nch > size) return false; if (!get_data()) return false; return true; } bool audio_chunk::is_spec_valid() const { return this->get_spec().is_valid(); } void audio_chunk::pad_with_silence_ex(t_size samples,unsigned hint_nch,unsigned hint_srate) { if (is_empty()) { if (hint_srate && hint_nch) { return set_data(0,samples,hint_nch,hint_srate); } else throw exception_io_data(); } else { if (hint_srate && hint_srate != get_srate()) samples = MulDiv_Size(samples,get_srate(),hint_srate); if (samples > get_sample_count()) { t_size old_size = get_sample_count() * get_channels(); t_size new_size = samples * get_channels(); set_data_size(new_size); pfc::memset_t(get_data() + old_size,(audio_sample)0,new_size - old_size); set_sample_count(samples); } } } void audio_chunk::pad_with_silence(t_size samples) { if (samples > get_sample_count()) { t_size old_size = get_sample_count() * get_channels(); t_size new_size = pfc::multiply_guarded(samples,(size_t)get_channels()); set_data_size(new_size); pfc::memset_t(get_data() + old_size,(audio_sample)0,new_size - old_size); set_sample_count(samples); } } void audio_chunk::set_silence(t_size samples) { t_size items = samples * get_channels(); set_data_size(items); pfc::memset_null_t(get_data(), items); set_sample_count(samples); } void audio_chunk::set_silence_seconds( double seconds ) { set_silence( (size_t) audio_math::time_to_samples( seconds, this->get_sample_rate() ) ); } void audio_chunk::insert_silence_fromstart(t_size samples) { t_size old_size = get_sample_count() * get_channels(); t_size delta = samples * get_channels(); t_size new_size = old_size + delta; set_data_size(new_size); audio_sample * ptr = get_data(); pfc::memmove_t(ptr+delta,ptr,old_size); pfc::memset_t(ptr,(audio_sample)0,delta); set_sample_count(get_sample_count() + samples); } bool audio_chunk::process_skip(double & skipDuration) { t_uint64 skipSamples = audio_math::time_to_samples(skipDuration, get_sample_rate()); if (skipSamples == 0) {skipDuration = 0; return true;} const t_size mySamples = get_sample_count(); if (skipSamples < mySamples) { skip_first_samples((t_size)skipSamples); skipDuration = 0; return true; } if (skipSamples == mySamples) { skipDuration = 0; return false; } skipDuration -= audio_math::samples_to_time(mySamples, get_sample_rate()); return false; } t_size audio_chunk::skip_first_samples(t_size samples_delta) { t_size samples_old = get_sample_count(); if (samples_delta >= samples_old) { set_sample_count(0); set_data_size(0); return samples_old; } else { t_size samples_new = samples_old - samples_delta; unsigned nch = get_channels(); audio_sample * ptr = get_data(); pfc::memmove_t(ptr,ptr+nch*samples_delta,nch*samples_new); set_sample_count(samples_new); set_data_size(nch*samples_new); return samples_delta; } } audio_sample audio_chunk::get_peak(audio_sample p_peak) const { return pfc::max_t(p_peak, get_peak()); } audio_sample audio_chunk::get_peak() const { return audio_math::calculate_peak(get_data(),get_sample_count() * get_channels()); } void audio_chunk::scale(audio_sample p_value) { audio_sample * ptr = get_data(); audio_math::scale(ptr,get_sample_count() * get_channels(),ptr,p_value); } namespace { struct sampleToIntDesc { unsigned bps, bpsValid; bool useUpperBits; float scale; }; template class sampleToInt { public: sampleToInt(sampleToIntDesc const & d) { clipLo = - ( (int_t) 1 << (d.bpsValid-1)); clipHi = ( (int_t) 1 << (d.bpsValid-1)) - 1; scale = (float) ( (int64_t) 1 << (d.bpsValid - 1) ) * d.scale; if (d.useUpperBits) { shift = d.bps - d.bpsValid; } else { shift = 0; } } inline int_t operator() (audio_sample s) const { int_t v; if (sizeof(int_t) > 4) v = (int_t) audio_math::rint64( s * scale ); else v = (int_t)audio_math::rint32( s * scale ); return pfc::clip_t( v, clipLo, clipHi) << shift; } private: int_t clipLo, clipHi; int8_t shift; float scale; }; } static void render_24bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { t_uint8 * outWalk = reinterpret_cast(out); sampleToInt gen(d); for(t_size walk = 0; walk < inLen; ++walk) { int32_t v = gen(in[walk]); *(outWalk ++) = (t_uint8) (v & 0xFF); *(outWalk ++) = (t_uint8) ((v >> 8) & 0xFF); *(outWalk ++) = (t_uint8) ((v >> 16) & 0xFF); } } static void render_8bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { sampleToInt gen(d); t_int8 * outWalk = reinterpret_cast(out); for(t_size walk = 0; walk < inLen; ++walk) { *outWalk++ = (t_int8)gen(in[walk]); } } static void render_16bit(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { sampleToInt gen(d); int16_t * outWalk = reinterpret_cast(out); for(t_size walk = 0; walk < inLen; ++walk) { *outWalk++ = (int16_t)gen(in[walk]); } } template static void render_32bit_(const audio_sample * in, t_size inLen, void * out, sampleToIntDesc const & d) { sampleToInt gen(d); // must use int64 for clipping int32_t * outWalk = reinterpret_cast(out); for(t_size walk = 0; walk < inLen; ++walk) { *outWalk++ = (int32_t)gen(in[walk]); } } bool audio_chunk::g_toFixedPoint(const audio_sample * in, void * out, size_t count, uint32_t bps, uint32_t bpsValid, bool useUpperBits, float scale) { const sampleToIntDesc d = {bps, bpsValid, useUpperBits, scale}; if (bps == 0) { PFC_ASSERT(!"How did we get here?"); return false; } else if (bps <= 8) { render_8bit(in, count, out, d); } else if (bps <= 16) { render_16bit(in, count, out, d); } else if (bps <= 24) { render_24bit(in, count, out, d); } else if (bps <= 32) { if (bpsValid <= 28) { // for speed render_32bit_(in, count, out, d); } else { render_32bit_(in, count, out, d); } } else { PFC_ASSERT(!"How did we get here?"); return false; } return true; } bool audio_chunk::toFixedPoint(class mem_block_container & out, uint32_t bps, uint32_t bpsValid, bool useUpperBits, float scale) const { bps = (bps + 7) & ~7; if (bps < bpsValid) return false; const size_t count = get_sample_count() * get_channel_count(); out.set_size( count * (bps/8) ); return g_toFixedPoint(get_data(), out.get_ptr(), count, bps, bpsValid, useUpperBits, scale); } bool audio_chunk::to_raw_data(mem_block_container & out, t_uint32 bps, bool useUpperBits, float scale) const { uint32_t bpsValid = bps; bps = (bps + 7) & ~7; const size_t count = get_sample_count() * get_channel_count(); out.set_size( count * (bps/8) ); void * outPtr = out.get_ptr(); audio_sample const * inPtr = get_data(); if (bps == 32) { float * f = (float*) outPtr; for(size_t w = 0; w < count; ++w) f[w] = inPtr[w] * scale; return true; } else { return g_toFixedPoint(inPtr, outPtr, count, bps, bpsValid, useUpperBits, scale); } } audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels) { return makeSpec( rate, channels, g_guess_channel_config(channels) ); } audio_chunk::spec_t audio_chunk::makeSpec(uint32_t rate, uint32_t channels, uint32_t mask) { spec_t spec = {}; spec.sampleRate = rate; spec.chanCount = channels; spec.chanMask = mask; return spec; } bool audio_chunk::spec_t::equals( const spec_t & v1, const spec_t & v2 ) { return v1.sampleRate == v2.sampleRate && v1.chanCount == v2.chanCount && v1.chanMask == v2.chanMask; } pfc::string8 audio_chunk::spec_t::toString(const char * delim) const { pfc::string_formatter temp; if ( sampleRate > 0 ) temp << sampleRate << "Hz"; if (chanCount > 0) { if ( temp.length() > 0 ) temp << delim; temp << chanCount << "ch"; } if ( chanMask != audio_chunk::channel_config_mono && chanMask != audio_chunk::channel_config_stereo ) { pfc::string8 strMask; audio_chunk::g_formatChannelMaskDesc( chanMask, strMask ); if ( temp.length() > 0) temp << delim; temp << strMask; } return temp; } audio_chunk::spec_t audio_chunk::get_spec() const { spec_t spec = {}; spec.sampleRate = this->get_sample_rate(); spec.chanCount = this->get_channel_count(); spec.chanMask = this->get_channel_config(); return spec; } void audio_chunk::set_spec(const spec_t & spec) { set_sample_rate(spec.sampleRate); set_channels( spec.chanCount, spec.chanMask ); } bool audio_chunk::spec_t::is_valid() const { if (this->chanCount==0 || this->chanCount>256) return false; if (!audio_chunk::g_is_valid_sample_rate(this->sampleRate)) return false; return true; } #ifdef _WIN32 WAVEFORMATEX audio_chunk::spec_t::toWFX() const { const uint32_t sampleWidth = sizeof(audio_sample); WAVEFORMATEX wfx = {}; wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; wfx.nChannels = chanCount; wfx.nSamplesPerSec = sampleRate; wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth; wfx.nBlockAlign = chanCount * sampleWidth; wfx.wBitsPerSample = sampleWidth * 8; return wfx; } WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEX() const { const uint32_t sampleWidth = sizeof(audio_sample); const bool isFloat = true; WAVEFORMATEXTENSIBLE wfxe; wfxe.Format = toWFX(); wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxe.Format.cbSize = sizeof(wfxe) - sizeof(wfxe.Format); wfxe.Samples.wValidBitsPerSample = sampleWidth * 8; wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(this->chanMask); wfxe.SubFormat = isFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; return wfxe; } WAVEFORMATEX audio_chunk::spec_t::toWFXWithBPS(uint32_t bps) const { const uint32_t sampleWidth = (bps+7)/8; WAVEFORMATEX wfx = {}; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = chanCount; wfx.nSamplesPerSec = sampleRate; wfx.nAvgBytesPerSec = sampleRate * chanCount * sampleWidth; wfx.nBlockAlign = chanCount * sampleWidth; wfx.wBitsPerSample = sampleWidth * 8; return wfx; } WAVEFORMATEXTENSIBLE audio_chunk::spec_t::toWFXEXWithBPS(uint32_t bps) const { const uint32_t sampleWidth = (bps + 7) / 8; const bool isFloat = false; WAVEFORMATEXTENSIBLE wfxe; wfxe.Format = toWFXWithBPS(bps); wfxe.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfxe.Format.cbSize = sizeof(wfxe) - sizeof(wfxe.Format); wfxe.Samples.wValidBitsPerSample = sampleWidth * 8; wfxe.dwChannelMask = audio_chunk::g_channel_config_to_wfx(this->chanMask); wfxe.SubFormat = isFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; return wfxe; } #endif // _WIN32 void audio_chunk::append(const audio_chunk& other) { if (other.get_spec() != this->get_spec()) { throw pfc::exception_invalid_params(); } this->grow_data_size(get_used_size() + other.get_used_size()); audio_sample* p = this->get_data() + get_used_size(); memcpy(p, other.get_data(), other.get_used_size() * sizeof(audio_sample)); set_sample_count(get_sample_count() + other.get_sample_count()); }