Author Topic: Question about BASS_SYNC_END | BASS_SYNC_MIXTIME in BASS_ChannelSetSync  (Read 528 times)

VorTechS

  • Posts: 265
Hi all, a very, very, long time with no silly questions!  :)

I decided, as you do, to re-write my CarPC audio player from pretty much scratch and came across an interesting problem I never noticed before, where one of my MP3's would never run to the end whilst plugged in to a mixer using a SYNCPROC.

I'm not sure how I've never noticed this in, what, 7 years since I wrote the original software.  But there we go.

I narrowed it down to using 'BASS_SYNC_END | BASS_SYNC_MIXTIME', and swapped it out for just BASS_SYNC_END and my problem was solved.  And it occured to me, I don't really have a decent description of what these options are.

SYNC_END is (at an educated guess) for when a stream plugged into the mixer has ended.

What I'd like to know is: what does the 'BASS_SYNC_MIXTIME' flag _actually_ mean in leymans terms, so that I can understand for future reference, why on some occasions the sync proc get's called earlier for some tracks plugged into a mixer (when there is only one track plugged in)

Thanks!




Ian @ un4seen

  • Administrator
  • Posts: 20433
The BASS_SYNC_MIXTIME flag means that your SYNCPROC function will be called as soon as the sync event occurs, eg. the decoder reaches the end in the BASS_SYNC_END case. Without that flag, the SYNCPROC call will be delayed until the event is actually heard, eg. you hear playback reach the end (which will be later due to buffering).

Note that some sync types are automatically "mixtime" (indicated in the sync documentation), so the flag will make no difference with those. All syncs set on "decoding channels" via BASS_ChannelSetSync are also automatically "mixtime" because there is no playback buffer then, but non-mixtime syncs can be set on mixer sources via BASS_Mixer_ChannelSetSync.

VorTechS_

  • Guest
Ian, thanks for that description which I think I understand. 

Essentially the difference between the two, is that MIXTIME occurs when the decoder has loaded the last part of the buffer.

I don't have access to my code at the moment, but for 'continuous' playback (ignoring the fact you'll never get 100% gapless playback) how should you go about loading in the next item.  At the moment I'm stopping the current stream (not entirely sure if that's the dream or the mixer) at the point the callback fires, loading the next stream, plugging that into the mixer and then playing it which is obviously why I am getting the behaviour I am seeing.

I appreciate given the myriad of options with auto-freeing streams, etc that's a bit vague. 

I'm staying to get a bit of DEJAVU here, I have a suspicion I've asked this before, so I'll have a hunt around in the meantime.

SoundMike

  • Posts: 331
