gpt4 book ai didi

audio - 快速连续播放片段时单击声音

转载 作者:行者123 更新时间:2023-12-02 23:48:49 25 4
gpt4 key购买 nike

我有一个非常简单的程序,可以播放4种不同的音调,具体取决于所按下的按钮。我发现,如果我快速连续播放多个音调或相同音调,会产生令人不快的咔嗒声。我确保音频样本中没有这些点击。绝对是由于快速播放片段而引起的。

谷歌搜索之后,我相当确定单击是由于片段之间的音调快速变化引起的。从有问题的音频中查看播放的波形,看起来一个剪辑在开始下一个剪辑之前先被取消了几分之一秒。我已经突出显示了似乎特别明显的部分。

展示这些音频点击的剪辑也可以通过here下载。

我的代码很简单。我正在使用XInput从连接的 Controller 读取输入,该 Controller 确定要播放的音调,并且正在使用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完成此操作?如果没有,是否可以尝试其他解决方案?

最佳答案

我知道理论上我们可以解决这个问题,但是我还没有针对所有情况的实际工作代码。 (当我这样做时,我将对其进行编辑。)

首先,这是一种可行的简单情况:尝试使用mciSendStringA而不是使用PlaySound:

    if(auto err = mciSendStringA("play test.wav", null, 0, null)) 
writeln(err);

我没有补充说明,Windows实际上具有该功能,并且实际上具有许多小的命令字符串和文件格式(尽管如果程序终止,所有声音都将停止,因此请确保程序继续运行,例如留在 Controller 中循环或调用Sleep(something))。

我使用了很多Win32,有时我对它有多少东西感到惊讶。原型(prototype):
    extern(Windows) uint mciSendStringA(in char*,char*,uint,void*); 

winmm.lib中找到。

基本上可以,但是在我的测试中,一次播放同一文件两次没有任何效果。一起播放不同的文件会使它们混合在一起。因此,这是部分解决方案。

下一步是使用mciSendCommand函数-比发送字符串的级别低一点,因此您可以打开多个设备并尝试以这种方式获得更多的重叠:

http://msdn.microsoft.com/en-us/library/windows/desktop/dd743675%28v=vs.85%29.aspx

我还没有尝试过,但是它看起来很简单,我怀疑它可能对您已经足够了。为每个按钮打开一些设备,以便您可以快速击打它们几次,并在它们之间循环,希望在需要时多次混契约(Contract)一声音。

原型(prototype)是:
extern(Windows) uint /*MCIERROR*/ mciSendCommandA(MCIDEVICEID,UINT,DWORD,DWORD);

是的,在msdn示例中,它强制转换为void *,然后强制转换为DWORD。布拉赫相关结构:
struct MCI_OPEN_PARMSA { 
DWORD dwCallback;
MCIDEVICEID wDeviceID; // aka uint
LPCSTR lpstrDeviceType;
LPCSTR lpstrElementName;
LPCSTR lpstrAlias;
}

struct MCI_PLAY_PARMS {
DWORD dwCallback;
DWORD dwFrom;
DWORD dwTo;
}

您也可以从这里借用一些常量:

https://github.com/AndrejMitrovic/DWinProgramming/blob/master/WindowsAPI/win32/mmsystem.d#L693

(如果您已经在使用win32绑定(bind),那就太好了!但是我认为它们在小事情上有点痛苦,因此我尽量避免使用它们,而是在需要时更喜欢从MSDN复制/粘贴原型(prototype)+结构+常量。)

您应该能够使用这些定义和core.sys.windows.windows获得MSDN示例。也不要忘记 pragma(lib, "winmm");

我认为,一个完全有效的解决方案肯定会起作用,但也要困难得多,它将使用低级界面自行混合声音,然后将结果发送到设备。我还没有这个工作,我今天没时间了,但希望明天能给你点东西。

基本步骤是:

1)调用waveOutOpen获取设备。设置一个需要更多数据时调用的回调函数。

2)使用waveOutPrepareHeader准备一个缓冲区-或多个缓冲区

3)当您的回调请求(可能需要在单独的线程中)使用当前注释时,使用waveOutWrite馈送数据。混合两个样本只是将这些值加在一起的情况(如果它们溢出则进行剪切-听起来很糟糕,但希望实际上不会发生),因此,如果您要播放多个声音,请随便添加它们。

不要忘记在任何回调函数上使用extern(Windows)!

4)加载样本可能意味着读取.wav文件。并不是很困难,Windows具有辅助功能,或者您可以自己完成。我也将为此显示代码。

目前为止,我在simpleaudio.d https://github.com/adamdruppe/arsd/blob/master/simpleaudio.d中找到了AudioOutput结构和WinMM版本。它现在有一个可怕的API,必须进行彻底的更改-在Linux上可以接受,但在Windows上却很糟糕。在这两个平台上,回调提要而不是write(data)都应该更好地工作,这就是我要做的。

我现在在演示中遇到的问题是缓冲区之间的间隙...导致咔嗒声。是的但是我敢肯定,只有通过适当的回调方法和缓冲区大小才能解决延迟。

不过,该MCI功能可能会为您下一步工作,如果多个设备都可以工作,甚至可能是最后一步。

顺便说一句:您也可以使它执行MIDI命令,而不是演奏波形并获得各种有趣的东西。 Simpleaudio.d的低级Midi已经开始运行-演示主程序甚至显示了钢琴音阶。将其安装到xbox Controller 中应该不会太难...按下按钮时要注意,释放时要注意,甚至不要考虑时间。.并不是对这个问题的答案,但可以玩的很酷同样!

关于audio - 快速连续播放片段时单击声音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27644963/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com