【发布时间】:2014-12-25 07:02:48
【问题描述】:
我有一个非常简单的程序,可以播放 4 种不同的音调,具体取决于按下的按钮。我发现如果我快速连续演奏多个音调或相同的音调,会产生令人不快的咔嗒声。我已确保我的音频样本中不存在这些点击;肯定是一个接一个的快速播放造成的。
在谷歌搜索之后,我相当确定点击是由于剪辑之间音高的快速变化。查看来自违规音频的播放波形,看起来一个剪辑在开始下一个剪辑之前首先被取消了几分之一秒。我已经强调了这似乎特别明显的部分。
展示这些音频点击的剪辑也可以下载here。
我的代码很简单。我正在使用 XInput 从连接的控制器读取输入,这决定了要播放的音调,我正在使用 WinMM 从 wav 文件中输出声音。它是用 D 编程语言编写的,但我对其进行了修改,使其不使用特定于 D 的功能,以使其尽可能类似于 C 并避免混淆。
SHORT keyPressed(int vkey)
{
enum highBit { val = 0x8000 }
return cast(SHORT)(GetKeyState(vkey) & highBit.val);
}
enum Button
{
DPAD_UP = 0x0001,
DPAD_DOWN = 0x0002,
DPAD_LEFT = 0x0004,
DPAD_RIGHT = 0x0008,
START = 0x0010,
BACK = 0x0020,
LEFT_THUMB = 0x0040,
RIGHT_THUMB = 0x0080,
LEFT_SHOULDER = 0x0100,
RIGHT_SHOULDER = 0x0200,
A = 0x1000,
B = 0x2000,
X = 0x4000,
Y = 0x8000,
}
struct XINPUT_GAMEPAD
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
}
struct XINPUT_STATE
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
bool isPressed(int button)
{
return cast(bool)(Gamepad.wButtons & button);
}
}
int main()
{
HANDLE xinputDLL = initXinput();
XINPUT_STATE oldState;
XINPUT_STATE newState;
while (!keyPressed(VK_ESCAPE))
{
oldState = newState;
XInputGetState(0, &newState);
enum flags { val = SND_ASYNC | SND_FILENAME | SND_NODEFAULT }
if (newState.isPressed(Button.A) && !oldState.isPressed(Button.A))
{
PlaySoundA(toStringz("Piano.ff.A4.wav"), null, flags.val);
}
if (newState.isPressed(Button.B) && !oldState.isPressed(Button.B))
{
PlaySoundA(toStringz("Piano.ff.B4.wav"), null, flags.val);
}
if (newState.isPressed(Button.X) && !oldState.isPressed(Button.X))
{
PlaySoundA(toStringz("Piano.ff.C5.wav"), null, flags.val);
}
if (newState.isPressed(Button.Y) && !oldState.isPressed(Button.Y))
{
PlaySoundA(toStringz("Piano.ff.F4.wav"), null, flags.val);
}
}
denitXinput(xinputDLL);
return 0;
}
假设我对咔哒声的来源是正确的,我认为解决方案是让每个样本淡入下一个样本。但是,我不确定如何执行此操作,因为 WinMM documentation 似乎相对稀疏,而且我对此缺乏经验。
我在播放音频样本时点击问题的解决方案是让每个样本淡入下一个样本吗?如果是这样,我如何使用 WinMM 完成此操作?如果没有,我可以尝试其他解决方案吗?
【问题讨论】:
-
我不熟悉 WinMM,但如果你想让它听起来像钢琴,我认为你应该以某种方式将音符混合在一起,让它们在演奏时相互重叠同时。如果你只是切断声音,它会引起咔哒声。所以,如果我是你,我会研究如何创建混合在一起的通道,并在不同的通道中播放样本。您正在考虑的短暂褪色也可能有所帮助,但它很容易导致明显的延迟。
-
tbh,我没听到...您发布的 ogg 文件对我来说听起来不错。如果你同时播放两个声音,它们会重叠吗?我认为操作系统混合了它们,但不确定......并且剪辑已经有一些自然淡入淡出,所以我在想什么(用你自己的淡入淡出和混合做一个低级别的 wavOut 调用)可能不会有帮助。跨度>
-
顺便说一句,我昨天写了一些非常相似的东西:xbox360 controller support with xinput and js on linux and audio out。我先做了 linux,昨天启动了 Windows 端,所以我打开了所有低级 wavOut msdn 调用。今天可能没有时间完成它,但也许明天结束时我会有一个小的 D 库 - 和 WinMM 示例 - 用于游戏杆、wav 和音频准备就绪,这也可能会有所帮助。
-
@AdamD.Ruppe 这不是巧合 =) 我受到您的代码的启发,开始使用 WinMM 和 XInput。播放该文件时,您应该能够通过稍微调高音量来听到咔嗒声。当这 3 个样本快速连续播放时,这一点在最后尤其明显。不同的声音似乎没有重叠;当我播放另一个声音时,如果一个声音还没有结束,看起来一个很快就被剪掉了,然后第二个开始了。
-
我想我在 PlaySound (
enum SND_NOSTOP = 16;btw) 上找到了SND_NOSTOP标志。尝试添加它,看看会发生什么。然后它可能会混合,或者它可能会返回错误,但来自 MSDN “如果未指定此标志,PlaySound 会尝试停止当前在同一进程中播放的任何声音。在其他进程中播放的声音不受影响。” - 这可以解释你得到什么。