Author Topic: Glitches with ASIO  (Read 5565 times)

SoundMike

  • Posts: 369
Glitches with ASIO
« on: 9 Dec '11 - 05:55 »
I frequently receive emails from users having pops, bangs, you-name-it when using ASIO outputs. I've adjusted my app to set the BASS_ASIO_Start buflen to the maximum returned by BASS_ASIO_GetInfo, and I also ask users to set their sound interface's ASIO buffer size to the maximum. With the M-Audio Fast Track Ultra this is 4096 samples. This should be OK as that allows for about 85ms at 48K. But I've recently been trying to sort out the audio glitches with a user who has this device and has the settings set as I recommended.

There are probably a number of contributing factors, such as the fact that in this scenario there are 6 ASIO output channels being driven via BASSASIO from 5 mixer streams (4 mono and 1 stereo), and 2 or more files may be playing at the same time. Also, the program is written in VB6 - not the best environment for performance, but I am working on porting the app to a better development environment.

My ASIOProc routine is minimal:
Code: [Select]
Function AsioProc_(ByVal input_ As Long, ByVal channel As Long, ByVal buffer As Long, ByVal length As Long, ByVal user As Long) As Long
    Dim nThisAsioBufLen As Long

    nThisAsioBufLen = BASS_ChannelGetData(gaMixerStreams(user).nMixerStreamHandle, ByVal buffer, length)
    If (nThisAsioBufLen = -1&) Then nThisAsioBufLen = 0&
   
    AsioProc_ = nThisAsioBufLen
End Function

The only sure way I can avoid glitches is to pre-load audio files into memory and then use BASS_StreamCreateFile(True,...). This seems to virtually guarantee glitch-free playback. In a recent email, Ian, you mentioned that "files are read in blocks of at least 4Kb". I'm wondering if it would help my situation if I could significantly increase this size and thus reduce the amount of disk activity and increase the likelihood that required data is already loaded. For example, when just using basic BASS without BASSMix etc, my app uses pre-buffer and playback buffer lengths of 5000ms and I never get reports of glitches with this configuration.

Since I'm having reports of this problem with ASIO, I'm concerned that I'll get the same problems reported with WASAPI as BASSWASAPI uses the same callback architecture as BASSASIO.

So what do you think about a new BASS_SetConfig option to set the disk file read transfer size?

Ian @ un4seen

  • Administrator
  • Posts: 26137
Re: Glitches with ASIO
« Reply #1 on: 9 Dec '11 - 16:18 »
I'm not sure whether increasing the minimum file reading length will help, eg. larger reads could mean larger delays. I think the answer is to have a parallel buffering system like when using BASS output, eg. your own "update thread". For example, you could have a thread that gets data from the mixer and passes it to a "push" stream, and have the ASIOPROC get the data from there. It could look something like this (in C/C++)...

Code: [Select]
bufstream=BASS_StreamCreate(freq, chans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE, STREAMPROC_PUSH, 0); // create a push stream with same format as the mixer
bufbytes=BASS_ChannelSeconds2Bytes(bufstream, buftime); // the amount of data wanted in the buffer

...

DWORD CALLBACK AsioProc(BOOL input, DWORD channel, void *buffer, DWORD length, void *user)
{
    DWORD c=BASS_ChannelGetData(bufstream, buffer, length); // get data from the buffer stream
    if (c==-1) c=0; // an error, no data
    return c;
}

...

// in the buffering thread
void *data=malloc(bufbytes); // allocate intermediate buffer
while (!quit) {
DWORD c=BASS_StreamPutData(bufstream, 0, 0); // check how much data is currently buffered
if (bufbytes>c) { // less than wanted
c=BASS_ChannelGetData(mixer, data, bufbytes-c); // get data from the mixer
if (c==-1) break; // error, quit
BASS_StreamPutData(bufstream, data, c); // pass it the buffer stream
}
Sleep(buftime*1000/2); // wait a bit
}
free(data);

I'm not sure whether it'll be possible to create a buffering thread in VB6. If not, you could try using timeSetEvent instead.

SoundMike

  • Posts: 369
Re: Glitches with ASIO
« Reply #2 on: 10 Dec '11 - 03:06 »
Thanks, Ian. I didn't think larger reads would be a problem if the disk reads are double-buffered.

However, I've now implemented the push stream code you suggested - not in my VB6 code but in the version coded in my development environment, Pure Basic, which supports threads very well. I've just been running a test for about the last half-hour and it's running cleanly - no glitches at all. I did find I had to keep buffer sizes quite low to minimise latency, eg the time taken for the program to respond to 'pause', level changes, etc. I've now set the ASIO buflen back to the bufpref value returned by BASS_ASIO_GetInfo instead of using bufmax. Also, I experimented a bit with the buftime parameter in your example code. Should I be basing that on some algorithm? I've currently hard-coded buftime = 256ms.

SoundMike

  • Posts: 369
Re: Glitches with ASIO
« Reply #3 on: 14 Feb '12 - 05:35 »
Ian,

Using a push stream for ASIO seems to be going well but one of my beta testers has reported that he gets increased latency. I then provided him with a version of my program that had the option to either use or not use push streams, and he confirmed that without using push streams the latency is as good as in my current production release (which doesn't use push streams), but if he selects the option to use push streams then he gets the increased latency. In the words of the user, without push streams response is "really fast" but with push streams the latency is "too high to use in future performances".

This user can probably continue without using the push streams as he has not encountered any playback issues, but I'm wondering if there's anything I can do to reduce/control the extra latency caused by using push streams?

Ian @ un4seen

  • Administrator
  • Posts: 26137
Re: Glitches with ASIO
« Reply #4 on: 14 Feb '12 - 14:00 »
The buffering will indeed increase latency, ie. changes made to the mix will be heard a bit later because the buffered data needs to be heard first. To reduce the extra latency, you need to reduce the buffer length. Referring to the code I posted above, what value are you giving the buftime/bufbytes variables, and what ASIO buffer length (in the BASS_ASIO_Start call) are you using? You could try setting "buftime" to 2x the ASIO buffer. Note the latter is in samples while the former is in seconds, so you need to divide by the sample rate...

Code: [Select]
buftime=2.0*asiobuf/asiorate;
If you get interruptions in the output with that, you can try increasing the "2.0" number.

SoundMike

  • Posts: 369
Re: Glitches with ASIO
« Reply #5 on: 15 Feb '12 - 01:57 »
Thanks, Ian. I was using a buftime of 256ms but have now changed it as per your suggestion. After experimenting a bit with ASIO buffer settings on my M-Audio Fast Track Ultra I found that I needed to set a minimum buftime in my program of 50ms - which should be OK. Lower values were giving some clicks in playback. I'll probably need to provide my users with an option to set their own (preferably higher) buftime - although I'll need to work out a user-friendly way to express that!

Ian @ un4seen

  • Administrator
  • Posts: 26137
Re: Glitches with ASIO
« Reply #6 on: 15 Feb '12 - 14:49 »
Smaller buffers may work better if you replace the Sleep call in the buffering thread with an event (see CreateEvent), ie. call WaitForSingleObject there and SetEvent in the ASIOPROC (after the BASS_ChannelGetData call). But if the aim is to negate IO delays, you will want to avoid going too small, ie. the buffer needs to long enough to absorb the IO delay.