我正在尝试使用ASIO SDK v2.3创建Windows控制台DSD音频播放器。我在文档中使用了hostsample示例。到目前为止,我已经能够成功播放基于PCM的数据。不幸的是,当切换到DSD示例格式时,出现堆损坏错误。
我的声卡支持DSD ASIO播放。使用HQPlayer和foobar2000对其进行了测试。
要切换到DSD格式模式,请参阅ASIO SDK文档并使用ASIOFuture函数进行切换。
long init_asio_static_data(DriverInfo *asioDriverInfo)
{
// collect the informational data of the driver
// get the number of available channels
ASIOIoFormat NeedThis = { kASIODSDFormat };
if (ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat, &NeedThis))
{
return -7;
}
.
.
if (ASIOSetSampleRate(2822400) == ASE_OK)
.
.
}
这样,驱动程序将更多数据复制到缓冲区时将使用 SampleFormat ASIOSTDSDInt8LSB1 。
case ASIOSTDSDInt8LSB1:
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize);
break;
这就是我添加到示例代码中的全部内容。由此看来,我认为驱动程序将以DSD模式处理并输出缓冲区。
不幸的是,在播放过程中,我在
遇到堆损坏错误ASIOError ASIODisposeBuffers(void)
{
if(!theAsioDriver)
return ASE_NotPresent;
return theAsioDriver->disposeBuffers(); //<--**Heap corruption**
}
我的代码或API有问题吗?
我也是C / C ++编程和音频编程的初学者,所以请原谅本文中的非技术性。
完整代码:
// hostsample.cpp : a simple ASIO host example.
// - instantiates the driver
// - get the information from the driver
// - built up some audio channels
// - plays silence for 20 seconds
// - destruct the driver
// Note: This sample cannot work with the "ASIO DirectX Driver" as it does
// not have a valid Application Window handle, which is used as sysRef
// on the Windows platform.
#include <stdio.h>
#include <string.h>
#include "asiosys.h"
#include "asio.h"
#include "asiodrivers.h"
// name of the ASIO device to be used
#if WINDOWS
// #define ASIO_DRIVER_NAME "ASIO Multimedia Driver"
#define ASIO_DRIVER_NAME "Creative SBX AE series DSD ASIO"
#elif MAC
// #define ASIO_DRIVER_NAME "Apple Sound Manager"
#define ASIO_DRIVER_NAME "ASIO Sample"
#endif
#define TEST_RUN_TIME 20.0 // run for 20 seconds
#define SAMPLERATE 2822400
enum {
// number of input and outputs supported by the host application
// you can change these to higher or lower values
kMaxInputChannels = 32,
kMaxOutputChannels = 32
};
// internal data storage
typedef struct DriverInfo
{
// ASIOInit()
ASIODriverInfo driverInfo;
// ASIOGetChannels()
long inputChannels;
long outputChannels;
// ASIOGetBufferSize()
long minSize;
long maxSize;
long preferredSize;
long granularity;
// ASIOGetSampleRate()
ASIOSampleRate sampleRate;
// ASIOOutputReady()
bool postOutput;
// ASIOGetLatencies ()
long inputLatency;
long outputLatency;
// ASIOCreateBuffers ()
long inputBuffers; // becomes number of actual created input buffers
long outputBuffers; // becomes number of actual created output buffers
ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
// ASIOGetChannelInfo()
ASIOChannelInfo channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
// The above two arrays share the same indexing, as the data in them are linked together
// Information from ASIOGetSamplePosition()
// data is converted to double floats for easier use, however 64 bit integer can be used, too
double nanoSeconds;
double samples;
double tcSamples; // time code samples
// bufferSwitchTimeInfo()
ASIOTime tInfo; // time info state
unsigned long sysRefTime; // system reference time, when bufferSwitch() was called
// Signal the end of processing in this example
bool stopped;
} DriverInfo;
DriverInfo asioDriverInfo = { 0 };
ASIOCallbacks asioCallbacks;
//----------------------------------------------------------------------------------
// some external references
extern AsioDrivers* asioDrivers;
bool loadAsioDriver(char *name);
// internal prototypes (required for the Metrowerks CodeWarrior compiler)
int main(int argc, char* argv[]);
long init_asio_static_data(DriverInfo *asioDriverInfo);
ASIOError create_asio_buffers(DriverInfo *asioDriverInfo);
unsigned long get_sys_reference_time();
// callback prototypes
void bufferSwitch(long index, ASIOBool processNow);
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
void sampleRateChanged(ASIOSampleRate sRate);
long asioMessages(long selector, long value, void* message, double* opt);
//----------------------------------------------------------------------------------
long init_asio_static_data(DriverInfo *asioDriverInfo)
{ // collect the informational data of the driver
// get the number of available channels
ASIOIoFormat NeedThis = { kASIODSDFormat };
if (ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat, &NeedThis))
{
return -7;
}
if (ASIOGetChannels(&asioDriverInfo->inputChannels, &asioDriverInfo->outputChannels) == ASE_OK)
{
printf("ASIOGetChannels (inputs: %d, outputs: %d);\n", asioDriverInfo->inputChannels, asioDriverInfo->outputChannels);
// get the usable buffer sizes
if (ASIOGetBufferSize(&asioDriverInfo->minSize, &asioDriverInfo->maxSize, &asioDriverInfo->preferredSize, &asioDriverInfo->granularity) == ASE_OK)
{
printf("ASIOGetBufferSize (min: %d, max: %d, preferred: %d, granularity: %d);\n",
asioDriverInfo->minSize, asioDriverInfo->maxSize,
asioDriverInfo->preferredSize, asioDriverInfo->granularity);
// get the currently selected sample rate
if (ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK)
{
printf("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate);
if (ASIOCanSampleRate(SAMPLERATE) == ASE_OK)
{
// Driver does not store it's internal sample rate, so set it to a know one.
// Usually you should check beforehand, that the selected sample rate is valid
// with ASIOCanSampleRate().
if (ASIOSetSampleRate(SAMPLERATE) == ASE_OK)
{
if (ASIOGetSampleRate(&asioDriverInfo->sampleRate) == ASE_OK)
printf("ASIOGetSampleRate (sampleRate: %f);\n", asioDriverInfo->sampleRate);
else
return -6;
}
else
return -5;
}
else
return -4;
// check wether the driver requires the ASIOOutputReady() optimization
// (can be used by the driver to reduce output latency by one block)
if (ASIOOutputReady() == ASE_OK)
asioDriverInfo->postOutput = true;
else
asioDriverInfo->postOutput = false;
printf("ASIOOutputReady(); - %s\n", asioDriverInfo->postOutput ? "Supported" : "Not supported");
return 0;
}
return -3;
}
return -2;
}
return -1;
}
//----------------------------------------------------------------------------------
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
#if NATIVE_INT64
#define ASIO64toDouble(a) (a)
#else
const double twoRaisedTo32 = 4294967296.;
#define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
#endif
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow)
{ // the actual processing callback.
// Beware that this is normally in a seperate thread, hence be sure that you take care
// about thread synchronization. This is omitted here for simplicity.
static long processedSamples = 0;
// store the timeInfo for later use
asioDriverInfo.tInfo = *timeInfo;
// get the time stamp of the buffer, not necessary if no
// synchronization to other media is required
if (timeInfo->timeInfo.flags & kSystemTimeValid)
asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
else
asioDriverInfo.nanoSeconds = 0;
if (timeInfo->timeInfo.flags & kSamplePositionValid)
asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
else
asioDriverInfo.samples = 0;
if (timeInfo->timeCode.flags & kTcValid)
asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
else
asioDriverInfo.tcSamples = 0;
// get the system reference time
asioDriverInfo.sysRefTime = get_sys_reference_time();
#if WINDOWS && _DEBUG
// a few debug messages for the Windows device driver developer
// tells you the time when driver got its interrupt and the delay until the app receives
// the event notification.
static double last_samples = 0;
char tmp[128];
sprintf(tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
OutputDebugString(tmp);
last_samples = asioDriverInfo.samples;
#endif
// buffer size in samples
long buffSize = asioDriverInfo.preferredSize;
// perform the processing
for (int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++)
{
if (asioDriverInfo.bufferInfos[i].isInput == false)
{
// OK do processing for the outputs only
switch (asioDriverInfo.channelInfos[i].type)
{
case ASIOSTInt16LSB:
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2);
break;
case ASIOSTInt24LSB: // used for 20 bits as well
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3);
break;
case ASIOSTInt32LSB:
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8);
break;
// these are used for 32 bit data buffer, with different alignment of the data inside
// 32 bit PCI bus systems can more easily used with these
case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTInt16MSB:
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2);
break;
case ASIOSTInt24MSB: // used for 20 bits as well
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3);
break;
case ASIOSTInt32MSB:
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8);
break;
// these are used for 32 bit data buffer, with different alignment of the data inside
// 32 bit PCI bus systems can more easily used with these
case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4);
break;
case ASIOSTDSDInt8LSB1:
case ASIOSTDSDInt8MSB1:
case ASIOSTDSDInt8NER8: // DSD 8 bit data, 1 sample per byte. No Endianness required.
memset(asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize);
break;
}
}
}
// finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
if (asioDriverInfo.postOutput)
ASIOOutputReady();
if (processedSamples >= asioDriverInfo.sampleRate * TEST_RUN_TIME) // roughly measured
asioDriverInfo.stopped = true;
else
processedSamples += buffSize;
return 0L;
}
//----------------------------------------------------------------------------------
void bufferSwitch(long index, ASIOBool processNow)
{ // the actual processing callback.
// Beware that this is normally in a seperate thread, hence be sure that you take care
// about thread synchronization. This is omitted here for simplicity.
// as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs to be created
// though it will only set the timeInfo.samplePosition and timeInfo.systemTime fields and the according flags
ASIOTime timeInfo;
memset(&timeInfo, 0, sizeof(timeInfo));
// get the time stamp of the buffer, not necessary if no
// synchronization to other media is required
if (ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
bufferSwitchTimeInfo(&timeInfo, index, processNow);
}
//----------------------------------------------------------------------------------
void sampleRateChanged(ASIOSampleRate sRate)
{
// do whatever you need to do if the sample rate changed
// usually this only happens during external sync.
// Audio processing is not stopped by the driver, actual sample rate
// might not have even changed, maybe only the sample rate status of an
// AES/EBU or S/PDIF digital input at the audio device.
// You might have to update time/sample related conversion routines, etc.
}
//----------------------------------------------------------------------------------
long asioMessages(long selector, long value, void* message, double* opt)
{
// currently the parameters "value", "message" and "opt" are not used.
long ret = 0;
switch (selector)
{
case kAsioSelectorSupported:
if (value == kAsioResetRequest
|| value == kAsioEngineVersion
|| value == kAsioResyncRequest
|| value == kAsioLatenciesChanged
// the following three were added for ASIO 2.0, you don't necessarily have to support them
|| value == kAsioSupportsTimeInfo
|| value == kAsioSupportsTimeCode
|| value == kAsioSupportsInputMonitor)
ret = 1L;
break;
case kAsioResetRequest:
// defer the task and perform the reset of the driver during the next "safe" situation
// You cannot reset the driver right now, as this code is called from the driver.
// Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
// Afterwards you initialize the driver again.
asioDriverInfo.stopped; // In this sample the processing will just stop
ret = 1L;
break;
case kAsioResyncRequest:
// This informs the application, that the driver encountered some non fatal data loss.
// It is used for synchronization purposes of different media.
// Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
// Windows Multimedia system, which could loose data because the Mutex was hold too long
// by another thread.
// However a driver can issue it in other situations, too.
ret = 1L;
break;
case kAsioLatenciesChanged:
// This will inform the host application that the drivers were latencies changed.
// Beware, it this does not mean that the buffer sizes have changed!
// You might need to update internal delay data.
ret = 1L;
break;
case kAsioEngineVersion:
// return the supported ASIO version of the host application
// If a host applications does not implement this selector, ASIO 1.0 is assumed
// by the driver
ret = 2L;
break;
case kAsioSupportsTimeInfo:
// informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
// is supported.
// For compatibility with ASIO 1.0 drivers the host application should always support
// the "old" bufferSwitch method, too.
ret = 1;
break;
case kAsioSupportsTimeCode:
// informs the driver wether application is interested in time code info.
// If an application does not need to know about time code, the driver has less work
// to do.
ret = 0;
break;
}
return ret;
}
//----------------------------------------------------------------------------------
ASIOError create_asio_buffers(DriverInfo *asioDriverInfo)
{ // create buffers for all inputs and outputs of the card with the
// preferredSize from ASIOGetBufferSize() as buffer size
long i;
ASIOError result;
// fill the bufferInfos from the start without a gap
ASIOBufferInfo *info = asioDriverInfo->bufferInfos;
// prepare inputs (Though this is not necessaily required, no opened inputs will work, too
if (asioDriverInfo->inputChannels > kMaxInputChannels)
asioDriverInfo->inputBuffers = kMaxInputChannels;
else
asioDriverInfo->inputBuffers = asioDriverInfo->inputChannels;
for (i = 0; i < asioDriverInfo->inputBuffers; i++, info++)
{
info->isInput = ASIOTrue;
info->channelNum = i;
info->buffers[0] = info->buffers[1] = 0;
}
// prepare outputs
if (asioDriverInfo->outputChannels > kMaxOutputChannels)
asioDriverInfo->outputBuffers = kMaxOutputChannels;
else
asioDriverInfo->outputBuffers = asioDriverInfo->outputChannels;
for (i = 0; i < asioDriverInfo->outputBuffers; i++, info++)
{
info->isInput = ASIOFalse;
info->channelNum = i;
info->buffers[0] = info->buffers[1] = 0;
}
// create and activate buffers
result = ASIOCreateBuffers(asioDriverInfo->bufferInfos,
asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers,
asioDriverInfo->preferredSize, &asioCallbacks);
if (result == ASE_OK)
{
// now get all the buffer details, sample word length, name, word clock group and activation
for (i = 0; i < asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers; i++)
{
asioDriverInfo->channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
asioDriverInfo->channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
result = ASIOGetChannelInfo(&asioDriverInfo->channelInfos[i]);
if (result != ASE_OK)
break;
}
if (result == ASE_OK)
{
// get the input and output latencies
// Latencies often are only valid after ASIOCreateBuffers()
// (input latency is the age of the first sample in the currently returned audio block)
// (output latency is the time the first sample in the currently returned audio block requires to get to the output)
result = ASIOGetLatencies(&asioDriverInfo->inputLatency, &asioDriverInfo->outputLatency);
if (result == ASE_OK)
printf("ASIOGetLatencies (input: %d, output: %d);\n", asioDriverInfo->inputLatency, asioDriverInfo->outputLatency);
}
}
return result;
}
int main(int argc, char* argv[])
{
// load the driver, this will setup all the necessary internal data structures
if (loadAsioDriver((char*)ASIO_DRIVER_NAME))
{
// initialize the driver
if (ASIOInit(&asioDriverInfo.driverInfo) == ASE_OK)
{
printf("asioVersion: %d\n"
"driverVersion: %d\n"
"Name: %s\n"
"ErrorMessage: %s\n",
asioDriverInfo.driverInfo.asioVersion, asioDriverInfo.driverInfo.driverVersion,
asioDriverInfo.driverInfo.name, asioDriverInfo.driverInfo.errorMessage);
if (init_asio_static_data(&asioDriverInfo) == 0)
{
// ASIOControlPanel(); you might want to check wether the ASIOControlPanel() can open
// set up the asioCallback structure and create the ASIO data buffer
asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages;
asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
if (create_asio_buffers(&asioDriverInfo) == ASE_OK)
{
if (ASIOStart() == ASE_OK)
{
// Now all is up and running
fprintf(stdout, "\nASIO Driver started succefully.\n\n");
while (!asioDriverInfo.stopped)
{
#if WINDOWS
Sleep(100); // goto sleep for 100 milliseconds
#elif MAC
unsigned long dummy;
Delay(6, &dummy);
#endif
fprintf(stdout, "%d ms / %d ms / %d samples", asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)asioDriverInfo.samples);
// create a more readable time code format (the quick and dirty way)
double remainder = asioDriverInfo.tcSamples;
long hours = (long)(remainder / (asioDriverInfo.sampleRate * 3600));
remainder -= hours * asioDriverInfo.sampleRate * 3600;
long minutes = (long)(remainder / (asioDriverInfo.sampleRate * 60));
remainder -= minutes * asioDriverInfo.sampleRate * 60;
long seconds = (long)(remainder / asioDriverInfo.sampleRate);
remainder -= seconds * asioDriverInfo.sampleRate;
fprintf(stdout, " / TC: %2.2d:%2.2d:%2.2d:%5.5d", (long)hours, (long)minutes, (long)seconds, (long)remainder);
fprintf(stdout, " \r");
#if !MAC
fflush(stdout);
#endif
}
ASIOStop();
}
ASIODisposeBuffers();
}
}
ASIOExit();
}
asioDrivers->removeCurrentDriver();
}
return 0;
}
unsigned long get_sys_reference_time()
{ // get the system reference time
#if WINDOWS
return timeGetTime();
#elif MAC
static const double twoRaisedTo32 = 4294967296.;
UnsignedWide ys;
Microseconds(&ys);
double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo);
return (unsigned long)(r / 1000.);
#endif
}
答案 0 :(得分:0)
嗨,我也正在创建dsd音频播放器。您解决了这个问题吗?
ASIOIoFormat NeedThis = { kASIODSDFormat };
if (ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat, &NeedThis))
{
return -7;
}
也许这些短语会放在函数的末尾。