C++ ASIO,访问缓冲区
C++ ASIO, accessing buffers
我没有音频编程方面的经验,而且 C++ 是相当低级的语言,所以我对它有一些小问题。我使用从 http://www.steinberg.net/en/company/developers.html 下载的 ASIO SDK 2.3。
我正在根据 SDK 中的示例编写自己的主机。
现在我已经设法完成了整个示例,看起来它正在运行。我的 PC 上连接了外部声卡。我已经成功地为这个设备加载驱动程序,配置它,处理回调,将数据从模拟转换为数字等常见的东西。
我现在卡住的部分:
当我通过我的设备播放一些曲目时,我可以看到混音器(设备的软件)中的条形图移动。所以设备以正确的方式连接。在我的代码中,我选择了带有在混音器中移动的条形名称的输入和输出。我还使用 ASIOCreateBuffers() 为每个 input/output.
创建缓冲区
如果我错了,请纠正我:
当 ASIOStart() 被调用并且驱动程序处于运行状态时,当我将声音信号输入到我的外部设备时,我相信缓冲区已经充满了数据,对吗?
我正在阅读文档,但我有点迷茫 - 如何访问设备发送到应用程序并存储在 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 结构和使用公共变量。
我的第一个问题是:.
char* buf = asioDriver.bufferInfos[i].buffers[index];
因为我遇到无法将 (void*) 转换为 char* 的错误,但这可能解决了问题:
char* buf = static_cast<char*>(bufferInfos[i].buffers[doubleBufferIndex]);
我的第二个问题是数据转换。我已经检查了您推荐给我的文件,但我发现它有点黑魔法。现在我正在尝试以您为榜样并且:
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() 会更快地进行转换。
我没有音频编程方面的经验,而且 C++ 是相当低级的语言,所以我对它有一些小问题。我使用从 http://www.steinberg.net/en/company/developers.html 下载的 ASIO SDK 2.3。
我正在根据 SDK 中的示例编写自己的主机。
现在我已经设法完成了整个示例,看起来它正在运行。我的 PC 上连接了外部声卡。我已经成功地为这个设备加载驱动程序,配置它,处理回调,将数据从模拟转换为数字等常见的东西。
我现在卡住的部分: 当我通过我的设备播放一些曲目时,我可以看到混音器(设备的软件)中的条形图移动。所以设备以正确的方式连接。在我的代码中,我选择了带有在混音器中移动的条形名称的输入和输出。我还使用 ASIOCreateBuffers() 为每个 input/output.
创建缓冲区如果我错了,请纠正我: 当 ASIOStart() 被调用并且驱动程序处于运行状态时,当我将声音信号输入到我的外部设备时,我相信缓冲区已经充满了数据,对吗?
我正在阅读文档,但我有点迷茫 - 如何访问设备发送到应用程序并存储在 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 结构和使用公共变量。
我的第一个问题是:.
char* buf = asioDriver.bufferInfos[i].buffers[index];
因为我遇到无法将 (void*) 转换为 char* 的错误,但这可能解决了问题:
char* buf = static_cast<char*>(bufferInfos[i].buffers[doubleBufferIndex]);
我的第二个问题是数据转换。我已经检查了您推荐给我的文件,但我发现它有点黑魔法。现在我正在尝试以您为榜样并且:
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() 会更快地进行转换。