... but for 'continuous' playback (ignoring the fact you'll never get 100% gapless playback)...
You can, IMO, get 100% gapless playback. See Ian's posting here www.un4seen.com/forum/?topic=16636.msg116359#msg116359.

I've used this technique in my program (SCS) and it works extremely well. A couple of key points to remember are:

VorTechS

  • Posts: 265
Thanks for that Mike.

I've implemented cache of stream handles and initial tests seem to work better; when one track ends the next plays nicely, although I haven't gotten round to testing on the tracks I identified as having the problem yet.

That's because I've gotten diverted, as I'm now no longer able to skip tracks.

Code: [Select]
            // Stop the mixer from playing
            Bass.BASS_ChannelStop(_mixerHandle);

            // Stop the mixer from playing
            Bass.BASS_ChannelStop(_streamHandles[_currentPlaylistItem]);

            // Remove the current handle from the mixer
            Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_ChannelRemove(_streamHandles[_currentPlaylistItem]);

            // Free the stream resources
            Bass.BASS_StreamFree(_streamHandles[_currentPlaylistItem]);


My understanding is that the above should clear the mixer of the current stream. 

I then call the same function as the Sync End to plug in the next track, and when the mixer channel is 'played' again (through Bass_ChannelPlay) both the previous track AND the track being switched to plays.
« Last Edit: 6 Nov '17 - 16:11 by VorTechS »

Ian @ un4seen

  • Administrator
  • Posts: 20433
When you say the previous track is still playing, do you just mean a small part of it, approximately equal to the length of the playback buffer (BASS_CONFIG_BUFFER)? If so, the problem is that the mixer's playback buffer wasn't cleared. You can clear it by using restart=true in the BASS_ChannelPlay call (or calling BASS_ChannelSetPosition with pos=0).

VorTechS_

  • Guest
Hi Ian,

The stream being removed carries on from where it was stopped.

Ill try adding the restart parameter to see what happens.

Thanks.

VorTechS

  • Posts: 265
When you say the previous track is still playing, do you just mean a small part of it, approximately equal to the length of the playback buffer (BASS_CONFIG_BUFFER)? If so, the problem is that the mixer's playback buffer wasn't cleared. You can clear it by using restart=true in the BASS_ChannelPlay call (or calling BASS_ChannelSetPosition with pos=0).

I tried adding the restart parameter, and that made no difference.  Here are the two functions involved:

Code: [Select]

        private void CurrentTrackEnded(int mixerHandle, int channel, int channelData, IntPtr userPointer)
        {
            RemoveMetaDataSynchHandler();

            PluginFailureReason reason = PlugInNextTrack();

            // Clear the current item from the queue
            _streamHandles[_currentPlaylistItem] = 0;

            if (reason == PluginFailureReason.None)
            {
                _onPlayerStateChanged?.BeginInvoke(PlayerPlayState.Playing, _currentPlaylistItem, null, null);
            }
            else if (reason == PluginFailureReason.ReachedPlaylistEnd)
            {
                NextPlaylist();
            }
            else
            {
                // Stop the current stream from playing
                StopPlayback();
            }
        }

        private PluginFailureReason PlugInNextTrack(double trackTime = 0)
        {
            PluginFailureReason failureReason = PluginFailureReason.None;

            bool wasPluggedIn = false;

            _currentPlaylistItem += 1;

            if (_currentPlaylistItem <= _currentPlaylistCount - 1)
            {
                VerifyTrackHandles(ErrorPoint.SwitchingTracksAuto, _currentPlaylistItem);

                if (_streamHandles != null && _streamHandles.Count > 1)
                {
                    if (_streamHandles[_currentPlaylistItem] != 0)
                    {
                        Bass.BASS_ChannelSetPosition(_streamHandles[_currentPlaylistItem], trackTime); // rewind the file (in case it's already been played)
                        Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamAddChannel(_mixerHandle, _streamHandles[_currentPlaylistItem], BASSFlag.BASS_MIXER_NORAMPIN); // add it to the mixer
                        Bass.BASS_ChannelSetPosition(_mixerHandle, 0, BASSMode.BASS_POS_BYTES); // reset the mixer to continue playing (it's ended)

                        if (_playBackState == PlayerPlayState.Stopped)
                        {
                            Bass.BASS_ChannelPlay(_mixerHandle, true);
                        }
                    }
                    else
                    {
                        failureReason = PluginFailureReason.FailedToCreateStream;
                    }
                }
                else
                {
                    failureReason = PluginFailureReason.FailedToCreateStream;
                }
            }
            else
            {
                failureReason = PluginFailureReason.ReachedPlaylistEnd;
            }

            return failureReason;
        }

When skipping a track, essentially a 'full stop' is initiated, and then the call made to plug in the next track, which looks like this:

Code: [Select]

        public void NextTrack()
        {
            _isStoppingTrack = true;

            StopPlayback(true);

            // Move to the next track
            CurrentTrackEnded(_mixerHandle, 0, 0, IntPtr.Zero);
        }

        public void StopPlayback(bool switchingTracks = false)
        {
            // Stop the media tracking timer
            DestroyTimer();

            // Stop the current stream from playing
            Bass.BASS_ChannelStop(_streamHandles[_currentPlaylistItem]);

            // Stop the mixer from playing
            Bass.BASS_ChannelStop(_mixerHandle);

            Bass.BASS_ChannelSetPosition(_streamHandles[_currentPlaylistItem], 0);
            Bass.BASS_ChannelSetPosition(_mixerHandle, 0);

            // Remove the current handle from the mixer
            Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_ChannelRemove(_streamHandles[_currentPlaylistItem]);

            // Free the stream resource
            Bass.BASS_StreamFree(_streamHandles[_currentPlaylistItem]);

            // Remove any Meta Data Sync Handlers (Stream tag information)
            RemoveMetaDataSynchHandler();

            if (!switchingTracks)
            {
                // Mark the track as having ended because the track finished playing
                _lastMediaEndReason = MediaEndReason.PlaybackStopped;

                // Stop everything
                _onPlayerStateChanged?.BeginInvoke(PlayerPlayState.Stopped, _currentPlaylistItem, null, null);
            }

            _playBackState = PlayerPlayState.Stopped;
        }


...and that included the code to set the stream (and mixer position for good measure) to zero.  And yet still the old stream goes on from where it left off!

Streams are initially created in this manner:

Code: [Select]
                // Get a bass handle from a local file source
                mediaStreamHandle = Bass.BASS_StreamCreateFile(_currentPlaylistFile, 0, 0, Un4seen.Bass.BASSFlag.BASS_STREAM_DECODE | Un4seen.Bass.BASSFlag.BASS_SAMPLE_FLOAT | Un4seen.Bass.BASSFlag.BASS_MIXER_NORAMPIN | Un4seen.Bass.BASSFlag.BASS_MUSIC_PRESCAN);


...and the PluginNextTrack is used to initially play the stream too.

For good measure, here's how the mixer is created:

Code: [Select]

        // Create a mixer instance, and set the synch call back
        private int CreateMixer()
        {
            // If bass was successfully started
            if (_bassStarted)
            {
                // Create a 2 - channel mixer
                _mixerHandle = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_MIXER_END);

                // If the mixer failed to create
                if (_mixerHandle == 0)
                {
                    // Set the player to an error state
                    SetErrorState(ErrorPoint.MixerCreation, Bass.BASS_ErrorGetCode(), -1);
                }
                else
                {
                    // Create a new callback for when the current track in the mixer has ended
                    _mixerSynchProc = new Un4seen.Bass.SYNCPROC(CurrentTrackEnded);

                    // Set the mixer synch callback
                    _mixerSyncHandle = Bass.BASS_ChannelSetSync(_mixerHandle, Un4seen.Bass.BASSSync.BASS_SYNC_END | Un4seen.Bass.BASSSync.BASS_SYNC_MIXTIME, 0, _mixerSynchProc, IntPtr.Zero);

                    if (_mixerSyncHandle == 0)
                    {
                        // Set the player to an error state
                        SetErrorState(ErrorPoint.MixerCreation, Bass.BASS_ErrorGetCode(), -1);
                    }
                }
            }
            else
            {
                // Set an error state of error with initialisation...
                SetErrorState(ErrorPoint.MixerCreation, Un4seen.Bass.BASSError.BASS_ERROR_INIT, -1);
            }

            // Return the mixer handle
            return _mixerHandle;

        }


