diff --git a/coder/bit_alloc.h b/coder/bit_alloc.h index 6c35d5e..39fb056 100644 --- a/coder/bit_alloc.h +++ b/coder/bit_alloc.h @@ -47,6 +47,10 @@ inline DualAllocResult allocate_bits_dual( for (auto& x : max_bits_in) { x = 8; } + + // for (int i = max_bits_in.size() / 2; i < max_bits_in.size(); i++) { + // max_bits_in[i] = 0; + // } static thread_local std::vector bits0, bits1, max_b, active; static thread_local std::vector s2_0, s2_1, factor0, factor1, global_score; diff --git a/coder/coder.cpp b/coder/coder.cpp index 6c09125..891cdaa 100644 --- a/coder/coder.cpp +++ b/coder/coder.cpp @@ -12,7 +12,6 @@ #include #include #include - #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #include "wavelet.h" @@ -21,16 +20,13 @@ #include "joint_stereo.h" #include "quantize.h" #include "entropy_encoder.h" - #include "settings.h" #include "pred_state.h" #include "codec_utils.h" #include "transient.h" #include "energy_shape.h" #include "sbr_encode.h" - -#include "pow_filter.h" - +#include "pow_filter.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -117,19 +113,16 @@ std::vector compress_block_adaptive_joint( } bool use_is = (stereo && target_kbps < 510.0f); - std::vector mode_ms(band_count, 0); std::vector is_band(band_count, 0); std::vector> ch0_bands(band_count); std::vector> ch1_bands(band_count); std::vector> ch0_original(band_count); - for (int i = 0; i < band_count; ++i) ch0_original[i] = left_coeffs[i]; int is_start_default = band_count; int is_start_max = band_count; - std::vector> is_r_vals(band_count); for (auto& a : is_r_vals) a.fill(0.5f); std::vector is_inv_flags(band_count, false); @@ -150,6 +143,7 @@ std::vector compress_block_adaptive_joint( SETTINGS.enable_adaptive_is && (SETTINGS.adaptive_is_base_override != 0) && (in_range || SETTINGS.adaptive_is_base_override > 0); + if (allow_adaptive) { int base_max = get_adaptive_is_max_base(target_kbps, SETTINGS); is_start_max = get_is_start_from_base(base_max, total_bands); @@ -172,17 +166,20 @@ std::vector compress_block_adaptive_joint( ch1_bands[i] = right_band; continue; } + float El = 0.0f, Er = 0.0f; for (int j = 0; j < n; ++j) { El += left_band[j] * left_band[j]; Er += right_band[j] * right_band[j]; } + auto [mid, side] = mid_side(left_band, right_band); float Em = 0.0f, Es = 0.0f; for (int j = 0; j < n; ++j) { Em += mid[j] * mid[j]; Es += side[j] * side[j]; } + bool use_ms = use_mid_side(El, Er, Em, Es, enable_ms, target_kbps); if (use_ms) { mode_ms[i] = 1; @@ -194,11 +191,15 @@ std::vector compress_block_adaptive_joint( ch1_bands[i] = right_band; } } + if (use_is) { + BandEnergy be = compute_band_energy(ch0_bands, ch1_bands, is_band, stereo, band_count); int is_seg_threshold = 6 * total_bands / 16; + for (int i = is_start_default; i < band_count; ++i) { bool force_is = !adaptive_is || (i >= is_start_max); bool choose_is = force_is; + if (!force_is) { choose_is = should_use_is_band( left_coeffs[i], right_coeffs[i], @@ -207,17 +208,21 @@ std::vector compress_block_adaptive_joint( SETTINGS.is_equal_ratio_threshold, SETTINGS.is_equal_ratio_threshold_low_bitrate); } + if (choose_is) { is_band[i] = 1; mode_ms[i] = 1; + std::array r_arr; bool inv_flag = false; bool use_segmented = false; std::vector Y; + compute_is_parameters_ex(left_coeffs[i], right_coeffs[i], - Y, r_arr, - inv_flag, use_segmented, - i, total_bands); + Y, r_arr, + inv_flag, use_segmented, + i, total_bands); + is_r_vals[i] = r_arr; is_inv_flags[i] = inv_flag; is_use_segmented[i] = (i < is_seg_threshold); @@ -257,18 +262,20 @@ std::vector compress_block_adaptive_joint( static std::vector> prev_ch0, prev_ch1; std::vector priority0, priority1; + if (stereo) { priority0 = compute_channel_priority(ch0_bands, prev_ch0, coeff_counts, - target_kbps, sr, level, false); + target_kbps, sr, level, false); priority1 = compute_channel_priority(ch1_bands, prev_ch1, coeff_counts, - target_kbps, sr, level, true); + target_kbps, sr, level, true); } else { priority0 = compute_channel_priority(ch0_bands, prev_ch0, coeff_counts, - target_kbps, sr, level, false); + target_kbps, sr, level, false); priority1.assign(band_count, 0.0f); } std::vector min_bits(band_count, 0); std::vector max_bits(band_count, 10); + if (use_is) { for (int i = 0; i < band_count; ++i) { if (is_band[i]) { @@ -355,6 +362,7 @@ std::vector compress_block_adaptive_joint( uint32_t idx = get_scale_idx(step, sb); full_scale0[i] = idx; scale_raw0.push_back(idx); + if (can_state_pred) { uint32_t val = idx; if (i < (int)pred_state.prev_active0.size() && @@ -372,6 +380,7 @@ std::vector compress_block_adaptive_joint( uint32_t idx = get_scale_idx(step, sb); full_scale1[i] = idx; scale_raw1.push_back(idx); + if (can_state_pred) { uint32_t val = idx; if (i < (int)pred_state.prev_active1.size() && @@ -434,6 +443,7 @@ std::vector compress_block_adaptive_joint( payload_writer.write_bits(k_sub, 3); rice_encode(payload_writer, uvals, k_sub); } + if (stereo) { for (int i = 0; i < band_count; ++i) { if (!active1[i] || bits1[i] <= 0) continue; @@ -448,7 +458,6 @@ std::vector compress_block_adaptive_joint( } payload_writer.flush(); auto payload = payload_writer.data(); - uint16_t payload_len = (uint16_t)payload.size(); header.push_back(payload_len & 0xFF); header.push_back((payload_len >> 8) & 0xFF); @@ -492,17 +501,14 @@ std::vector compress_block_adaptive_joint( std::vector sbr_indices; for (int i = 0; i < sbr_end; ++i) if (!active0[i]) sbr_indices.push_back(i); - sbr_write(out, sbr, sbr_indices, can_state_pred, pred_state); } pred_state.prev_scale0 = full_scale0; pred_state.prev_scale1 = full_scale1; - pred_state.prev_active0.assign(band_count, 0); for (int i = 0; i < band_count; ++i) if (active0[i]) pred_state.prev_active0[i] = 1; - if (stereo) { pred_state.prev_active1.assign(band_count, 0); for (int i = 0; i < band_count; ++i) @@ -520,6 +526,7 @@ std::vector compress_block_adaptive_joint( pred_state.prev_sbr_noise[i] = sbr.noise_flag[i] ? 1 : 0; } } + pred_state.ready = true; pred_state.last_bi = block_index; @@ -531,13 +538,14 @@ void save_compressed_buffered(const std::vector>& blocks, const std::string& path, uint32_t sr, int num_channels, float target_kbps, + uint32_t total_frames, size_t write_buffer_blocks = 8192) { std::ofstream f(path, std::ios::binary); if (!f) throw std::runtime_error("Cannot create output file"); - + f.write("WHA1", 4); - uint8_t version = 20; + uint8_t version = 21; f.write((char*)&version, 1); f.write((char*)&sr, 4); uint8_t ch = (uint8_t)num_channels; @@ -545,9 +553,11 @@ void save_compressed_buffered(const std::vector>& blocks, uint32_t block_count = (uint32_t)blocks.size(); f.write((char*)&block_count, 4); f.write((char*)&target_kbps, 4); - uint8_t block_format_version = 20; + uint8_t block_format_version = 21; f.write((char*)&block_format_version, 1); + f.write((char*)&total_frames, 4); + int mode_bytes = ((int)block_count + 7) / 8; std::vector mode_packed(mode_bytes, 0); for (uint32_t i = 0; i < block_count; ++i) @@ -557,6 +567,7 @@ void save_compressed_buffered(const std::vector>& blocks, std::vector write_buffer; write_buffer.reserve(write_buffer_blocks * 4096); + auto flush_buffer = [&]() { if (!write_buffer.empty()) { f.write((char*)write_buffer.data(), write_buffer.size()); @@ -577,7 +588,7 @@ void save_compressed_buffered(const std::vector>& blocks, int long_blocks = (int)std::count(block_modes.begin(), block_modes.end(), true); int short_blocks = (int)block_count - long_blocks; - std::cout << "Saved container v20 to " << path << " (" + std::cout << "Saved container v21 to " << path << " (" << blocks.size() << " blocks, " << long_blocks << " long, " << short_blocks << " short)" << std::endl; @@ -592,6 +603,7 @@ compress_audio_streaming(const std::string& input_path, drwav wav; if (!drwav_init_file(&wav, input_path.c_str(), nullptr)) throw std::runtime_error("Cannot open WAV file"); + sr = wav.sampleRate; channels = wav.channels; uint64_t total_frames = wav.totalPCMFrameCount; @@ -616,7 +628,6 @@ compress_audio_streaming(const std::string& input_path, std::vector audio_buffer; size_t buffer_start = 0; - bool eof = false; bool pow_flushed = false; @@ -632,20 +643,22 @@ compress_audio_streaming(const std::string& input_path, } break; } + size_t samples_to_read = read_buffer_size_samples * num_channels; std::vector chunk(samples_to_read); drwav_uint64 frames_read = drwav_read_pcm_frames_f32(&wav, read_buffer_size_samples, chunk.data()); + if (frames_read == 0) { eof = true; continue; } + chunk.resize(frames_read * num_channels); std::vector processed; 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(); } }; @@ -654,11 +667,13 @@ compress_audio_streaming(const std::string& input_path, while (current_pos < (int)total_frames) { int analyze_len = std::min(2048, (int)total_frames - current_pos); ensure_data_available(current_pos + analyze_len); + if ((int)(buffer_start + audio_buffer.size() / num_channels) <= current_pos) break; std::vector analyze_buf(analyze_len * num_channels); size_t offset = (current_pos - (int)buffer_start) * num_channels; + if (offset + analyze_buf.size() <= audio_buffer.size()) { std::copy(audio_buffer.begin() + offset, audio_buffer.begin() + offset + analyze_buf.size(), @@ -699,6 +714,7 @@ compress_audio_streaming(const std::string& input_path, std::vector block(block_size * num_channels, 0.0f); size_t offset_block = (current_pos - (int)buffer_start) * num_channels; size_t avail_block = audio_buffer.size() - offset_block; + if (avail_block > 0) { size_t copy_samples = std::min(avail_block, block.size()); std::copy(audio_buffer.begin() + offset_block, @@ -719,6 +735,7 @@ compress_audio_streaming(const std::string& input_path, window[block_size - 1 - i] = t * t * (3.0f - 2.0f * t); } } + for (int c = 0; c < num_channels; ++c) for (int i = 0; i < block_size; ++i) block[i * num_channels + c] *= window[i]; @@ -732,6 +749,7 @@ compress_audio_streaming(const std::string& input_path, effective_budget = target_bits + max_reservoir; uint32_t block_index = (uint32_t)blocks_raw.size(); + auto comp = compress_block_adaptive_joint( block, wpt, num_channels, level, effective_budget, target_kbps, sr, block_size, @@ -763,6 +781,7 @@ compress_audio_streaming(const std::string& input_path, audio_buffer.begin() + remove_samples); buffer_start = keep_start; } + ensure_data_available(current_pos + 2048); } @@ -863,5 +882,36 @@ int main(int argc, char** argv) { std::cout << "Actual bitrate: " << actual_bitrate_kbps << " kbps" << std::endl; std::cout << "Encoding speed: " << realtime_speed << "x realtime" << std::endl; + save_compressed_buffered(blocks, block_modes, out_container, + sr, num_channels, tk, total_samples, read_buffer); + + 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; + + double audio_duration_sec = + (total_samples > 0) ? (double)total_samples / (double)sr : 0.0; + + size_t total_file_bytes = 22; + for (const auto& blk : blocks) + total_file_bytes += 4 + blk.size(); + + double actual_bitrate_kbps = + (audio_duration_sec > 0.0) + ? (double)total_file_bytes * 8.0 / 1000.0 / audio_duration_sec + : 0.0; + + double realtime_speed = + (user_time_sec > 0.0) ? (audio_duration_sec / user_time_sec) : 0.0; + + std::cout << "Compressed: " << block_count + << " blocks, user time=" << user_time_sec << "s" << std::endl; + std::cout << "Audio duration: " << audio_duration_sec << " s" << std::endl; + std::cout << "File size: " << total_file_bytes << " bytes" << std::endl; + std::cout << "Target bitrate: " << target_kbps << " kbps" << std::endl; + std::cout << "Actual bitrate: " << actual_bitrate_kbps << " kbps" << std::endl; + std::cout << "Encoding speed: " << realtime_speed << "x realtime" << std::endl; + return 0; } diff --git a/coder/perc_prior.h b/coder/perc_prior.h index aa7a28c..e68eafc 100644 --- a/coder/perc_prior.h +++ b/coder/perc_prior.h @@ -9,7 +9,8 @@ inline std::vector compute_channel_priority( { const float eps = 1e-12f; - int band_count = (int)coeffs.size(); + const int band_count = (int)coeffs.size(); + if (prev_coeffs.size() != coeffs.size()) prev_coeffs = coeffs; @@ -17,33 +18,42 @@ inline std::vector compute_channel_priority( priority.resize(band_count); float global_energy = 0.0f; + for (const auto& band : coeffs) for (float v : band) global_energy += v * v; - float global_energy_sq = global_energy + eps; + const float global_energy_sq = global_energy + eps; - int total_bands = 1 << level; + const int total_bands = 1 << level; std::vector weight_original(total_bands, 1.0f); for (int i = 0; i < total_bands; ++i) { - int popcnt = __builtin_popcount((unsigned)i); - float w = std::exp2((level - 2 * popcnt) * 0.5f); - weight_original[i] = w; - } + const int popcnt = + __builtin_popcount((unsigned)i); - std::vector perm(total_bands); - for (int i = 0; i < total_bands; ++i) - perm[i] = i ^ (i >> 1); + const float w = + std::exp2((level - 2 * popcnt) * 0.5f); - std::vector freq_weight(band_count, 1.0f); + weight_original[i] = w; + } - for (int i = 0; i < total_bands && i < band_count; ++i) { - int mapped = perm[i]; - if (mapped < band_count) { - freq_weight[mapped] = weight_original[i] * 0.75f; - } + std::vector freq_weight( + band_count, + 1.0f + ); + + for (int i = 0; + i < total_bands && i < band_count; + ++i) + { + const int old_index = + i ^ (i >> 1); + + if (old_index < total_bands) + freq_weight[i] = + weight_original[old_index] * 0.75f; } for (int i = 0; i < band_count; ++i) { @@ -55,27 +65,34 @@ inline std::vector compute_channel_priority( float l1 = 0.0f; for (float v : cur) { - float a = std::fabs(v); + const float a = std::fabs(v); max_v = std::max(max_v, a); l1 += a; } - float mean = l1 / std::max(cur.size(), 1); + const float mean = + l1 / std::max(cur.size(), 1); float peakiness = - (max_v - mean) / (max_v + eps); - peakiness = std::clamp(peakiness, 0.0f, 1.0f); + (max_v - mean) / + (max_v + eps); + + peakiness = + std::clamp(peakiness, 0.0f, 1.0f); float abs_energy = 0.0f; + for (float v : cur) abs_energy += v * v; float energy_local = - std::sqrt(abs_energy + eps) / (std::sqrt(global_energy_sq) + eps); + std::sqrt(abs_energy + eps) / + (std::sqrt(global_energy_sq) + eps); - energy_local = std::clamp(energy_local, 0.0f, 1.0f); + energy_local = + std::clamp(energy_local, 0.0f, 1.0f); - float structure = + const float structure = 0.65f * peakiness + 0.35f * energy_local; @@ -83,7 +100,8 @@ inline std::vector compute_channel_priority( float norm_prev = 0.0f; float norm_cur = 0.0f; - size_t N = std::min(cur.size(), prev.size()); + const size_t N = + std::min(cur.size(), prev.size()); for (size_t k = 0; k < N; ++k) { corr += cur[k] * prev[k]; @@ -92,11 +110,14 @@ inline std::vector compute_channel_priority( } float predict = - corr / (std::sqrt(norm_prev * norm_cur) + eps); + corr / + (std::sqrt(norm_prev * norm_cur) + eps); - predict = std::clamp(predict, 0.0f, 1.0f); + predict = + std::clamp(predict, 0.0f, 1.0f); float l2 = 0.0f; + for (float v : cur) l2 += v * v; @@ -106,29 +127,72 @@ inline std::vector compute_channel_priority( (l1 * l1) / (cur.size() * l2 * l2 + eps); - sparsity = std::clamp(sparsity, 0.0f, 1.0f); + sparsity = + std::clamp(sparsity, 0.0f, 1.0f); float inter_scale = 0.5f; if (i > 0 && i < band_count - 1) { - auto band_energy = [&](int b) -> float { + auto band_energy = + [&](int b) -> float + { float e = 0.0f; - for (float v : coeffs[b]) e += v * v; + + for (float v : coeffs[b]) + e += v * v; + return std::sqrt(e + eps); }; - float e_prev = band_energy(i - 1); - float e_next = band_energy(i + 1); - float e_cur = std::sqrt(abs_energy + eps); + const float e_prev = + band_energy(i - 1); + + const float e_next = + band_energy(i + 1); + + const float e_cur = + std::sqrt(abs_energy + eps); + + const float avg_neighbors = + 0.5f * (e_prev + e_next); - float avg_neighbors = 0.5f * (e_prev + e_next); + inter_scale = + 1.0f - + std::fabs(e_cur - avg_neighbors) / + (avg_neighbors + eps); inter_scale = - 1.0f - std::fabs(e_cur - avg_neighbors) / - (avg_neighbors + eps); + std::clamp(inter_scale, 0.0f, 1.0f); + } + + const float cur_peak = max_v; + + float prev_peak = 0.0f; + + for (float v : prev) + prev_peak = + std::max(prev_peak, std::fabs(v)); + + constexpr float PEAK_THRESHOLD = 0.01f; - inter_scale = std::clamp(inter_scale, 0.0f, 1.0f); + float transient = 0.0f; + + if (cur_peak > PEAK_THRESHOLD && + prev_peak > PEAK_THRESHOLD) + { + const float peak_ratio = + cur_peak / prev_peak; + + transient = + std::log2(peak_ratio + 1.0f); + + transient = + std::clamp( + transient, + 0.0f, + 4.0f + ); } float cur_peak = max_v; @@ -150,14 +214,16 @@ inline std::vector compute_channel_priority( } float p_val = - 0.35f * predict + - 0.55f * sparsity + - 0.20f * structure + - 0.20f * inter_scale + + 0.35f * predict + + 0.55f * sparsity + + 0.20f * structure + + 0.20f * inter_scale + transient; p_val *= freq_weight[i]; - p_val = std::tanh(1.3f * p_val); + + p_val = + std::tanh(1.3f * p_val); priority[i] = p_val; } @@ -169,21 +235,27 @@ inline std::vector compute_channel_priority( for (int i = 0; i < band_count; ++i) { priority[i] = - 0.70f * priority[i] + - 0.30f * prev_priority[i]; + 0.70f * priority[i] + + 0.30f * prev_priority[i]; } + prev_priority = priority; prev_coeffs = coeffs; float sum = 0.0f; + for (float v : priority) sum += v; if (sum < eps) { - std::fill(priority.begin(), priority.end(), 1.0f / band_count); + std::fill( + priority.begin(), + priority.end(), + 1.0f / band_count + ); + return priority; } return priority; } - diff --git a/coder/quantize.h b/coder/quantize.h index 5a261b4..cb89ba6 100644 --- a/coder/quantize.h +++ b/coder/quantize.h @@ -237,6 +237,14 @@ inline QuantResult quantize_levels( quantized_per_level[l] = std::move(quantized); } + // for (auto x : quantized_per_level) { + // for (auto y : x) { + // std::cout << y << ' '; + // } + // std::cout << " | "; + // } + // std::cout << '\n'; + return { std::move(scales), std::move(quantized_per_level) }; } diff --git a/coder/sbr_encode.h b/coder/sbr_encode.h index 0201f06..8f5cbe0 100644 --- a/coder/sbr_encode.h +++ b/coder/sbr_encode.h @@ -41,7 +41,11 @@ inline SBREncodeResult sbr_analyze( float rms = std::sqrt(static_cast(sum_sq / orig.size()) + eps); float crest = max_abs / (rms + eps); - result.noise_flag[i] = (crest < 2.5f); + // if (i >= band_count / 2) { + // std::cout << crest << '\n'; + // } + + result.noise_flag[i] = (crest < 3.5f); int sb = get_scale_bits(i); result.rms_idx[i] = get_scale_idx(rms, sb); } diff --git a/coder/wavelet.h b/coder/wavelet.h index e989015..554730f 100644 --- a/coder/wavelet.h +++ b/coder/wavelet.h @@ -130,51 +130,79 @@ class PWPT : public DD64in { } std::vector gray_permutation(int level) const { - int n = 1 << level; + const int n = 1 << level; std::vector perm(n); + for (int i = 0; i < n; ++i) perm[i] = i ^ (i >> 1); + return perm; } public: - std::vector> wpt(const std::vector& signal, - int levels, - float bitrate, - int sr, - int channels) { + std::vector> wpt( + const std::vector& signal, + int levels, + float bitrate, + int sr, + int channels) + { std::vector data = signal; + wpt_decompose(data, levels); + const size_t N = data.size(); const int total_bands = 1 << levels; const size_t band_size = N >> levels; if (levels > 1) { - auto perm = gray_permutation(levels); + const auto perm = gray_permutation(levels); + std::vector ordered(N); + for (int i = 0; i < total_bands; ++i) { - const size_t src = i * band_size; - const size_t dst = perm[i] * band_size; - std::copy(data.begin() + src, - data.begin() + src + band_size, - ordered.begin() + dst); + const size_t src = static_cast(perm[i]) * band_size; + const size_t dst = static_cast(i) * band_size; + + std::copy( + data.begin() + src, + data.begin() + src + band_size, + ordered.begin() + dst + ); } + data.swap(ordered); } if (sr >= 44100) { - int atten_start = total_bands * 3 / 4; + { + const int atten_start = total_bands * 7 / 8; + for (int i = atten_start; i < total_bands; ++i) { float* begin = data.data() + i * band_size; - float* end = begin + band_size; + float* end = begin + band_size; + + for (float* p = begin; p != end; ++p) + *p *= 0.0f; + } + } + const int atten_start = total_bands * 3 / 4; + + for (int i = atten_start; i < total_bands; ++i) { + float* begin = data.data() + i * band_size; + float* end = begin + band_size; + for (float* p = begin; p != end; ++p) *p *= 0.125f; } - if (32 <= bitrate / float(channels) && bitrate / float(channels) < 64) { + if (32 <= bitrate / float(channels) && + bitrate / float(channels) < 64) + { for (int i = atten_start; i < total_bands; ++i) { float* begin = data.data() + i * band_size; - float* end = begin + band_size; + float* end = begin + band_size; + for (float* p = begin; p != end; ++p) *p *= 0.125f; } @@ -182,10 +210,14 @@ class PWPT : public DD64in { } std::vector> tree(total_bands); + for (int i = 0; i < total_bands; ++i) { - tree[i].assign(data.begin() + i * band_size, - data.begin() + (i + 1) * band_size); + tree[i].assign( + data.begin() + i * band_size, + data.begin() + (i + 1) * band_size + ); } + return tree; } }; diff --git a/decoder/decoder.cpp b/decoder/decoder.cpp index dbd873a..b56dbba 100644 --- a/decoder/decoder.cpp +++ b/decoder/decoder.cpp @@ -9,8 +9,7 @@ #include #include #include -#include - +#include #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" #include "wavelet.h" @@ -24,7 +23,6 @@ #include "sbr_decode.h" #include "overlap_add.h" #include "pow_filter.h" - #ifndef M_PI #define M_PI 3.14159265358979323846 #endif @@ -35,61 +33,66 @@ 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)"); - + if (version != 18 && version != 19 && version != 20 && version != 21) + throw std::runtime_error("Unsupported container version (only v18/v19/v20/v21)"); + 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)"); - + if (block_format_version != 18 && block_format_version != 19 && block_format_version != 20 && block_format_version != 21) + throw std::runtime_error("Unsupported block format version (only v18/v19/v20/v21)"); + + uint32_t total_frames = 0; + if (version >= 21) { + total_frames = read_u32(f); + } + 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, 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; @@ -98,15 +101,22 @@ void decompress_wha_to_wav(const std::string& in_wha, std::vector> quant0, quant1; std::vector> ch0_bands, ch1_bands; std::vector> recon_chs; - + + uint64_t frames_written = 0; + auto write_samples = [&](const std::vector& samples) { if (samples.empty()) return; size_t frames = samples.size() / num_channels; if (frames == 0) return; - + + if (total_frames > 0 && frames_written + frames > total_frames) { + frames = total_frames - frames_written; + 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) { @@ -116,19 +126,25 @@ void decompress_wha_to_wav(const std::string& in_wha, return; } } + + if (total_frames > 0 && frames_written + frames_to_write > total_frames) { + frames_to_write = total_frames - frames_written; + if (frames_to_write == 0) return; + } + drwav_write_pcm_frames(&wav, (uint64_t)frames_to_write, data_to_write); + frames_written += frames_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]; @@ -137,25 +153,24 @@ void decompress_wha_to_wav(const std::string& in_wha, 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; } - + 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); @@ -163,21 +178,21 @@ void decompress_wha_to_wav(const std::string& in_wha, size_t ptr = 0; 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; 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; } ptr += mode_bytes_loc; - + int mask_bytes = (expected_band_count + 7) >> 3; 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; } ptr += mask_bytes; - + if (stereo) { active1.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { @@ -185,30 +200,31 @@ void decompress_wha_to_wav(const std::string& in_wha, } ptr += mask_bytes; } - + need(1); uint8_t k_scale_byte = blk[ptr++]; int k_scale0 = k_scale_byte & 0x07; int k_scale1 = stereo ? ((k_scale_byte >> 3) & 0x07) : 0; 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"); - + BitReaderMSB payload_reader(blk.data() + ptr, payload_len); ptr += payload_len; - + is_r.resize(expected_band_count); for (auto& a : is_r) a.fill(0.5f); 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) { @@ -216,7 +232,6 @@ void decompress_wha_to_wav(const std::string& in_wha, 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); - int total_bits = 0; for (int idx : is_indices) { int bits = get_r_bits(idx); @@ -224,7 +239,6 @@ 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; @@ -244,7 +258,6 @@ 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]; ptr += inv_bytes; @@ -258,7 +271,6 @@ 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); @@ -277,7 +289,6 @@ 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]; ptr += inv_bytes; @@ -285,41 +296,39 @@ void decompress_wha_to_wav(const std::string& in_wha, } } } - + 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"); - 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); + 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; } }; - + 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; @@ -328,7 +337,7 @@ void decompress_wha_to_wav(const std::string& in_wha, quant0[i].resize(uvals.size()); for (size_t j = 0; j < uvals.size(); ++j) quant0[i][j] = zigzag_decode(uvals[j]); } - + if (stereo) { quant1.resize(expected_band_count); for (int i = 0; i < expected_band_count; ++i) { @@ -338,21 +347,33 @@ void decompress_wha_to_wav(const std::string& in_wha, quant1[i].resize(uvals.size()); for (size_t j = 0; j < uvals.size(); ++j) quant1[i][j] = zigzag_decode(uvals[j]); } - } + }; + + 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); + 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]); + } + 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].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].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) { @@ -375,7 +396,7 @@ void decompress_wha_to_wav(const std::string& in_wha, } } } - + recon_chs.resize(num_channels); for (int ch = 0; ch < num_channels; ++ch) { auto& bands = (ch == 0) ? ch0_bands : ch1_bands; @@ -384,28 +405,28 @@ void decompress_wha_to_wav(const std::string& in_wha, 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); 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; - 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; } 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) { @@ -417,25 +438,32 @@ void decompress_wha_to_wav(const std::string& in_wha, } 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) { - drwav_write_pcm_frames(&wav, (uint64_t)n_out, postproc->out_data()); + size_t frames_to_write = n_out; + if (total_frames > 0 && frames_written + frames_to_write > total_frames) { + frames_to_write = total_frames - frames_written; + } + if (frames_to_write > 0) { + drwav_write_pcm_frames(&wav, (uint64_t)frames_to_write, postproc->out_data()); + frames_written += frames_to_write; + } } } - + drwav_uninit(&wav); - std::cout << "Decompressed: " << out_wav << " (frames=" << ola.output_pos << ", sr=" << sr @@ -449,7 +477,7 @@ int main(int argc, char** argv) { std::cerr << "Usage: decoder [buffer_size_in_samples]" << std::endl; return 1; } - + size_t buffer_size = 8192; if (argc >= 4) { try { @@ -463,18 +491,18 @@ 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; @@ -487,7 +515,7 @@ int main(int argc, char** argv) { std::cerr << "ERROR: " << ex.what() << std::endl; return 2; } - + return 0; } // DECODER diff --git a/decoder/sbr_decode.h b/decoder/sbr_decode.h index 7702f3d..d012be6 100644 --- a/decoder/sbr_decode.h +++ b/decoder/sbr_decode.h @@ -150,55 +150,122 @@ inline SBRDecodeResult sbr_decode(const uint8_t* blk, size_t& ptr, size_t blk_si return result; } -inline void sbr_synthesize(std::vector>& ch0_bands, - const std::vector& active0, - const std::vector& band_shapes, - const SBRDecodeResult& sbr, - int sbr_end, - uint32_t block_idx) +inline void sbr_synthesize( + std::vector>& ch0_bands, + const std::vector& active0, + const std::vector& band_shapes, + const SBRDecodeResult& sbr, + int sbr_end, + uint32_t block_idx) { - if (!sbr.used) return; + if (!sbr.used) + return; + + const int band_count = (int)ch0_bands.size(); + + std::vector old_index(band_count); + + for (int i = 0; i < band_count; ++i) + old_index[i] = i ^ (i >> 1); std::vector available_sources; - for (int j = 0; j < (int)ch0_bands.size(); ++j) { + + for (int j = 0; j < band_count; ++j) { if (active0[j]) available_sources.push_back(j); } - std::vector source_used(ch0_bands.size(), false); + std::vector source_used( + band_count, + false + ); for (int i = 0; i < sbr_end; ++i) { - if (active0[i]) continue; - int N = band_shapes[i]; - float target_rms = sbr.rms[i]; - float target_energy = target_rms * target_rms * N; + if (active0[i]) + continue; + + const int N = band_shapes[i]; + + const float target_rms = + sbr.rms[i]; + + const float target_energy = + target_rms * target_rms * N; ch0_bands[i].resize(N); if (sbr.noise_flag[i]) { - uint32_t seed = block_idx * 1000000u + (uint32_t)i * 137u + 1u; + + uint32_t seed = + block_idx * 1000000u + + (uint32_t)i * 137u + + 1u; + float noise_energy = 0.0f; + for (int j = 0; j < N; ++j) { - seed = seed * 1664525u + 1013904223u; - float noise = (static_cast(seed) / 2147483648.0f) - 1.0f; + + seed = + seed * 1664525u + + 1013904223u; + + float noise = + (static_cast(seed) / + 2147483648.0f) - 1.0f; + ch0_bands[i][j] = noise; - noise_energy += noise * noise; + + noise_energy += + noise * noise; } - float gain = std::sqrt(target_energy / (noise_energy + 1e-12f)); + + const float gain = + std::sqrt( + target_energy / + (noise_energy + 1e-12f) + ); + for (int j = 0; j < N; ++j) ch0_bands[i][j] *= gain; - } else { - int source_band = -1; + } + + else { + + int source_band = -1; float best_weight = -1.0f; - const int IDEAL_DISTANCE = 2; - const float SIGMA = 3.0f; + + const int IDEAL_DISTANCE = 2; + const float SIGMA = 3.0f; + + const int target_old = + old_index[i]; for (int src : available_sources) { - if (source_used[src]) continue; - int distance = std::abs(src - i); - float d = (float)(distance - IDEAL_DISTANCE); - float weight = std::exp(-(d * d) / (2.0f * SIGMA * SIGMA)); + + if (source_used[src]) + continue; + + const int source_old = + old_index[src]; + + const int distance = + std::abs( + source_old - target_old + ); + + const float d = + (float)(distance - + IDEAL_DISTANCE); + + const float weight = + std::exp( + -(d * d) / + (2.0f * + SIGMA * + SIGMA) + ); + if (weight > best_weight) { best_weight = weight; source_band = src; @@ -206,17 +273,39 @@ inline void sbr_synthesize(std::vector>& ch0_bands, } if (source_band >= 0) { + source_used[source_band] = true; - const auto& src = ch0_bands[source_band]; - int src_size = (int)src.size(); - float src_energy = 0.0f; - for (float v : src) src_energy += v * v; - float gain = std::sqrt(target_energy / (src_energy + 1e-12f)); + + const auto& src = + ch0_bands[source_band]; + + const int src_size = + (int)src.size(); + + float src_energy = 0.0f; + + for (float v : src) + src_energy += v * v; + + const float gain = + std::sqrt( + target_energy / + (src_energy + 1e-12f) + ); + for (int j = 0; j < N; ++j) - ch0_bands[i][j] = src[j % src_size] * gain; - } else { - std::fill(ch0_bands[i].begin(), ch0_bands[i].end(), 0.0f); + ch0_bands[i][j] = + src[j % src_size] * gain; + } + + else { + std::fill( + ch0_bands[i].begin(), + ch0_bands[i].end(), + 0.0f + ); } } } } + diff --git a/decoder/wavelet.h b/decoder/wavelet.h index 73ff803..8d7eaf5 100644 --- a/decoder/wavelet.h +++ b/decoder/wavelet.h @@ -140,52 +140,67 @@ class PWPT : public DD64out { } std::vector gray_permutation(int level) const { - int n = 1 << level; + const int n = 1 << level; std::vector perm(n); + for (int i = 0; i < n; ++i) perm[i] = i ^ (i >> 1); + return perm; } - void apply_temporal_dither(std::vector& pcm, float kbps_per_channel) const { - if (kbps_per_channel > 96.0f) return; - - const float AMPLITUDE = 3e-5f; - + void apply_temporal_dither( + std::vector& pcm, + float kbps_per_channel) const + { + if (kbps_per_channel > 96.0f) + return; + + const float AMPLITUDE = 3e-5f; + uint32_t seed = (uint32_t)bi * 1000000u + 1u; - + for (size_t i = 0; i < pcm.size(); ++i) { seed = seed * 1664525u + 1013904223u; - float rand1 = static_cast(seed) / 4294967296.0f; - + float rand1 = + static_cast(seed) / 4294967296.0f; + seed = seed * 1664525u + 1013904223u; - float rand2 = static_cast(seed) / 4294967296.0f; - - float dither = (rand1 + rand2 - 1.0f) * AMPLITUDE; - + float rand2 = + static_cast(seed) / 4294967296.0f; + + float dither = + (rand1 + rand2 - 1.0f) * AMPLITUDE; + pcm[i] += dither; } } public: - std::vector iwpt(std::vector>& subbands, - float sr, - int levels, - float target_kbps, - float channels) { + std::vector iwpt( + std::vector>& subbands, + float sr, + int levels, + float target_kbps, + float channels) + { const int total_bands = 1 << levels; bi++; if (sr >= 44100) { - int atten_start = total_bands * 3 / 4; - for (size_t i = atten_start; i < subbands.size(); ++i) - for (float& x : subbands[i]) x *= 8.0f; + const int atten_start = total_bands * 3 / 4; + + for (int i = atten_start; i < total_bands; ++i) + for (float& x : subbands[i]) + x *= 8.0f; if (32 <= target_kbps / float(channels) && - target_kbps / float(channels) < 64) { - for (size_t i = atten_start; i < subbands.size(); ++i) - for (float& x : subbands[i]) x *= 8.0f; + target_kbps / float(channels) < 64) + { + for (int i = atten_start; i < total_bands; ++i) + for (float& x : subbands[i]) + x *= 8.0f; } // int cutoff_mid = total_bands * 4 / 8; @@ -195,19 +210,28 @@ class PWPT : public DD64out { // } } - if (levels > 1) { - auto perm = gray_permutation(levels); + const auto perm = gray_permutation(levels); + std::vector> reordered(total_bands); - for (int i = 0; i < total_bands; ++i) - reordered[i] = std::move(subbands[perm[i]]); + + for (int i = 0; i < total_bands; ++i) { + reordered[perm[i]] = std::move(subbands[i]); + } + subbands = std::move(reordered); } - std::vector pcm = wpt_reconstruct(subbands, levels); - - float kbps_per_channel = target_kbps / float(channels); - apply_temporal_dither(pcm, kbps_per_channel); + std::vector pcm = + wpt_reconstruct(subbands, levels); + + const float kbps_per_channel = + target_kbps / float(channels); + + apply_temporal_dither( + pcm, + kbps_per_channel + ); return pcm; }