25 May '13 - 22:56 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: C# BASS.NET app audio suddenly starts breaking up  (Read 744 times)
RobJellinghaus
Posts: 62


« on: 21 Jun '12 - 18:56 »
Reply with quoteQuote

Thanks in no small part to the fantastic support here on this forum, my Holofunk project (demoed to Beardyman last year) has kicked its time sync issues for good, and now can make some properly tight music.  http://robjsoftware.org has a recent example.

The most problematic bug I'm now facing is that sometimes, with no pattern I can discern, the app enters a state where the audio is breaking up.  It sounds like some kind of buffering problem.

The especially scary thing here is that this doesn't always happen.  Sometimes I can play with the thing for literally an hour and it works great.  But sometimes this happens in the first five minutes.  I want this app to be usable for performance, so a bug like this -- that makes it unlistenable -- is really freaky.

As often with audio bugs, it's much harder to describe it than to hear it.  My app can write a WAV file (via BASS, of course), and I happened to capture the bug just now.  Here's a 117MB uncompressed 48Khz WAV of me fooling around with Holofunk.  At 4:37, the bug bites, and the audio for the rest of the track is horrid.  (Fortunately, whatever the bug is, it got recorded by BASS!)

http://unrealities.com/holofunk/holofunk_20120621_102050.wav

Ian or Bernd, can one of you have a quick listen and see if you have any idea what kind of bug that sounds like? 

I'm not going to post all my BASS.NET code inline here, but it's all on the project's open source site:

http://holofunk.codeplex.com/SourceControl/changeset/view/dc3f89cd9bb1#HoloFunk%2fHolofunkBass%2fHolofunkBass.cs

That's the large majority of the BASS code, though there is a bit more elsewhere in the project.  If you have questions about what I'm doing, please ask away.

My best guess about what might be going on here is that my SYNCPROCs et al. are all in C#, and possibly a really badly timed GC is sending BASS into an out-of-sync state that it can't recover from.  I'm using BASS_SYNC_END, as suggested in this thread: http://www.un4seen.com/forum/?topic=12965.0 -- it's working great, I get tight synchronization and I notice no interference from .NET GCs.   ...except, possibly, for this bug.

Thanks very much for any insight you can provide.
Cheers!
Rob Jellinghaus
Logged
RobJellinghaus
Posts: 62


« Reply #1 on: 21 Jun '12 - 19:01 »
Reply with quoteQuote

...And listening to that track again, I can clearly hear that the "breakup" is some change in the processing of the input channel.  At that moment, I'm recording a new loop, a "bum bum bum bum" sort of bass line.  The first few "bum"s are fine, but then suddenly the breakup bug hits, and the last few are bad.  And they are recorded as bad; when the loop replays, you can hear the corruption at the same point every time.  All newly recorded audio is broken up after that.

So it seems to be an issue specifically with the input SYNCPROC -- all the looping of existing channels continues to work fine, but all input coming in is distorted.

Restarting the app works, but is not the kind of thing you ever want to have to do in performance!

Thanks again,
Rob
« Last Edit: 21 Jun '12 - 19:03 by RobJellinghaus » Logged
RobJellinghaus
Posts: 62


« Reply #2 on: 21 Jun '12 - 20:17 »
Reply with quoteQuote