I think I might try some drastic measures now, and have the stop procedure wipe the mixer off the face of the earth and see what happens!

VorTechS

  • Posts: 265
I think I might try some drastic measures now, and have the stop procedure wipe the mixer off the face of the earth and see what happens!

...which worked a treat.

Assuming this is valid, and isn't likely to cause any other problems that is!

Code: [Select]
Bass_StreamFree(_mixerHandle)

Although, obviously I'd prefer to not have to re-create the mixer every time.

Ian @ un4seen

  • Administrator
  • Posts: 20433
If the old file is still playing despite you calling BASS_StreamFree on it, then that sounds like the call may be failing. Please check the return value. Also check that the handle value used in that call is the same one that was used previously in a BASS_Mixer_StreamAddChannel call; perhaps the "_currentPlaylistItem" variable changed between the 2 calls?

VorTechS

  • Posts: 265
If the old file is still playing despite you calling BASS_StreamFree on it, then that sounds like the call may be failing. Please check the return value. Also check that the handle value used in that call is the same one that was used previously in a BASS_Mixer_StreamAddChannel call; perhaps the "_currentPlaylistItem" variable changed between the 2 calls?

Well.  I've done a bit of 'code re-organising' after switching to destroying the mixer on 'manual track changes'. 

This is because I ended up having problems with BassVis after having switched the source from the original mixer it was started with.

I re-instated the previous code for freeing up the active mixer stream and added error checking to both the freeing of the stream and channel removal, and removed the code that was creating a new mixer, low and behold it's all behaving properly now.

I don't recall fixing anything specifically to do with track indexing, so I have no idea how I've fixed it!

Thanks for the help Ian (and Mike too), top notch support as ever!

Ian @ un4seen

  • Administrator
  • Posts: 20433
Good to hear that it's working now, even if it is a bit of a mystery :)

VorTechS

  • Posts: 265
It's almost flawless.  :)

I keep getting an app. crash though with the following:

Code: [Select]

The program '[22968] PuggyPower.vshost.exe: Program Trace' has exited with code 0 (0x0).
The program '[22968] PuggyPower.vshost.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.


(I should probably come up with a better name for this project one day!)

...and even though I've all exception handling in Visual Studio turned on to 'Break on all errors', it never does, so I don't know which Bass call is causing the issue.

Details from Event Viewer are:

Code: [Select]
Faulting module name: bass.DLL, version: 2.4.12.1, time stamp: 0x57150522
Exception code: 0xc0000005
Fault offset: 0x0001d032

It's happened twice over the last 24 hours.  The only thing I've narrowed it down to, is that on both occasions the error occurred it was whilst automatically 'Changing Playlists'.  (So the last track in the current sequence finishes playing, the next playlist is loaded, the first two tracks are pre-loaded and playback started).

My guess is that it's a clash between the UI functions and Audio player functions.  The UI uses BASS as a fallback for Tag info where TagLib# fails for both the UI itself and to pass to BassVis when restarting the visualizations (which have been stopped due to playback being stopped as the current playlist ended).

So my gut feeling is that the error has occured here:

