0

I have no experience in audio programming and C++ is quite low level language so I have a little problems with it. I work with ASIO SDK 2.3 downloaded from http://www.steinberg.net/en/company/developers.html.

I am writing my own host based on example inside SDK.

For now I've managed to go through the whole sample and it looks like it's working. I have external sound card connected to my PC. I've successfully loaded driver for this device, configured it, handled callbacks, casting data from analog to digital etc. common stuff.

And part where I am stuck now: When I play some track via my device I can see bars moving in the mixer (device's software). So device is connected in right way. In my code I've picked the inputs and outputs with the names of the bars that are moving in mixer. I've also used ASIOCreateBuffers() to create buffer for each input/output.

  1. Now correct me if I am wrong: When ASIOStart() is called and driver is in running state, when I input the sound signal to my external device I believe the buffers get filled with data, right?

  2. I am reading the documentation but I am a bit lost - how can I access the data being sent by device to application, stored in INPUT buffers? Or signal? I need it for signal analysis or maybe recording in future.

EDIT: If I had made it to complicated then in a nutshell my question is: how can I access input stream data from code? I don't see any objects/callbacks letting me to do so in documentation.

F1sher
  • 7,140
  • 11
  • 48
  • 85
  • 2
    I believe the `boost-asio` tag is not applicable to this question, you are asking about an audio-related SDK, not the asynchronous io module. – xiaofeng.li Jan 09 '15 at 01:36

3 Answers3

2

The hostsample in the ASIO SDK is pretty close to what you need. In the bufferSwitchTimeInfo callback there is some code like this:

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];
        ....

Inside of that if block asioDriver.bufferInfos[i].buffers[index] is a pointer to the raw audio data (index is a parameter to the method).

The format of the buffer is dependent upon the driver and that can be discovered by testing asioDriverInfo.channelInfos[i].type. The types of formats will be 32bit int LSB first, 32bit int MSB first, and so on. You can find the list of values in the ASIOSampleType enum in asio.h. At this point you'll want to convert the samples to some common format for downstream signal processing code. If you're doing signal processing you'll probably want convert to double. The file host\asioconvertsample.cpp will give you some idea of what's involved in the conversion. The most common format you're going to encounter is probably INT32 MSB. Here is how you'd convert it to double.

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...
jaket
  • 9,140
  • 2
  • 25
  • 44
  • Thanks man. That really worked. Though I play music in my device and in program I get 0.0 only, but still I've accessed the memory block and got value. – F1sher Jan 13 '15 at 01:04
  • Actually :D like this:*((int*)asioDriverInfo.bufferInfos.buffers[index]+i) and double* pDoubleBuf = new double[_bufferSize]; shouldn't be in callback or loop because it's memory leak – F1sher Jan 14 '15 at 06:02
0

Thank you very much. Your answer helped quite much but as I am inexperienced with C++ a bit :P I find it a bit problematic.

In general I've written my own host based on hostsample. I didn't implement asioDriverInfo structure and use common variables for now.

  1. My first problem was:.

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

    as I got error that I can't cast (void*) to char* but this probably solved the problem:

    char* buf = static_cast<char*>(bufferInfos[i].buffers[doubleBufferIndex]);
    
  2. My second problem is with the data conversion. I've checked the file you've recommended me but I find it a little black magic. For now I am trying to follow your example and:

    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;
                }
            }
        } 
    

    I get error there:

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

    which is:

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

What I don't get is that in your example there is no table there: asioDriverInfo.bufferInfos.buffers[index] after bufferInfos and even if I fix it... to what kind of type should I cast it to make it work. P

PS. I am sure ASIOSTInt32LSB data type is fine for my PC.

F1sher
  • 7,140
  • 11
  • 48
  • 85
  • I just updated my answer to deal with the error. You need to cast from the void* to whatever datatype you need. for the 32-bit types that would just be to an int. For 24-bit types you'd need to cast to a byte and then shift it into an int, and so on. – jaket Jan 09 '15 at 17:19
0

The ASIO input and output buffers are accessible using void pointers, but using memcpy or memmove to access I/O buffer will create a memory copy which is to be avoided if you are doing real-time processing. I would suggest casting the pointer type to int* so you can directly access them.

It's also very slow in real-time processing to cast types 1 by 1 when you have like 100+ audio channels when AVX2 is supported on most CPUs. _mm256_loadu_si256() and _mm256_cvtepi32_ps() will do the conversion much faster.

Noob
  • 103
  • 9