From 71bec4bda3546bdc1afe152f30a319fbdf4e94ac Mon Sep 17 00:00:00 2001 From: TheM14 Date: Sun, 27 Sep 2026 01:24:53 +0800 Subject: [PATCH] Fix band-0 DC filter gating and improve settings UI --- kernel/include/eac3joc_core.h | 9 ++++++ kernel/src/joc_core/eac3joc_core.cpp | 34 +++++++++++++++------ kernel/src/joc_core/objects16.cpp | 9 ++++++ src/main.cpp | 2 +- src/prefs.cpp | 44 ++++++++++++++++------------ src/prefs.rc | 44 +++++++++++++++------------- src/resource.h | 2 ++ 7 files changed, 94 insertions(+), 50 deletions(-) diff --git a/kernel/include/eac3joc_core.h b/kernel/include/eac3joc_core.h index 060bd63..6cddf54 100644 --- a/kernel/include/eac3joc_core.h +++ b/kernel/include/eac3joc_core.h @@ -68,6 +68,15 @@ EJOC_API ejoc_renderer_handle EJOC_CALL ejoc_renderer_create(void); EJOC_API void EJOC_CALL ejoc_renderer_destroy(ejoc_renderer_handle handle); EJOC_API int EJOC_CALL ejoc_renderer_reset(ejoc_renderer_handle handle); EJOC_API int EJOC_CALL ejoc_renderer_set_threads(ejoc_renderer_handle handle, uint32_t total_threads); +/* +Enables or disables the Ls/Rs band-0 21-tap DC compensation. The caller derives +it from the JOC downmix configuration: only configurations 3 and 4 enable the +filter. When disabled, band 0 keeps the common per-band processing (surround +delay plus -j rotation) instead of being overwritten by the FIR. The delay line +and DC history advance either way, so the flag may change between frames. +Defaults to enabled when never called. +*/ +EJOC_API int EJOC_CALL ejoc_renderer_set_dc_filter(ejoc_renderer_handle handle, uint32_t enabled); EJOC_API uint32_t EJOC_CALL ejoc_renderer_thread_count(ejoc_renderer_handle handle); EJOC_API const char* EJOC_CALL ejoc_renderer_last_error(ejoc_renderer_handle handle); diff --git a/kernel/src/joc_core/eac3joc_core.cpp b/kernel/src/joc_core/eac3joc_core.cpp index 9ea832d..3b79a24 100644 --- a/kernel/src/joc_core/eac3joc_core.cpp +++ b/kernel/src/joc_core/eac3joc_core.cpp @@ -125,6 +125,10 @@ public: return error_[0] ? error_ : ""; } + void set_dc_filter(const bool enabled) noexcept { + dc_filter_enabled_ = enabled; + } + int process( const float* bed5, const float* lfe, @@ -305,16 +309,18 @@ private: for (int i = 0; i < 4; ++i) { dc_buffer[20 + i] = current[i][0]; } - for (int slot = 0; slot < 4; ++slot) { - Complex sum{0.0, 0.0}; - for (int tap = 0; tap < 21; ++tap) { - const Complex sample = dc_buffer[slot + tap]; - const double cr = kDcB[tap]; - const double ci = kDcA[tap]; - sum.re += sample.re * cr - sample.im * ci; - sum.im += sample.re * ci + sample.im * cr; + if (dc_filter_enabled_) { + for (int slot = 0; slot < 4; ++slot) { + Complex sum{0.0, 0.0}; + for (int tap = 0; tap < 21; ++tap) { + const Complex sample = dc_buffer[slot + tap]; + const double cr = kDcB[tap]; + const double ci = kDcA[tap]; + sum.re += sample.re * cr - sample.im * ci; + sum.im += sample.re * ci + sample.im * cr; + } + x_[channel][0][group + slot] = {2.0 * sum.re, 2.0 * sum.im}; } - x_[channel][0][group + slot] = {2.0 * sum.re, 2.0 * sum.im}; } for (int i = 0; i < 20; ++i) { surround_history_[surround][i] = dc_buffer[i + 4]; @@ -641,6 +647,8 @@ private: float analysis_phase_; alignas(64) Complex surround_delay_[2][10][64]; alignas(64) Complex surround_history_[2][20]; + // band-0 的 21-tap DC 补偿开关;仅 downmix 配置 3/4 由调用方置位。 + bool dc_filter_enabled_ = true; alignas(64) double lfe_delay_[kLfeDelay]; alignas(64) double matrix_previous_[15][5][64]; alignas(64) double synthesis_state_[15][640]; @@ -696,6 +704,14 @@ int EJOC_CALL ejoc_renderer_set_threads(ejoc_renderer_handle handle, uint32_t to return static_cast(handle)->set_threads(total_threads); } +int EJOC_CALL ejoc_renderer_set_dc_filter(ejoc_renderer_handle handle, uint32_t enabled) { + if (!handle) { + return -1; + } + static_cast(handle)->set_dc_filter(enabled != 0); + return 0; +} + uint32_t EJOC_CALL ejoc_renderer_thread_count(ejoc_renderer_handle handle) { if (!handle) { return 0; diff --git a/kernel/src/joc_core/objects16.cpp b/kernel/src/joc_core/objects16.cpp index 1fb2ef3..62b45a3 100644 --- a/kernel/src/joc_core/objects16.cpp +++ b/kernel/src/joc_core/objects16.cpp @@ -52,6 +52,15 @@ Status rebuild_objects16(ejoc_renderer_handle handle, const joc_frame_params& pa } out16->assign(static_cast(JOC_OUTPUT_CHANNELS) * JOC_FRAME_SAMPLES, 0.0f); + // band-0 的 21-tap DC 补偿只在 downmix 配置 3/4 下启用,其余配置 band 0 + // 走与其他 band 相同的处理。 + const bool dc_filter = params.dmx_config_idx == 3 || params.dmx_config_idx == 4; + if (ejoc_renderer_set_dc_filter(handle, dc_filter ? 1u : 0u) != 0) { + if (error != nullptr) { + *error = "ejoc_renderer_set_dc_filter failed"; + } + return Status::fail(JOC_ERR_RENDER_FAILED, stage::kDsp, "ejoc_renderer_set_dc_filter failed"); + } const int result = ejoc_renderer_process( handle, bed5_planar, lfe, params.present_mask, n_bands, n_dpoints, slope_idx, offset_ts, dq.data(), params.clipgain, 0.0625f, gain, out16->data()); diff --git a/src/main.cpp b/src/main.cpp index 3dc711f..b75c87f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,7 +12,7 @@ // Kept in one place: the string reported to foobar2000 and written to the log // must not drift apart. -#define JOC_VERSION "0.2.0" +#define JOC_VERSION "0.2.1" DECLARE_COMPONENT_VERSION("JOC decoder (E-AC-3 JOC)", JOC_VERSION, "Plays E-AC-3 JOC (Dolby Atmos) files: the JOC objects are " diff --git a/src/prefs.cpp b/src/prefs.cpp index c4bef44..e13427d 100644 --- a/src/prefs.cpp +++ b/src/prefs.cpp @@ -189,6 +189,7 @@ private: } if (pick_file(m_hwnd, path, title, filter)) { set_text(GetDlgItem(m_hwnd, IDC_EDIT_HRTF), path); + update_status(); notify(); } return TRUE; @@ -219,6 +220,10 @@ private: return TRUE; } if (code == EN_CHANGE || code == CBN_SELCHANGE) { + if (id == IDC_EDIT_HRTF || id == IDC_COMBO_LAYOUT || + id == IDC_EDIT_GAIN) { + update_status(); + } notify(); return TRUE; } @@ -338,35 +343,36 @@ private: void update_enabled_state() { update_status(); } void update_status() { - const joc_settings::Values values = joc_settings::read(); - std::string status; + const joc_settings::Values values = read_controls(); + std::string output_status; if (values.output != 0) { char text[96] = {}; const unsigned channels = joc_decode::speaker_channels(values.speaker_layout); std::snprintf(text, sizeof(text), "扬声器布局 %s(%u 声道)", values.speaker_layout.c_str(), channels); - status = text; - } else { - // Show what will actually be read, so an empty box is not a mystery. - joc_decode::Settings effective = joc_settings::current(); - effective.hrtf_source = - static_cast(values.hrtf_source); - effective.hrtf_file = values.hrtf_file; - const std::string file = joc_decode::resolve_hrtf_file(effective); - status = std::string(joc_settings::hrtf_source_name(values.hrtf_source)) + ":" + - (file.empty() ? std::string("无法确定默认路径") - : (values.hrtf_file.empty() ? "默认 " + file : file)); + output_status = text; } - // Say what is actually in effect, including the parts that do not apply - // to the selected output mode, so nothing has to be greyed out. + set_text(GetDlgItem(m_hwnd, IDC_LABEL_OUTPUT_STATUS), output_status); + + joc_decode::Settings effective = joc_settings::current(); + effective.hrtf_source = + static_cast(values.hrtf_source); + effective.hrtf_file = values.hrtf_file; + const std::string file = joc_decode::resolve_hrtf_file(effective); + std::string hrtf_status = + std::string(joc_settings::hrtf_source_name(values.hrtf_source)) + ":"; + if (values.hrtf_file.empty()) hrtf_status += "默认"; + hrtf_status += "\n" + + (file.empty() ? std::string("无法确定默认路径") : file); + set_text(GetDlgItem(m_hwnd, IDC_LABEL_STATUS), hrtf_status); + char gain[96] = {}; if (values.gain_enabled) { - std::snprintf(gain, sizeof(gain), "\n增益开 %.2f dB", values.gain_db); + std::snprintf(gain, sizeof(gain), "增益开 %.2f dB", values.gain_db); } else { - std::snprintf(gain, sizeof(gain), "\n增益关(输出不衰减)"); + std::snprintf(gain, sizeof(gain), "增益关(输出不衰减)"); } - status += gain; - set_text(GetDlgItem(m_hwnd, IDC_LABEL_STATUS), status); + set_text(GetDlgItem(m_hwnd, IDC_LABEL_GAIN_STATUS), gain); } bool changed() const { diff --git a/src/prefs.rc b/src/prefs.rc index ce0ed65..8ce17c8 100644 --- a/src/prefs.rc +++ b/src/prefs.rc @@ -7,11 +7,11 @@ // sample page uses (SDK\foobar2000\foo_sample\foo_sample.rc). A CAPTION statement // implies WS_CAPTION, which would draw a second frame inside the host's and shift // every control down by the caption height -- visible but not clickable. -IDD_JOC_PREFS DIALOGEX 0, 0, 330, 226 +IDD_JOC_PREFS DIALOGEX 0, 0, 330, 273 STYLE DS_SETFONT | WS_CHILD FONT 8, "Microsoft Sans Serif", 400, 0, 0x0 BEGIN - GROUPBOX "输出", IDC_GROUP_OUTPUT, 7, 5, 316, 64 + GROUPBOX "输出", IDC_GROUP_OUTPUT, 7, 5, 316, 61 CONTROL "双耳(HRTF)", IDC_RADIO_BINAURAL, "Button", BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, 16, 20, 72, 10 CONTROL "扬声器布局", IDC_RADIO_SPEAKER, "Button", @@ -22,31 +22,33 @@ BEGIN LTEXT "双耳模式", IDC_LABEL_MODE, 172, 22, 42, 10 COMBOBOX IDC_COMBO_MODE, 216, 19, 96, 90, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "房间尾音(秒)", IDC_LABEL_TAIL, 172, 41, 46, 10 - EDITTEXT IDC_EDIT_TAIL, 220, 38, 40, 13, ES_AUTOHSCROLL + LTEXT "房间尾音(秒)", IDC_LABEL_TAIL, 172, 41, 62, 10 + EDITTEXT IDC_EDIT_TAIL, 238, 38, 40, 13, ES_AUTOHSCROLL + LTEXT "", IDC_LABEL_OUTPUT_STATUS, 16, 52, 300, 10 - GROUPBOX "HRTF", IDC_GROUP_HRTF, 7, 74, 316, 84 + GROUPBOX "HRTF", IDC_GROUP_HRTF, 7, 71, 316, 100 CONTROL "SOFA 文件", IDC_RADIO_HRTF_SOFA, "Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, 16, 89, 56, 10 + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP, 16, 86, 56, 10 CONTROL "Rosella 个性化模型", IDC_RADIO_HRTF_ROSELLA, "Button", - BS_AUTORADIOBUTTON | WS_TABSTOP, 78, 89, 78, 10 - LTEXT "HRTF 文件(留空=默认位置)", IDC_LABEL_HRTF, 16, 106, 92, 10 - EDITTEXT IDC_EDIT_HRTF, 110, 103, 164, 13, ES_AUTOHSCROLL - PUSHBUTTON "浏览…", IDC_BROWSE_HRTF, 279, 103, 38, 13 + BS_AUTORADIOBUTTON | WS_TABSTOP, 78, 86, 78, 10 + LTEXT "HRTF 文件(留空=默认位置)", IDC_LABEL_HRTF, 16, 105, 106, 10 + EDITTEXT IDC_EDIT_HRTF, 126, 102, 148, 13, ES_AUTOHSCROLL + PUSHBUTTON "浏览…", IDC_BROWSE_HRTF, 279, 102, 38, 13 LTEXT "留空时读组件目录下 HRTF\\binaural.sofa 或 binaural.personalized_headphone;SOFA 由渲染器内部编译,滤波组表已编入库中。", - IDC_LABEL_HRTF_HINT, 16, 122, 300, 30 + IDC_LABEL_HRTF_HINT, 16, 121, 300, 20 + LTEXT "", IDC_LABEL_STATUS, 16, 145, 300, 20 - GROUPBOX "增益", IDC_GROUP_GAIN, 7, 162, 152, 56 + GROUPBOX "增益", IDC_GROUP_GAIN, 7, 176, 316, 48 CONTROL "应用增益", IDC_CHECK_GAIN, "Button", - BS_AUTOCHECKBOX | WS_TABSTOP, 16, 178, 58, 10 - LTEXT "dB", IDC_LABEL_GAIN, 80, 178, 12, 10 - EDITTEXT IDC_EDIT_GAIN, 96, 175, 40, 13, ES_AUTOHSCROLL + BS_AUTOCHECKBOX | WS_TABSTOP, 16, 192, 58, 10 + LTEXT "dB", IDC_LABEL_GAIN, 80, 192, 12, 10 + EDITTEXT IDC_EDIT_GAIN, 96, 189, 40, 13, ES_AUTOHSCROLL + LTEXT "", IDC_LABEL_GAIN_STATUS, 145, 192, 171, 10 LTEXT "双耳渲染后可能超过 0 dBFS,可用负值衰减。", - IDC_LABEL_GAIN_HINT, 16, 194, 134, 20 + IDC_LABEL_GAIN_HINT, 16, 208, 300, 10 - GROUPBOX "其它", IDC_GROUP_MISC, 165, 162, 158, 56 - LTEXT "ffmpeg.exe", IDC_LABEL_FFMPEG, 173, 178, 44, 10 - EDITTEXT IDC_EDIT_FFMPEG, 216, 175, 62, 13, ES_AUTOHSCROLL - PUSHBUTTON "浏览…", IDC_BROWSE_FFMPEG, 282, 175, 35, 13 - LTEXT "", IDC_LABEL_STATUS, 173, 194, 144, 20 + GROUPBOX "其它", IDC_GROUP_MISC, 7, 229, 316, 38 + LTEXT "ffmpeg.exe", IDC_LABEL_FFMPEG, 16, 245, 44, 10 + EDITTEXT IDC_EDIT_FFMPEG, 64, 242, 210, 13, ES_AUTOHSCROLL + PUSHBUTTON "浏览…", IDC_BROWSE_FFMPEG, 279, 242, 38, 13 END diff --git a/src/resource.h b/src/resource.h index dcddc23..910bd44 100644 --- a/src/resource.h +++ b/src/resource.h @@ -12,6 +12,7 @@ #define IDC_COMBO_MODE 2016 #define IDC_LABEL_TAIL 2017 #define IDC_EDIT_TAIL 2018 +#define IDC_LABEL_OUTPUT_STATUS 2019 #define IDC_GROUP_HRTF 2020 #define IDC_RADIO_HRTF_SOFA 2021 @@ -26,6 +27,7 @@ #define IDC_LABEL_GAIN 2032 #define IDC_EDIT_GAIN 2033 #define IDC_LABEL_GAIN_HINT 2034 +#define IDC_LABEL_GAIN_STATUS 2035 #define IDC_GROUP_MISC 2040 #define IDC_LABEL_FFMPEG 2041