Code: [Select]
        private void GetChannelProperties()
        {
            BASS_CHANNELINFO channelInfo = new Un4seen.Bass.BASS_CHANNELINFO();

            bool gotChannelInfo = Un4seen.Bass.Bass.BASS_ChannelGetInfo(_streamHandles[_currentPlaylistItem], channelInfo);
            BASSChannelType currentChannelType = BASSChannelType.BASS_CTYPE_UNKNOWN;

            if (gotChannelInfo)
            {
                // Get the channel type
                currentChannelType = channelInfo.ctype;
            }

            // Set the relevant properties about the playing media
            _currentChannelType = Un4seen.Bass.Utils.BASSChannelTypeToString(currentChannelType);

            if (_currentDeviceMode != DeviceMode.HTTP)
            {
                // Get the current stream length
                long streamLength = Bass.BASS_ChannelGetLength(_streamHandles[_currentPlaylistItem]);
                long filePosEnd = Bass.BASS_StreamGetFilePosition(_streamHandles[_currentPlaylistItem], BASSStreamFilePosition.BASS_FILEPOS_END);

                // Evaluate into the number of seconds
                double totalSeconds = Bass.BASS_ChannelBytes2Seconds(_streamHandles[_currentPlaylistItem], streamLength);

                // Get the average bitrate
                _currentBitRate = (filePosEnd / (125 * totalSeconds) + 0.5);
            }
        }

BUT it doesn't occur on every natural conclusion of a playlist, so quite clearly this is an itermittent race condition.

The function above was being called immediately after a UI delegate is called (which fires off amongst other things an asynchronous Text-to-speech call), but I've flipped this to be the other way around now so the UI call is last to see if that solves it.

It would help if I knew which call was failing though, but I'm not sure the detail in event viewer is enough to get a pointer to which function was at fault?

If not, I guess I'll have to output all BASS calls to the console to narrow down which one might be at fault! ;)

EWeiss

  • Posts: 355
Quote
This is because I ended up having problems with BassVis after having switched the source from the original mixer it was started with.
use latest Version.. 2.4.5.2
load her http://support.imploded.com/support/discussions Helium 12
an see how BassVis works.

the new Version of Helium used bassVis more then one year without any Trouble.
Wasapi, Mixer and all available add ons with him.

you can contact MikaelS how help you. ;)

greets
« Last Edit: 9 Nov '17 - 11:00 by EWeiss »

VorTechS

  • Posts: 265
Quote
This is because I ended up having problems with BassVis after having switched the source from the original mixer it was started with.
use latest Version..
load her http://support.imploded.com/support/discussions Helium 12
an see how BassVis works.

the new Version of Helium used bassVis more then one year without any Trouble.
Wasapi, Mixer and all available add ons with him.

greets

Hey my friend! ;)

As you know, I have not had problems in many years!  The easy-to-use library that I wrote, and you fixed, has never caused any problems in the way it was intended to be used.  Although perhaps it is time to move it from Visual Basic and to C# instead as I have done with my media player?

Anyway, the problem above is not really an issue, as I no longer need to destroy and create mixer handles.   

I will try the new version though as there was a problem I noticed with re-sizing visualizations, but this may have been linked to the mixer handle issue. 
I cannot remember as i did not give it much attention.

I think you sent me the details for the latest version, I will check.  (Those forums are not easy to navigate!)

It is good to see you still lurk around these parts! ;)

Ian @ un4seen

  • Administrator
  • Posts: 20433
Details from Event Viewer are:

Code: [Select]
Faulting module name: bass.DLL, version: 2.4.12.1, time stamp: 0x57150522
Exception code: 0xc0000005
Fault offset: 0x0001d032

From that "fault offset", it appears to have crashed while converting a block of floating-point data to 16-bit. Perhaps it was asked to convert too much and went beyond the end of the destination buffer. To find out, please generate a dump file for the crash. You can generate a dump file using the ProcDump tool. For example, run "procdump -e -ma -x . your.exe". Then ZIP and upload the generated dump file to have a look at here:

   ftp.un4seen.com/incoming/

In case it is something that has already been fixed, please try the latest BASS.DLL build (including when generating the dump file):

   www.un4seen.com/stuff/bass.zip

VorTechS

  • Posts: 265
Thanks Ian,

It's a shame I didn't get a notification from the website about your reply yesterday, as I'm currently running the application through Visual Studio having added console output for all of the calls being made to BASS. 

The process of doing that highlighted that on a sync callback for the current track ending I wasn't stopping media monitoring and so potentially I may have been making calls to get 'BASS_ChannelGetLength', 'BASS_ChannelBytes2Seconds' and 'BASS_Mixer_ChannelGetPosition' for the last known media item, and 'BASS_ChannelGetLevel' for the mixer to pass back to the visualization.  This might have been problemmatic because there was potentially nothing plugged into the mixer, or it was mid state as the current stream might have been freed / autofreeing itself.

Yesterday it took well over 4 hours for the bug to re-produce.

I probably won't get a good run at re-producing the issue until Monday, as I shall be off for the weekend in a couple of hours.  If it doesn't re-produce, then all well and good.

