本文完整阅读约需 26 分钟,如时间较长请考虑收藏后慢慢阅读~

Aegisub是一款简单好用的开源字幕制作软件,广受字幕制作者欢迎。但Aegisub官方最新版本是2014年更新的3.2.2版本,距今已有6年历史,存在着大量未修复的BUG与兼容性问题。其中MacOS下的Aegisub存在一个致命的BUG,即波形视图右侧的音量/缩放滑块无法正常使用,只能在0%和100%两者间调整,导致要么看不到波形(听不到音频),要么波形爆满(严重削波)。本文将分析该问题存在的原因,并给出一个修复该问题的编译版本,便于字幕制作者正常使用。

0x01 问题重现

正常情况下,波形图右边的滑块应该如图所示,从左至右分别为水平缩放、垂直缩放与音量。

但在最新的MacOS系统中,使用3.2.2版本的Aegisub会发现滑块变成了如图所示的样子,不仅无法正常滑动,而且只有0%与100%两个状态,严重影响使用。

0x02 分析问题

Aegisub是一款开放源代码软件,因此我首先想到的是查看其源代码。在它的GitHub页面中,我找到了这三个滑块所在的源码:src/audio_box.cpp

其中新建这三个滑块的代码如下所示:

AudioBox::AudioBox(wxWindow *parent, agi::Context *context)
: wxSashWindow(parent, -1, wxDefaultPosition, wxDefaultSize, wxSW_3D | wxCLIP_CHILDREN)
, controller(context->audioController.get())
, context(context)
, audio_open_connection(context->audioController->AddAudioPlayerOpenListener(&AudioBox::OnAudioOpen, this))
, panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_RAISED))
, audioDisplay(new AudioDisplay(panel, context->audioController.get(), context))
, HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH))
, VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
, VolumeBar(new wxSlider(panel, Audio_Volume, OPT_GET("Audio/Volume")->GetInt(), 0, 100, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
{
    // Other codes
}

可以看到控制三个滑块的属性名称分别为HorizontalZoomVerticalZoomVolumeBar,都是wxWidget中的wxSlider控件。

为了重现故障,我新建了一个wxWidgets工程,使用了类似的方法放置了三个控件,代码如下所示:

Simple::Simple(const wxString& title)
        : wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, wxSize(250, 150))
{
    Centre();

    this->SetBackgroundColour(wxColour(47, 47, 47));

    auto *panel = new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize);
    auto *HorizontalZoom = new wxSlider(panel, -1, 0, -50, 50, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH);
    HorizontalZoom->SetBackgroundColour(wxColour(255, 255, 255));
}

得到如图所示的结果:

因为我在这里特别给滑块设置了白色的背景,可以直观看到,滑块的轨道已经完全不可见,难怪无法滑动。

参考wxSlider控件的手册,设置该控件宽高的参数为第七个参数,即上面代码中的wxSize(-1, 20),其类型为wxSize类型,接受宽度和高度两个参数。

继续参考wxSize控件的手册,我们可以看到这两个参数都可以为-1,即表示默认宽高。那我们不妨将两个值都设置为-1试试看?设置后我得到了如下图所示的结果:

滑块居然恢复正常了!

0x03 思考问题

上面我们提到了,将wxSlider控件的宽高都设置为-1即默认后,滑块的宽高恢复了正常。那么为什么将高度设置为20的时候就缩小了呢?

为了继续探索,我将20修改为了100,即尺寸参数变为了wxSize(-1, 100),得到了如下图所示的结果:

这个时候滑块的实际高度是多少呢?我使用QQ的截图工具测量了一下它的尺寸:

可以看到,高度是100个像素,和我们所设置的完全一致。

如果将宽度设置成50个像素,我们可以得到如下图所示的结果,这进一步验证了我们的想法:

继续阅读Aegisub相关源码,可以发现它使用了BoxSizer布局模式(类似于CSS中盒式布局)。于是我仿造着继续撰写了如下代码:

Centre();

this->SetBackgroundColour(wxColour(47, 47, 47));

auto *panel = new wxPanel(this, -1, wxDefaultPosition, wxSize(40, 100));
auto *HorizontalZoom = new wxSlider(panel, -1, 0, -50, 50, wxDefaultPosition, wxSize(-1, 20),
                                    wxSL_VERTICAL | wxSL_BOTH | wxSL_INVERSE);
