diff --git a/coder/coder.cpp b/coder/coder.cpp index 671e542..6c09125 100644 --- a/coder/coder.cpp +++ b/coder/coder.cpp @@ -606,7 +606,7 @@ compress_audio_streaming(const std::string& input_path, std::vector block_modes; PredContext pred_ctx; - PowCodec pow_enc(num_channels, 0.85f, true); + PowCodec pow_enc(num_channels, 0.85f, true, target_kbps / float(num_channels)); int current_pos = 0; int prev_block_size = 1024; @@ -645,6 +645,7 @@ compress_audio_streaming(const std::string& input_path, processed.reserve(chunk.size()); pow_enc.process(chunk.data(), (size_t)frames_read, processed); audio_buffer.insert(audio_buffer.end(), processed.begin(), processed.end()); + // pow_enc.printProfile(); } }; diff --git a/coder/fftw_wisdom.dat b/coder/fftw_wisdom.dat new file mode 100644 index 0000000..a6f9a51 --- /dev/null +++ b/coder/fftw_wisdom.dat @@ -0,0 +1,43 @@ +(fftw-3.3.10 fftwf_wisdom #x9e7d4dee #xdb14fed1 #x34bf76a4 #xeb6e8fdf + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #x01a5171a #xacba19d0 #xc2d82daa #x54b1dcc3) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xe75b944c #x09ac1906 #x449b29ce #x7b6666b0) + (fftwf_codelet_hc2cbdftv_8_sse2 0 #x10048 #x10048 #x0 #xe34ff8c8 #xe84377ec #x9549329e #x4f4160df) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x35391f4d #x4848fdd9 #x0d135c52 #x15138e60) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x1f5f22d9 #xed218381 #x10c9c094 #xe9e7bb90) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #xfb238183 #x0d9cb107 #x97d478a7 #xb17e66a0) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #xbf76898c #x7d78c8ce #x8377740c #x9d79896b) + (fftwf_codelet_t2bv_64_avx 0 #x10048 #x10048 #x0 #x71b35416 #x033191ee #x08427f48 #x060813e0) + (fftwf_codelet_n2bv_8_avx 0 #x10048 #x10048 #x0 #x3c3f5102 #xa27b92c3 #x235c75ee #x2fe6d6a2) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x68609657 #xbc9ec93a #x11b42de9 #xb606cbdd) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xba38f62e #x4946eacc #xea4ed48e #xfce17397) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xa8e723ad #x5577b756 #x19ace181 #xf0b6f4b2) + (fftwf_codelet_n2bv_64_avx 0 #x10048 #x10048 #x0 #x6c447a57 #x88d0c094 #x74220cbd #xa4af46b7) + (fftwf_codelet_t2bv_32_avx 0 #x10048 #x10048 #x0 #x889f271b #xfc322bc3 #x77dab9f2 #x358ba725) + (fftwf_codelet_t2fv_4_avx 0 #x11048 #x11048 #x0 #x17fbf099 #xff0d04f9 #x27f8a3ed #x01c0ab6a) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x76fa2a50 #xf4668261 #xbf8b003e #xbfa64206) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #xdae94766 #xa548e347 #x8a9a261b #xff2066cd) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #xb6399110 #x4c96f9f0 #xaab72706 #x16fd8170) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x85773c49 #x9a968ccc #xbd37e819 #xac77e6a4) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x3d3fc101 #x304dfb91 #x50844c77 #xf91c631a) + (fftwf_codelet_r2cbIII_8 2 #x10048 #x10048 #x0 #xa67302e8 #xb2c984d0 #x07cec891 #xc1db9fd1) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #xc447d79d #x3bb36c12 #x2e7c8386 #xc241521c) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x33b55dab #xe700b258 #x1523ad42 #x1ea71b18) + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #x53014ae3 #x5bebcd21 #xb7222d08 #xcb21e60a) + (fftwf_codelet_r2cb_8 2 #x10048 #x10048 #x0 #x4fdd90d1 #xecd5c536 #x8797e617 #xe1a3e349) + (fftwf_codelet_t2fv_4_avx 0 #x11048 #x11048 #x0 #xb0bde699 #x3baa1ac0 #xdbbed5b2 #x3b1456c3) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #x287a5212 #x9c627490 #xa9e4b096 #x3f08a900) + (fftwf_codelet_n1bv_64_avx 0 #x10048 #x10048 #x0 #x9b5c38be #xb6c1f945 #xbe407a24 #xd2b81773) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xffbbd2d6 #x1e2662c0 #x80466bbc #x2e3a8851) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x3f67885c #x1d118905 #x1b4c027b #x7ecffc87) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x42ead677 #x86451fba #xaefc212f #xfe3ad2af) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #x6ca4f32d #x29172dca #x83ff648b #xa2fc9877) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x1d5f8951 #x83ff8a7f #x97795bcc #x27178c7e) + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #xc266d7ae #xcc604c28 #xabefc5b9 #x5c765009) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #x03406dff #xc8fa0a2c #x62a9a61e #xa394d874) + (fftwf_codelet_t2fv_8_avx 0 #x11048 #x11048 #x0 #x80a102c8 #x332f47bc #x10cdb888 #xb57ff6f6) + (fftwf_codelet_n2bv_16_avx 0 #x10048 #x10048 #x0 #x09828f7b #x31d0c35d #xa617d809 #x4ccb96bd) + (fftwf_codelet_t2bv_64_avx 0 #x10048 #x10048 #x0 #xd86f0fcc #x804581bb #x5ccfdfba #x14108c49) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #xf6dad419 #xb3f2ed7c #x4d3ef187 #x7a0d85d4) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x9b71d905 #x7d7faf81 #x09c45860 #x2a522386) + (fftwf_codelet_n2fv_32_avx 0 #x11048 #x11048 #x0 #x3bd95895 #x03d8e6a7 #x3814b4a9 #x79dda258) +) diff --git a/coder/pow_filter.h b/coder/pow_filter.h index 6a4a07b..da3bd96 100644 --- a/coder/pow_filter.h +++ b/coder/pow_filter.h @@ -1,11 +1,10 @@ #pragma once - #include #include -#include #include #include #include +#include #include #ifndef M_PI @@ -19,155 +18,225 @@ class PowCodec { static constexpr int NBINS = NFFT / 2 + 1; static constexpr int PAD = NFFT / 2; - PowCodec(int num_channels, float gamma, bool encode) - : num_channels_(num_channels), gamma_(gamma), encode_(encode), - channels_(num_channels) - { - if (num_channels_ <= 0) throw std::runtime_error("PowCodec: bad channels"); - - in_ = fftwf_alloc_real(NFFT); - out_c_ = fftwf_alloc_complex(NBINS); - in_c2r_ = fftwf_alloc_complex(NBINS); - out_ = fftwf_alloc_real(NFFT); - - r2c_ = fftwf_plan_dft_r2c_1d(NFFT, in_, out_c_, FFTW_ESTIMATE); - c2r_ = fftwf_plan_dft_c2r_1d(NFFT, in_c2r_, out_, FFTW_ESTIMATE); - - win_.resize(NFFT); - win_sq_.resize(NFFT); - for (int i = 0; i < NFFT; ++i) { - float s = std::sin((float)M_PI * i / NFFT); - win_[i] = std::sqrt(std::sin((float)(M_PI / 2.0) * s * s)); - win_sq_[i] = win_[i] * win_[i]; - } - } - - ~PowCodec() { - fftwf_destroy_plan(r2c_); - fftwf_destroy_plan(c2r_); - fftwf_free(in_); - fftwf_free(out_c_); - fftwf_free(in_c2r_); - fftwf_free(out_); - } - - PowCodec(const PowCodec&) = delete; - PowCodec& operator=(const PowCodec&) = delete; - - void process(const float* in, size_t frames, std::vector& out) { - if (frames == 0) return; - - for (size_t f = 0; f < frames; ++f) { - for (int c = 0; c < num_channels_; ++c) { - channels_[c].queue.push_back(in[f * num_channels_ + c]); - } - for (int c = 0; c < num_channels_; ++c) { - channels_[c].total_in += 1; - } - } - - std::vector> ch_out(num_channels_); - for (int c = 0; c < num_channels_; ++c) - processChannel(channels_[c], ch_out[c], false); - - size_t n_out = ch_out.empty() ? 0 : ch_out[0].size(); - for (size_t f = 0; f < n_out; ++f) - for (int c = 0; c < num_channels_; ++c) - out.push_back(ch_out[c][f]); - } - - void flush(std::vector& out) { - std::vector> ch_out(num_channels_); - for (int c = 0; c < num_channels_; ++c) - processChannel(channels_[c], ch_out[c], true); - - size_t n_out = ch_out.empty() ? 0 : ch_out[0].size(); - for (size_t f = 0; f < n_out; ++f) - for (int c = 0; c < num_channels_; ++c) - out.push_back(ch_out[c][f]); - } - - void reset() { - for (auto& cs : channels_) cs = ChannelState{}; - } + PowCodec(int num_channels, float gamma, bool encode, float kbps_per_channel) + : num_channels_(num_channels), gamma_(gamma), encode_(encode), + kbps_per_channel_(kbps_per_channel), + channels_(num_channels), ch_out_(num_channels) + { + if (num_channels_ <= 0) throw std::runtime_error("PowCodec: bad channels"); + if (std::fabs(gamma_ - 0.85f) > 1e-6f) + throw std::runtime_error("PowCodec: this optimized version requires gamma = 0.85"); + + in_ = fftwf_alloc_real(NFFT); + out_c_ = fftwf_alloc_complex(NBINS); + in_c2r_ = fftwf_alloc_complex(NBINS); + out_ = fftwf_alloc_real(NFFT); + + if (!in_ || !out_c_ || !in_c2r_ || !out_) throw std::runtime_error("PowCodec: FFTW alloc failed"); + + const char* wisdom_file = "fftw_wisdom.dat"; + fftwf_import_wisdom_from_filename(wisdom_file); + + r2c_ = fftwf_plan_dft_r2c_1d(NFFT, in_, out_c_, FFTW_MEASURE); + c2r_ = fftwf_plan_dft_c2r_1d(NFFT, in_c2r_, out_, FFTW_MEASURE); + + if (!r2c_ || !c2r_) throw std::runtime_error("PowCodec: FFTW plan failed"); + + fftwf_export_wisdom_to_filename(wisdom_file); + + win_.resize(NFFT); + win_sq_.resize(NFFT); + for (int i = 0; i < NFFT; ++i) { + float s = std::sin((float)M_PI * (float)i / (float)NFFT); + win_[i] = std::sqrt(std::sin((float)(M_PI / 2.0) * s * s)); + win_sq_[i] = win_[i] * win_[i]; + } + } + + ~PowCodec() { + if (r2c_) fftwf_destroy_plan(r2c_); + if (c2r_) fftwf_destroy_plan(c2r_); + fftwf_free(in_); fftwf_free(out_c_); fftwf_free(in_c2r_); fftwf_free(out_); + } + + PowCodec(const PowCodec&) = delete; + PowCodec& operator=(const PowCodec&) = delete; + + void process(const float* in, size_t frames, std::vector& out) { + if (frames == 0) return; + out.reserve(out.size() + frames * num_channels_); + for (size_t f = 0; f < frames; ++f) { + for (int c = 0; c < num_channels_; ++c) { + channels_[c].queue.push_back(in[f * num_channels_ + c]); + channels_[c].total_in++; + } + } + for (int c = 0; c < num_channels_; ++c) { + ch_out_[c].clear(); + processChannel(channels_[c], ch_out_[c], false); + } + const size_t n_out = ch_out_.empty() ? 0 : ch_out_[0].size(); + for (size_t f = 0; f < n_out; ++f) { + for (int c = 0; c < num_channels_; ++c) { + out.push_back(ch_out_[c][f]); + } + } + } + + void flush(std::vector& out) { + for (int c = 0; c < num_channels_; ++c) { + ch_out_[c].clear(); + processChannel(channels_[c], ch_out_[c], true); + } + const size_t n_out = ch_out_.empty() ? 0 : ch_out_[0].size(); + out.reserve(out.size() + n_out * num_channels_); + for (size_t f = 0; f < n_out; ++f) { + for (int c = 0; c < num_channels_; ++c) { + out.push_back(ch_out_[c][f]); + } + } + } + + void reset() { + for (auto& cs : channels_) cs = ChannelState{}; + } private: struct ChannelState { - std::vector queue; - std::vector acc; - std::vector wacc; - int64_t queue_start = 0; - int64_t acc_start = 0; - int64_t next_frame = 0; - int64_t emitted = 0; - int64_t total_in = 0; - bool seeded = false; - bool tail_padded = false; + std::vector queue; + std::vector acc; + std::vector wacc; + int64_t queue_start = 0; + int64_t acc_start = 0; + int64_t next_frame = 0; + int64_t emitted = 0; + int64_t total_in = 0; + bool seeded = false; + bool tail_padded = false; }; - int num_channels_; - float gamma_; - bool encode_; + int num_channels_; + float gamma_; + bool encode_; + float kbps_per_channel_; std::vector channels_; - - fftwf_plan r2c_{}; - fftwf_plan c2r_{}; - float* in_ = nullptr; - fftwf_complex* out_c_ = nullptr; - fftwf_complex* in_c2r_ = nullptr; - float* out_ = nullptr; - std::vector win_, win_sq_; + std::vector> ch_out_; + + fftwf_plan r2c_{}; + fftwf_plan c2r_{}; + float* in_ = nullptr; + fftwf_complex* out_c_ = nullptr; + fftwf_complex* in_c2r_ = nullptr; + float* out_ = nullptr; + std::vector win_; + std::vector win_sq_; void processFrameInPlace(float* frame) { for (int i = 0; i < NFFT; ++i) in_[i] = frame[i] * win_[i]; fftwf_execute(r2c_); - + float mag[NBINS]; - float phase[NBINS]; float max_mag = 0.0f; - for (int i = 0; i < NBINS; ++i) { - float re = out_c_[i][0]; - float im = out_c_[i][1]; - float m = std::sqrt(re * re + im * im); - mag[i] = m; - phase[i] = std::atan2(im, re); + const float re = out_c_[i][0]; + const float im = out_c_[i][1]; + const float m = std::sqrt(re * re + im * im); + mag[i] = m; if (m > max_mag) max_mag = m; } - - if (gamma_ != 1.0f && max_mag > 1e-20f) { - float inv_max = 1.0f / max_mag; + + if (kbps_per_channel_ < 64.0f) { + max_mag *= 4.0f; + } + if (max_mag > 1e-20f) { + const float inv_max = 1.0f / max_mag; if (encode_) { + constexpr float GAMMA = 0.85f; for (int i = 0; i < NBINS; ++i) { float n = mag[i] * inv_max; - mag[i] = std::pow(n, gamma_) * max_mag; + n = powf(n, GAMMA); + mag[i] = n * max_mag; } } else { - float inv_g = 1.0f / gamma_; + constexpr float INV_GAMMA = 1.1764705882352941f; for (int i = 0; i < NBINS; ++i) { float n = mag[i] * inv_max; - mag[i] = std::pow(n, inv_g) * max_mag; + n = powf(n, INV_GAMMA); + mag[i] = n * max_mag; + } + + if (kbps_per_channel_ < 64.0f) { + constexpr int NUM_SUBBANDS = 32; + constexpr float VALLEY_STRENGTH = 0.5f; + constexpr float VALLEY_EXPONENT = 1.0f + VALLEY_STRENGTH; + + float enhanced[NBINS]; + const int base_size = NBINS / NUM_SUBBANDS; + const int remainder = NBINS % NUM_SUBBANDS; + int offset = 0; + + for (int band = 0; band < NUM_SUBBANDS; ++band) { + const int band_size = base_size + (band < remainder ? 1 : 0); + const int begin = offset; + const int end = begin + band_size; + offset = end; + + if (band_size <= 0) continue; + + float sum = 0.0f; + for (int i = begin; i < end; ++i) { + sum += mag[i]; + } + + const float level = sum / static_cast(band_size); + if (level <= 1e-20f) { + for (int i = begin; i < end; ++i) { + enhanced[i] = mag[i]; + } + continue; + } + + for (int i = begin; i < end; ++i) { + const float x = std::max(mag[i], 0.0f); + if (x < level) { + const float ratio = x / level; + enhanced[i] = level * powf(ratio, VALLEY_EXPONENT); + } else { + enhanced[i] = x; + } + } + } + + for (int i = 0; i < NBINS; ++i) { + mag[i] = enhanced[i]; + } } } } - + for (int i = 0; i < NBINS; ++i) { - float m = mag[i]; - float p = phase[i]; - in_c2r_[i][0] = m * std::cos(p); - in_c2r_[i][1] = m * std::sin(p); + const float re = out_c_[i][0]; + const float im = out_c_[i][1]; + const float old_mag = std::sqrt(re * re + im * im); + if (old_mag > 1e-20f) { + const float scale = mag[i] / old_mag; + in_c2r_[i][0] = re * scale; + in_c2r_[i][1] = im * scale; + } else { + in_c2r_[i][0] = 0.0f; + in_c2r_[i][1] = 0.0f; + } } + fftwf_execute(c2r_); - - const float norm = 1.0f / (float)NFFT; - for (int i = 0; i < NFFT; ++i) - frame[i] = out_[i] * norm * win_[i]; + constexpr float norm = 1.0f / (float)NFFT; + for (int i = 0; i < NFFT; ++i) frame[i] = out_[i] * norm * win_[i]; } void ensureAcc(ChannelState& cs, int64_t end_pos) { - int64_t cur_end = cs.acc_start + (int64_t)cs.acc.size(); + const int64_t cur_end = cs.acc_start + (int64_t)cs.acc.size(); if (end_pos <= cur_end) return; - size_t new_size = (size_t)(end_pos - cs.acc_start); + const size_t new_size = (size_t)(end_pos - cs.acc_start); cs.acc.resize(new_size, 0.0f); cs.wacc.resize(new_size, 0.0f); } @@ -175,100 +244,98 @@ class PowCodec { void processChannel(ChannelState& cs, std::vector& out, bool final) { if (!cs.seeded) { if (cs.queue.size() < (size_t)PAD + 1 && !final) return; - - int have = (int)cs.queue.size(); - std::vector prefix((size_t)PAD, 0.0f); + const int have = (int)cs.queue.size(); + std::vector new_queue; + new_queue.reserve(PAD + have); if (have >= PAD + 1) { for (int i = 0; i < PAD; ++i) { - int idx = PAD - i; - prefix[i] = cs.queue[(size_t)idx]; + new_queue.push_back(cs.queue[PAD - i]); + } + } else { + for (int i = 0; i < PAD; ++i) { + new_queue.push_back(0.0f); } } - std::vector nq; - nq.reserve(prefix.size() + cs.queue.size()); - nq.insert(nq.end(), prefix.begin(), prefix.end()); - nq.insert(nq.end(), cs.queue.begin(), cs.queue.end()); - cs.queue = std::move(nq); - cs.queue_start = 0; - cs.next_frame = 0; - cs.seeded = true; + for (int i = 0; i < have; ++i) { + new_queue.push_back(cs.queue[i]); + } + cs.queue = std::move(new_queue); + cs.queue_start = 0; + cs.next_frame = 0; + cs.seeded = true; } - + if (final && !cs.tail_padded) { cs.tail_padded = true; - int64_t real_end = (int64_t)PAD + cs.total_in; - - if ((int64_t)cs.queue.size() < real_end) - cs.queue.resize((size_t)real_end, 0.0f); - + const int64_t real_end = (int64_t)PAD + cs.total_in; + if ((int64_t)cs.queue.size() < real_end) cs.queue.resize((size_t)real_end, 0.0f); if (cs.total_in >= (int64_t)PAD + 2) { for (int i = 0; i < PAD; ++i) { - int64_t idx = real_end - 2 - i; - float v = 0.0f; - if (idx >= 0 && idx < (int64_t)cs.queue.size()) - v = cs.queue[(size_t)idx]; + const int64_t idx = real_end - 2 - i; + float v = (idx >= 0 && idx < (int64_t)cs.queue.size()) ? cs.queue[(size_t)idx] : 0.0f; cs.queue.push_back(v); } } else { cs.queue.resize(cs.queue.size() + (size_t)PAD, 0.0f); } } - - const int64_t input_end = (int64_t)PAD + cs.total_in; - + + const int64_t input_end = (int64_t)PAD + cs.total_in; while (true) { if (final && cs.next_frame >= input_end) break; - int64_t q_idx = cs.next_frame - cs.queue_start; if (q_idx < 0) { cs.queue_start = cs.next_frame; q_idx = 0; } - int64_t avail = (int64_t)cs.queue.size() - q_idx; if (avail < NFFT) { if (!final) break; if (avail <= 0 && cs.next_frame >= input_end) break; cs.queue.resize((size_t)(q_idx + NFFT), 0.0f); } - float frame[NFFT]; - for (int i = 0; i < NFFT; ++i) - frame[i] = cs.queue[(size_t)(q_idx + i)]; - + for (int i = 0; i < NFFT; ++i) frame[i] = cs.queue[(size_t)(q_idx + i)]; processFrameInPlace(frame); - ensureAcc(cs, cs.next_frame + NFFT); - int64_t a_idx = cs.next_frame - cs.acc_start; + const int64_t a_idx = cs.next_frame - cs.acc_start; for (int i = 0; i < NFFT; ++i) { - cs.acc [(size_t)(a_idx + i)] += frame[i]; + cs.acc[(size_t)(a_idx + i)] += frame[i]; cs.wacc[(size_t)(a_idx + i)] += win_sq_[i]; } cs.next_frame += HOP; } - - int64_t finalized = cs.next_frame; - int64_t emit_end = std::min(finalized, input_end); + + const int64_t finalized = cs.next_frame; + int64_t emit_end = std::min(finalized, input_end); if (emit_end < cs.emitted) emit_end = cs.emitted; - + out.reserve(out.size() + (emit_end - cs.emitted)); for (int64_t p = cs.emitted; p < emit_end; ++p) { - if (p < PAD) continue; - int64_t idx = p - cs.acc_start; + if (p < PAD) continue; + const int64_t idx = p - cs.acc_start; float v = 0.0f; - if (idx >= 0 && idx < (int64_t)cs.acc.size() && cs.wacc[(size_t)idx] > 1e-12f) + if (idx >= 0 && idx < (int64_t)cs.acc.size() && cs.wacc[(size_t)idx] > 1e-12f) { v = cs.acc[(size_t)idx] / cs.wacc[(size_t)idx]; + } out.push_back(v); } cs.emitted = emit_end; - + int64_t q_drop = cs.next_frame - cs.queue_start; - if (q_drop > 0 && q_drop <= (int64_t)cs.queue.size()) { - cs.queue.erase(cs.queue.begin(), cs.queue.begin() + (size_t)q_drop); - cs.queue_start += q_drop; + if (q_drop > 4096 && q_drop < (int64_t)cs.queue.size()) { + size_t drop = (size_t)q_drop; + size_t remaining = cs.queue.size() - drop; + std::memmove(cs.queue.data(), cs.queue.data() + drop, remaining * sizeof(float)); + cs.queue.resize(remaining); + cs.queue_start += drop; } - + int64_t a_drop = cs.emitted - cs.acc_start; - if (a_drop > 0 && a_drop <= (int64_t)cs.acc.size()) { - cs.acc .erase(cs.acc .begin(), cs.acc .begin() + (size_t)a_drop); - cs.wacc.erase(cs.wacc.begin(), cs.wacc.begin() + (size_t)a_drop); - cs.acc_start += a_drop; + if (a_drop > 4096 && a_drop < (int64_t)cs.acc.size()) { + size_t drop = (size_t)a_drop; + size_t remaining = cs.acc.size() - drop; + std::memmove(cs.acc.data(), cs.acc.data() + drop, remaining * sizeof(float)); + std::memmove(cs.wacc.data(), cs.wacc.data() + drop, remaining * sizeof(float)); + cs.acc.resize(remaining); + cs.wacc.resize(remaining); + cs.acc_start += drop; } } }; \ No newline at end of file diff --git a/decoder/decoder.cpp b/decoder/decoder.cpp index 65bab67..dbd873a 100644 --- a/decoder/decoder.cpp +++ b/decoder/decoder.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" @@ -17,14 +18,12 @@ #include "dequantize.h" #include "joint_stereo.h" #include "postfilter.h" - #include "pred_state.h" #include "codec_utils.h" #include "container_io.h" #include "sbr_decode.h" #include "overlap_add.h" - -#include "pow_filter.h" +#include "pow_filter.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -36,158 +35,157 @@ void decompress_wha_to_wav(const std::string& in_wha, { std::ifstream f(in_wha, std::ios::binary); if (!f) throw std::runtime_error("Cannot open " + in_wha); - + auto magic = read_n(f, 4); if (magic.size() != 4 || std::string((char*)magic.data(), 4) != "WHA1") throw std::runtime_error("Not a WHA container"); - + uint8_t version = read_u8(f); if (version != 18 && version != 19 && version != 20) throw std::runtime_error("Unsupported container version (only v18/v19/v20)"); - + uint32_t sr = read_u32(f); uint8_t num_channels = read_u8(f); uint32_t block_count = read_u32(f); float target_kbps = read_f32(f); uint8_t block_format_version = read_u8(f); - + if (block_format_version != 18 && block_format_version != 19 && block_format_version != 20) throw std::runtime_error("Unsupported block format version (only v18/v19/v20)"); - + bool pred_enabled = (block_format_version >= 19); - float per_channel_kbps = target_kbps / num_channels; float overlap_factor = (per_channel_kbps <= 0.0f) ? 0.0f : 1.0f; - + int mode_bytes = (block_count + 7) / 8; std::vector mode_packed = read_n(f, mode_bytes); std::vector block_modes = unpack_bits(mode_packed.data(), mode_bytes, block_count); - + drwav_data_format fmt; fmt.container = drwav_container_riff; fmt.format = DR_WAVE_FORMAT_IEEE_FLOAT; fmt.channels = num_channels; fmt.sampleRate = sr; fmt.bitsPerSample = 32; - + drwav wav; if (!drwav_init_file_write(&wav, out_wav.c_str(), &fmt, nullptr)) throw std::runtime_error("dr_wav failed to init"); - + PWPT wpt; ModeState long_state; ModeState short_state; bool stereo = (num_channels == 2); - - PowCodec pow_dec((int)num_channels, 0.85f, false); - + + PowCodec pow_dec((int)num_channels, 0.85f, false, target_kbps / float(num_channels)); OverlapAddState ola; ola.init(num_channels, write_batch_frames); - + std::vector write_buf; write_buf.reserve(write_batch_frames * num_channels); - + std::unique_ptr postproc; if (shouldApplySpectralFloor((int)num_channels, target_kbps)) { SpectralFloorPostFX::Config pp_cfg; postproc = std::make_unique((int)num_channels, pp_cfg); } - + + std::vector window, window_sq; + std::vector mode_ms, active0, active1; + std::vector> is_r; + std::vector is_inv, is_use_segmented; + std::vector steps0, steps1; + std::vector> quant0, quant1; + std::vector> ch0_bands, ch1_bands; + std::vector> recon_chs; + auto write_samples = [&](const std::vector& samples) { if (samples.empty()) return; size_t frames = samples.size() / num_channels; if (frames == 0) return; + + const float* data_to_write = samples.data(); + size_t frames_to_write = frames; + if (postproc) { int n_out = postproc->process(samples.data(), (int)frames); - if (n_out > 0) - drwav_write_pcm_frames(&wav, (uint64_t)n_out, postproc->out_data()); - } else { - drwav_write_pcm_frames(&wav, frames, samples.data()); + if (n_out > 0) { + data_to_write = postproc->out_data(); + frames_to_write = n_out; + } else { + return; + } } + drwav_write_pcm_frames(&wav, (uint64_t)frames_to_write, data_to_write); }; - + auto flush_write_buffer = [&]() { if (write_buf.empty()) return; std::vector decoded; decoded.reserve(write_buf.size()); + pow_dec.process(write_buf.data(), write_buf.size() / num_channels, decoded); write_samples(decoded); write_buf.clear(); }; - + int prev_block_size = 1024; - for (uint32_t bi = 0; bi < block_count; ++bi) { bool use_long_block = block_modes[bi]; int level = use_long_block ? 5 : 4; int block_size = use_long_block ? 2048 : 1024; - int right_overlap = use_long_block - ? (int)(96 * overlap_factor) - : (int)(48 * overlap_factor); - int left_overlap = (prev_block_size == 2048) - ? (int)(96 * overlap_factor) - : (int)(48 * overlap_factor); - - if (bi == 0) - left_overlap = 0; - + int right_overlap = use_long_block ? (int)(96 * overlap_factor) : (int)(48 * overlap_factor); + int left_overlap = (prev_block_size == 2048) ? (int)(96 * overlap_factor) : (int)(48 * overlap_factor); + if (bi == 0) left_overlap = 0; + int hop = block_size - right_overlap; int total_bands = 1 << level; - + std::vector band_shapes = compute_band_shapes(block_size, level); int expected_band_count = total_bands; - + ModeState& pred_state = use_long_block ? long_state : short_state; pred_state.ensure(expected_band_count); - + bool can_state_pred = false; if (pred_enabled && pred_state.ready && bi > pred_state.last_bi) { uint32_t age = bi - pred_state.last_bi; - if (age <= SAME_MODE_PRED_MAX_AGE) - can_state_pred = true; + if (age <= SAME_MODE_PRED_MAX_AGE) can_state_pred = true; } - - std::vector window, window_sq; + + window.clear(); window_sq.clear(); make_window(window, window_sq, block_size, left_overlap, right_overlap); - + uint32_t block_len = read_u32(f); std::vector blk(block_len); f.read((char*)blk.data(), block_len); - if ((size_t)f.gcount() < block_len) - throw std::runtime_error("Block truncated"); - + if ((size_t)f.gcount() < block_len) throw std::runtime_error("Block truncated"); + size_t ptr = 0; - auto need = [&](size_t n) { - if (ptr + n > blk.size()) - throw std::runtime_error("Block truncated"); - }; - + auto need = [&](size_t n) { if (ptr + n > blk.size()) throw std::runtime_error("Block truncated"); }; + int mode_bytes_loc = (expected_band_count + 7) / 8; - std::vector mode_ms(expected_band_count, 0); + mode_ms.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { - uint8_t byte = blk[ptr + (i >> 3)]; - mode_ms[i] = (byte >> (i & 7)) & 1; + uint8_t byte = blk[ptr + (i >> 3)]; mode_ms[i] = (byte >> (i & 7)) & 1; } ptr += mode_bytes_loc; - + int mask_bytes = (expected_band_count + 7) >> 3; - std::vector active0(expected_band_count, 0); + active0.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { - uint8_t byte = blk[ptr + (i >> 3)]; - active0[i] = (byte >> (i & 7)) & 1; + uint8_t byte = blk[ptr + (i >> 3)]; active0[i] = (byte >> (i & 7)) & 1; } ptr += mask_bytes; - - std::vector active1; + if (stereo) { - active1.assign(expected_band_count, 0); + active1.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { - uint8_t byte = blk[ptr + (i >> 3)]; - active1[i] = (byte >> (i & 7)) & 1; + uint8_t byte = blk[ptr + (i >> 3)]; active1[i] = (byte >> (i & 7)) & 1; } ptr += mask_bytes; } - + need(1); uint8_t k_scale_byte = blk[ptr++]; int k_scale0 = k_scale_byte & 0x07; @@ -195,33 +193,30 @@ void decompress_wha_to_wav(const std::string& in_wha, bool block_is_full = (k_scale_byte & 0x80) != 0; bool use_scale_pred = pred_enabled && can_state_pred && ((k_scale_byte & 0x40) != 0); int sbr_end = block_is_full ? (3 * expected_band_count / 4) : 0; - + need(2); uint16_t payload_len; memcpy(&payload_len, &blk[ptr], 2); ptr += 2; - if (ptr + payload_len > blk.size()) - throw std::runtime_error("Payload truncated"); - + if (ptr + payload_len > blk.size()) throw std::runtime_error("Payload truncated"); + BitReaderMSB payload_reader(blk.data() + ptr, payload_len); ptr += payload_len; - - std::vector> is_r(expected_band_count); + + is_r.resize(expected_band_count); for (auto& a : is_r) a.fill(0.5f); - std::vector is_inv(expected_band_count, false); - std::vector is_use_segmented(expected_band_count, false); + is_inv.resize(expected_band_count, false); + is_use_segmented.resize(expected_band_count, false); bool is_used = false; - + if (block_format_version >= 18 && stereo && target_kbps < 510.0f) { int is_start = get_is_start_band(target_kbps, total_bands); if (is_start < expected_band_count) { int segmented_threshold = 6 * total_bands / 16; - if (block_format_version >= 20) { std::vector is_indices; - for (int i = is_start; i < expected_band_count; ++i) - if (mode_ms[i]) is_indices.push_back(i); - + for (int i = is_start; i < expected_band_count; ++i) if (mode_ms[i]) is_indices.push_back(i); + int total_bits = 0; for (int idx : is_indices) { int bits = get_r_bits(idx); @@ -229,7 +224,7 @@ void decompress_wha_to_wav(const std::string& in_wha, } int total_bytes = (total_bits + 7) / 8; int inv_bytes = ((int)is_indices.size() + 7) / 8; - + if (ptr + total_bytes + inv_bytes <= blk.size()) { if (!is_indices.empty()) { is_used = true; @@ -249,10 +244,9 @@ void decompress_wha_to_wav(const std::string& in_wha, } } ptr += total_bytes; - auto inv_tmp = unpack_bits(blk.data() + ptr, inv_bytes, - (int)is_indices.size()); - for (size_t j = 0; j < is_indices.size(); ++j) - is_inv[is_indices[j]] = inv_tmp[j]; + + auto inv_tmp = unpack_bits(blk.data() + ptr, inv_bytes, (int)is_indices.size()); + for (size_t j = 0; j < is_indices.size(); ++j) is_inv[is_indices[j]] = inv_tmp[j]; ptr += inv_bytes; } } @@ -264,7 +258,7 @@ void decompress_wha_to_wav(const std::string& in_wha, } int total_bytes = (total_bits + 7) / 8; int inv_bytes = (expected_band_count - is_start + 7) / 8; - + if (ptr + total_bytes + inv_bytes <= blk.size()) { is_used = true; BitReaderMSB r_reader(blk.data() + ptr, total_bytes); @@ -283,175 +277,135 @@ void decompress_wha_to_wav(const std::string& in_wha, } } ptr += total_bytes; - auto inv_tmp = unpack_bits(blk.data() + ptr, inv_bytes, - expected_band_count - is_start); - for (int i = is_start; i < expected_band_count; ++i) - is_inv[i] = inv_tmp[i - is_start]; + + auto inv_tmp = unpack_bits(blk.data() + ptr, inv_bytes, expected_band_count - is_start); + for (int i = is_start; i < expected_band_count; ++i) is_inv[i] = inv_tmp[i - is_start]; ptr += inv_bytes; } } } } - - SBRDecodeResult sbr = sbr_decode( - blk.data(), ptr, blk.size(), - active0, sbr_end, expected_band_count, - block_format_version, can_state_pred, pred_state); - - std::vector steps0(expected_band_count, 0.0f); - std::vector steps1(expected_band_count, 0.0f); - - auto decode_scales = [&](std::vector& steps, - const std::vector& active, - int k_scale, - std::vector& prev_scale, - const std::vector& prev_active) - { + + SBRDecodeResult sbr = sbr_decode(blk.data(), ptr, blk.size(), active0, sbr_end, expected_band_count, block_format_version, can_state_pred, pred_state); + + steps0.resize(expected_band_count, 0.0f); + steps1.resize(expected_band_count, 0.0f); + + auto decode_scales = [&](std::vector& steps, const std::vector& active, int k_scale, std::vector& prev_scale, const std::vector& prev_active) { for (int i = 0; i < expected_band_count; ++i) { if (!active[i]) continue; auto idx_vec = rice_decode(payload_reader, 1, k_scale); - if (idx_vec.empty()) - throw std::runtime_error("Scale index missing"); + if (idx_vec.empty()) throw std::runtime_error("Scale index missing"); + uint32_t val = idx_vec[0]; - - int sb = get_scale_bits(i); - int max_idx = (1 << sb) - 1; + int sb = get_scale_bits(i); + int max_idx = (1 << sb) - 1; uint32_t idx; - - if (use_scale_pred && - i < (int)prev_active.size() && prev_active[i]) - { - int32_t d = zigzag_decode(val); + + if (use_scale_pred && i < (int)prev_active.size() && prev_active[i]) { + int32_t d = zigzag_decode(val); int64_t v = (int64_t)prev_scale[i] + d; - if (v < 0) v = 0; - if (v > max_idx) v = max_idx; + if (v < 0) v = 0; + if (v > max_idx) v = max_idx; idx = (uint32_t)v; - } else { - idx = val; - if (idx > (uint32_t)max_idx) idx = max_idx; + } else { + idx = val; + if (idx > (uint32_t)max_idx) idx = max_idx; } - steps[i] = idx_to_step(idx, sb); - if (i < (int)prev_scale.size()) - prev_scale[i] = idx; + if (i < (int)prev_scale.size()) prev_scale[i] = idx; } }; - - decode_scales(steps0, active0, k_scale0, - pred_state.prev_scale0, pred_state.prev_active0); - if (stereo) - decode_scales(steps1, active1, k_scale1, - pred_state.prev_scale1, pred_state.prev_active1); - - std::vector> quant0(expected_band_count); + + decode_scales(steps0, active0, k_scale0, pred_state.prev_scale0, pred_state.prev_active0); + if (stereo) decode_scales(steps1, active1, k_scale1, pred_state.prev_scale1, pred_state.prev_active1); + + quant0.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { if (!active0[i]) continue; - int k_sub = (int)payload_reader.read_bits(3); + int k_sub = (int)payload_reader.read_bits(3); auto uvals = rice_decode(payload_reader, band_shapes[i], k_sub); quant0[i].resize(uvals.size()); - for (size_t j = 0; j < uvals.size(); ++j) - quant0[i][j] = zigzag_decode(uvals[j]); + for (size_t j = 0; j < uvals.size(); ++j) quant0[i][j] = zigzag_decode(uvals[j]); } - - std::vector> quant1; + if (stereo) { quant1.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { if (!active1[i]) continue; - int k_sub = (int)payload_reader.read_bits(3); + int k_sub = (int)payload_reader.read_bits(3); auto uvals = rice_decode(payload_reader, band_shapes[i], k_sub); quant1[i].resize(uvals.size()); - for (size_t j = 0; j < uvals.size(); ++j) - quant1[i][j] = zigzag_decode(uvals[j]); + for (size_t j = 0; j < uvals.size(); ++j) quant1[i][j] = zigzag_decode(uvals[j]); } } - - std::vector> ch0_bands(expected_band_count); - std::vector> ch1_bands(expected_band_count); - + + ch0_bands.resize(expected_band_count); + ch1_bands.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { - if (active0[i]) - dequantize_band(quant0[i], steps0[i], ch0_bands[i]); - else - ch0_bands[i].assign(band_shapes[i], 0.0f); - + if (active0[i]) dequantize_band(quant0[i], steps0[i], ch0_bands[i]); + else { ch0_bands[i].clear(); ch0_bands[i].assign(band_shapes[i], 0.0f); } + if (stereo) { - if (active1[i]) - dequantize_band(quant1[i], steps1[i], ch1_bands[i]); - else - ch1_bands[i].assign(band_shapes[i], 0.0f); + if (active1[i]) dequantize_band(quant1[i], steps1[i], ch1_bands[i]); + else { ch1_bands[i].clear(); ch1_bands[i].assign(band_shapes[i], 0.0f); } } } - sbr_synthesize(ch0_bands, active0, band_shapes, sbr, sbr_end, bi); - + if (stereo) { int is_start = get_is_start_band(target_kbps, total_bands); for (int i = 0; i < expected_band_count; ++i) { if (mode_ms[i]) { - if (block_format_version >= 18 && is_used && - i >= is_start && is_start < expected_band_count) - { + if (block_format_version >= 18 && is_used && i >= is_start && is_start < expected_band_count) { std::vector left, right; - apply_is(ch0_bands[i], is_r[i], is_inv[i], - is_use_segmented[i], left, right); - ch0_bands[i] = std::move(left); - ch1_bands[i] = std::move(right); + apply_is(ch0_bands[i], is_r[i], is_inv[i], is_use_segmented[i], left, right); + ch0_bands[i] = std::move(left); ch1_bands[i] = std::move(right); } else { std::vector left_tmp, right_tmp; ms_to_lr(ch0_bands[i], ch1_bands[i], left_tmp, right_tmp); - ch0_bands[i] = std::move(left_tmp); - ch1_bands[i] = std::move(right_tmp); + ch0_bands[i] = std::move(left_tmp); ch1_bands[i] = std::move(right_tmp); } - } - else { - if (block_format_version >= 18 && is_used && - i >= is_start && is_start < expected_band_count) - { + } else { + if (block_format_version >= 18 && is_used && i >= is_start && is_start < expected_band_count) { std::vector left_tmp, right_tmp; ms_to_lr(ch0_bands[i], ch1_bands[i], left_tmp, right_tmp); - ch0_bands[i] = std::move(left_tmp); - ch1_bands[i] = std::move(right_tmp); + ch0_bands[i] = std::move(left_tmp); ch1_bands[i] = std::move(right_tmp); } } } } - - std::vector> recon_chs(num_channels); + + recon_chs.resize(num_channels); for (int ch = 0; ch < num_channels; ++ch) { auto& bands = (ch == 0) ? ch0_bands : ch1_bands; auto rec = wpt.iwpt(bands, sr, level, target_kbps, num_channels); - if (rec.size() < (size_t)block_size) - rec.resize(block_size, 0.0f); - else - rec.resize(block_size); + if (rec.size() < (size_t)block_size) rec.resize(block_size, 0.0f); + else rec.resize(block_size); recon_chs[ch] = std::move(rec); } - + int local_start = (ola.current_pos - ola.output_pos) + (int)ola.head_offset; overlap_add_block(ola, recon_chs, block_size, window, window_sq, local_start); ola.current_pos += hop; - extract_ready_samples(ola, write_buf); - - if (write_buf.size() >= write_batch_frames * num_channels) - flush_write_buffer(); - compact_ola_buffer(ola); - + + if (write_buf.size() >= write_batch_frames * num_channels) { + flush_write_buffer(); + } + if (pred_enabled) { pred_state.prev_active0.assign(expected_band_count, 0); - for (int i = 0; i < expected_band_count; ++i) - if (active0[i]) pred_state.prev_active0[i] = 1; - + for (int i = 0; i < expected_band_count; ++i) if (active0[i]) pred_state.prev_active0[i] = 1; + if (stereo) { pred_state.prev_active1.assign(expected_band_count, 0); - for (int i = 0; i < expected_band_count; ++i) - if (active1[i]) pred_state.prev_active1[i] = 1; + for (int i = 0; i < expected_band_count; ++i) if (active1[i]) pred_state.prev_active1[i] = 1; } else { pred_state.prev_active1.assign(expected_band_count, 0); } - + pred_state.prev_sbr_valid.assign(expected_band_count, 0); pred_state.prev_sbr_noise.assign(expected_band_count, 0); for (int idx : sbr.indices) { @@ -461,27 +415,27 @@ void decompress_wha_to_wav(const std::string& in_wha, pred_state.prev_sbr_rms[idx] = sbr.rms_idx[idx]; } } - pred_state.ready = true; - pred_state.last_bi = bi; + pred_state.ready = true; pred_state.last_bi = bi; } - prev_block_size = block_size; } - + extract_remaining(ola, write_buf); flush_write_buffer(); - + std::vector pow_tail; pow_dec.flush(pow_tail); write_samples(pow_tail); - + if (postproc) { int n_out = postproc->flush(); - if (n_out > 0) + if (n_out > 0) { drwav_write_pcm_frames(&wav, (uint64_t)n_out, postproc->out_data()); + } } - + drwav_uninit(&wav); + std::cout << "Decompressed: " << out_wav << " (frames=" << ola.output_pos << ", sr=" << sr @@ -492,11 +446,10 @@ void decompress_wha_to_wav(const std::string& in_wha, int main(int argc, char** argv) { if (argc < 3) { - std::cerr << "Usage: decoder [buffer_size_in_samples]" - << std::endl; + std::cerr << "Usage: decoder [buffer_size_in_samples]" << std::endl; return 1; } - + size_t buffer_size = 8192; if (argc >= 4) { try { @@ -510,22 +463,21 @@ int main(int argc, char** argv) { buffer_size = 8192; } } - + try { struct rusage usage_before, usage_after; getrusage(RUSAGE_SELF, &usage_before); - + decompress_wha_to_wav(argv[1], argv[2], buffer_size); - + getrusage(RUSAGE_SELF, &usage_after); double user_time_sec = (usage_after.ru_utime.tv_sec - usage_before.ru_utime.tv_sec) + (usage_after.ru_utime.tv_usec - usage_before.ru_utime.tv_usec) / 1000000.0; - + drwav wav_info; if (drwav_init_file(&wav_info, argv[2], nullptr)) { - double duration = - (double)wav_info.totalPCMFrameCount / (double)wav_info.sampleRate; + double duration = (double)wav_info.totalPCMFrameCount / (double)wav_info.sampleRate; double speed = (user_time_sec > 0.0) ? (duration / user_time_sec) : 0.0; std::cout << "Decoding speed: " << speed << "x realtime (" << user_time_sec << "s user time)" << std::endl; @@ -535,7 +487,7 @@ int main(int argc, char** argv) { std::cerr << "ERROR: " << ex.what() << std::endl; return 2; } - + return 0; } // DECODER diff --git a/decoder/fftw_wisdom.dat b/decoder/fftw_wisdom.dat new file mode 100644 index 0000000..a6f9a51 --- /dev/null +++ b/decoder/fftw_wisdom.dat @@ -0,0 +1,43 @@ +(fftw-3.3.10 fftwf_wisdom #x9e7d4dee #xdb14fed1 #x34bf76a4 #xeb6e8fdf + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #x01a5171a #xacba19d0 #xc2d82daa #x54b1dcc3) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xe75b944c #x09ac1906 #x449b29ce #x7b6666b0) + (fftwf_codelet_hc2cbdftv_8_sse2 0 #x10048 #x10048 #x0 #xe34ff8c8 #xe84377ec #x9549329e #x4f4160df) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x35391f4d #x4848fdd9 #x0d135c52 #x15138e60) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x1f5f22d9 #xed218381 #x10c9c094 #xe9e7bb90) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #xfb238183 #x0d9cb107 #x97d478a7 #xb17e66a0) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #xbf76898c #x7d78c8ce #x8377740c #x9d79896b) + (fftwf_codelet_t2bv_64_avx 0 #x10048 #x10048 #x0 #x71b35416 #x033191ee #x08427f48 #x060813e0) + (fftwf_codelet_n2bv_8_avx 0 #x10048 #x10048 #x0 #x3c3f5102 #xa27b92c3 #x235c75ee #x2fe6d6a2) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x68609657 #xbc9ec93a #x11b42de9 #xb606cbdd) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xba38f62e #x4946eacc #xea4ed48e #xfce17397) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xa8e723ad #x5577b756 #x19ace181 #xf0b6f4b2) + (fftwf_codelet_n2bv_64_avx 0 #x10048 #x10048 #x0 #x6c447a57 #x88d0c094 #x74220cbd #xa4af46b7) + (fftwf_codelet_t2bv_32_avx 0 #x10048 #x10048 #x0 #x889f271b #xfc322bc3 #x77dab9f2 #x358ba725) + (fftwf_codelet_t2fv_4_avx 0 #x11048 #x11048 #x0 #x17fbf099 #xff0d04f9 #x27f8a3ed #x01c0ab6a) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x76fa2a50 #xf4668261 #xbf8b003e #xbfa64206) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #xdae94766 #xa548e347 #x8a9a261b #xff2066cd) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #xb6399110 #x4c96f9f0 #xaab72706 #x16fd8170) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x85773c49 #x9a968ccc #xbd37e819 #xac77e6a4) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x3d3fc101 #x304dfb91 #x50844c77 #xf91c631a) + (fftwf_codelet_r2cbIII_8 2 #x10048 #x10048 #x0 #xa67302e8 #xb2c984d0 #x07cec891 #xc1db9fd1) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #xc447d79d #x3bb36c12 #x2e7c8386 #xc241521c) + (fftwf_codelet_hc2cbdft_2 0 #x10048 #x10048 #x0 #x33b55dab #xe700b258 #x1523ad42 #x1ea71b18) + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #x53014ae3 #x5bebcd21 #xb7222d08 #xcb21e60a) + (fftwf_codelet_r2cb_8 2 #x10048 #x10048 #x0 #x4fdd90d1 #xecd5c536 #x8797e617 #xe1a3e349) + (fftwf_codelet_t2fv_4_avx 0 #x11048 #x11048 #x0 #xb0bde699 #x3baa1ac0 #xdbbed5b2 #x3b1456c3) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #x287a5212 #x9c627490 #xa9e4b096 #x3f08a900) + (fftwf_codelet_n1bv_64_avx 0 #x10048 #x10048 #x0 #x9b5c38be #xb6c1f945 #xbe407a24 #xd2b81773) + (fftwf_codelet_r2cf_8 2 #x11048 #x11048 #x0 #xffbbd2d6 #x1e2662c0 #x80466bbc #x2e3a8851) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x3f67885c #x1d118905 #x1b4c027b #x7ecffc87) + (fftwf_codelet_hc2cfdftv_8_sse2 0 #x11048 #x11048 #x0 #x42ead677 #x86451fba #xaefc212f #xfe3ad2af) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #x6ca4f32d #x29172dca #x83ff648b #xa2fc9877) + (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x1d5f8951 #x83ff8a7f #x97795bcc #x27178c7e) + (fftwf_codelet_r2cbIII_2 2 #x10048 #x10048 #x0 #xc266d7ae #xcc604c28 #xabefc5b9 #x5c765009) + (fftwf_codelet_n2fv_64_avx 0 #x11048 #x11048 #x0 #x03406dff #xc8fa0a2c #x62a9a61e #xa394d874) + (fftwf_codelet_t2fv_8_avx 0 #x11048 #x11048 #x0 #x80a102c8 #x332f47bc #x10cdb888 #xb57ff6f6) + (fftwf_codelet_n2bv_16_avx 0 #x10048 #x10048 #x0 #x09828f7b #x31d0c35d #xa617d809 #x4ccb96bd) + (fftwf_codelet_t2bv_64_avx 0 #x10048 #x10048 #x0 #xd86f0fcc #x804581bb #x5ccfdfba #x14108c49) + (fftwf_codelet_r2cb_2 2 #x10048 #x10048 #x0 #xf6dad419 #xb3f2ed7c #x4d3ef187 #x7a0d85d4) + (fftwf_codelet_r2cfII_8 2 #x11048 #x11048 #x0 #x9b71d905 #x7d7faf81 #x09c45860 #x2a522386) + (fftwf_codelet_n2fv_32_avx 0 #x11048 #x11048 #x0 #x3bd95895 #x03d8e6a7 #x3814b4a9 #x79dda258) +) diff --git a/decoder/overlap_add.h b/decoder/overlap_add.h index f314b8f..de1e246 100644 --- a/decoder/overlap_add.h +++ b/decoder/overlap_add.h @@ -3,6 +3,7 @@ #include #include #include +#include struct OverlapAddState { std::vector accum; @@ -27,7 +28,6 @@ inline void make_window(std::vector& window, { window.assign(block_size, 1.0f); window_sq.assign(block_size, 1.0f); - if (left_overlap > 1) { for (int i = 0; i < left_overlap; ++i) { float t = (float)i / left_overlap; @@ -60,12 +60,17 @@ inline void overlap_add_block(OverlapAddState& ola, ola.weight.resize(needed, 0.0f); } + float* accum_ptr = ola.accum.data(); + float* weight_ptr = ola.weight.data(); + const float* win_ptr = window.data(); + const float* win_sq_ptr = window_sq.data(); + for (int c = 0; c < ch; ++c) { const float* src = recon_chs[c].data(); for (int i = 0; i < block_size; ++i) { int local_idx = (local_start + i) * ch + c; - ola.accum[local_idx] += src[i] * window[i]; - ola.weight[local_idx] += window_sq[i]; + accum_ptr[local_idx] += src[i] * win_ptr[i]; + weight_ptr[local_idx] += win_sq_ptr[i]; } } } @@ -75,42 +80,44 @@ inline int extract_ready_samples(OverlapAddState& ola, { int ready = ola.current_pos - ola.output_pos; if (ready <= 0) return 0; - int ch = ola.num_channels; + size_t offset = write_buf.size(); write_buf.resize(offset + ready * ch); float* out_ptr = write_buf.data() + offset; + + const float* accum_ptr = ola.accum.data(); + const float* weight_ptr = ola.weight.data(); for (int i = 0; i < ready; ++i) { int local_idx = ((int)ola.head_offset + i) * ch; for (int c = 0; c < ch; ++c) { - float val = (ola.weight[local_idx + c] > 1e-9f) - ? (ola.accum[local_idx + c] / ola.weight[local_idx + c]) - : 0.0f; + float w = weight_ptr[local_idx + c]; + float val = (w > 1e-9f) ? (accum_ptr[local_idx + c] / w) : 0.0f; out_ptr[i * ch + c] = val; } } - ola.head_offset += ready; ola.output_pos = ola.current_pos; return ready; } inline void compact_ola_buffer(OverlapAddState& ola) { - size_t compact_threshold = - (ola.write_batch_frames > 2048) ? ola.write_batch_frames / 2 : 2048; - if (ola.head_offset > compact_threshold && - ola.head_offset * 2 > ola.accum.size() / ola.num_channels) - { - size_t remove_elements = ola.head_offset * ola.num_channels; - if (remove_elements > 0 && remove_elements <= ola.accum.size()) { - ola.accum.erase(ola.accum.begin(), - ola.accum.begin() + remove_elements); - ola.weight.erase(ola.weight.begin(), - ola.weight.begin() + remove_elements); - ola.head_offset = 0; - } + if (ola.head_offset == 0) return; + + size_t ch = ola.num_channels; + size_t remove_elements = ola.head_offset * ch; + size_t total_elements = ola.accum.size(); + size_t remaining = total_elements - remove_elements; + + if (remaining > 0) { + std::memmove(ola.accum.data(), ola.accum.data() + remove_elements, remaining * sizeof(float)); + std::memmove(ola.weight.data(), ola.weight.data() + remove_elements, remaining * sizeof(float)); } + + ola.accum.resize(remaining); + ola.weight.resize(remaining); + ola.head_offset = 0; } inline int extract_remaining(OverlapAddState& ola, @@ -119,17 +126,19 @@ inline int extract_remaining(OverlapAddState& ola, int ch = ola.num_channels; size_t remaining = ola.accum.size() / ch - ola.head_offset; if (remaining == 0) return 0; - + size_t offset = write_buf.size(); write_buf.resize(offset + remaining * ch); float* out_ptr = write_buf.data() + offset; + + const float* accum_ptr = ola.accum.data(); + const float* weight_ptr = ola.weight.data(); for (size_t i = 0; i < remaining; ++i) { size_t local_idx = (ola.head_offset + i) * ch; for (int c = 0; c < ch; ++c) { - float val = (ola.weight[local_idx + c] > 1e-9f) - ? (ola.accum[local_idx + c] / ola.weight[local_idx + c]) - : 0.0f; + float w = weight_ptr[local_idx + c]; + float val = (w > 1e-9f) ? (accum_ptr[local_idx + c] / w) : 0.0f; out_ptr[i * ch + c] = val; } } diff --git a/decoder/pow_filter.h b/decoder/pow_filter.h index 7bd9650..da3bd96 100644 --- a/decoder/pow_filter.h +++ b/decoder/pow_filter.h @@ -1,11 +1,10 @@ #pragma once - #include #include -#include #include #include #include +#include #include #ifndef M_PI @@ -19,155 +18,225 @@ class PowCodec { static constexpr int NBINS = NFFT / 2 + 1; static constexpr int PAD = NFFT / 2; - PowCodec(int num_channels, float gamma, bool encode) - : num_channels_(num_channels), gamma_(gamma), encode_(encode), - channels_(num_channels) - { - if (num_channels_ <= 0) throw std::runtime_error("PowCodec: bad channels"); - - in_ = fftwf_alloc_real(NFFT); - out_c_ = fftwf_alloc_complex(NBINS); - in_c2r_ = fftwf_alloc_complex(NBINS); - out_ = fftwf_alloc_real(NFFT); - - r2c_ = fftwf_plan_dft_r2c_1d(NFFT, in_, out_c_, FFTW_ESTIMATE); - c2r_ = fftwf_plan_dft_c2r_1d(NFFT, in_c2r_, out_, FFTW_ESTIMATE); - - win_.resize(NFFT); - win_sq_.resize(NFFT); - for (int i = 0; i < NFFT; ++i) { - float s = std::sin((float)M_PI * i / NFFT); - win_[i] = std::sqrt(std::sin((float)(M_PI / 2.0) * s * s)); - win_sq_[i] = win_[i] * win_[i]; - } - } - - ~PowCodec() { - fftwf_destroy_plan(r2c_); - fftwf_destroy_plan(c2r_); - fftwf_free(in_); - fftwf_free(out_c_); - fftwf_free(in_c2r_); - fftwf_free(out_); - } - - PowCodec(const PowCodec&) = delete; - PowCodec& operator=(const PowCodec&) = delete; - - void process(const float* in, size_t frames, std::vector& out) { - if (frames == 0) return; - - for (size_t f = 0; f < frames; ++f) { - for (int c = 0; c < num_channels_; ++c) { - channels_[c].queue.push_back(in[f * num_channels_ + c]); - } - for (int c = 0; c < num_channels_; ++c) { - channels_[c].total_in += 1; - } - } - - std::vector> ch_out(num_channels_); - for (int c = 0; c < num_channels_; ++c) - processChannel(channels_[c], ch_out[c], false); - - size_t n_out = ch_out.empty() ? 0 : ch_out[0].size(); - for (size_t f = 0; f < n_out; ++f) - for (int c = 0; c < num_channels_; ++c) - out.push_back(ch_out[c][f]); - } - - void flush(std::vector& out) { - std::vector> ch_out(num_channels_); - for (int c = 0; c < num_channels_; ++c) - processChannel(channels_[c], ch_out[c], true); - - size_t n_out = ch_out.empty() ? 0 : ch_out[0].size(); - for (size_t f = 0; f < n_out; ++f) - for (int c = 0; c < num_channels_; ++c) - out.push_back(ch_out[c][f]); - } - - void reset() { - for (auto& cs : channels_) cs = ChannelState{}; - } + PowCodec(int num_channels, float gamma, bool encode, float kbps_per_channel) + : num_channels_(num_channels), gamma_(gamma), encode_(encode), + kbps_per_channel_(kbps_per_channel), + channels_(num_channels), ch_out_(num_channels) + { + if (num_channels_ <= 0) throw std::runtime_error("PowCodec: bad channels"); + if (std::fabs(gamma_ - 0.85f) > 1e-6f) + throw std::runtime_error("PowCodec: this optimized version requires gamma = 0.85"); + + in_ = fftwf_alloc_real(NFFT); + out_c_ = fftwf_alloc_complex(NBINS); + in_c2r_ = fftwf_alloc_complex(NBINS); + out_ = fftwf_alloc_real(NFFT); + + if (!in_ || !out_c_ || !in_c2r_ || !out_) throw std::runtime_error("PowCodec: FFTW alloc failed"); + + const char* wisdom_file = "fftw_wisdom.dat"; + fftwf_import_wisdom_from_filename(wisdom_file); + + r2c_ = fftwf_plan_dft_r2c_1d(NFFT, in_, out_c_, FFTW_MEASURE); + c2r_ = fftwf_plan_dft_c2r_1d(NFFT, in_c2r_, out_, FFTW_MEASURE); + + if (!r2c_ || !c2r_) throw std::runtime_error("PowCodec: FFTW plan failed"); + + fftwf_export_wisdom_to_filename(wisdom_file); + + win_.resize(NFFT); + win_sq_.resize(NFFT); + for (int i = 0; i < NFFT; ++i) { + float s = std::sin((float)M_PI * (float)i / (float)NFFT); + win_[i] = std::sqrt(std::sin((float)(M_PI / 2.0) * s * s)); + win_sq_[i] = win_[i] * win_[i]; + } + } + + ~PowCodec() { + if (r2c_) fftwf_destroy_plan(r2c_); + if (c2r_) fftwf_destroy_plan(c2r_); + fftwf_free(in_); fftwf_free(out_c_); fftwf_free(in_c2r_); fftwf_free(out_); + } + + PowCodec(const PowCodec&) = delete; + PowCodec& operator=(const PowCodec&) = delete; + + void process(const float* in, size_t frames, std::vector& out) { + if (frames == 0) return; + out.reserve(out.size() + frames * num_channels_); + for (size_t f = 0; f < frames; ++f) { + for (int c = 0; c < num_channels_; ++c) { + channels_[c].queue.push_back(in[f * num_channels_ + c]); + channels_[c].total_in++; + } + } + for (int c = 0; c < num_channels_; ++c) { + ch_out_[c].clear(); + processChannel(channels_[c], ch_out_[c], false); + } + const size_t n_out = ch_out_.empty() ? 0 : ch_out_[0].size(); + for (size_t f = 0; f < n_out; ++f) { + for (int c = 0; c < num_channels_; ++c) { + out.push_back(ch_out_[c][f]); + } + } + } + + void flush(std::vector& out) { + for (int c = 0; c < num_channels_; ++c) { + ch_out_[c].clear(); + processChannel(channels_[c], ch_out_[c], true); + } + const size_t n_out = ch_out_.empty() ? 0 : ch_out_[0].size(); + out.reserve(out.size() + n_out * num_channels_); + for (size_t f = 0; f < n_out; ++f) { + for (int c = 0; c < num_channels_; ++c) { + out.push_back(ch_out_[c][f]); + } + } + } + + void reset() { + for (auto& cs : channels_) cs = ChannelState{}; + } private: struct ChannelState { - std::vector queue; - std::vector acc; - std::vector wacc; - int64_t queue_start = 0; - int64_t acc_start = 0; - int64_t next_frame = 0; - int64_t emitted = 0; - int64_t total_in = 0; - bool seeded = false; - bool tail_padded = false; + std::vector queue; + std::vector acc; + std::vector wacc; + int64_t queue_start = 0; + int64_t acc_start = 0; + int64_t next_frame = 0; + int64_t emitted = 0; + int64_t total_in = 0; + bool seeded = false; + bool tail_padded = false; }; - int num_channels_; - float gamma_; - bool encode_; + int num_channels_; + float gamma_; + bool encode_; + float kbps_per_channel_; std::vector channels_; - - fftwf_plan r2c_{}; - fftwf_plan c2r_{}; - float* in_ = nullptr; - fftwf_complex* out_c_ = nullptr; - fftwf_complex* in_c2r_ = nullptr; - float* out_ = nullptr; - std::vector win_, win_sq_; + std::vector> ch_out_; + + fftwf_plan r2c_{}; + fftwf_plan c2r_{}; + float* in_ = nullptr; + fftwf_complex* out_c_ = nullptr; + fftwf_complex* in_c2r_ = nullptr; + float* out_ = nullptr; + std::vector win_; + std::vector win_sq_; void processFrameInPlace(float* frame) { for (int i = 0; i < NFFT; ++i) in_[i] = frame[i] * win_[i]; fftwf_execute(r2c_); - + float mag[NBINS]; - float phase[NBINS]; float max_mag = 0.0f; - for (int i = 0; i < NBINS; ++i) { - float re = out_c_[i][0]; - float im = out_c_[i][1]; - float m = std::sqrt(re * re + im * im); - mag[i] = m; - phase[i] = std::atan2(im, re); + const float re = out_c_[i][0]; + const float im = out_c_[i][1]; + const float m = std::sqrt(re * re + im * im); + mag[i] = m; if (m > max_mag) max_mag = m; } - - if (gamma_ != 1.0f && max_mag > 1e-20f) { - float inv_max = 1.0f / max_mag; + + if (kbps_per_channel_ < 64.0f) { + max_mag *= 4.0f; + } + if (max_mag > 1e-20f) { + const float inv_max = 1.0f / max_mag; if (encode_) { + constexpr float GAMMA = 0.85f; for (int i = 0; i < NBINS; ++i) { float n = mag[i] * inv_max; - mag[i] = std::pow(n, gamma_) * max_mag; + n = powf(n, GAMMA); + mag[i] = n * max_mag; } } else { - float inv_g = 1.0f / gamma_; + constexpr float INV_GAMMA = 1.1764705882352941f; for (int i = 0; i < NBINS; ++i) { float n = mag[i] * inv_max; - mag[i] = std::pow(n, inv_g) * max_mag; + n = powf(n, INV_GAMMA); + mag[i] = n * max_mag; + } + + if (kbps_per_channel_ < 64.0f) { + constexpr int NUM_SUBBANDS = 32; + constexpr float VALLEY_STRENGTH = 0.5f; + constexpr float VALLEY_EXPONENT = 1.0f + VALLEY_STRENGTH; + + float enhanced[NBINS]; + const int base_size = NBINS / NUM_SUBBANDS; + const int remainder = NBINS % NUM_SUBBANDS; + int offset = 0; + + for (int band = 0; band < NUM_SUBBANDS; ++band) { + const int band_size = base_size + (band < remainder ? 1 : 0); + const int begin = offset; + const int end = begin + band_size; + offset = end; + + if (band_size <= 0) continue; + + float sum = 0.0f; + for (int i = begin; i < end; ++i) { + sum += mag[i]; + } + + const float level = sum / static_cast(band_size); + if (level <= 1e-20f) { + for (int i = begin; i < end; ++i) { + enhanced[i] = mag[i]; + } + continue; + } + + for (int i = begin; i < end; ++i) { + const float x = std::max(mag[i], 0.0f); + if (x < level) { + const float ratio = x / level; + enhanced[i] = level * powf(ratio, VALLEY_EXPONENT); + } else { + enhanced[i] = x; + } + } + } + + for (int i = 0; i < NBINS; ++i) { + mag[i] = enhanced[i]; + } } } } - + for (int i = 0; i < NBINS; ++i) { - float m = mag[i]; - float p = phase[i]; - in_c2r_[i][0] = m * std::cos(p); - in_c2r_[i][1] = m * std::sin(p); + const float re = out_c_[i][0]; + const float im = out_c_[i][1]; + const float old_mag = std::sqrt(re * re + im * im); + if (old_mag > 1e-20f) { + const float scale = mag[i] / old_mag; + in_c2r_[i][0] = re * scale; + in_c2r_[i][1] = im * scale; + } else { + in_c2r_[i][0] = 0.0f; + in_c2r_[i][1] = 0.0f; + } } + fftwf_execute(c2r_); - - const float norm = 1.0f / (float)NFFT; - for (int i = 0; i < NFFT; ++i) - frame[i] = out_[i] * norm * win_[i]; + constexpr float norm = 1.0f / (float)NFFT; + for (int i = 0; i < NFFT; ++i) frame[i] = out_[i] * norm * win_[i]; } void ensureAcc(ChannelState& cs, int64_t end_pos) { - int64_t cur_end = cs.acc_start + (int64_t)cs.acc.size(); + const int64_t cur_end = cs.acc_start + (int64_t)cs.acc.size(); if (end_pos <= cur_end) return; - size_t new_size = (size_t)(end_pos - cs.acc_start); + const size_t new_size = (size_t)(end_pos - cs.acc_start); cs.acc.resize(new_size, 0.0f); cs.wacc.resize(new_size, 0.0f); } @@ -175,100 +244,98 @@ class PowCodec { void processChannel(ChannelState& cs, std::vector& out, bool final) { if (!cs.seeded) { if (cs.queue.size() < (size_t)PAD + 1 && !final) return; - - int have = (int)cs.queue.size(); - std::vector prefix((size_t)PAD, 0.0f); + const int have = (int)cs.queue.size(); + std::vector new_queue; + new_queue.reserve(PAD + have); if (have >= PAD + 1) { for (int i = 0; i < PAD; ++i) { - int idx = PAD - i; - prefix[i] = cs.queue[(size_t)idx]; + new_queue.push_back(cs.queue[PAD - i]); + } + } else { + for (int i = 0; i < PAD; ++i) { + new_queue.push_back(0.0f); } } - std::vector nq; - nq.reserve(prefix.size() + cs.queue.size()); - nq.insert(nq.end(), prefix.begin(), prefix.end()); - nq.insert(nq.end(), cs.queue.begin(), cs.queue.end()); - cs.queue = std::move(nq); - cs.queue_start = 0; - cs.next_frame = 0; - cs.seeded = true; + for (int i = 0; i < have; ++i) { + new_queue.push_back(cs.queue[i]); + } + cs.queue = std::move(new_queue); + cs.queue_start = 0; + cs.next_frame = 0; + cs.seeded = true; } - + if (final && !cs.tail_padded) { cs.tail_padded = true; - int64_t real_end = (int64_t)PAD + cs.total_in; - - if ((int64_t)cs.queue.size() < real_end) - cs.queue.resize((size_t)real_end, 0.0f); - + const int64_t real_end = (int64_t)PAD + cs.total_in; + if ((int64_t)cs.queue.size() < real_end) cs.queue.resize((size_t)real_end, 0.0f); if (cs.total_in >= (int64_t)PAD + 2) { for (int i = 0; i < PAD; ++i) { - int64_t idx = real_end - 2 - i; - float v = 0.0f; - if (idx >= 0 && idx < (int64_t)cs.queue.size()) - v = cs.queue[(size_t)idx]; + const int64_t idx = real_end - 2 - i; + float v = (idx >= 0 && idx < (int64_t)cs.queue.size()) ? cs.queue[(size_t)idx] : 0.0f; cs.queue.push_back(v); } } else { cs.queue.resize(cs.queue.size() + (size_t)PAD, 0.0f); } } - - const int64_t input_end = (int64_t)PAD + cs.total_in; - + + const int64_t input_end = (int64_t)PAD + cs.total_in; while (true) { if (final && cs.next_frame >= input_end) break; - int64_t q_idx = cs.next_frame - cs.queue_start; if (q_idx < 0) { cs.queue_start = cs.next_frame; q_idx = 0; } - int64_t avail = (int64_t)cs.queue.size() - q_idx; if (avail < NFFT) { if (!final) break; if (avail <= 0 && cs.next_frame >= input_end) break; cs.queue.resize((size_t)(q_idx + NFFT), 0.0f); } - float frame[NFFT]; - for (int i = 0; i < NFFT; ++i) - frame[i] = cs.queue[(size_t)(q_idx + i)]; - + for (int i = 0; i < NFFT; ++i) frame[i] = cs.queue[(size_t)(q_idx + i)]; processFrameInPlace(frame); - ensureAcc(cs, cs.next_frame + NFFT); - int64_t a_idx = cs.next_frame - cs.acc_start; + const int64_t a_idx = cs.next_frame - cs.acc_start; for (int i = 0; i < NFFT; ++i) { - cs.acc [(size_t)(a_idx + i)] += frame[i]; + cs.acc[(size_t)(a_idx + i)] += frame[i]; cs.wacc[(size_t)(a_idx + i)] += win_sq_[i]; } cs.next_frame += HOP; } - - int64_t finalized = cs.next_frame; - int64_t emit_end = std::min(finalized, input_end); + + const int64_t finalized = cs.next_frame; + int64_t emit_end = std::min(finalized, input_end); if (emit_end < cs.emitted) emit_end = cs.emitted; - + out.reserve(out.size() + (emit_end - cs.emitted)); for (int64_t p = cs.emitted; p < emit_end; ++p) { - if (p < PAD) continue; - int64_t idx = p - cs.acc_start; + if (p < PAD) continue; + const int64_t idx = p - cs.acc_start; float v = 0.0f; - if (idx >= 0 && idx < (int64_t)cs.acc.size() && cs.wacc[(size_t)idx] > 1e-12f) + if (idx >= 0 && idx < (int64_t)cs.acc.size() && cs.wacc[(size_t)idx] > 1e-12f) { v = cs.acc[(size_t)idx] / cs.wacc[(size_t)idx]; + } out.push_back(v); } cs.emitted = emit_end; - + int64_t q_drop = cs.next_frame - cs.queue_start; - if (q_drop > 0 && q_drop <= (int64_t)cs.queue.size()) { - cs.queue.erase(cs.queue.begin(), cs.queue.begin() + (size_t)q_drop); - cs.queue_start += q_drop; + if (q_drop > 4096 && q_drop < (int64_t)cs.queue.size()) { + size_t drop = (size_t)q_drop; + size_t remaining = cs.queue.size() - drop; + std::memmove(cs.queue.data(), cs.queue.data() + drop, remaining * sizeof(float)); + cs.queue.resize(remaining); + cs.queue_start += drop; } - + int64_t a_drop = cs.emitted - cs.acc_start; - if (a_drop > 0 && a_drop <= (int64_t)cs.acc.size()) { - cs.acc .erase(cs.acc .begin(), cs.acc .begin() + (size_t)a_drop); - cs.wacc.erase(cs.wacc.begin(), cs.wacc.begin() + (size_t)a_drop); - cs.acc_start += a_drop; + if (a_drop > 4096 && a_drop < (int64_t)cs.acc.size()) { + size_t drop = (size_t)a_drop; + size_t remaining = cs.acc.size() - drop; + std::memmove(cs.acc.data(), cs.acc.data() + drop, remaining * sizeof(float)); + std::memmove(cs.wacc.data(), cs.wacc.data() + drop, remaining * sizeof(float)); + cs.acc.resize(remaining); + cs.wacc.resize(remaining); + cs.acc_start += drop; } } }; \ No newline at end of file