Either way, I shall revert my change and run the EXE standalone to get a procdump for you as soon as I can!

VorTechS

  • Posts: 265
Just as I'm thinking about going home, the access violation fires and here's my pseudo call stack:

Code: [Select]
BASS: BASS_ChannelBytes2Seconds (MediaInfoTimer_Elapsed) [Stream Position]
BASS: BASS_ChannelBytes2Seconds (MediaInfoTimer_Elapsed) [Stream Length]
BASS: BASS_ChannelGetLevel (MediaInfoTimer_Elapsed)
BASS: BASS_ChannelIsActive (MediaInfoTimer_Elapsed)
BASS: BASS_ChannelStop (StopPlayback) [StreamHandle]
BASS: BASS_ChannelStop (StopPlayback) [MixerHandle]
BASS: BASS_ChannelRemove (StopPlayback)
BASS: BASS_ErrorGetCode (StopPlayback)
BASS: BASS_StreamFree (StopPlayback)
BASS: BASS_ErrorGetCode (StopPlayback)
BASS: BASS_StreamCreateFile (CreateLocalMediaStream)
BASS: BASS_ChannelBytes2Seconds (CreateLocalMediaStream)
BASS: BASS_ChannelSetPosition (PlugInNextTrack)
BASS: BASS_Mixer_StreamAddChannel (PlugInNextTrack)
BASS: BASS_ChannelSetPosition (PlugInNextTrack)
BASS: BASS_ChannelPlay (PlugInNextTrack)
BASS: BASS_StreamCreateFile (CreateLocalMediaStream)
BASS: BASS_StreamCreateFile (CreateLocalMediaStream)
BASS: BASS_ChannelGetInfo (GetChannelProperties)
BASS: BASSChannelTypeToString (GetChannelProperties)
BASS: BASS_ChannelGetLength (GetChannelProperties)
BASS: BASS_StreamGetFilePosition (GetChannelProperties)
BASS: BASS_ChannelBytes2Seconds (GetChannelProperties)
The program '[29020] PuggyPower.vshost.exe: Program Trace' has exited with code 0 (0x0).
The program '[29020] PuggyPower.vshost.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.

...which makes for some very interesting reading.

PluginNextTrack is the sync callback handler for the track ending, which shows it creates a local media stream and starts playing it.  The next two calls, are getting the next tracks in the sequence - although there shouldn't be two of them, I would have just expected one.
Clearly GetChannelProperties is the call causing problems here, and here's the code for that:

Code: [Select]
        private void GetChannelProperties()
        {
            BASS_CHANNELINFO channelInfo = new Un4seen.Bass.BASS_CHANNELINFO();

            #if DebugBASSCalls
                Console.WriteLine("BASS: BASS_ChannelGetInfo (GetChannelProperties)");                       
            #endif

            bool gotChannelInfo = Un4seen.Bass.Bass.BASS_ChannelGetInfo(_streamHandles[_currentPlaylistItem], channelInfo);
            BASSChannelType currentChannelType = BASSChannelType.BASS_CTYPE_UNKNOWN;

            if (gotChannelInfo)
            {
                // Get the channel type
                currentChannelType = channelInfo.ctype;
            }

            #if DebugBASSCalls
                Console.WriteLine("BASS: BASSChannelTypeToString (GetChannelProperties)");                       
            #endif

            // Set the relevant properties about the playing media
            _currentChannelType = Un4seen.Bass.Utils.BASSChannelTypeToString(currentChannelType);

            if (_currentDeviceMode != DeviceMode.HTTP)
            {
                #if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_ChannelGetLength (GetChannelProperties)");                       
                #endif

                // Get the current stream length
                long streamLength = Bass.BASS_ChannelGetLength(_streamHandles[_currentPlaylistItem]);

                #if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_StreamGetFilePosition (GetChannelProperties)");                       
                #endif

                long filePosEnd = Bass.BASS_StreamGetFilePosition(_streamHandles[_currentPlaylistItem], BASSStreamFilePosition.BASS_FILEPOS_END);

                #if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_ChannelBytes2Seconds (GetChannelProperties)");                       
                #endif

                // Evaluate into the number of seconds
                double totalSeconds = Bass.BASS_ChannelBytes2Seconds(_streamHandles[_currentPlaylistItem], streamLength);

                // Get the average bitrate
                _currentBitRate = (filePosEnd / (125 * totalSeconds) + 0.5);
            }
        }

...called from this function (which is the starting point for playing a playlist):

