Author Topic: Crackling and popping when casting after a period of time  (Read 1385 times)

Chris Oakley

  • Posts: 96
Hi, I've noticed after a period of time casting that I start to get crackling and popping on the stream. Now as you can imagine I've been through all sorts of tests and tweaks surrounding this from checking the actual sound source to testing other streaming software.

What I don't understand is why it appears after a random period of time. It could be an hour or it could be 24 hours and the clicks and pops aren't constant - they are random. There's no pattern.

If there was a problem with the processing of the data it would happen right away surely? This is how I start the casting:

Code: [Select]
    Encoder = BassEnc.BASS_Encode_Start(CastingMixer, Com, BASSEncode.BASS_ENCODE_NOHEAD Or BASSEncode.BASS_ENCODE_FP_16BIT Or BASSEncode.BASS_ENCODE_QUEUE, _myEndProc, IntPtr.Zero)

I've tried with the Encode Queue flag, as above, and without. Neither makes a difference.

I'm using a split stream and mixer too feed the encoder:

Code: [Select]
    If CastingSplitStream <> 0 Then
        Bass.BASS_StreamFree(CastingSplitStream)
        CastingSplitStream = 0
    End If
    CastingSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SPLIT_SLAVE, Nothing)
    CastingMixer = BassMix.BASS_Mixer_StreamCreate(Frequency, Channels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(CastingMixer, CastingSplitStream, BASSFlag.BASS_MIXER_BUFFER Or BASSFlag.BASS_MIXER_LIMIT)

I'm processing the data for this with:

Code: [Select]
        Dim LastMS As Integer = Environment.TickCount - 10
        Do Until bkgGetLocalMixer Is Nothing OrElse bkgGetLocalMixer.CancellationPending

            Dim TimeNowMS As Integer = Environment.TickCount
            Dim Levels(2) As Single
            Bass.BASS_ChannelGetLevel(CastingMixer, Levels, (TimeNowMS - LastMS) / 1000, BASSLevel.BASS_LEVEL_STEREO)
            LastMS = TimeNowMS
            Threading.Thread.Sleep(10)

        Loop

I have also tried:

Code: [Select]
        Do Until bkgGetLocalMixer Is Nothing OrElse bkgGetLocalMixer.CancellationPending
            Dim Levels(2) As Single
            Bass.BASS_ChannelGetLevel(CastingMixer, Levels, 1, BASSLevel.BASS_LEVEL_STEREO)
            Threading.Thread.Sleep(100)
        Loop

But again neither makes a difference.

I've tried different machines, with a DSP and without. I just can't fathom it out. The audio is being fed using WASAPI and the device is initialised like this:

Code: [Select]
                Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0)
                Bass.BASS_Init(0, Frequency, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero)
                _wasapi = New BassWasapiHandler(DeviceIndex, False, Frequency, TheMainMixerChannels, 0F, 0F)
                _wasapi.Init()
                DeviceFrequency = _wasapi.SampleRate
                _wasapi.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE, False)
                If _wasapi.DeviceMute Then
                    _wasapi.DeviceMute = False
                End If
                _wasapi.Start()
                RChan = _wasapi.OutputChannel

Any ideas would be welcome because this is a real puzzler.

Ian @ un4seen

  • Administrator
  • Posts: 21372
To help narrow down where the problem is, please try setting another encoder on the same source (CastingMixer) to write a local copy, and check whether the problem is present in the written file. If the problem isn't present in the written file, the problem is probably client-side. Is the client using BASS? If so, you could try the same there, ie. set an encoder on the client's playback stream to write a local copy, and check whether the problem is present in the written file. If the problem isn't present in that either then it might be that the playback is briefly stalling. If it isn't already setting a BASS_SYNC_STALL sync, try setting that sysnc and see if it gets triggered when the problem happens.

Please also confirm how you are casting, eg. BASS_Encode_CastInit or BASS_Encode_ServerInit? And what BASS and BASSenc versions are used, and whether the client is using WASAPI or DirectSound output.

Chris Oakley

  • Posts: 96
I'll try the logging to a file as well as the SYNC_STALL and see what results I get. When you say "client" I presume you mean the application providing the audio? If so, then yes, that is also using BASS and it's outputting via WASAPI.