...and one final note:  I just upgraded to BASS 2.4.9, BASS.NET 2.4.9, BASSMix 2.4.7, and BASSEnc 2.4.9.1... and the bug is still present :-(  Played with it for fifteen minutes this time before The Input Distortion From Hell bit again.  Rats!
Logged
radio42
Posts: 4012


« Reply #3 on: 21 Jun '12 - 22:51 »
Reply with quoteQuote

As you are within the .Net world - could it be, that the GarbageCollector is taking place at that certain times?
E.g. when working with quite low latencies, the GarbageCollector might at no time take longer than the audio update time.
This is because the GC might stop any threads calling into managed code (mainly callbacks).

Also read this post and the following, which pretty much might explain your simptom:
http://www.un4seen.com/forum/?topic=12661.msg88096#msg88096

The only real solution to that is using mixed-mode assemblies and to avoid any callbacks calling directly into managed code.
A sample of a mixed mode assembly is given here:
http://www.un4seen.com/forum/?topic=4932.msg60399#msg60399
Logged
RobJellinghaus
Posts: 62


« Reply #4 on: 22 Jun '12 - 07:36 »
Reply with quoteQuote

Bernd, I am aware of the mixed-mode issues; I have read that thread already.  My questions for you specifically are:

First,
- Why would a single badly timed GC cause all input, from that moment forward, to sound distorted? 

I would expect a badly timed GC to perhaps cause a gap in the audio, or a single click, or something of that sort.  But what I am hearing is that BASSASIO gets into a state where all newly recorded sound is corrupted.  And moreover, the corruption takes the form of a steady "vibration" -- it's not due to repeated GCs, it's too even for that. 

Moreover, it's not an issue of GC in general -- my app is certainly having many GCs that do not cause this problem.

I have read the thread you cite, but it's talking about occasional dropouts.  That's not at all what I'm experiencing; I'm experiencing the input stream getting into a state where all audio is distorted for the remainder of my application's running time. 

Could an excessively long GC in the middle of an ASIO C# SYNCPROC cause BASSASIO to get permanently out of sync?

Second,
- May I respectfully ask, did you actually listen to the .wav file I linked?

I am prepared to believe that mixed-mode might be the solution -- I mentioned GC in my original post -- but I would like you and/or Ian to have a listen to the bad sound my app starts making, before concluding that it really is a GC issue.

Thanks very much.
Logged
radio42
Posts: 4012


« Reply #5 on: 22 Jun '12 - 11:50 »
Reply with quoteQuote

Quote
Why would a single badly timed GC cause all input, from that moment forward, to sound distorted?
I would expect a badly timed GC to perhaps cause a gap in the audio, or a single click, or something of that sort.  But what I am hearing is that BASSASIO gets into a state where all newly recorded sound is corrupted.  And moreover, the corruption takes the form of a steady "vibration" -- it's not due to repeated GCs, it's too even for that.

In that case I agree, if all input from that moment onward is distorted it should NOT really be related to a GC, as that should indeed only result in a single 'gap' or 'hickup'.
So in that case I am also unsure what might cause your effect.

Being honest I hadn't the time to listen yet - will do so...now!

Quote
I am prepared to believe that mixed-mode might be the solution -- I mentioned GC in my original post -- but I would like you and/or Ian to have a listen to the bad sound my app starts making, before concluding that it really is a GC issue.
Note sure, if a mixed-mode assembly will really help, as there is still one issue left wven with mixed-mode assemblies:
You can not handle SYNCPROCs in mixed-mode assemblies as well - as a SYNCPROC still MUST call into managed code regardless - so in essence in the end it will be your mixed-mode assembly calling in at least.
Best practice whatsoever might still be to invoke any code in a SYNCPROC async - so that the SYNCPROC itself can return immediately and your invoked C# syncproc code runs independent and in parallel to that.

Anyhow, looking at some of your code - you now call the PushStream like this:
    "PushSampleToStream(Sample<float> sample, int lengthSamples)"
So it looks you hand-over the number of samples to push as well.
How do you calculate them?

After listening to your .wav it might be the case, that you are not pushing 'enough' samples from a certain point in time.
So the initial effect might indeed be caused by e.g. a GC - but from that moment on you start pushing too less samples to the ASIOPROC for playback, so that the Output ASIOPROC doesn't have enough sample data as it actually needs.
So how do you calculate the number of samples to push?
Would it be possible (in your code) to compare the AVAILABLE number of samples (in you loop buffer) with the number of samples requested by the Output ASIOPROC?
Maybe the Output ASIOPROC requests more data as available in the loop buffer?
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #6 on: 22 Jun '12 - 17:09 »
Reply with quoteQuote

As often with audio bugs, it's much harder to describe it than to hear it.  My app can write a WAV file (via BASS, of course), and I happened to capture the bug just now.  Here's a 117MB uncompressed 48Khz WAV of me fooling around with Holofunk.  At 4:37, the bug bites, and the audio for the rest of the track is horrid.  (Fortunately, whatever the bug is, it got recorded by BASS!)

http://unrealities.com/holofunk/holofunk_20120621_102050.wav

That does sound strange! From your code, it looks like the WAV file is of the mixer output, so the problem would appear to be unrelated to the ASIO output, but perhaps it's in the ASIO input. It's hard to tell from the WAV file, but once the problem begins, is it only affecting newly recorded tracks, ie. are there old tracks in the mix that are still sounding OK? If you're unsure, you could check by muting the new track(s) once the problem begins. Check both the ASIO output and the WAV output, in case the problem is actually affecting both ASIO output and input.

If the problem is in the ASIO input, then it might be that the input ASIOPROC function took too long to process the data. Does increasing the ASIO buffer length (in the BASS_ASIO_Start call) make any difference? I see that the ASIOPROC is adding data to a push stream via BASS_StreamPutData. That might require memory being reallocated, which can introduce a delay (perhaps longer than the ASIO buffer lasts) if the stream is holding a lot of data; the data may need to be moved to a new memory location. If you know in advance how much data will be held by the push stream, you could pre-allocate a large enough buffer to avoid reallocation being necessary. That can be done by passing buffer=NULL in a BASS_StreamPutData call...

m_inputPushStream = Bass.BASS_StreamCreatePush(...); // create the push stream
Bass.BASS_StreamPutData(m_inputPushStream, NULL, buflen); // pre-allocate buffer space

By the way, does the problem ever go away eventually, ie. do freshly recorded tracks start sounding OK again at some point?

Please also confirm what BASS/mix/ASIO DLL versions you are using, and try upgrading if any aren't the latest (all 3 were updated fairly recently).
Logged
RobJellinghaus
Posts: 62


« Reply #7 on: 23 Jun '12 - 21:33 »
Reply with quoteQuote

Thanks very much, guys, you continue to be so awesome.  If I ever make any money off of this thing I am sending you guys a big chunk!

It is plausible that it is a problem with the input ASIOPROC getting effectively starved by the GC.  I am about to go to another demo but I will reply in more detail later.

I wonder if I could force this condition to happen by deliberately injecting a one-time delay into the input ASIOPROC... I will experiment with that.

Ian, there definitely are other already-recorded tracks that are still sounding fine -- the problem is very specific to the input channel, which enters a bad state where all incoming audio is distorted.  This situation persists for the lifetime of the app; it never gets back to a good state.

Bernd, the short story is that I save samples in chunks that fit my available buffers, and I never allocate memory -- I preallocate half a freakin' gig of RAM to hold samples!  There may be other .NET memory allocations happening, though I've eliminated this on the ASIOPROC thread -- but that's not enough to prevent GC interference from other .NET threads, of course.

More soon, thanks again.
Logged
radio42
Posts: 4012


« Reply #8 on: 24 Jun '12 - 11:30 »
Reply with quoteQuote

When more thinking about it, a similar issues happened to me once as well (if I recall some of my own experiences correctly ;-)
In my situation an intermediate mixer stream resp. splitter stream was involved inbetween the ASIOIN and ASIOOUT.
I guess there was a situation, in which the ASIOIN wasn't 'pushing' enough samples to the intermediate mixer/splitter, so that there where always a few samples missing as requested by the ASIOOUT.
Resp. in other words, the mixer which was feeding the ASIOOUT was requesting a bit more samples then the ASIOIN was delivering in the same moment.
As far as i remember all was quite unpredictable due to internal timing issues, e.g. if the ASIOIN was 'started' slightly before the ASIOOUT all was okay, but if it was started at the same time or just a little after, it resulted in the same 'distorted' sound (as explained above, there where to less samples pushed by the input and as such not available in the output, so that the output had to miss them).

Not sure, if that makes sense and applies in your case, biut as far as i remember, a mixer always requested a few more samples as actually needed for its internal resampling?
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #9 on: 25 Jun '12 - 16:36 »
Reply with quoteQuote

Ian, there definitely are other already-recorded tracks that are still sounding fine -- the problem is very specific to the input channel, which enters a bad state where all incoming audio is distorted.  This situation persists for the lifetime of the app; it never gets back to a good state.

Does the problem continue even if you call BASS_ASIO_Stop and BASS_ASIO_Start again?

It does sound like the problem is originating from the ASIO input in this case, but just to be sure, you could try setting the WAV writer on the ASIO input stream ("m_inputPushStream") instead of the mixer and see if the problem is still present in the WAV file then. By the way, have you reproduced the problem with another ASIO device/driver? Perhaps it is something device/driver-specific.

Not sure, if that makes sense and applies in your case, biut as far as i remember, a mixer always requested a few more samples as actually needed for its internal resampling?

Yep, resampling (either by BASSmix or BASSASIO) will require a bit of extra sample data, so there might be a small gap/click at the start due to the input providing less data than needed for the output (as some data is taken to fill the resampling buffers). I'm not sure that would explain what's happening in this case, but it is generally best to avoid resampling when the input is being forwarded to the output by leaving the ASIO channels going at the device's rate, ie. don't use BASS_ASIO_ChannelSetRate. The device rate can still be set as wanted via BASS_ASIO_SetRate. The mixer and input "push" streams should also use that same rate.
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines