Jump to content

Audio gone until restart


FeelsBadMan
  • Priority: No Priority Status: Backlog candidate

When disconnecting an audio output device either intentionally to switch devices or accidentally by moving a cable or battery for bluetooth headset running out) and reconnecting it Unibet completely loses audio with no way to regain it until you restart the software. It's the only app that does this. As you can see in the videos Unibet has no audio, while Stars recovers audio immediately without pressing any buttons.

I've tried everything to recover audio on unibet without restarting, every in client audio setting, mute unmute, force the default audio device in windows, removed and readded the device, enabling and disabling the device, different headset, different PC.  

Sometimes, albeit rare, the client starts with no audio, which you won't realize until you start playing, If it would have any way of recovering audio then this would not be a problem.

When multiple devices are connected it successfully moves from one to the other, which was broken a few months ago, but now works, so it's a good workaround, but when only one device is connected then it won't work properly. @Phlo23 has also performed some tests of this issue and could not reproduce it, which is odd cause I have it on multiple PC's.  

o    When did issue occur: All the time for a long time.

o    Which client(s) are impacted: Desktop

o    Info about your device: Windows 10 & Bluetooth True Wireless Headphones JLab or Wireless Headset Sennheiser or wired generic headset or bluetooth speaker

 

Added comment with possible solution in the comment section 👇

Video example of Unibet sound not working: 

Video example of Stars sound working: 

 

 


User Feedback

Recommended Comments

Thanks, it seems I missed this thread. I can report a repeat of the incident, today at approximately 20:30 BST. What happened this time was that I logged into the client. I then waited a bit (again in the range of 10 minutes or so) and then when I began to play (Hexapros in this instance), the sound was gone (confirmed by playing a YouTube video that had sound). It's like the client is disconnecting itself from the sound system after a certain amount of idle time. Like it closes the connection.

Link to comment
Share on other sites

This is going to be a lot of guessing, even more than usual, so it might be completely and utterly wrong and the unibet implementation might be completely different.

I've noticed that XAUDIO2 is being used, so I tried the sample project from microsoft and could immediately reproduce the bug in there too. What happens in that sample project and might happen here too, is when there is no valid device it either permanently tries to play the same failed buffer over and over again, or it tries to wait for an hBufferEndEvent from OnBufferEnd that will never come. 

The solutions that seem to work in fixing this issue in this sample project are:

Option1: Listening to device changes using IMMNotificationClient (only available windows Vista and up), and when it goes from no devices to 1 device, set a global flag to reinitialize the audio from the beginning, break out of the buffering loop, destroy the voice and whatever else, and proceed to try re-initializing with a delay between tries.  

Option2: Keep track of state.SamplesPlayed and if it repeats in the buffering loop more than X times, not too low that it triggers on a small lag or a device change, but not too high that it takes a long time to trigger, then same as before, set a global flag to reinitialize the audio from the beginning, break out of the buffering loop, destroy the voice and whatever else, and proceed to try re-initializing with a delay between tries.  

If there is a WaitForSingleObject hBufferEndEvent, that will need to not be INFINITE, otherwise it will never count past 0.

Option 1 is likely a lot better and less hacky but also a lot more code so will add the Option 2 sample code and modifications in // *** ...*** in order to quickly determine if something similar is happening here, or if it has nothing to do with how unibet plays sound, in which case this is a waste of time 😅

The Buffer Loop with no waiting where it just replays the same buffer, and fix:

    hr = pSourceVoice->Start(0);

    // Let the sound play
    BOOL isRunning = TRUE;
    // *** Keeping track of how many samples have been played ***
    int prevSamplesPlayed = 0; // *** Reset samples here ***
    int sameSamplesPlayed = 0; // *** Reset samples here ***
    while (SUCCEEDED(hr) && isRunning)
    {
        XAUDIO2_VOICE_STATE state;
        pSourceVoice->GetState(&state);

        if (prevSamplesPlayed == state.SamplesPlayed) {
            sameSamplesPlayed++;
        }
        else {
            sameSamplesPlayed = 0; // *** Reset samples here ***
        }
        // *** If there have been only a small ammount of duplicates continue normally ***
        if (sameSamplesPlayed < 10) { 
            prevSamplesPlayed = state.SamplesPlayed;
            isRunning = (state.BuffersQueued > 0) != 0;
        }
        // *** If the threshold of duplicates has been exceeded set the reinitializing
        // flag to true and break out.
        // Use the flag to break out of any remaining running audio loops and 
        // audio functions, or use any other method to safely dispose of them ***
        else {
            isRunning = FALSE;
            reInitialize = TRUE;
            break;
        }     
        Sleep( 10 );
    }
    pSourceVoice->DestroyVoice();
    return hr;
}

The Buffer Loop with waiting where it never reaches the end of the wait, and fix:

        BOOL isRunning = TRUE;
        int prevSamplesPlayed = 0; // *** Reset samples here ***
        int sameSamplesPlayed = 0; // *** Reset samples here ***

        for (;;)
        {
            DWORD flags = 0;
            hr = reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, nullptr, nullptr, nullptr);
            if (FAILED(hr))
            {
                break;
            }

            WaitForSingleObject(readerContext.hReadSample, INFINITE);

            if (readerContext.endOfStream)
            {
                readerContext.Restart();

                // Restart loop
                PROPVARIANT var = {};
                var.vt = VT_I8;

                hr = reader->SetCurrentPosition(GUID_NULL, var);
                if (SUCCEEDED(hr)) {
                    continue;
                }
                else {
                    break;
                }
            }

            ComPtr<IMFMediaBuffer> mediaBuffer;
            hr = readerContext.sample->ConvertToContiguousBuffer(mediaBuffer.GetAddressOf());
            if (FAILED(hr))
            {
                return hr;
            }

            BYTE* audioData = nullptr;
            DWORD sampleBufferLength = 0;

            hr = mediaBuffer->Lock(&audioData, nullptr, &sampleBufferLength);
            if (FAILED(hr))
            {
                return hr;
            }
            if (bufferSize[currentStreamBuffer] < sampleBufferLength)
            {
                buffers[currentStreamBuffer].reset(new uint8_t[sampleBufferLength]);
                bufferSize[currentStreamBuffer] = sampleBufferLength;
            }


            memcpy_s(buffers[currentStreamBuffer].get(), sampleBufferLength, audioData, sampleBufferLength);

            hr = mediaBuffer->Unlock();
            if (FAILED(hr))
            {
                return hr;
            }

            //
            // Wait to se if our XAudio2 source voice has played enough data for us to give
            // it another buffer full of audio. We'd like to keep no more than MAX_BUFFER_COUNT - 1
            // buffers on the queue, so that one buffer is always free for the MF streamer
            //
            XAUDIO2_VOICE_STATE state;
            
            while (isRunning)
            {
                pSourceVoice->GetState(&state);
              
                if (prevSamplesPlayed == state.SamplesPlayed) {
                    sameSamplesPlayed++;
                }
                else {
                    sameSamplesPlayed = 0; // *** Reset samples here ***
                }
                // *** If there have been only a small ammount of duplicates continue normally ***
                if (sameSamplesPlayed < 10) {
                    prevSamplesPlayed = state.SamplesPlayed;
                }
                // *** If the threshold of duplicates has been exceeded set the reinitializing
                // flag to true and break out.
                // Use the flag to break out of any remaining running audio loops and 
                // audio functions, or use any other method to safely dispose of them ***
                else {
                   isRunning = FALSE;
                   reInitialize = TRUE;
                   break;
                }
                if (state.BuffersQueued < MAX_BUFFER_COUNT - 1)
                {
                    break;
                }
                // *** Make sure the wait isn't set to INFINITE
                // Not sure what the optimal value would be for these tiny buffers ***
                WaitForSingleObject(voiceContext.hBufferEndEvent, 100);
            }

            XAUDIO2_BUFFER buf = {};
            buf.AudioBytes = sampleBufferLength;
            buf.pAudioData = buffers[currentStreamBuffer].get();
            pSourceVoice->SubmitSourceBuffer(&buf);

            currentStreamBuffer++;
            currentStreamBuffer %= MAX_BUFFER_COUNT;
            // *** If flag true break out ***
            if (reInitialize) {
                break;
            }
        }

The Voice Mastering that fails and needs to be reinitialized, same for both examples:

    //
    // Create a mastering voice
    //
    IXAudio2MasteringVoice* pMasteringVoice = nullptr;

    if( FAILED( hr = pXAudio2->CreateMasteringVoice( &pMasteringVoice ) ) )
    {
        pXAudio2.Reset();
        CoUninitialize();
        Sleep(1000); // *** Short delay so it doesn't retry too fast ***
        // *** Reinitialize the audio, maybe in a more elegant way than just calling main :D ***
        main();
        return 0;
    }
    // *** Set the reinitializing flag back to false once it succesfully creates a mastering voice ***
    reInitialize = FALSE; 

 

Edited by FeelsBadMan
  • Thanks 1
Link to comment
Share on other sites



Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×
×
  • Create New...