Code: [Select]
        private bool StartPlayBack(StateChangeReason applicationPoint, PlayerPlayState previousState, int playListItemIndex, double trackTime)
        {
            _currentPlaylistCount = Convert.ToInt32(_currentPlaylist?.Count);

            if (_currentPlaylist == null || playListItemIndex > _currentPlaylistCount)
            {
                _currentPlaylist = _onPlayerGetNextPlaylist();

                InitializePlaylist();
            }

            // Create a default value to return of false
            bool playBackStarted = false;

            if (_currentPlaylist != null && playListItemIndex > -1)
            {
                // Get the next track handles
                VerifyTrackHandles(applicationPoint, playListItemIndex);

                if (_streamHandles?.Count > 0)
                {
                    _currentPlaylistItem = playListItemIndex;

                    PluginFailureReason failureReason = PlugInNextTrack(playListItemIndex, trackTime);

                    // If everything started okay
                    if (failureReason == PluginFailureReason.None)
                    {
                        playBackStarted = true;

                        // Tell the client we are playing!
                        _playBackState = PlayerPlayState.Playing;

                        // Get the media properties where we can
                        GetChannelProperties();

                        // Start the media timer
                        CreateTimer();

                        RaiseDelegate(PlayerDelegateType.PlayerStateChanged, StateChangeReason.PlayBack, PlayerPlayState.Playing, _currentPlaylistItem);
                    }
                    else
                    {
                        // Free up anything required
                        StopPlayback(StateChangeReason.PlayBack);

                        #if DebugBASSCalls
                            Console.WriteLine("BASS: BASS_ErrorGetCode (StartPlayBack)");                       
                        #endif

                        // Set an appropriate error state
                        SetErrorState(applicationPoint | StateChangeReason.PlayBack, Bass.BASS_ErrorGetCode(), playListItemIndex);
                    }
                }
                else
                {
                    #if DebugBASSCalls
                        Console.WriteLine("BASS: BASS_ErrorGetCode (StartPlayBack)");                       
                    #endif

                    // Set an appropriate error state
                    SetErrorState(applicationPoint | StateChangeReason.PlayBack, Bass.BASS_ErrorGetCode(), playListItemIndex);
                }
            }

            return playBackStarted;
        }

Within 10ms of the call to the GetChannelProperties function, the timer tracking the media state (position, etc) fires... so it could feasibly still be that causing a problem!

As this is reproducable as it is, I'll set things in motion for a procdump!

VorTechS

  • Posts: 265
Details from Event Viewer are:

Code: [Select]
Faulting module name: bass.DLL, version: 2.4.12.1, time stamp: 0x57150522
Exception code: 0xc0000005
Fault offset: 0x0001d032

From that "fault offset", it appears to have crashed while converting a block of floating-point data to 16-bit. Perhaps it was asked to convert too much and went beyond the end of the destination buffer. To find out, please generate a dump file for the crash. You can generate a dump file using the ProcDump tool. For example, run "procdump -e -ma -x . your.exe". Then ZIP and upload the generated dump file to have a look at here:

   ftp.un4seen.com/incoming/

In case it is something that has already been fixed, please try the latest BASS.DLL build (including when generating the dump file):

   www.un4seen.com/stuff/bass.zip

I'm not sure the first attempt to upload via cmdline worked, as I got a connection error.  The second upload definitely did:

VorTechS_PuggyPower.exe_171110_184034_Final.zip


Ian @ un4seen

  • Administrator
  • Posts: 20433
The dump file was received, thanks. It's a bit of an ugly one. It appears to be a case of heap corruption, presumably caused by writing beyond the end of a buffer/array. The thread that crashed is a DirectSound thread, so a good first place to look for problems would be STREAMPROC and DSPPROC callback functions (they write to DirectSound buffers). Do you have any of those? Also try disabling any BASS_ChannelSetFX calls to see if one of them may be the cause.

VorTechS

  • Posts: 265
Thanks Ian.

I have no STREAMPROC or DSPPROC callback functions. 

There is only one call to BASS_ChannelSetFX which occurs when the application first starts up (and the mixer is created):

Code: [Select]
            _playerEQ = Bass.BASS_ChannelSetFX(_mixerHandle, Un4seen.Bass.BASSFXType.BASS_FX_BFX_PEAKEQ, 0);

I'll disable the EQ related functions, and run through again.   Should I generate another dump file?

I'm also going to modify my console output to include relevant handles, as my gut feeling is this it could be something to do with reading data from a stream that's been removed from the mixer and/or freed?

Specifically this:

