This project has moved and is read-only. For the latest updates, please go here.

AsioOut - AsioSampleType (Int32LSB24) and channel question

Feb 28, 2014 at 1:39 PM
Hi!

At the moment I have some troubles at playing audio using the methods WaveOutEvent (too high latency), DirectSound (application crashes randomly [DirectSound buffer full]) and Wasapi (the soundcard crashes after a few minutes!). So my last hope to get this all working is by using AsioOut.

I have 2 soundcards installed in my PC. AsioOut works fine with one of them because the AsioSampleType of the OutputChannels is Int32LSB. The other one does not work because the SampleType Int32LSB24 is not yet supported (there is no convertor implemented). The convertors all look so simple, but I have no idea, what I have to do :P I hope someone can help me there.

My 2nd question is, can I specify the asio channel(s) where the audio should be played? I have 32 Output Channels on my soundcard and I want to use the channels 10 and 11 for one stereo signal and 12/13 for a second stereo signal. How can i achieve this?


Stefan
Feb 28, 2014 at 1:47 PM
for the converter, you coiuld try with ConvertorFloatToIntGeneric, but using clampTo24Bit instead of clampToInt
Basically I think its 24 bit samples in 32 bit words, so the only real challenge is making sure the unused byte is at the right end

As for the second issue, have a look at AsioOut ChannelOffset, which is a bit of a hack, but would let you start from channel 10

MultiplexingSampleProvider/MultiplexingWaveProvider will let you join together your stereo signals into 4 channel audio
Feb 28, 2014 at 3:19 PM
hi!

Thanks for the very quick response. I've copied the Convertor and changed what you have suggested. Now that the "Int32LSB24 is not yet supported" Exception isn't thrown anymore I have a new problem. I get an exception during the AsioOut.Init-method. More specificly: The function CreateBuffers(int, int, bool) from ASIODriverExt.cs throws the Exception Error code [ASE_InvalidMode] while calling ASIO method <createBuffers>,
unsafe
            {
                fixed (ASIOBufferInfo* infos = &bufferInfos[0])
                {
                    IntPtr pOutputBufferInfos = new IntPtr(infos);

                    // Create the ASIO Buffers with the callbacks
                    driver.createBuffers(pOutputBufferInfos, nbTotalChannels, bufferSize, ref callbacks);
                }
            }
bufferInfos.length == 64
nbTotalChannels == 64 (32 input, 32 output)
bufferSize == 256 (at first it was 800, but then I looked at the bufferSize when using the other soundcard. there the bufferSize was 256, so I changed it to 256 using the AsioSettingsDialog)

In an ASIO SDK documention I found
ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);
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.
Maybe the 256 are wrong, but I've chosen this value from a DropDownList from the AsioSettingsDialog.



The other thing with the ChannelOffset seems to work. :)


Stefan
Feb 28, 2014 at 4:06 PM
hmm, hard to know what's going wrong there. You'll just have to experiment a bit more with the parameters. What sample rate are you using?
Also, are you calling AsioOut.Init or InitRecordAndPlayback, and if so, what are the in and out channel counts?
Mar 3, 2014 at 11:05 AM
I've tried different sample rates: 8000, 44100, 48000, 96000

I'm calling AsioOut.Init
MultiplexingWaveProvider multiplexer = new MultiplexingWaveProvider(new List<IWaveProvider>() { monoA, monoB }, 2);
// --> WaveFormat: {16 bit PCM: 44kHz 2 channels}
multiplexer.ConnectInputToOutput(0, 0);
multiplexer.ConnectInputToOutput(1, 1);

asioOutA = new AsioOut(0);
asioOutA.ChannelOffset = 10;
asioOutA.Init(multiplexer);

asioOutA.Play();
I don't know what I'm doing wrong. If I use my other soundcard (8 In/8 Out-channels) everything works fine. Maybe it is a problem that I have 32 Input and 32 Output channels. Could it be that there is some Array too small? But I don't think so.
In the array of ASIOBufferInfo objects there is also everything fine, I would say. 32 items having .isInput = true, another 32 having .isInput = false. And the channelNum goes from 0 to 63. There is not much that can go wrong in this CreateBuffers method regarding to the ASIO SDK doc.

As I stated in my original post, there are also troubles if I use DirectSound or Wasapi. Using Wasapi causes the following: Sound will be played for some seconds (between 1 and 60) then the whole Operating System freezes (can't move mouse) for 0.5-1 second and I can hear a strange/ugly beep on the Speakers. This repeates until the soundcard completely crashes and I have to restart my PC(!). It looks like there are strange driver or hardware issues with this card. I thought maybe if I use ASIO the problems will disappear (the asio driver model is different from the normal windows drivers, right?), but it seems like I don't even get this running.


Stefan
Mar 3, 2014 at 11:08 AM
shame, I'd definitely try to see if you can find updated ASIO drivers for your card. I know some soundcard manufacturers tend to drag their heels when it comes to updating drivers for new versions of Windows.
Mar 10, 2014 at 2:25 PM
Ok, I think I know the problem now. In my code sample above you can see this MultiplexingWaveProvider. The sources for this monoA and monoB-WaveStreams come from 2 WaveIn-Providers. It seems like that I can't have WaveIn and AsioOut for the same soundcard. Is that true? Can't test it with my other soundcard at the moment.

So now that this is working (I just have AsioOut now and record there) I would need the conversation from the ASIO format (Int32LSB24) to the 32bit float format in the function public int GetAsInterleavedSamples(float[] samples) of the AsioAudioAvailableEventArgs class.

For converting to the ASIO format I use this
public static void ConvertorFloatToIntGeneric24(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
        {
           unsafe
           {
              float* inputSamples = (float*)inputInterleavedBuffer;
              int*[] samples = new int*[nbChannels];
              for (int i = 0; i < nbChannels; i++)
              {
                 samples[i] = (int*)asioOutputBuffers[i];
              }

              for (int i = 0; i < nbSamples; i++)
              {
                 for (int j = 0; j < nbChannels; j++)
                 {
                    *samples[j]++ = clampTo24Bit(*inputSamples++);
                 }
              }
           }
        }
Stefan
Mar 11, 2014 at 1:02 PM
It seems like my brutforce method did work (after 5 hours :P).

I've written this in the GetAsInterleavedSamples-Method
 else if (AsioSampleType == Asio.AsioSampleType.Int32LSB24)
            {
               for (int n = 0; n < SamplesPerBuffer; n++)
               {
                  for (int ch = 0; ch < channels; ch++)
                  {
                     byte* pSample = ((byte*)InputBuffers[ch] + n * 4);

                     int sample = pSample[0] | (pSample[1] << 8) | ((sbyte)pSample[2] << 16);

                     samples[index++] = sample / 8388608.0f;
                  }
               }
            }
I looked at the code from the AsioSampleType.Int24LSB conversation. What I don't understand: Why is in the outer for-loop n < SamplesPerBuffer and not n < SamplesPerBuffer / 4? I multiply by 4 in the loop. I thought I'll get an index out of the range of the buffer.

And it seems like if you do too much in this function (I tried to Console.WriteLine some values) this code isn't working anymore. So it really has to be fast! Or there was another problem.


Stefan