HorizontalZoom->SetBackgroundColour(wxColour(255, 255, 255));

wxSizer *box = new wxBoxSizer(wxHORIZONTAL);
box->Add(HorizontalZoom, 1, wxEXPAND, 0);

panel->SetSizer(box);

此处可以清晰看到,滑块的样式为横向。但我们的的确确设置的样式参数为wxSL_VERTICAL。文档中并未看到关于样式改变的内容,那么这极有可能是一个BUG。

为证实猜想,我继续参考Aegisub的源码,补全了另外两个滑块,得到的结果与Aegisub的BUG极为相似:

上面的实验充分说明了Aegisub波形图右侧滑块的故障原因,即使用wxSize()设置滑块尺寸情况下滑块变为横向,且宽高均为最小值导致其滑轨消失。

由于我手头没有找到2014年发布的Mac OS X Yosemite可供测试,无法证实其故障出现的时间。但既然知道问题出现的原因,不妨先解决它。

0x04 解决问题

继续阅读wxSlider的文档,可以看到第七个参数的默认值为wxDefaultSize,其含义同wxSize(-1, -1),我们将前文代码中的尺寸参数设置为wxDefaultSize后运行,看到了正常的结果:

由于在BoxSizer布局情况下,控件默认填充到盒内,因此设置尺寸不是必需项。而设置为wxDefaultSizewxSize(-1, -1)能保证该BUG不再出现,这就是解决问题的最简单方法。

知道了解决问题的方法,我将Aegisub3.2.2版本的源码下载了下来,修改了三个滑块的尺寸配置为wxDefaultSize,然后配置编译环境进行编译,最终得到了正常的结果,问题得到解决。

0x05 软件分享

由于该软件使用C++开发,编译环境配置较为复杂,且考虑到Aegisub的用户大多不是程序员,因此我将修复后的软件进行构建与打包,分享给需要的读者:链接

0x06 后记

本篇文章撰写完成后,我尝试着搜索了一下此BUG,在Aegisub的GitHub Issues中找到了一个Issue:macOS: Audio volume/zoom sliders are broken这个Issue最终被一个Patch解决,但由于未发布新版本,未能在正式版中得到修复,其diff如下所示:

From 3bc5e8f04f097ce70056be18f9919f643c0e37cb Mon Sep 17 00:00:00 2001
From: Thomas Goyne <[email protected]>
Date: Fri, 7 Jul 2017 16:46:52 -0700
Subject: [PATCH] Remove pointless explicit heights for the audio sliders

The containing sizer overrides them instantly, but for whatever reason
supplying them breaks things on macOS.
---
 src/audio_box.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/audio_box.cpp b/src/audio_box.cpp
index 8d45c83bb..4d1f1518f 100644
--- a/src/audio_box.cpp
+++ b/src/audio_box.cpp
@@ -63,9 +63,9 @@ AudioBox::AudioBox(wxWindow *parent, agi::Context *context)
 , audio_open_connection(context->audioController->AddAudioPlayerOpenListener(&AudioBox::OnAudioOpen, this))
 , panel(new wxPanel(this, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_RAISED))
 , audioDisplay(new AudioDisplay(panel, context->audioController.get(), context))
-, HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH))
-, VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
-, VolumeBar(new wxSlider(panel, Audio_Volume, OPT_GET("Audio/Volume")->GetInt(), 0, 100, wxDefaultPosition, wxSize(-1, 20), wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
+, HorizontalZoom(new wxSlider(panel, Audio_Horizontal_Zoom, -OPT_GET("Audio/Zoom/Horizontal")->GetInt(), -50, 30, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH))
+, VerticalZoom(new wxSlider(panel, Audio_Vertical_Zoom, OPT_GET("Audio/Zoom/Vertical")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
+, VolumeBar(new wxSlider(panel, Audio_Volume, OPT_GET("Audio/Volume")->GetInt(), 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL|wxSL_BOTH|wxSL_INVERSE))
 {
    SetSashVisible(wxSASH_BOTTOM, true);
    Bind(wxEVT_SASH_DRAGGED, &AudioBox::OnSashDrag, this);

可以看到,该Patch的解决方法与本文所述完全一致,不同的是本文在修复后进行了构建打包,便于用户直接使用。