Code: [Select]
        private void MediaInfoTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                using (new SafeLock(_mediaInfoLock, new TimeSpan(0, 0, 0, 0, 5)))
                {
                    if (_mediaInfoTimer != null)
                    {
                        _mediaInfoTimer.Enabled = false;

                        if (!_isStoppingTrack)
                        {
                            if (_streamHandles?.Count > 0 && _currentPlaylistItem <= _streamHandles?.Count - 1)
                            {
                                #if DebugBASSCalls
                                    Console.WriteLine("BASS: BASS_ChannelIsActive (MediaInfoTimer_Elapsed)");                       
                                #endif

                                if (Bass.BASS_ChannelIsActive(_streamHandles[_currentPlaylistItem]) == Un4seen.Bass.BASSActive.BASS_ACTIVE_PLAYING)
                                {
                                    #if DebugBASSCalls
                                        Console.WriteLine("BASS: BASS_Mixer_ChannelGetPosition (MediaInfoTimer_Elapsed)");                       
                                    #endif

                                    // Get the current stream position
                                    long currentStreamPos = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_ChannelGetPosition(_streamHandles[_currentPlaylistItem]);

                                    #if DebugBASSCalls
                                        Console.WriteLine("BASS: BASS_ChannelGetLength (MediaInfoTimer_Elapsed)");                       
                                    #endif

                                    // Get the current stream length
                                    long streamLength = Bass.BASS_ChannelGetLength(_streamHandles[_currentPlaylistItem]);

                                    #if DebugBASSCalls
                                        Console.WriteLine("BASS: BASS_ChannelBytes2Seconds (MediaInfoTimer_Elapsed) [Stream Position]");                       
                                    #endif

                                    // Evaluate into the number of seconds
                                    double elapsedSeconds = Bass.BASS_ChannelBytes2Seconds(_streamHandles[_currentPlaylistItem], currentStreamPos);

                                    _currentPlaybackTime = elapsedSeconds;

                                    #if DebugBASSCalls
                                        Console.WriteLine("BASS: BASS_ChannelBytes2Seconds (MediaInfoTimer_Elapsed) [Stream Length]");                       
                                    #endif

                                    double totalSeconds = Bass.BASS_ChannelBytes2Seconds(_streamHandles[_currentPlaylistItem], streamLength);

                                    #if DebugBASSCalls
                                        Console.WriteLine("BASS: BASS_ChannelGetLevel (MediaInfoTimer_Elapsed)");                       
                                    #endif

                                    //  Get The VU Monitor Level Data
                                    int currentLevels = Bass.BASS_ChannelGetLevel(_mixerHandle);

                                    RaiseDelegate(PlayerDelegateType.CurrentMediaState, elapsedSeconds, totalSeconds, currentLevels);
                                }
                               
                            }
                        }
                    }
                }

                if (_mediaInfoTimer != null)
                {
                    _mediaInfoTimer.Enabled = true;
                }
            }
            catch
            {

            }
        }

Ian @ un4seen

  • Administrator
  • Posts: 20433
I have no STREAMPROC or DSPPROC callback functions. 

There is only one call to BASS_ChannelSetFX which occurs when the application first starts up (and the mixer is created):

Code: [Select]
            _playerEQ = Bass.BASS_ChannelSetFX(_mixerHandle, Un4seen.Bass.BASSFXType.BASS_FX_BFX_PEAKEQ, 0);

I'll disable the EQ related functions, and run through again.   Should I generate another dump file?

Yes, please do.

VorTechS

  • Posts: 265
So, I removed all calls to the EQ functions (which are only ever used when a mixer instance is created, and/or when the user modifies the EQ settings) and added additional output to the console (Stream handle and Mixer handle), and the software has been running since 3.30pm (GMT) yesterday with no crash!

Albeit with the sound muted (_currentVolume = 0):

Code: [Select]
Un4seen.Bass.Bass.BASS_ChannelSetAttribute(_mixerHandle, BASSAttribute.BASS_ATTRIB_VOL, (float)_currentVolume);

....as it's a work machine, I didn't want to potentially freak people out.

Obviously I only see my side of the equation, and have no comprehension of what's going on 'under the hood', but hopefully that tells you something, because it doesn't make a whole lot of sense to me.   

I shall keep the software running through the day, whilst I await your next suggestion / request.

EQ related code is below:

Mixer Instance Created

