<commit_before>#include "pch.h"
#include "AudioRenderer.h"

namespace SaneAudioRenderer
{
    namespace
    {
        DspChunk PreProcess(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps,
                            const WAVEFORMATEX& sampleFormat, REFERENCE_TIME previousSampleEndTime)
        {
            DspChunk chunk;

            // TODO: can gradually accumulate error - decide what to do with it

            auto timeToFrames = [&](REFERENCE_TIME time)
            {
                return (size_t)(time * sampleFormat.nSamplesPerSec / OneSecond);
            };

            if (sampleProps.tStop <= previousSampleEndTime)
            {
                // Drop the sample.
                assert(chunk.IsEmpty());
            }
            else if (sampleProps.tStart < previousSampleEndTime)
            {
                // Crop the sample.
                size_t cropFrames = timeToFrames(previousSampleEndTime - sampleProps.tStart);
                if (cropFrames > 0)
                {
                    size_t cropBytes = cropFrames * sampleFormat.nChannels * sampleFormat.wBitsPerSample / 8;

                    AM_SAMPLE2_PROPERTIES croppedSampleProps = sampleProps;
                    assert((int32_t)cropBytes < croppedSampleProps.lActual);
                    croppedSampleProps.pbBuffer += cropBytes;
                    croppedSampleProps.lActual -= (int32_t)cropBytes;

                    chunk = DspChunk(pSample, croppedSampleProps, sampleFormat);
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else if (sampleProps.tStart > previousSampleEndTime)
            {
                // Zero-pad the sample.
                size_t extendFrames = timeToFrames(sampleProps.tStart - previousSampleEndTime);
                if (extendFrames > 0)
                {
                    DspChunk tempChunk(pSample, sampleProps, sampleFormat);

                    chunk = DspChunk(tempChunk.GetFormat(), tempChunk.GetChannelCount(),
                                     tempChunk.GetFrameCount() + extendFrames, tempChunk.GetRate());

                    size_t extendBytes = extendFrames * chunk.GetFrameSize();

                    assert(chunk.GetSize() == tempChunk.GetSize() + extendBytes);
                    ZeroMemory(chunk.GetData(), extendBytes);
                    memcpy(chunk.GetData() + extendBytes, tempChunk.GetConstData(), tempChunk.GetSize());
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else
            {
                // Leave the sample untouched.
                chunk = DspChunk(pSample, sampleProps, sampleFormat);
            }

            return chunk;
        }
    }

    AudioRenderer::AudioRenderer(ISettings* pSettings, IMyClock* pClock, CAMEvent& bufferFilled, HRESULT& result)
        : m_deviceManager(result)
        , m_graphClock(pClock)
        , m_flush(TRUE/*manual reset*/)
        , m_bufferFilled(bufferFilled)
        , m_settings(pSettings)
    {
        if (FAILED(result))
            return;

        try
        {
            if (!m_settings || !m_graphClock)
                throw E_UNEXPECTED;

            if (static_cast<HANDLE>(m_flush) == NULL ||
                static_cast<HANDLE>(m_bufferFilled) == NULL)
            {
                throw E_OUTOFMEMORY;
            }

            m_settingsSerial = m_settings->Serial();

            if (!m_deviceManager.CreateDevice(m_device))
                throw E_OUTOFMEMORY;
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
    }

    AudioRenderer::~AudioRenderer()
    {
        // Just in case.
        if (m_state != State_Stopped)
            Stop();

        m_device = {};
    }

    bool AudioRenderer::Enqueue(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            try
            {
                chunk = PreProcess(pSample, sampleProps, m_inputFormat.Format, m_lastSampleEnd);

                if (chunk.IsEmpty())
                    return true;

                m_dspMatrix.Process(chunk);
                //m_dspGain.Process(chunk);
                m_dspRate.Process(chunk);
                m_dspCrossfeed.Process(chunk);
                //m_dspLimiter.Process(chunk);

                DspChunk::ToFloat(chunk);

                m_lastSampleEnd = sampleProps.tStop;
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        return Push(chunk);
    }

    bool AudioRenderer::Finish(bool blockUntilEnd)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            try
            {
                m_dspMatrix.Finish(chunk);
                //m_dspGain.Finish(chunk);
                m_dspRate.Finish(chunk);
                m_dspCrossfeed.Finish(chunk);
                //m_dspLimiter.Finish(chunk);

                DspChunk::ToFloat(chunk);
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        auto doBlock = [this]
        {
            TimePeriodHelper timePeriodHelper(1);

            try
            {
                for (;;)
                {
                    int64_t actual, target;

                    {
                        CAutoLock objectLock(this);

                        UINT64 deviceClockFrequency, deviceClockPosition;
                        ThrowIfFailed(m_device.audioClock->GetFrequency(&deviceClockFrequency));
                        ThrowIfFailed(m_device.audioClock->GetPosition(&deviceClockPosition, nullptr));

                        actual = llMulDiv(deviceClockPosition, OneSecond, deviceClockFrequency, 0);
                        target = llMulDiv(m_pushedFrames, OneSecond, m_device.format.Format.nSamplesPerSec, 0);

                        if (actual >= target)
                        {
                            assert(actual == target);
                            break;
                        }
                    }

                    if (m_flush.Wait(std::max(1, (int32_t)((target - actual) * 1000 / OneSecond))))<commit_msg>Unslave clock when entering EOS wait<commit_after>#include "pch.h"
#include "AudioRenderer.h"

namespace SaneAudioRenderer
{
    namespace
    {
        DspChunk PreProcess(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps,
                            const WAVEFORMATEX& sampleFormat, REFERENCE_TIME previousSampleEndTime)
        {
            DspChunk chunk;

            // TODO: can gradually accumulate error - decide what to do with it

            auto timeToFrames = [&](REFERENCE_TIME time)
            {
                return (size_t)(time * sampleFormat.nSamplesPerSec / OneSecond);
            };

            if (sampleProps.tStop <= previousSampleEndTime)
            {
                // Drop the sample.
                assert(chunk.IsEmpty());
            }
            else if (sampleProps.tStart < previousSampleEndTime)
            {
                // Crop the sample.
                size_t cropFrames = timeToFrames(previousSampleEndTime - sampleProps.tStart);
                if (cropFrames > 0)
                {
                    size_t cropBytes = cropFrames * sampleFormat.nChannels * sampleFormat.wBitsPerSample / 8;

                    AM_SAMPLE2_PROPERTIES croppedSampleProps = sampleProps;
                    assert((int32_t)cropBytes < croppedSampleProps.lActual);
                    croppedSampleProps.pbBuffer += cropBytes;
                    croppedSampleProps.lActual -= (int32_t)cropBytes;

                    chunk = DspChunk(pSample, croppedSampleProps, sampleFormat);
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else if (sampleProps.tStart > previousSampleEndTime)
            {
                // Zero-pad the sample.
                size_t extendFrames = timeToFrames(sampleProps.tStart - previousSampleEndTime);
                if (extendFrames > 0)
                {
                    DspChunk tempChunk(pSample, sampleProps, sampleFormat);

                    chunk = DspChunk(tempChunk.GetFormat(), tempChunk.GetChannelCount(),
                                     tempChunk.GetFrameCount() + extendFrames, tempChunk.GetRate());

                    size_t extendBytes = extendFrames * chunk.GetFrameSize();

                    assert(chunk.GetSize() == tempChunk.GetSize() + extendBytes);
                    ZeroMemory(chunk.GetData(), extendBytes);
                    memcpy(chunk.GetData() + extendBytes, tempChunk.GetConstData(), tempChunk.GetSize());
                }
                else
                {
                    chunk = DspChunk(pSample, sampleProps, sampleFormat);
                }
            }
            else
            {
                // Leave the sample untouched.
                chunk = DspChunk(pSample, sampleProps, sampleFormat);
            }

            return chunk;
        }
    }

    AudioRenderer::AudioRenderer(ISettings* pSettings, IMyClock* pClock, CAMEvent& bufferFilled, HRESULT& result)
        : m_deviceManager(result)
        , m_graphClock(pClock)
        , m_flush(TRUE/*manual reset*/)
        , m_bufferFilled(bufferFilled)
        , m_settings(pSettings)
    {
        if (FAILED(result))
            return;

        try
        {
            if (!m_settings || !m_graphClock)
                throw E_UNEXPECTED;

            if (static_cast<HANDLE>(m_flush) == NULL ||
                static_cast<HANDLE>(m_bufferFilled) == NULL)
            {
                throw E_OUTOFMEMORY;
            }

            m_settingsSerial = m_settings->Serial();

            if (!m_deviceManager.CreateDevice(m_device))
                throw E_OUTOFMEMORY;
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
    }

    AudioRenderer::~AudioRenderer()
    {
        // Just in case.
        if (m_state != State_Stopped)
            Stop();

        m_device = {};
    }

    bool AudioRenderer::Enqueue(IMediaSample* pSample, const AM_SAMPLE2_PROPERTIES& sampleProps)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            try
            {
                chunk = PreProcess(pSample, sampleProps, m_inputFormat.Format, m_lastSampleEnd);

                if (chunk.IsEmpty())
                    return true;

                m_dspMatrix.Process(chunk);
                //m_dspGain.Process(chunk);
                m_dspRate.Process(chunk);
                m_dspCrossfeed.Process(chunk);
                //m_dspLimiter.Process(chunk);

                DspChunk::ToFloat(chunk);

                m_lastSampleEnd = sampleProps.tStop;
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        return Push(chunk);
    }

    bool AudioRenderer::Finish(bool blockUntilEnd)
    {
        DspChunk chunk;

        {
            CAutoLock objectLock(this);
            assert(m_state != State_Stopped);

            try
            {
                m_dspMatrix.Finish(chunk);
                //m_dspGain.Finish(chunk);
                m_dspRate.Finish(chunk);
                m_dspCrossfeed.Finish(chunk);
                //m_dspLimiter.Finish(chunk);

                DspChunk::ToFloat(chunk);
            }
            catch (std::bad_alloc&)
            {
                chunk = DspChunk();
                assert(chunk.IsEmpty());
            }
        }

        auto doBlock = [this]
        {
            TimePeriodHelper timePeriodHelper(1);

            m_graphClock->UnslaveClockFromAudio();

            try
            {
                for (;;)
                {
                    int64_t actual, target;

                    {
                        CAutoLock objectLock(this);

                        UINT64 deviceClockFrequency, deviceClockPosition;
                        ThrowIfFailed(m_device.audioClock->GetFrequency(&deviceClockFrequency));
                        ThrowIfFailed(m_device.audioClock->GetPosition(&deviceClockPosition, nullptr));

                        actual = llMulDiv(deviceClockPosition, OneSecond, deviceClockFrequency, 0);
                        target = llMulDiv(m_pushedFrames, OneSecond, m_device.format.Format.nSamplesPerSec, 0);

                        if (actual >= target)
                        {
                            assert(actual == target);
                            break;
                        }
                    }

                    if (m_flush.Wait(std::max(1, (int32_t)((target - actual) * 1000 / OneSecond))))
{'fraction_non_alphanumeric': 0.08685499058380414, 'fraction_numerical': 0.003615819209039548, 'mean_word_length': 1.3576629373113123, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1319151', 'n_tokens_mistral': 3606, 'n_tokens_neox': 3307, 'n_words': 575}