For the CastInit, sorry, I forgot to put that in my original post, I'm doing this:

Code: [Select]
        nRet = BassEnc.BASS_Encode_CastInit(Encoder, StreamURL.Trim, IIf(Username.Trim = "", Password.Trim, Username.Trim & ":" & Password.Trim), Content, Name, WebURL, Genre, Description, Nothing, Bitrate, IsPublic)

Chris Oakley

  • Posts: 96
Also, a little further detail - something to listen to so you can see what I mean. Extract the zip and there are two files:

example-original.mp3 - this is the file as it is originally
example-clip.mp3 - this is what was not only cast but also sent to a file at the same time. Should be able to hear the clipping.

I'm using the word clipping, but it could be described as crackling, popping or even like scratches on a dirty vinyl record. Either way it shouldn't be there, but why after a period of time it appears is beyond me because surely once the data has been processed, it's gone. It's not sat in a buffer anywhere and nothing needs clearing from what I can tell.

Chris Oakley

  • Posts: 96
ADDITIONAL: I've just realised as well that the main dll's were out of date versions:

bass.dll - 2.4.12.1
bass.net.dll - 2.4.12.6
basswasapi.dll - 2.4.1.2
bassenc.dll - 2.4.13.0
bassmix.dll - 2.4.8.0

I've brought them all up to date now and will continue to test.

Don't know if this would potentially contribute to the problems I'm having. Any thoughts?

Ian @ un4seen

  • Administrator
  • Posts: 21372
Also, a little further detail - something to listen to so you can see what I mean. Extract the zip and there are two files:

example-original.mp3 - this is the file as it is originally
example-clip.mp3 - this is what was not only cast but also sent to a file at the same time. Should be able to hear the clipping.

I'm using the word clipping, but it could be described as crackling, popping or even like scratches on a dirty vinyl record. Either way it shouldn't be there, but why after a period of time it appears is beyond me because surely once the data has been processed, it's gone. It's not sat in a buffer anywhere and nothing needs clearing from what I can tell.

That doesn't sound like a stalling/stuttering playback problem, so the BASS_SYNC_STALL sync probably won't reveal anything. Is the "clip" file from an encoder set on the same source as the caster? I notice its level is higher than the original, so I guess there are some DSP/FX applied by the server? If so, what exactly are they, and does disabling them stop the problem happening?

Chris Oakley

  • Posts: 96
Thanks Ian - Yes, I applied the SYNC_STALL but it didn't trigger. I did that today. I'm not sure about the server adding DSP or effects. I wasn't aware Centovacast can do that. However, through the same server other streaming apps work fine.

I'm currently running the test from an extremely stripped down app which I've put together, based on code from the main app. I'm waiting to see if it does the same thing. If it doesn't then I will have a better angle to investigate from.

If it does the same thing I can zip it up and pop it on here and you will be able to see at least what I've done.

Ian @ un4seen

  • Administrator
  • Posts: 21372
By "DSP/FX applied by the server", I meant your software that is generating the stream (not the Shoutcast/Icecast server). Is it not applying any DSP/FX, ie. no BASS_ChannelSetDSP/FX calls? Please also confirm whether the "clip" file was from the same source as the caster, ie. both BASS_Encode_Start calls had the same "handle" parameter.

Chris Oakley

  • Posts: 96
Ahh I see. No there's no DSP or FX being applied. I've always noticed WASAPI seems to be a little louder, although it could be the software playing the audio has made it a little louder.

To record the file I split off the main source and create a mixer to set the sample rate required by the user. This has its own handle:

