C++ ASIO,访问缓冲区

C++ ASIO, accessing buffers

我没有音频编程方面的经验,而且 C++ 是相当低级的语言,所以我对它有一些小问题。我使用从 http://www.steinberg.net/en/company/developers.html 下载的 ASIO SDK 2.3。

我正在根据 SDK 中的示例编写自己的主机。

现在我已经设法完成了整个示例,看起来它正在运行。我的 PC 上连接了外部声卡。我已经成功地为这个设备加载驱动程序,配置它,处理回调,将数据从模拟转换为数字等常见的东西。

我现在卡住的部分: 当我通过我的设备播放一些曲目时,我可以看到混音器(设备的软件)中的条形图移动。所以设备以正确的方式连接。在我的代码中,我选择了带有在混音器中移动的条形名称的输入和输出。我还使用 ASIOCreateBuffers() 为每个 input/output.

创建缓冲区
  1. 如果我错了,请纠正我: 当 ASIOStart() 被调用并且驱动程序处于运行状态时,当我将声音信号输入到我的外部设备时,我相信缓冲区已经充满了数据,对吗?

  2. 我正在阅读文档,但我有点迷茫 - 如何访问设备发送到应用程序并存储在 INPUT 缓冲区中的数据?还是信号?我需要它来进行信号分析或将来记录。

编辑:如果我让它变得复杂,那么简而言之,我的问题是:如何从代码访问输入流数据?我在文档中没有看到任何 objects/callbacks 让我这样做。

ASIO SDK 中的主机示例非常接近您的需要。在 bufferSwitchTimeInfo 回调中有一些这样的代码:

for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++)
{
    int ch = asioDriverInfo.bufferInfos[i].channelNum;
    if (asioDriverInfo.bufferInfos[i].isInput == ASIOTrue)
    {
        char* buf = asioDriver.bufferInfos[i].buffers[index];
        ....

其中 if 块 asioDriver.bufferInfos[i].buffers[index] 是指向原始音频数据的指针(index 是该方法的参数)。

缓冲区的格式取决于驱动程序,可以通过测试发现 asioDriverInfo.channelInfos[i].type。格式的类型将是 32 位 int LSB 在前,32 位 int MSB 在前,等等。您可以在 asio.h 中的 ASIOSampleType 枚举中找到值列表。此时您需要将样本转换为下游信号处理代码的一些通用格式。如果您正在进行信号处理,您可能需要转换为 double。文件 host\asioconvertsample.cpp 将使您对转换中涉及的内容有所了解。您将遇到的最常见格式可能是 INT32 MSB。这是将其转换为双精度的方法。

for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++)
{
    int ch = asioDriverInfo.bufferInfos[i].channelNum;
    if (asioDriverInfo.bufferInfos[i].isInput == ASIOTrue)
    {
        switch (asioDriverInfo.channelInfos[i].type)
        {
            case ASIOInt32LSB:
            {
                double* pDoubleBuf = new double[_bufferSize];
                for (int i = 0 ; i < _bufferSize ; ++i)
                {
                    pDoubleBuf[i] = *(int*)asioDriverInfo.bufferInfos.buffers[index] / (double)0x7fffffff;
                }
                // now pDoubleBuf contains one channels worth of samples in the range of -1.0 to 1.0.
                break;
            }
            // and so on...

非常感谢。您的回答很有帮助,但由于我对 C++ 有点缺乏经验:P 我发现它有点问题。

一般来说我都是基于hostsample自己写的host。我暂时没有实现 asioDriverInfo 结构和使用公共变量。

  1. 我的第一个问题是:.

    char* buf = asioDriver.bufferInfos[i].buffers[index];
    

    因为我遇到无法将 (void*) 转换为 char* 的错误,但这可能解决了问题:

    char* buf = static_cast<char*>(bufferInfos[i].buffers[doubleBufferIndex]);
    
  2. 我的第二个问题是数据转换。我已经检查了您推荐给我的文件,但我发现它有点黑魔法。现在我正在尝试以您为榜样并且:

    for (int i = 0; i < inputBuffers + outputBuffers; i++)
    {
        if (bufferInfos[i].isInput) 
        {
            switch (channelInfos[i].type)
            {
                case ASIOSTInt32LSB:
                {
                    double* pDoubleBuf = new double[buffSize];
                    for (int j = 0 ; j < buffSize ; ++j)
                    {
                        pDoubleBuf[j] = bufferInfos[i].buffers[doubleBufferIndex] / (double)0x7fffffff; 
                    }
                    break;
                }
            }
        } 
    

    我在那里遇到错误:

    pDoubleBuf[j] = bufferInfos[i].buffers[doubleBufferIndex] / (double)0x7fffffff; 
    

    即:

    error C2296: '/' : illegal, left operand has type 'void *'
    

我不明白的是,在您的示例中,那里没有 table:asioDriverInfo.bufferInfos.buffers[index] 在 bufferInfos 之后,即使我将其修复...到哪种类型我应该施放它以使其工作吗? P

PS。我确信 ASIOSTInt32LSB 数据类型适合我的电脑。

ASIO 输入和输出缓冲区可以使用 void 指针访问,但是使用 memcpy 或 memmove 访问 I/O 缓冲区将创建一个内存副本,如果您正在进行实时处理,则应避免这种情况。我建议将指针类型转换为 int* 以便您可以直接访问它们。

当大多数 CPU 支持 AVX2 时,当您有 100 多个音频通道时,实时处理 1 对 1 转换类型也非常慢。 _mm256_loadu_si256() 和 _mm256_cvtepi32_ps() 会更快地进行转换。