3

作为一名吉他手,我一直想开发自己的录音、混音软件。我在 Direct Sound、Windows 多媒体(waveOutOpen 等)方面有一些经验。我意识到这将是一个复杂的项目,但纯粹是为了我自己的使用和学习,即没有期限!我打算使用 C++,但还不确定它是最好的 SDK/API。我希望软件是可扩展的,因为我可能希望在将来添加效果。几个前提...

  1. 在 Windows XP 上运行
  2. 最小延迟
  3. VU 表(在所有轨道上) 这让我回避 Direct Sound,因为似乎没有办法从主缓冲区读取音频数据。
  4. 配音(即在播放现有曲目的同时录制新曲目)。
  5. 包括节拍器

我最初的想法是使用 WMM 并使用 waveOutWrite 函数来播放音频数据。我想这本质上是一个音频流播放器。为了让事情更简单,我将采样率硬编码为 16 位,44.1kHZ(我的声卡支持的最佳采样率)。我需要的是关于整体架构的一些想法和指导。

例如,假设我的速度是 60 BPM,拍号是 4/4。我希望节拍器在每个小节/小节的开头播放一个咔嗒声。现在假设我已经录制了节奏音轨。播放时,我需要编排(双关语)将哪些数据发送到主声音缓冲区。在某些时候,我可能还想添加乐器、鼓(主要是)。同样,我需要知道如何在正确的时间将正确的音频数据发送到主音频缓冲区。我很欣赏时机是这里的关键。我不确定如何从各个音轨中获取正确的数据以发送到主声音缓冲区。

我最初的想法是有一个定时线程,它会定期询问每个曲目,“我需要数据来覆盖 N 毫秒的播放时间”。其中 N 取决于主缓冲区大小。

我明白这是一个复杂的问题,我只需要一些关于如何解决上述问题的指导。

另一个问题是 WMM 或 DirectSound 更适合我的需要。甚至ASIO?但是,主要问题是如何使用流式传输机制收集正确的轨道数据(来自多个轨道)以发送到主缓冲区,并保持最小的延迟?

任何帮助表示赞赏,

非常感谢

卡尔

感谢您的回复。但是,我的主要问题是如何对所有这些进行计时,以确保每个轨道在正确的时间将适当的数据写入主缓冲区。我当然愿意(免费)图书馆帮助我实现我的主要目标。

4

2 回答 2

2

正如你所发现的,你正在进入一个痛苦的世界。如果您真的在为 Windows XP 构建音频软件并期望低延迟,那么您肯定希望避免操作系统提供的任何音频 API,并像几乎所有商业软件一样使用 ASIO。虽然情况有所好转,但 ASIO 不会很快消失。

为了大大减轻您的痛苦,我建议您看看Juce,这是一个用于构建音频主机软件和插件的跨平台框架。它已被用于构建许多商业产品。

他们涵盖了许多真正令人讨厌的架构危害,并且附带了主机应用程序和插件的示例。

于 2013-06-25T19:31:03.823 回答
2

由于您打算支持 XP(我建议这样做,因为即使扩展支持也将在明年结束),您真的别无选择,只能使用 ASIO。可以从Steinberg下载相应的 SDK 。在 Windows Vista 及更高版本中, WASAPI 独占模式可能是更好的选择,因为可用性更广,但是文档严重缺乏 IMO。无论如何,您应该看看PortAudio,它有助于包装这些 API(与Juce不同的是,它是免费的。

WMM、DirectSound 和 XAudio 2 都无法为实时监控实现足够低的延迟。低延迟 API 通常会定期为每个数据块调用回调。

由于每个回调处理给定数量的样本,您可以根据采样率和样本计数器计算时间(只需在回调调用中累积)。提示:不要用浮点数累加。那就是疯狂。使用 64 位采样计数器,因为最小的增量总是1./sampleRate.

实际上,您的回调函数将(对于每个轨道)调用一个getSamples(size_t n, float* out)(或类似的)方法并总结结果(即混合它们)。然后,每个单独的轨道都可以有一个集成的采样时间来计算当前需要什么。对于周期性事物(无限波、循环、节拍器),您可以轻松计算每个周期的样本数并拥有一个模数计数器。这将导致周期四舍五入,但如前所述,浮点累加器是禁忌,但它们可以正常处理周期性信号。

在节拍器示例的情况下,您可能有一个带有n样本和一段m样本的波形“click.wav”。只要计数器小于您播放波形的相应样本,您的计数器就会周期性地从0到。例如,一个简单的节拍器会在每个节拍上播放一个咔嗒声,它可能看起来像这样:m-1n

class Metronome
{
    std::vector<float> waveform;
    size_t counter, period;

public:
    Metronome(std::vector<float> const & waveform, float bpm, float sampleRate) : waveform(waveform), counter(0)
    {
        float secondsPerBeat = 60.f/bpm; // bpm/60 = bps
        float samplesPerBeat = sampleRate * secondsPerBeat;
        period = (size_t)round(samplesPerBeat);
    }

    void getSamples(size_t n, float* out)
    {
        while(n--)
        {
            *out++ = counter < waveform.size() ? waveform[counter] : 0.f;
            counter += 1;
            counter -= counter >= period ? period : 0;
        }
    }
};

此外,您可以在 Internet 上查看 VST/AU 插件编程教程,因为它们具有从样本数量确定时间的相同“问题”。

于 2013-06-25T19:39:52.883 回答