Code: [Select]
    LoggingSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SPLIT_SLAVE, Nothing)
    LoggingMixer = BassMix.BASS_Mixer_StreamCreate(LogFrequency, Channels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(LoggingMixer, LoggingSplitStream, BASSFlag.BASS_DEFAULT)

The caster follows the same process. I split off the main source and create a mixer to adjust the sample rate. This has its own handle too:

Code: [Select]
    CastingSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SPLIT_SLAVE, Nothing)
    CastingMixer = BassMix.BASS_Mixer_StreamCreate(Frequency, Channels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(CastingMixer, CastingSplitStream, BASSFlag.BASS_MIXER_BUFFER Or BASSFlag.BASS_MIXER_LIMIT)

There is a third split for monitoring levels. I this one isn't set to be a SPLIT_SLAVE and this is the one I pull the data from:

Code: [Select]
    LevelSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE, Nothing)
    LevelMixer = BassMix.BASS_Mixer_StreamCreate(Frequency, TheMainMixerChannels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(LevelMixer, LevelSplitStream, BASSFlag.BASS_MIXER_BUFFER Or BASSFlag.BASS_MIXER_LIMIT)

However, I find I have to also process the data for the CastingMixer and the LoggingMixer too. I thought I didn't have to do that as you just pull the data from the split that isn't the SLAVE - or have I gotten all mixed up? If I don't process them in a worker loop then they don't cast or log.

Chris Oakley

  • Posts: 96
Okay, just further to the info above the test app is doing it too so I've attached it for you to have a look at if you wouldn't mind. It's not big and only casts, no logging, no level monitoring - no bells and whilstles.

You would just need to enter your stream details in. If you want to use the stream I'm using let me know and I can PM you the details.
« Last Edit: 12 Oct '18 - 16:34 by Chris Oakley »

Ian @ un4seen

  • Administrator
  • Posts: 21372
To record the file I split off the main source and create a mixer to set the sample rate required by the user. This has its own handle:

Code: [Select]
    LoggingSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SPLIT_SLAVE, Nothing)
    LoggingMixer = BassMix.BASS_Mixer_StreamCreate(LogFrequency, Channels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(LoggingMixer, LoggingSplitStream, BASSFlag.BASS_DEFAULT)

The caster follows the same process. I split off the main source and create a mixer to adjust the sample rate. This has its own handle too:

Code: [Select]
    CastingSplitStream = BassMix.BASS_Split_StreamCreate(MixerChan, BASSFlag.BASS_STREAM_DECODE Or BASSFlag.BASS_SPLIT_SLAVE, Nothing)
    CastingMixer = BassMix.BASS_Mixer_StreamCreate(Frequency, Channels, BASSFlag.BASS_STREAM_DECODE)
    Dim AddEncMix As Boolean = BassMix.BASS_Mixer_StreamAddChannel(CastingMixer, CastingSplitStream, BASSFlag.BASS_MIXER_BUFFER Or BASSFlag.BASS_MIXER_LIMIT)

Is LoggingMixer what the "clip" file encoder is set on? If so, please try setting that encoder on the same stream as the casting encoder (CastingMixer). That (instead of separate splitters/mixers) will also be a more efficient way to do it, when the intention is to have both encoders receive the same data.

Chris Oakley

  • Posts: 96
I could try that but I do this splitting and mixers because the user may want to log in mono at different rates than the main casting stream. Does that make sense?

I don't know if you saw my additional post above, the test app I made has the same issue and that doesn't have any logging mixers or extra bits. It's just a simple app performing the casting in the same way as the main app, but after a few hours the clipping sounds appear.

I have attached it to the post above if you feel like taking a look.

Ian @ un4seen

  • Administrator
  • Posts: 21372
I could try that but I do this splitting and mixers because the user may want to log in mono at different rates than the main casting stream. Does that make sense?

Yes, but in this case we're trying to locate where the problem is being introduced, so we need to be sure that the local file encoder is receiving the same data as the caster.

I don't know if you saw my additional post above, the test app I made has the same issue and that doesn't have any logging mixers or extra bits. It's just a simple app performing the casting in the same way as the main app, but after a few hours the clipping sounds appear.

I have attached it to the post above if you feel like taking a look.

I'm not a .Net user myself, but I will load it up and give it a try later (probably have to be Monday now).

Chris Oakley

  • Posts: 96
That would be brilliant if you could Ian. There's nothing fancy in the code it's just dealing with calls to Bass generally so should be easy enough to follow.

Ian @ un4seen

  • Administrator
  • Posts: 21372
I notice your test app is still using the old DLLs. Does updating them to the latest versions make any difference?

For comparison, please also try the pre-compiled CAST.EXE example that's included in the BASSenc package (C\BIN folder) and see if you get the problem happening with that too.

Chris Oakley

  • Posts: 96
Oh - I don't know how that happened. The dlls should have been the latest versions. I've updated the on my system now and I'm running the cast test app again to see if it's better. I did do this on the main app though and it didn't help - but I'll do this to double check.

I'll have a look at the cast.exe as well, although that looks to be a VB6 app rather than a .NET one.

Chris Oakley

  • Posts: 96
UPDATE: New Dll's don't make any difference. Will test the cast.exe now.

Chris Oakley

  • Posts: 96
Ok. I can't use the cast.exe test file because it doesn't allow me to choose WASAPI.

WASAPI is where the problem is - DirectSound is fine, it doesn't suffer from the same problem.

Have you actually had a look at my test app yet Ian or tried casting with it for a few hours?

Ian @ un4seen

  • Administrator
  • Posts: 21372
Yes, I did try running your test (with a Shoutcast server on the LAN) for a couple of hours yesterday but the problem didn't seem to happen. I didn't listen to the stream for the whole time though, just connected briefly several times to check it. Does the problem come and go? If so, it's possible I may have just missed it.

When you tried using DirectSound instead of WASAPI for the recording, was the rest of the code exactly the same, ie. 2 mixers and a splitter still involved? Can you try modifying your test to use DirectSound (BASS_RecordStart) instead of WASAPI, and see what happens then?

From what you say, it sounds like the problem is introduced to the data before it reaches the casting, ie. the problem isn't related to the casting. To check that, can you try replacing the casting with a local file encoder, and see if the problem is present in the written file?

Chris Oakley

  • Posts: 96
Interesting. Thanks for having a try. It doesn't come and go, once it's there it's there for the duration.

When using DirectSound I don't get the issue in the cast or the recording. Everything was still the same and I have already tried the BASS_RecordStart instead in the main app.

The upshot is DirectSound is fine and WASAPI (and ASIO as well) both have this artifact after the same amount of time.

It does sound like the data is being misinterpreted or something like that. The clicks appear in the stereo image so they sort of move from side to side randomly. I can confirm that when I play a file instead of streaming the captured sound device there is no clicking.

Chris Oakley

  • Posts: 96
Just as an update I've now removed all splitters and mixers and I'm just encoding and casting from the main WASAPI endpoint / channel. Want to rule out the mixers and splitters. Will know in about 2 hours.

Chris Oakley

  • Posts: 96
The results are in and not using a mixer or splits and streaming the WASAPI endpoint directly is fine. So I've now put an initial mixer in place to test that step. If that's fine then I'll add a split and keep going till it stops working.

Ian @ un4seen

  • Administrator
  • Posts: 21372
Great, now we're getting somewhere :)

You mentioned the problem not happening when using DirectSound recording. Did you have the same mixer/splitter setup then, or not?

Chris Oakley

  • Posts: 96
Yes same setup with DirectSound.

Okay, test complete and it started crackling again. This is the code I used to create the mixer which I added the main RChan WASAPI endpoint into:

Code: [Select]
        MixerChan = BassMix.BASS_Mixer_StreamCreate(DeviceFrequency, 2, BASSFlag.BASS_STREAM_DECODE)

I then just process that in background worker with:

Code: [Select]
        Dim LastMS As Integer = Environment.TickCount - 20

        Do Until bkgMainMixer Is Nothing OrElse bkgMainMixer.CancellationPending

            Dim TimeNowMS As Integer
            Dim Levels(2) As Single
            TimeNowMS = Environment.TickCount
            Bass.BASS_ChannelGetLevel(MixerChan, Levels, (TimeNowMS - LastMS) / 1000, BASSLevel.BASS_LEVEL_STEREO)
            LastMS = TimeNowMS
            Threading.Thread.Sleep(20)

        Loop


I have a sleep of 20 set here, but I've also used 10 and it makes no difference.

Ian @ un4seen

  • Administrator
  • Posts: 21372
OK. What happens if you try using the BASS_ENCODE_LIMIT flag in the BASS_Encode_Start call and then process the mixer as quickly as possible in the loop? Like this:

Code: [Select]
        Do Until bkgMainMixer Is Nothing OrElse bkgMainMixer.CancellationPending

            Dim Levels(2) As Single
            Bass.BASS_ChannelGetLevel(MixerChan, Levels, 1, BASSLevel.BASS_LEVEL_STEREO)
            Threading.Thread.Sleep(20)

        Loop

If the problem still happens then, what happens if you switch to using DirectSound recording (instead of WASAPI) but keep the rest of the code the same?