Asio SDK 2.3
Asio SDK 2.3
Asio SDK 2.3
ASIO 2.3
Audio Streaming Input Output
Development Kit
Documentation Release #3
Page 1 of 50
Contents
I. Overview
1. Introduction
2. The Goal
3. The Design
II. Implementation Guide
1. Instantiation
2. Operation
3. Driver Query by the Host Application
4. Host Query by the Driver
5. Audio Streaming
6. Media Synchronisation (Sampleposition and System Time)
7. Driver Notifications to the Host Application
III. Function Reference
1. Initialization/Termination
2. Start/Stop
3. Inquiry methods and sample rate
4. Buffer preparation
5. Miscellaneous
6. Callbacks
7. Type definitions
IV. Host Utility API Reference
1. AsioDrivers Class
V. Appendix
A. Using the bufferSwitchTimeInfo() callback
B. Latency vs. Sample Placement
C. Test methods
D. Platform/OS Differences
E. ASIODriver class for the driver implementation
F. Sony DSD Support
G. Microsoft Windows 64 bit
Page 2 of 50
I. Overview
1. Introduction
This document presents an overview of the design goals and implementation tradeoffs for an
efficient personal computer based audio processing system. Due to technological advances in the
computer industry in the recent years, and the ever-growing use of the Steinberg Virtual Studio
Technology (VST) as a standard for plug-in effects, it is clear that the personal computer will be a
major target platform for a vast number of (previously impractical) audio-processing tasks.
2. The Goal
The personal computer platform really misses a relatively simple way of accessing multiple audio
inputs and outputs. Today's operating system's audio support is designed for stereo input and stereo
output only. There is no provision to extend this without creating major problems, i.e.
synchronization issues between the different input and output channels.
With the Steinberg Audio Stream I/O (ASIO) Steinberg wants to help hardware and software
manufacturers to create hardware and driver software which extends the personal computer's audio
connectivity and meets the expectations of the customer (musician and audio engineer).
The Audio Stream I/O API addresses the areas of efficient audio processing, high data throughput,
synchronization, low latency and extensibility on the audio hardware side. The interface is not
bound to any fixed number of input and output channels (of course this numbers is limited by the
processing power and data throughput of the computer system itself). It puts no limitation on the
sample rate (32 kHz to 96 kHz or higher), sample format (16, 24, 32 bit or 32/64 bit floating point
formats). It takes advantage of today's computer architectures for high data throughput (PCI,
FireWire). It supports sophisticated hardware solutions for additional audio processing but it
remains simple in comparison to other approaches.
3. The Design
The audio subsystem/hardware is treated as a software component (called audio driver). That is
ASIO requires that the hardware manufacturers provide a driver, which abstracts the audio
hardware in the way ASIO can deal with.
For efficient processing and great flexibility ASIO implements channels (input and output) as
circular buffers with blocks of data. Actually a double buffer mechanism is used, which can be
easily adapted to a great number of different buffer implementations. One buffer contains always
data for one single channel only. This approach allows the host application to implement additional
processing algorithms very effectively (opposed to an interleaved buffer model). The size of the
data blocks is ascertained from the audio driver, to allow the best support for the audio hardware.
The audio driver allocates the memory for the actual data blocks. This allows the hardware
manufacturer to select the best memory access method for their audio I/O solution. DMA or
memory mapped I/O can be supported with equal efficiency. This is one of the keys for achieving
Page 3 of 50
low latency in audio processing. Since current operating systems are in transition towards multitasking, with all the advantages and tradeoffs, ASIO supports all features (timestamped event
notification and adaptive data pre-fetch/processing) necessary for asynchronous operation, usually
occurring in preemptive multitasking operating systems such as MacOS X, Windows 95/NT, IRIX,
BeOS. This will become also important for multi processor machines (SMP) or network distributed
processing. (A direction in which personal computer systems will be heading to in the near future.)
Page 4 of 50
2. Operation
The Finite State Machine diagram illustrates the states of the operation.
Loaded:
EXIT
INITIALIZED
DISPOSE
BUFFERS
CREATE
BUFFERS
PREPARED
STOP
START
RUNNING
Initialized:
Prepared:
Running:
Stop
DisposeBuffers
Exit
Note: As ASIOGetLatencies() will also have to include the audio buffer size of the
ASIOCreateBuffers() call, the application has to call this function after the buffer creation. In the
case that the call occurs beforehand the driver should assume preferred buffer size.
Additional queries introduced with ASIO 2.0. These queries will occur via the ASIOFuture() call.
kAsioCanTimeInfo
kAsioCanTimeCode
kAsioCanInputMonitor
Additional queries introduced with ASIO 2.3. They also occur via the ASIOFuture() call.
kAsioCanReportOverload
Page 6 of 50
Page 7 of 50
5. Audio Streaming
The audio streaming starts after the ASIOStart() call. Prior to starting the hardware streaming the
driver will issue one or more bufferSwitch() or bufferSwitchTimeInfo() callbacks to fill its first
output buffer(s). Since the hardware did not provide any input data yet the input channels' buffers
should be filled with silence by the driver.
After the driver started the hardware streaming the bufferSwitch() or bufferSwitchTimeInfo()
callbacks will always be called when the hardware requires more data. The driver passes the index
of the double buffer half, which has to be processed, to the application. Upon return of the callback
the application has read all input data and provided all output data.
Note: In order to reduce the amount of temporary buffers for a driver/hardware solution which has
to further process the audio data, e.g. copy the data to the hardware, the ASIO API provides the
ASIOOutputReady() method. ASIOOutputReady() is called immediately when the application has
finished the processing of the buffers. This is a provision for drivers which require an immediate
return of the bufferSwitch() or bufferSwitchTimeInfo() callback on interrupt driven systems like the
Apple Macintosh, which cannot spend too long in the callback at a low level interrupt time. Instead
the application will process the data at an interruptible execution level and inform the driver once it
finished the processing. On a thread based system like Windows 95/98/NT or 2000 the callback can
always process the data.
0
0
2000
0
1
1
2000
1024
2
0
2023
2048
3
1
2046
3072
4
0
2069
4096
5
1
2092
5120
6
0
2116
6144
Please note in the above table the same system time is used for the first two callbacks, as the
hardware did not start yet. Since ASIO implements a constant streaming model, the time
information of the first few bufferSwitch callbacks has to be ignored. The media synchronization
process will start once the host application detects the constant streaming process. (It can be
assumed that the streaming will be constant after the third bufferSwitch callback, more
Page 8 of 50
Note: A host application has to defer processing of these notifications to a later "secure" time as the
driver has to finish its processing of the notification. Especially on the kAsioResetRequest it is a
bad idea to unload the driver during the asioMessage callback since the callback has to return back
into the driver, which would then be no longer present.
sampleRateDidChange() informs the host application that the driver detected a sample rate
change. Usually only used for an external clock source which changes
its sample rate.
kAsioOverload
Page 9 of 50
Start/Stop
ASIOError ASIOStart(void);
ASIOError ASIOStop(void);
Buffer preparation
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize,
ASIOCallbacks *callbacks);
ASIOError ASIODisposeBuffers(void);
ASIOError ASIOOutputReady(void);
Miscellaneous
ASIOError ASIOControlPanel(void);
ASIOError ASIOFuture(long selector, void *params);
Callbacks
void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);
ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool
directProcess);
void (*sampleRateDidChange) (ASIOSampleRate sRate);
long (*asioMessage) (long selector, long value, void* message, double* opt);
Page 10 of 50
1. Initialization/Termination
-------------------------------------------------------------------ASIOError ASIOInit(ASIODriverInfo *info);
Purpose:
Initialize the AudioStreamIO.
Parameter:
info
Returns:
If neither input nor output is present ASE_NotPresent will be returned. ASE_NoMemory,
ASE_HWMalfunction are other possible error conditions
struct ASIODriverInfo
{
long
asioVersion;
long
driverVersion;
char
name[32];
char
errorMessage[124];
void
*sysRef;
};
Page 11 of 50
-------------------------------------------------------------------ASIOError ASIOExit(void);
Purpose:
Terminates the AudioStreamIO.
Parameter:
None.
Returns:
If neither input nor output is present ASE_NotPresent will be returned.
Notes:
This implies ASIOStop() and ASIODisposeBuffers(), meaning that no host callbacks must be
accessed after ASIOExit().
Page 12 of 50
2. Start/Stop
-------------------------------------------------------------------ASIOError ASIOStart(void);
Purpose:
Start input and output processing synchronously.
This will
- reset the sample counter to zero
- start the hardware (both input and output)
The first call to the hosts' bufferSwitch(index == 0) then tells the host to read from input buffer A
(index 0), and start processing to output buffer A while output buffer B (which has been filled by
the host prior to calling ASIOStart()) is possibly sounding (see also ASIOGetLatencies())
Parameter:
None.
Returns:
If neither input nor output is present, ASE_NotPresent will be returned. If the hardware fails to
start, ASE_HWMalfunction will be returned.
Notes:
There is no restriction on the time that ASIOStart() takes to perform (that is, it is not considered a
real-time trigger).
-------------------------------------------------------------------ASIOError ASIOStop(void);
Purpose:
Stops input and output processing altogether.
Parameter:
None.
Returns:
If neither input nor output is present ASE_NotPresent will be returned.
Notes:
On return from ASIOStop(), the driver must not call the hosts bufferSwitch() routine. On a preemptive multitasking OS you have to make sure that no pending events will call bufferSwitch()
after the driver returned from this function.
Page 13 of 50
Returns:
If no input/output is present ASE_NotPresent will be returned. If only inputs, or only outputs are
available, the according other parameter will be zero, and ASE_OK is returned.
outputLatency
on return will hold the 'age' of the first sample frame in the input buffer when
the hosts reads it in bufferSwitch() (this is theoretical, meaning it does not
include the overhead and delay between the actual physical switch, and the
time when bufferSwitch() enters). This will usually be the size of one block in
sample frames, plus device specific latencies.
on return will specify the time between the buffer switch, and the time when
the next play buffer will start to sound. The next play buffer is defined as the
one the host starts processing after (or at) bufferSwitch(), indicated by the
index parameter (0 for buffer A, 1 for buffer B).
It will usually be either one block, if the host writes directly to a DMA buffer
or two or more blocks if the buffer is 'latched' by the driver. As an example,
on ASIOStart(), the host will have filled the play buffer at index 1 already;
when it gets the callback (with the parameter index == 0), this tells it to read
from the input buffer 0, and start to fill the play buffer 0 (assuming that now
play buffer 1 is already sounding). In this case, the output latency is one
block. If the driver decides to copy buffer 1 at that time, and pass it to the
hardware at the next slot (which is most commonly done, but should be
avoided), the output latency becomes two blocks instead, resulting in a total
i/o latency of at least 3 blocks. As memory access is the main bottleneck in
native DSP processing, and to achieve lower latency, it is highly
recommended to try to avoid copying (this is also why the driver is the owner
of the buffers). To summarize, the minimum i/o latency can be achieved if the
Page 14 of 50
input buffer is processed by the host into the output buffer which will
physically start to sound on the next time slice. Also note that the host expects
the bufferSwitch() callback to be accessed for each time slice in order to
retain sync, possibly recursively; if it fails to process a block in time, it will
suspend its operation for some time in order to recover.
Returns:
If no input/output is present ASE_NotPresent will be returned.
Returns:
If no input/output is present ASE_NotPresent will be returned.
Notes:
When minimum and maximum buffer size are equal, the preferred buffer size has to be the same
value as well; granularity should be 0 in this case.
Page 15 of 50
Returns:
If the stated sample rate is not supported, ASE_NoClock will be returned. If no input/output is
present ASE_NotPresent will be returned.
Returns:
If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned.
If no input/output is present ASE_NotPresent will be returned.
Returns:
If sampleRate is unknown ASE_NoClock will be returned. If the current clock is external, and
sampleRate is != 0, ASE_InvalidMode will be returned. If no input/output is present
ASE_NotPresent will be returned.
Page 16 of 50
Returns:
If no input/output is present ASE_NotPresent will be returned.
struct ASIOClockSource {
long
index;
};
Page 17 of 50
Returns:
If no input/output is present ASE_NotPresent will be returned. If the clock can not be selected
because an input channel which carries the current clock source is active, ASE_InvalidMode *may*
be returned (this depends on the properties of the driver and/or hardware).
Notes:
Should *not* return ASE_NoClock if there is no clock signal present at the selected source; this
will be inquired via ASIOGetSampleRate(). It should call the host callback procedure
sampleRateHasChanged(), if the switch causes a sample rate change, or if no external clock is
present at the selected source.
on return will hold the sample position on return. The sample position is
reset to zero when ASIOStart() gets called.
on return will hold the system time when the sample position was
latched.
Returns:
If no input/output is present, ASE_NotPresent will be returned. If there is no clock,
ASE_SPNotAdvancing will be returned.
Notes:
In order to be able to synchronize properly, the sample position / time stamp pair must refer to the
*current block*, that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch()
callback and expect the time for the first sample of the current block. Thus, when requested in the
very first bufferSwitch after ASIOStart(), the sample position should be zero, and the time stamp
should refer to the very time where the stream was started. It also means that the sample position
must be block aligned. The driver must ensure proper interpolation if the system time can not be
determined for the block position. The driver is responsible for precise time stamps as it usually has
most direct access to lower level resources. Proper behavior of ASIOGetSamplePosition() and
ASIOGetLatencies() are essential for precise media synchronization!
Page 18 of 50
Purpose:
Retrieve information about the nature of a channel.
Parameter:
info
Returns:
If no input/output is present ASE_NotPresent will be returned.
Notes:
If possible, the string should be organized such that the first characters are most significantly
describing the nature of the port, to allow for identification even if the view showing the port name
is too small to display more than 8 characters, for instance.
struct ASIOChannelInfo
{
long
channel;
ASIOBool
isInput;
ASIOBool
isActive;
long
channelGroup; on output: the channel group that the channel belongs to.
For drivers which support different types of channels, like
analog, S/PDIF, AES/EBU, ADAT etc interfaces, there
should be a reasonable grouping of these types. Groups
are always independent from a channel index, that is, a
channel index always counts from 0 to
numInputs/numOutputs regardless of the group it may
belong to. There will always be at least one group (group
0). Please also note that by default, the host may decide to
activate channels 0 and 1; thus, these should belong to the
most useful type (analog i/o, if present).
ASIOSampleType type;
char
};
Page 19 of 50
name[32];
4. Buffer preparation
-------------------------------------------------------------------ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long
bufferSize, ASIOCallbacks *callbacks);
Purpose:
Allocates input/output buffers for all input and output channels to be activated.
Parameter:
bufferInfos
numChannels
is the sum of all input and output channels to be created; thus bufferInfos is a
pointer to an array of numChannels ASIOBufferInfo structures.
bufferSize
callbacks
Returns:
If not enough memory is available ASE_NoMemory will be returned. If no input/output is present
ASE_NotPresent will be returned. If bufferSize is not supported, or one or more of the bufferInfos
elements contain invalid settings, ASE_InvalidMode will be returned.
Notes:
If individual channel selection is not possible but requested, the driver has to handle this. Namely,
bufferSwitch() will only fill buffers of enabled outputs. If possible, processing and buss activities
overhead should be avoided for channels that were not enabled here.
struct ASIOBufferInfo
{
ASIOBool
isInput;
long
channelNum;
void
*buffers[2];
};
Page 20 of 50
-------------------------------------------------------------------ASIOError ASIODisposeBuffers(void);
Purpose:
Releases all buffers for the device.
Parameter:
None.
Returns:
If no buffer were ever prepared, ASE_InvalidMode will be returned. If no input/output is present
ASE_NotPresent will be returned.
-------------------------------------------------------------------ASIOError ASIOOutputReady(void);
Purpose:
This tells the driver that the host has completed processing the output buffers. If sample data format
required by the hardware differs from the supported ASIO sample formats, but the hardware buffers
are DMA buffers, the driver will have to convert the audio stream data. As the bufferSwitch
callback is usually issued at DMA block switch time, the driver will have to convert the *previous*
host buffer, which increases the output latency by one block.
When the host finds out that ASIOOutputReady() returns true, it will issue this call whenever it
completed output processing. Then the driver can convert the host data directly to the DMA buffer
to be played next, reducing output latency by one block.
Another way to look at it is, that the buffer switch is called in order to pass the *input* stream to
the host, so that it can process the input into the output, and the output stream is passed to the driver
when the host has completed its process.
Parameter:
None
Returns:
Only if the above mentioned scenario is given, and a reduction of output latency can be achieved by
this mechanism, should ASE_OK be returned. Otherwise (and usually) ASE_NotPresent should be
returned in order to prevent further calls to this function. Note that the host may want to determine
if it can use this when the system is not yet fully initialized, so ASE_OK should always be returned
if the mechanism makes sense.
Notes: Please remember to adjust ASIOGetLatencies() according to whether ASIOOutputReady()
was ever called or not, if your driver supports this scenario. Also note that the engine may fail to
call ASIOOutputReady() in time in overload cases. As already mentioned, bufferSwitch should be
called for every block regardless of whether a block could be processed in time.
Page 21 of 50
5. Miscellaneous
-------------------------------------------------------------------ASIOError ASIOControlPanel(void);
Purpose:
Request the driver to start a control panel component for device specific user settings. This might
not be accessible on all platforms.
Parameter:
None.
Returns:
If no panel is available ASE_NotPresent will be returned. Actually, the return code is ignored.
Notes:
If the user applied settings which require a re-configuration of parts or all of the engine and/or
driver (such as a change of the block size), the asioMessage callback can be used (see
ASIOCallbacks).
Returns:
The return value is also selector dependent. If the selector is unknown, ASE_InvalidParameter
should be returned to prevent further calls with this selector. On success, ASE_SUCCESS must be
returned
Notes:
See selectors defined below.
Page 22 of 50
ASIOFuture() selectors:
kAsioEnableTimeCodeRead
kAsioDisableTimeCodeRead
Purpose:
Enable or Disable the time code reader if the hardware device supports time code.
Parameter:
None.
Returns:
ASE_SUCCESS if request is accepted or ASE_NotPresent otherwise
Note:
If the hardware/driver does not support time code reader facilities ASE_NotPresent should be
returned.
kAsioSetInputMonitor
Purpose:
Set the direct input monitoring state.
Parameter:
params
Returns:
ASE_SUCCESS if request is accepted or otherwise ASE_NotPresent
Note:
If the hardware does not support patching and mixing a straight 1 to 1 routing is suggested. The
driver should ignore all the information of ASIOInputMonitor it cannot deal with, usually
these might be either or all of output, gain, pan.
Output is the base channel of a stereo channel pair, i.e. output is always an even channel
(0,2,4...). If an odd input channel should be monitored and no panning or output routing can be
applied, the driver has to use the next higher output (imply a hard right pan).
struct ASIOInputMonitor
{
long
input;
long
output;
long
gain;
+12
dB, 0x20000000 equals 0 dB)
ASIOBool state;
long
pan;
};
Page 23 of 50
kAsioSetIoFormat
Purpose:
Set the I/O format of the device.
Parameter:
params
Returns:
ASE_SUCCESS if request is accepted or otherwise ASE_NotPresent
Note:
See Appendix F for further details.
kAsioGetIoFormat
Purpose:
Get the I/O format of the device.
Parameter:
params
Returns:
ASE_SUCCESS if request is accepted or otherwise ASE_NotPresent
Note:
See Appendix F for further details.
kAsioCanDoIoFormat
Purpose:
Query the device for support of the given I/O format.
Parameter:
params
Returns:
ASE_SUCCESS if request is accepted or otherwise ASE_NotPresent
Note:
See Appendix F for further details.
kAsioCanReportOverload
Purpose:
Page 24 of 50
Query the driver if the device can detect overload conditions and report them back to the host. It
is not a constraint to support this, but we encourage driver implementors to do so, as we think
that the driver is the best place to do this. It is essential for users to know if overloads and thus
drop-outs in the audio stream occurred. If the driver doesnt provide this information, the host
has to implement some overload detection itself, which will most likely be inferior and less
reliable.
Parameter:
params
ignore
Returns:
ASE_SUCCESS if the driver detects overload conditions and will
report them to the host.
Note:
If the driver returns ASE_SUCCESS the host will switch off its own overload detection and
solely relies on the drivers reports.
Overloads are reported to the host via the AsioMessage() callback and the kAsioOverload
message selector.
kAsioGetInternalBufferSamples
Purpose:
Query the driver for the internal buffering of the device. Additionally to the by ASIO demanded
double buffer, driver designs might provide for even further internal buffering due to technical
constraints. Knowing this additional buffering may help the host in e.g. projecting the systems
performance headroom. Though it is not mandatory, but we encourage every driver
manufacturer to support kAsioGetInternalBufferSamples.
Parameter:
params
Returns:
ASE_SUCCESS if ASIOInternalBufferInfo structure got filled up with
pertinent information. Returns ASE_InvalidParameter if not supported.
Note:
struct ASIOInternalBufferInfo
{
long inputSamples; // size of driver's internal input buffering which
is included in ASIOGetLatencies
long outputSamples; // size of driver's internal output buffering
which is included in ASIOGetLatencies
};
Page 25 of 50
6. Callbacks
Returns:
No return value.
Notes:
bufferSwitch() may be called at interrupt time for highest efficiency.
Page 26 of 50
directProcess
Returns:
Pointer to ASIOTime structure with "output" time code information.
Notes:
bufferSwitchTimeInfo() may be called at interrupt time for highest efficiency.
This new callback with time info makes ASIOGetSamplePosition() and various calls to
ASIOGetSampleRate obsolete, and allows for timecode sync etc. Therefore it is the preferred
callback; it will be used if the driver successfully calls asioMessage with selector
kAsioSupportsTimeInfo.
struct ASIOTime {
long
reserved[4];
must be 0
AsioTimeInfo
timeInfo;
required
ASIOTimeCode
timeCode;
};
struct AsioTimeInfo {
double
speed;
ASIOTimeStamp
systemTime;
ASIOSamples
samplePosition;
ASIOSampleRate sampleRate;
long
flags;
see AsioTimeInfoFlags
char
reserved[12];
must be 0
};
Page 27 of 50
AsioTimeInfoFlags
kSystemTimeValid
kSamplePositionValid
kSampleRateValid
kSpeedValid
kSampleRateChanged
kClockSourceChanged
struct ASIOTimeCode {
double
speed;
ASIOSamples
long
char
};
ASIOTimeCodeFlags
kTcValid
kTcRunning
kTcReverse
kTcOnspeed
kTcStill
kTcSpeedValid
Page 28 of 50
The detected sample rate. 0 when sample rate is not known (for instance,
clock loss when externally synchronized).
Returns:
No return value.
Notes:
The host application usually will just store the information. Actual action of the host application is
not specified.
-------------------------------------------------------------------long (*asioMessage) (long selector, long value, void* message, double* opt);
Purpose:
Generic callback use for various purposes, see selectors below.
Parameter:
selector
value
message
opt
Returns:
Specific to the selector, undefined selectors will return 0.
Notes:
This callback was not implemented in the first ASIO host implementation of Cubase VST 3.0 for
Macintosh, all other ASIO host application will have this callback.
Page 29 of 50
asioMessage selectors
kAsioSelectorSupported
Purpose:
Test whether the host application supports a specific selector.
Parameter:
value
kAsioEngineVersion
Purpose:
Ask the host application for its ASIO implementation.
Parameter:
None.
Host Application Returns:
Host ASIO implementation version, 2 or higher
kAsioResetRequest
Purpose:
Requests a driver reset. If accepted, this will close the driver (ASIOExit() ) and re-open it again
(ASIOInit() etc.) at the next "safe" time (usually during the application IDLE time). Some
drivers need to reconfigure for instance when the sample rate changes, or some basic changes
have been made in ASIOControlPanel().
Parameter:
None.
Host Application Returns:
Return value is always 1L.
Note:
The request is merely passed to the application, there is no way to determine if it gets accepted
at this time (but it usually will be).
Page 30 of 50
kAsioBufferSizeChange
Purpose:
Informs the application that the driver has a new preferred buffer size.
Parameter:
value
kAsioResyncRequest
Purpose:
The driver went out of sync, such that the timestamp is no longer valid. This is a request to restart the engine and slave devices (sequencer).
Parameter:
None.
Host Application Returns:
1L if request is accepted or 0 otherwise
kAsioLatenciesChanged
Purpose:
Informs the application that the driver's latencies have changed. The engine will re-fetch the
latencies.
Parameter:
None.
Host Application Returns:
Returns always 1L if the selector is supported is accepted or 0 otherwise
Page 31 of 50
kAsioSupportsTimeInfo
Purpose:
Ask the application whether it supports the bufferSwitchTimeInfo() callback.
Parameter:
None.
Host Application Returns:
1L if bufferSwitchTimeInfo() is supported or 0 otherwise
kAsioSupportsTimeCode
Purpose:
Ask the application whether it supports time code reading in the bufferSwitchTimeInfo()
callback.
Parameter:
None.
Host Application Returns:
1L if time code reading is supported or 0 otherwise
Note:
This requires that the bufferSwitchTimeInfo() callback is supported by the host application.
kAsioOverload
Purpose:
Notify the host that the driver detected an overload condition, i.e. an interrupt or drop-out in the
audio stream.
Parameter:
None.
Host Application Returns:
Nothing, respectively the return value can be ignored
Note:
It is not mandatory for a driver to send overload messages unless previously the driver returned
ASE_SUCCESS to kAsioCanReportOverload during an ASIOFuture() call.
Page 32 of 50
7. Type definitions
-------------------------------------------------------------------Sample Types
1. Big Endian formats
The basic types, sample data is left aligned in the data word. The most significant Byte of the data
word is stored first in memory.
ASIOSTInt16MSB
ASIOSTInt24MSB
This is the packed 24 bit format. 2 data words will spawn consecutive 6
bytes in memory. (Used for 18 and 20 bits as well, if they use this packed
format)
ASIOSTInt32MSB
This format should also be used for 24 bit data, if the sample data is left
aligned. Lowest 8 bit should be reset or dithered whatever the
hardware/software provides.
ASIOSTFloat32MSB
ASIOSTFloat64MSB
sample data fills the least significant 16 bits, the other bits are sign extended
ASIOSTInt32MSB18
sample data fills the least significant 18 bits, the other bits are sign extended
ASIOSTInt32MSB20
sample data fills the least significant 20 bits, the other bits are sign extended
ASIOSTInt32MSB24
sample data fills the least significant 24 bits, the other bits are sign extended
Page 33 of 50
ASIOSTInt24LSB
This is the packed 24 bit format. 2 data words will spawn consecutive 6
bytes in memory. (Used for 18 and 20 bits as well, if they use this packed
format)
ASIOSTInt32LSB
This format should also be used for 24 bit data, if the sample data is left
aligned. Lowest 8 bit should be reset or dithered whatever the
hardware/software provides.
ASIOSTFloat32LSB
ASIOSTFloat64LSB
ASIOSTInt32LSB18
ASIOSTInt32LSB20
ASIOSTInt32LSB24
Page 34 of 50
Page 35 of 50
-------------------------------------------------------------------ASIOSamples ASIOTimeStamp
Number of samples - data type is 64-bit integer
Timestamp data type is 64-bit integer. The Time format is Nanoseconds.
Note:
The 64 bit data is passed via a structure with two 32-bit values.
struct {
unsigned long hi; // most significant bits (Bits 32..63)
unsigned long lo; // least significant bits (Bits 0..31)
};
Unfortunately the ASIO API was implemented it before compiler supported consistently 64 bit
integer types. By using the structure the data layout on a little-endian system like the Intel x86
architecture will result in a "non native" storage of the 64 bit data. The most significant 32 bit are
stored first in memory, the least significant bits are stored in the higher memory space. However
each 32 bit is stored in the native little-endian fashion.
-------------------------------------------------------------------ASIOSampleRate
Sample rates are expressed in IEEE 754 64 bit double float, native format as host computer
-------------------------------------------------------------------ASIOBool ASIOFalse
indicates a false value
ASIOTrue
indicates a true value
Error codes
ASE_OK
ASE_SUCCESS
ASE_NotPresent
ASE_HWMalfunction
ASE_InvalidParameter
ASE_InvalidMode
ASE_SPNotAdvancing
ASE_NoClock
ASE_NoMemory
Page 36 of 50
Returns:
Returns true if the driver was instantiated successfully, false
otherwise.
-------------------------------------------------------------------long getDriverNames(char **names, long maxDrivers);
Purpose:
Retrieves the names of the available drivers for the host
application.
Parameter:
names
maxDrivers
Returns:
Returns the number of filled entries of the names array. The value 0 described the fact that no driver
is available.
Page 37 of 50
Returns:
Returns true if a driver is currently instantiated, false if no driver is instantiated.
-------------------------------------------------------------------long getCurrentDriverIndex();
Purpose:
Retrieves the index into the names array of the currently instantiated driver. See getDriverNames().
Parameter:
None.
Returns:
Returns index into the names array. If no driver is instantiated -1 will be returned.
-------------------------------------------------------------------void removeCurrentDriver();
Purpose:
Removes the currently instantiated driver from memory any further access to the driver will be
denied.
Parameter:
None.
Returns:
None.
Note:
Successful removal of the driver is implied, even if no driver was instantiated.
Page 38 of 50
V. Appendix
A. Using the bufferSwitchTimeInfo() callback
It is recommended to use the new method with time info even if the ASIO device does not support
timecode; continuous calls to ASIOGetSamplePosition() and ASIOGetSampleRate() are avoided,
and there is a defined relationship between callback time and the time info.
See the example code for a driver on the next page.
To initiate time info mode, after you have received the callbacks pointer in ASIOCreateBuffers(),
you will call the asioMessage() callback with kAsioSupportsTimeInfo as the argument. If this
returns 1, the host has accepted time info mode. Now the host expects the new callback
bufferSwitchTimeInfo to be used instead of the old bufferSwitch method. The ASIOTime structure
is assumed to be valid and accessible until the callback returns.
Note:
The AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions. As a matter of
convention, the relationship between the sample position counter and the time code at buffer switch
time is (ignoring offset between time-code and sample position when time-code is running):
on input:
on output:
This means that for 'real' calculations, one has to take into account the according latencies.
Page 39 of 50
Example:
ASIOTime asioTime;
in createBuffers() {
memset(&asioTime, 0, sizeof(ASIOTime));
AsioTimeInfo* ti = &asioTime.timeInfo;
ti->sampleRate = theSampleRate;
ASIOTimeCode* tc = &asioTime.timeCode;
tc->speed = 1.; tc->frameRate = AsioFPS_30;
// frame rate
timeInfoMode = false;
canTimeCode = false;
if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1) {
timeInfoMode = true;
#if kCanTimeCode
if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1)
canTimeCode = true;
#endif
}
}
void switchBuffers(long doubleBufferIndex, bool processNow) {
if(timeInfoMode) {
AsioTimeInfo* ti = &asioTime.timeInfo;
ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;
ti->systemTime = theNanoSeconds;
ti->samplePosition = theSamplePosition;
if(ti->sampleRate != theSampleRate)
ti->flags |= kSampleRateChanged;
ti->sampleRate = theSampleRate;
#if kCanTimeCode
if(canTimeCode && timeCodeEnabled) {
ASIOTimeCode* tc = &asioTime.timeCode;
tc->timeCodeSamples = tcSamples;
tc->flags = kTcValid | kTcRunning;
// if so...
}
ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime,
doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);
#else
callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ?
ASIOTrue : ASIOFalse);
#endif
} else
callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse);
}
Page 40 of 50
Page 41 of 50
The output sample will appear at the output with a delay of 230 samples. Therefore it needs to be
placed by the application into the audio buffer at position 26.
Input signal at sample position 256 will appear at sample position 495 inside the audio buffers.
Since the driver informed the application about this delay it can place the input signal in relation to
its internal time line (time reference).
Output latency:
The output latency specifies the time in number sample frames a sample will reach the output after
it was passed from the application to the driver in an audio data buffer during a buffer switch. In
order to provide the correct latency value for ASIOGetLatencies() you have to perform the
following calculation:
(number of queued audio buffers * size of buffer) + hardware dependent latency
In case the hardware implements a double buffer and accesses the same memory as the
application/driver data buffer usually one audio buffer is queued up, only the hardware dependent
latency as found in digital transmitter or DAC's needs to be added to the audio buffer size.
If the hardware has to copy the data from the ASIO data buffers to another buffer you should look
at ASIOOutputReady() whether you can use this notification mechanism to keep one audio data
buffer as fixed latency.
Page 42 of 50
Input latency:
The input latency specifies the number of sample frames an incoming audio sample is passed to the
application during a buffer switch callback.
(number of queued audio buffers * size of buffer) + hardware dependent latency
In case the hardware implements a double buffer and accesses the same memory as the
application/driver data buffer usually one audio buffer is queued up, only the hardware dependent
latency as found in digital receivers or ADC's needs to be added to the audio buffer size.
In order to reduce the input latency if the driver needs to copy the audio data to the ASIO data
buffer, the copy operation should be executed immediately before the bufferSwitch() callback.
Note: The latency is always constant for a specific driver/hardware combination and audio data
buffer size. It is not affected by the time the buffer switch callback is actually issued and it does not
reflect the time the samples of the current bufferSwitch() callback will require to pass through the
system. This is due to the fact that the audio stream is continuously processed and the audio data
buffer of each buffer switch is adjacently joined to audio data from the previous buffer switch.
Page 43 of 50
Page 44 of 50
4. MIDI/Audio drift
Same test as above. Use Audio click as signal for the audio hardware (Metronome Dialog) MIDI
notes and audio clicks should not move apart. If they do, the timestamp/sample-position result from
ASIOGetSamplePos() needs to be checked.
1. MIDI/Audio offset
2. Stereo recording left channel audio click/right MIDI click
3. Bus assignment
4. Open last and first output/input bus and check for the right routing
Page 45 of 50
D. Platform/OS Differences
MacOS 8/9
Beginning with ASIO 2.3 support for MacOS 8/9 got dropped.
Windows
1. ASIO bufferSwitch() is usually implemented in its own thread, created by the driver. Thread
execution is either invoked by event notification from the kernel driver or by issuing an APC in the
thread context. The driver should set an appropriate high priority for the thread.
Starting with Windows Vista, Microsoft introduced the Multimedia Class Scheduler Service
(MMCSS). On Windows Vista or any newer Windows version, ASIO driver threads must be in the
Pro Audio class of MMCSS and their priority set to CRITICAL. This guarantees the highest
execution priority for the bufferSwitch().
It lies within the sole responsibility of the ASIO driver to set the priorities of the threads it owns.
An ASIO host shall by no means alter these priorities.
2. It is advised that the driver provides enough audio buffer size choices to avoid audio dropouts on
a busy system. Due to the implementation of the thread scheduler in Windows 95/98 low priority
threads can block the high priority callback thread. The time quantum is between 15 and 19 ms. We
observed that a buffer size of around 50 ms (2048 samples at 48 kHz sample rate) will provide
enough head room to avoid drop outs on a heavy loaded system. Though higher buffer sizes might
be required depending on the constraints of the driver. The driver can implement smaller audio
buffer sizes if the user can select them.
3. Win16Mutex contention should be avoided. Please keep in mind that calling the Win16 sub
system can block thread execution for a long time. Several Windows 95/98 sub-systems like GDI
and the window management are implemented in the Win16 sub-system which is secured against
re-entrancy by the Win16Mutex. Graphic animations like the "fading" Tool Tips will execute for
their entire duration in the Win16 sub system and can halt the Win16Mutex for a very long time
(we observed durations of up to 1.5 s). Therefore it is necessary to avoid Win16 sub system calls in
the driver threads.
4. On Windows the time reference for the timestamp is the multimedia timer, timeGetTime(), with
a resolution of 1 ms. The time base for this timer can be obtained at the kernel level with the
VMMCall GetSytemTime or GetUpdatedSystemTimeAddress.
5. The asioMessage() callback will be present for all Windows ASIO host applications. The initial
Macintosh ASIO host application, Cubase VST 3.0 for Macintosh, did not provide this callback.
Page 46 of 50
ASIOCallbacks structure via the createBuffers() method, it has to assume ASIO 1.0 compliance.
During the createBuffers() call the driver can query the host for its version via asioMessage with
the kAsioEngineVersion selector.
~AsioDriver()
The desctructor implies ASIOExit(). The driver will not receive the ASIOExit() call, instead the
destructor will be invoked.
Page 48 of 50
The CLSID of the driver can be identical for the 32 bit and the 64 bit driver.
Both 32 bit and 64 bit registry entries will use the InprocServer32 value. There is no
InprocServer64 value.
Page 50 of 50