Code: [Select]
        // Create a mixer instance, and set the synch call back
        private int CreateMixer()
        {
            // If bass was successfully started
            if (_bassStarted)
            {
                if (_mixerHandle != 0)
                {
                    ResetMixer();
                }

#if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_Mixer_StreamCreate (CreateMixer)");
#endif

                // Create a 2 - channel mixer
                _mixerHandle = Un4seen.Bass.AddOn.Mix.BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_MIXER_END);
                SetMixerVolume();

                // If the mixer failed to create
                if (_mixerHandle == 0)
                {
#if DebugBASSCalls
                        Console.WriteLine("BASS: BASS_ErrorGetCode (CreateMixer)");
#endif

                    // Set the player to an error state
                    SetErrorState(StateChangeReason.MixerCreation, Bass.BASS_ErrorGetCode(), -1);
                }
                else
                {
                    // Create a new callback for when the current track in the mixer has ended
                    _mixerSynchProc = new Un4seen.Bass.SYNCPROC(CurrentTrackEnded);

#if DebugBASSCalls
                        Console.WriteLine("BASS: BASS_ChannelSetSync (CreateMixer)");
#endif

                    // Set the mixer synch callback
                    _mixerSyncHandle = Bass.BASS_ChannelSetSync(_mixerHandle, Un4seen.Bass.BASSSync.BASS_SYNC_END | Un4seen.Bass.BASSSync.BASS_SYNC_MIXTIME, 0, _mixerSynchProc, IntPtr.Zero);

                    if (_mixerSyncHandle == 0)
                    {
#if DebugBASSCalls
                            Console.WriteLine("BASS: BASS_ErrorGetCode (CreateMixer)");
#endif

                        // Set the player to an error state
                        SetErrorState(StateChangeReason.MixerCreation, Bass.BASS_ErrorGetCode(), -1);
                    }
                }
            }
            else
            {
#if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_ErrorGetCode (CreateMixer)");
#endif

                // Set an error state of error with initialisation...
                SetErrorState(StateChangeReason.MixerCreation, Un4seen.Bass.BASSError.BASS_ERROR_INIT, -1);
            }

            // Return the mixer handle
            return _mixerHandle;

        }


EQ Bands / Levels applied

Code: [Select]
        public void ApplyEQSettings()
        {
#if !DebugBASSCalls
                Un4seen.Bass.AddOn.Fx.BASS_BFX_PEAKEQ chanEQ = new Un4seen.Bass.AddOn.Fx.BASS_BFX_PEAKEQ();

#if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_FX_GetVersion (ApplyEQSettings)");                       
#endif

                int fxVersion = Un4seen.Bass.AddOn.Fx.BassFx.BASS_FX_GetVersion();

#if DebugBASSCalls
                    Console.WriteLine("BASS: BASS_ChannelSetFX (ApplyEQSettings)");                       
#endif

                // Set peaking equalizer effect with no bands on the current mixer
                _playerEQ = Bass.BASS_ChannelSetFX(_mixerHandle, Un4seen.Bass.BASSFXType.BASS_FX_BFX_PEAKEQ, 0);

                if (_playerEQ == 0)
                {
#if DebugBASSCalls
                        Console.WriteLine("BASS: BASS_ErrorGetCode (ApplyEQSettings)");                       
#endif

                    SetErrorState(StateChangeReason.EQConfiguration, Un4seen.Bass.Bass.BASS_ErrorGetCode(), -1);
                }
                else
                {
                    chanEQ.fBandwidth = 2.5f;
                    chanEQ.fQ = 0.0f;
                    chanEQ.fGain = 0.0f;
                    chanEQ.lChannel = Un4seen.Bass.AddOn.Fx.BASSFXChan.BASS_BFX_CHANALL;
                }

                // Now configure the band parameters
                for (int bandIndex = 0; bandIndex < _EQBands.Length; bandIndex++)
                {
                    // Create a new FX EQ instance
                    chanEQ.lBand = bandIndex;

                    // Now configure the EQ center
                    chanEQ.fCenter = _EQBands[bandIndex];

#if DebugBASSCalls
                        Console.WriteLine("BASS: BASS_FXSetParameters (ApplyEQSettings)");                       
#endif

                    // Apply to the current stream
                    if (!Un4seen.Bass.Bass.BASS_FXSetParameters(_playerEQ, chanEQ))
                    {
#if DebugBASSCalls
                            Console.WriteLine("BASS: BASS_ErrorGetCode (ApplyEQSettings)");                       
#endif

                        SetErrorState(StateChangeReason.EQConfiguration, Un4seen.Bass.Bass.BASS_ErrorGetCode(), -1);
                    }
                }

#endif
        }


Both of the functions above are only called when the application is first started. 

There are 10-bands of EQ set: 31hz, 63hz, 125hz, 250hz, 500hz, 1khz, 2khz, 4khz, 8khz and 16khz, all at zero levels and the EQ was NOT changed at all in any of the tests.


VorTechS

  • Posts: 265
I did get another Access Violation exception, not long after I posted the above so I've restarted with the ProcDump attached. 

It occured unsurprisingly at the exact the same point, and the Visual Studio instance was up to 1.7GB RAM 'memory usage'.

I've restarted the application under ProcDump again.

VorTechS

  • Posts: 265
*headscratch*  I just had a 'lock up' in the application.  Which was odd, it's never done that before.  I couldn't attach the debugger because obviously procdump was holding on to it.

So I hit CTRL + C on procdump to end monitoring, and then the application continued!  (But monitoring didn't seem to end?)

Any idea what causes that behaviour?

(I deliberately started debugging the heavy project just as the track was ending, as the last time the Access Violation occured happened to co-incide with the same scenario)

Update as of 2.30pm GMT:  No crash yet since restarting immediately after this post, after 4 natural changes of playlist...
« Last Edit: 14 Nov '17 - 14:32 by VorTechS »