Sample Code

Windows Driver Samples/ DirectMusic Software Synthesizer Sample/ C++/ csynth.cpp/

//
//      Copyright (c) 1996-2000 Microsoft Corporation.  All rights reserved.
//      CSynth.cpp
//

#include "common.h"
#include "fltsafe.h"

#define STR_MODULENAME "DDKSynth.sys:CSynth: "


#pragma code_seg()
/*****************************************************************************
 * CSynth::CSynth()
 *****************************************************************************
 * Contructor for CSynth object.  Initialize the voice list, the stereo mode,
 * sample rate, performance statistics, etc.
 */
CSynth::CSynth()
{
    FLOATSAFE fs;

    DWORD nIndex;
    CVoice *pVoice;

    m_fCSInitialized = FALSE;
    ::InitializeCriticalSection(&m_CriticalSection);
    m_fCSInitialized = TRUE;

    for (nIndex = 0;nIndex < MAX_NUM_VOICES;nIndex++)
    {
        pVoice = new CVoice;
        if (pVoice != NULL)
        {
            m_VoicesFree.AddHead(pVoice);
        }
    }
    for (nIndex = 0;nIndex < NUM_EXTRA_VOICES;nIndex++)
    {
        pVoice = new CVoice;
        if (pVoice != NULL)
        {
            m_VoicesExtra.AddHead(pVoice);
        }
    }
    m_ppControl = NULL;
    m_dwControlCount = 0;
    m_nMaxVoices = MAX_NUM_VOICES;
    m_nExtraVoices = NUM_EXTRA_VOICES;
    m_stLastStats = 0;
    m_fAllowPanWhilePlayingNote = TRUE;
    m_fAllowVolumeChangeWhilePlayingNote = TRUE;
    ResetPerformanceStats();
    m_dwSampleRate = 22050;
    m_dwStereo = 1;
    m_stLastTime = 0;
    SetSampleRate(SAMPLE_RATE_22);
    SetStereoMode(2);
    SetGainAdjust(600);
}

/*****************************************************************************
 * CSynth::~CSynth()
 *****************************************************************************
 * Destructor for CSynth object.  Delete the voices in the lists.
 */
CSynth::~CSynth()
{
    CVoice *pVoice;
    if (m_fCSInitialized)
    {
        // If CS never initialized, nothing else will have been set up
        //
        Close();

        pVoice = m_VoicesInUse.RemoveHead();
        while (pVoice != nullptr)
        {
            delete pVoice;
            pVoice = m_VoicesInUse.RemoveHead();
        }
        pVoice = m_VoicesFree.RemoveHead();
        while (pVoice != nullptr)
        {
            delete pVoice;
            pVoice = m_VoicesFree.RemoveHead();
        }
        pVoice = m_VoicesExtra.RemoveHead();
        while (pVoice != nullptr)
        {
            delete pVoice;
            pVoice = m_VoicesExtra.RemoveHead();
        }
        DeleteCriticalSection(&m_CriticalSection);
    }
}

/*****************************************************************************
 * ChangeVoiceCount()
 *****************************************************************************
 * Change the number of voices in a given voice list.
 */
static short ChangeVoiceCount(CVoiceList *pList,short nOld,short nCount)
{
    if (nCount > nOld)
    {
        short nNew = nCount - nOld;
        for (;nNew != 0; nNew--)
        {
            CVoice *pVoice = new CVoice;
            if (pVoice != NULL)
            {
                pList->AddHead(pVoice);
            }
        }
    }
    else
    {
        short nNew = nOld - nCount;
        for (;nNew > 0; nNew--)
        {
            CVoice *pVoice = pList->RemoveHead();
            if (pVoice != NULL)
            {
                delete pVoice;
            }
            else
            {
                nCount += nNew;
                break;
            }
        }
    }
    return nCount;
}

/*****************************************************************************
 * CSynth::SetMaxVoices()
 *****************************************************************************
 * Set the maximum number of voices available.
 */
HRESULT CSynth::SetMaxVoices(short nVoices,short nTempVoices)
{
    if (nVoices < 1)
    {
        nVoices = 1;
    }
    if (nTempVoices < 1)
    {
        nTempVoices = 1;
    }
    ::EnterCriticalSection(&m_CriticalSection);
    m_nMaxVoices = ChangeVoiceCount(&m_VoicesFree,m_nMaxVoices,nVoices);
    m_nExtraVoices = ChangeVoiceCount(&m_VoicesExtra,m_nExtraVoices,nTempVoices);
    ::LeaveCriticalSection(&m_CriticalSection);
    return S_OK;
}

/*****************************************************************************
 * CSynth::SetNumChannelGroups()
 *****************************************************************************
 * Set the number of channel groups (virtual MIDI cables).  For each channel
 * group, there is a separate CControlLogic object.
 */
HRESULT CSynth::SetNumChannelGroups(DWORD dwCableCount)
{
    HRESULT hr = S_OK;
    CControlLogic **ppControl;
    if ((dwCableCount < 1) || (dwCableCount > MAX_CHANNEL_GROUPS))
    {
        return E_INVALIDARG;
    }

    ::EnterCriticalSection(&m_CriticalSection);
    if (m_dwControlCount != dwCableCount)
    {
        ppControl = new(NonPagedPool,'PSmD') CControlLogic *[dwCableCount]; //  DmSP
        if (ppControl)
        {
            DWORD dwX;
            for (dwX = 0; dwX < dwCableCount; dwX++)
            {
                ppControl[dwX] = NULL;
            }
            if (m_dwControlCount < dwCableCount)
            {
                for (dwX = 0; dwX < m_dwControlCount; dwX++)
                {
                    ppControl[dwX] = m_ppControl[dwX];
                }
                for (;dwX < dwCableCount; dwX++)
                {
                    ppControl[dwX] = new(NonPagedPool,'CSmD') CControlLogic;    //  DmSC
                    if (ppControl[dwX])
                    {
                        hr = ppControl[dwX]->Init(&m_Instruments, this);
                        if (FAILED(hr))
                        {
                            delete ppControl[dwX];
                            ppControl[dwX] = NULL;
                            dwCableCount = dwX;
                            break;
                        }

                        ppControl[dwX]->SetGainAdjust(m_vrGainAdjust);
                    }
                    else
                    {
                        dwCableCount = dwX;
                        break;
                    }
                }
            }
            else
            {
                AllNotesOff();
                _Analysis_assume_(dwCableCount < m_dwControlCount);
                for (dwX = 0; dwX < dwCableCount; dwX++)
                {
                    ppControl[dwX] = m_ppControl[dwX];
                }
                for (; dwX < m_dwControlCount; dwX++)
                {
                    if (m_ppControl[dwX])
                    {
                        delete m_ppControl[dwX];
                    }
                }
            }
            if (m_ppControl)
            {
                delete m_ppControl;
            }
            m_ppControl = ppControl;
            m_dwControlCount = dwCableCount;
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    ::LeaveCriticalSection(&m_CriticalSection);

    return hr;
}

/*****************************************************************************
 * CSynth::SetGainAdjust()
 *****************************************************************************
 * Set the gain for the overall synth.  Set gain on each CControlLogic object.
 */
void CSynth::SetGainAdjust(VREL vrGainAdjust)
{
    DWORD idx;

    m_vrGainAdjust = vrGainAdjust;
    ::EnterCriticalSection(&m_CriticalSection);

    for (idx = 0; idx < m_dwControlCount; idx++)
    {
        m_ppControl[idx]->SetGainAdjust(m_vrGainAdjust);
    }

    ::LeaveCriticalSection(&m_CriticalSection);
}

/*****************************************************************************
 * CSynth::Open()
 *****************************************************************************
 * Open the synth with the given number of channel groups.
 */
HRESULT CSynth::Open(DWORD dwCableCount, DWORD dwVoices)
{
    HRESULT hr = S_OK;
    if ((dwCableCount < 1) || (dwCableCount > MAX_CHANNEL_GROUPS))
    {
        return E_INVALIDARG;
    }
    ::EnterCriticalSection(&m_CriticalSection);
    hr = SetNumChannelGroups(dwCableCount);
    if (SUCCEEDED(hr))
    {
        short nTemp = (short) dwVoices / 4;
        if (nTemp < 4) nTemp = 4;
        SetMaxVoices((short) dwVoices, nTemp);
    }


    m_vrGainAdjust = 0;
    ::LeaveCriticalSection(&m_CriticalSection);
    return hr;
}

/*****************************************************************************
 * CSynth::Close()
 *****************************************************************************
 * Close down the synth:, silence it, delete the list of CControlLogic objects.
 */
HRESULT CSynth::Close()
{
    ::EnterCriticalSection(&m_CriticalSection);
    AllNotesOff();
    DWORD dwX;
    for (dwX = 0; dwX < m_dwControlCount; dwX++)
    {
        if (m_ppControl[dwX])
        {
            delete m_ppControl[dwX];
        }
    }
    m_dwControlCount = 0;
    if (m_ppControl)
    {
        delete [] m_ppControl;
        m_ppControl = NULL;
    }
    m_stLastStats = 0;
    m_stLastTime = 0;


    ::LeaveCriticalSection(&m_CriticalSection);
    return S_OK;
}

/*****************************************************************************
 * CSynth::GetMaxVoices()
 *****************************************************************************
 * Returns the maximum number of voices available.
 */
HRESULT CSynth::GetMaxVoices(
    short * pnMaxVoices,    // Returns maximum number of allowed voices for continuous play.
    short * pnTempVoices )  // Returns number of extra voices for voice overflow.
{
    if (pnMaxVoices != NULL)
    {
        *pnMaxVoices = m_nMaxVoices;
    }
    if (pnTempVoices != NULL)
    {
        *pnTempVoices = m_nExtraVoices;
    }
    return S_OK;
}

/*****************************************************************************
 * CSynth::SetSampleRate()
 *****************************************************************************
 * Set the sample rate of the synth.  This silences the synth.  The SR is
 * forwarded to the instrument manager.
 */
HRESULT CSynth::SetSampleRate(DWORD dwSampleRate)
{
    HRESULT hr = S_OK;
    ::EnterCriticalSection(&m_CriticalSection);
    AllNotesOff();
    m_stLastTime *= dwSampleRate;
    m_stLastTime /= m_dwSampleRate;
    // m_stLastTime = MulDiv(m_stLastTime,dwSampleRate,m_dwSampleRate);
    m_stLastStats = 0;
    m_dwSampleRate = dwSampleRate;
    m_stMinSpan = dwSampleRate / 100;   // 10 ms.
    m_stMaxSpan = (dwSampleRate + 19) / 20;    // 50 ms.
    ::LeaveCriticalSection(&m_CriticalSection);
    m_Instruments.SetSampleRate(dwSampleRate);
    return hr;
}

/*****************************************************************************
 * CSynth::Activate()
 *****************************************************************************
 * Make the synth active.
 */
HRESULT CSynth::Activate(DWORD dwSampleRate, DWORD dwChannels )
{
    NTSTATUS status = STATUS_SUCCESS;
    m_stLastTime = 0;
    SetSampleRate(dwSampleRate);
    SetStereoMode(dwChannels);
    ResetPerformanceStats();
    return status;
}

/*****************************************************************************
 * CSynth::Deactivate()
 *****************************************************************************
 * Gag the synth.
 */
HRESULT CSynth::Deactivate()
{
    AllNotesOff();
    return S_OK;
}

/*****************************************************************************
 * CSynth::GetPerformanceStats()
 *****************************************************************************
 * Get the latest perf statistics.
 */
HRESULT CSynth::GetPerformanceStats(PerfStats *pStats)
{
    if (pStats == NULL)
    {
        return E_POINTER;
    }
    *pStats = m_CopyStats;
    return (S_OK);
}

#pragma code_seg(push, "PAGE")
/*****************************************************************************
 * CSynth::Mix()
 *****************************************************************************
 * Mix into the given buffer.  This is called by Render in the software
 * synth case, or this could be called by a request from hardware.
 */
void CSynth::Mix(short *pBuffer,DWORD dwLength,LONGLONG llPosition)
{
    PAGED_CODE();

    FLOATSAFE fs;

    STIME stEndTime;
    CVoice *pVoice;
    CVoice *pNextVoice;
    long lNumVoices = 0;
    ::EnterCriticalSection(&m_CriticalSection);

    LONG    lTime = - (LONG)::GetTheCurrentTime();

    stEndTime = llPosition + dwLength;
    StealNotes(stEndTime);
    DWORD dwX;
    for (dwX = 0; dwX < m_dwControlCount; dwX++)
    {
        m_ppControl[dwX]->QueueNotes(stEndTime);
    }
    pVoice = m_VoicesInUse.GetHead();
    for (;pVoice != NULL;pVoice = pNextVoice)
    {
        pNextVoice = pVoice->GetNext();

        pVoice->Mix(pBuffer,dwLength,llPosition,stEndTime);
        lNumVoices++;

        if (pVoice->m_fInUse == FALSE)
        {
            m_VoicesInUse.Remove(pVoice);
            m_VoicesFree.AddHead(pVoice);
           // m_BuildStats.dwTotalSamples += (pVoice->m_stStopTime - pVoice->m_stStartTime);
            if (pVoice->m_stStartTime < m_stLastStats)
            {
                m_BuildStats.dwTotalSamples += (long) (pVoice->m_stStopTime - m_stLastStats);
            }
            else
            {
                m_BuildStats.dwTotalSamples += (long) (pVoice->m_stStopTime - pVoice->m_stStartTime);
            }
        }
    }
    for (dwX = 0; dwX < m_dwControlCount; dwX++)
    {
        m_ppControl[dwX]->ClearMIDI(stEndTime);
    }
    FinishMix(pBuffer,dwLength);
    if (stEndTime > m_stLastTime)
    {
        m_stLastTime = stEndTime;
    }
    lTime += ::GetTheCurrentTime();

    m_BuildStats.dwTotalTime += lTime;

    if ((m_stLastStats + m_dwSampleRate) <= m_stLastTime)
    {
        DWORD dwElapsed = (DWORD) (m_stLastTime - m_stLastStats);
        pVoice = m_VoicesInUse.GetHead();
        for (;pVoice != NULL;pVoice = pVoice->GetNext())
        {
            if (pVoice->m_stStartTime < m_stLastStats)
            {
                m_BuildStats.dwTotalSamples += dwElapsed;
            }
            else
            {
                m_BuildStats.dwTotalSamples += (long) (m_stLastTime - pVoice->m_stStartTime);
            }
        }
        if (dwElapsed == 0) dwElapsed = 1;
        if (m_BuildStats.dwTotalSamples == 0) m_BuildStats.dwTotalSamples = 1;
        m_BuildStats.dwVoices =
            (m_BuildStats.dwTotalSamples + (dwElapsed >> 1)) / dwElapsed;
        {
            m_BuildStats.dwCPU = MulDiv(m_BuildStats.dwTotalTime,
                m_dwSampleRate, dwElapsed);
        }
        m_CopyStats = m_BuildStats;
        RtlZeroMemory(&m_BuildStats, sizeof(m_BuildStats));
        m_stLastStats = m_stLastTime;
    }
    ::LeaveCriticalSection(&m_CriticalSection);
}
#pragma code_seg(pop)
/*****************************************************************************
 * CSynth::OldestVoice()
 *****************************************************************************
 * Get the most likely candidate to be shut down, to support voice stealing.
 * Priority is looked at first, then age.
 */
CVoice *CSynth::OldestVoice()
{
    CVoice *pVoice;
    CVoice *pBest = NULL;
    pVoice = m_VoicesInUse.GetHead();
    pBest = pVoice;
    if (pBest)
    {
        pVoice = pVoice->GetNext();
        for (;pVoice;pVoice = pVoice->GetNext())
        {
            if (!pVoice->m_fTag)
            {
                if (pBest->m_fTag)
                {
                    pBest = pVoice;
                }
                else
                {
                    if (pVoice->m_dwPriority <= pBest->m_dwPriority)
                    {
                        if (pVoice->m_fNoteOn)
                        {
                            if (pBest->m_fNoteOn)
                            {
                                if (pBest->m_stStartTime > pVoice->m_stStartTime)
                                {
                                    pBest = pVoice;
                                }
                            }
                        }
                        else
                        {
                            if (pBest->m_fNoteOn ||
                                (pBest->m_vrVolume > pVoice->m_vrVolume))
                            {
                                pBest = pVoice;
                            }
                        }
                    }
                }
            }
        }
        if (pBest->m_fTag)
        {
            pBest = NULL;
        }
    }
    return pBest;
}

/*****************************************************************************
 * CSynth::StealVoice()
 *****************************************************************************
 * Steal a voice, if possible.  If none are at or below this priority, then
 * return NULL, and this voice will go unheard.  If there IS a voice to be
 * stolen, silence it first.
 */
CVoice *CSynth::StealVoice(DWORD dwPriority)
{
    CVoice *pVoice;
    CVoice *pBest = NULL;
    pVoice = m_VoicesInUse.GetHead();
    for (;pVoice != NULL;pVoice = pVoice->GetNext())
    {
        if (pVoice->m_dwPriority <= dwPriority)
        {
            if (!pBest)
            {
                pBest = pVoice;
            }
            else
            {
                if (pVoice->m_fNoteOn == FALSE)
                {
                    if ((pBest->m_fNoteOn == TRUE) ||
                        (pBest->m_vrVolume > pVoice->m_vrVolume))
                    {
                        pBest = pVoice;
                    }
                }
                else
                {
                    if (pBest->m_stStartTime > pVoice->m_stStartTime)
                    {
                        pBest = pVoice;
                    }
                }
            }
        }
    }
    if (pBest != NULL)
    {
        pBest->ClearVoice();
        pBest->m_fInUse = FALSE;
        m_VoicesInUse.Remove(pBest);
        pBest->SetNext(NULL);
    }
    return pBest;
}

/*****************************************************************************
 * CSynth::QueueVoice()
 *****************************************************************************
 * This method queues a voice in the list of currently
 * synthesizing voices. It places them in the queue so that
 * the higher priority voices are later in the queue. This
 * allows the note stealing algorithm to take off the top of
 * the queue.
 * And, we want older playing notes to be later in the queue
 * so the note ons and offs overlap properly. So, the queue is
 * sorted in priority order with older notes later within one
 * priority level.
 */
void CSynth::QueueVoice(CVoice *pVoice)
{
    CVoice *pScan = m_VoicesInUse.GetHead();
    CVoice *pNext = NULL;
    if (!pScan) // Empty list?
    {
        m_VoicesInUse.AddHead(pVoice);
        return;
    }
    if (pScan->m_dwPriority > pVoice->m_dwPriority)
    {   // Are we lower priority than the head of the list?
        m_VoicesInUse.AddHead(pVoice);
        return;
    }

    pNext = pScan->GetNext();
    for (;pNext;)
    {
        if (pNext->m_dwPriority > pVoice->m_dwPriority)
        {
            // Lower priority than next in the list.
            pScan->SetNext(pVoice);
            pVoice->SetNext(pNext);
            return;
        }
        pScan = pNext;
        pNext = pNext->GetNext();
    }
    // Reached the end of the list.
    pScan->SetNext(pVoice);
    pVoice->SetNext(NULL);
}

/*****************************************************************************
 * CSynth::StealNotes()
 *****************************************************************************
 * Clear out notes at a given time.
 */
void CSynth::StealNotes(STIME stTime)
{
    CVoice *pVoice;
    long lToMove = m_nExtraVoices - m_VoicesExtra.GetCount();
    if (lToMove > 0)
    {
        for (;lToMove > 0;)
        {
            pVoice = m_VoicesFree.RemoveHead();
            if (pVoice != NULL)
            {
                m_VoicesExtra.AddHead(pVoice);
                lToMove--;
            }
            else break;
        }
        if (lToMove > 0)
        {
            pVoice = m_VoicesInUse.GetHead();
            for (;pVoice;pVoice = pVoice->GetNext())
            {
                if (pVoice->m_fTag) // Voice is already slated to be returned.
                {
                    lToMove--;
                }
            }
            for (;lToMove > 0;lToMove--)
            {
                pVoice = OldestVoice();
                if (pVoice != NULL)
                {
                    pVoice->QuickStopVoice(stTime);
                    m_BuildStats.dwNotesLost++;
                }
                else break;
            }
        }
    }
}


/*****************************************************************************
 * CSynth::FinishMix()
 *****************************************************************************
 * Cleanup after the mix.
 */
void CSynth::FinishMix(_Inout_updates_(_Inexpressible_(dwLength << m_dwStereo)) short *pBuffer, _In_ DWORD dwLength)
{
    DWORD dwIndex;
    long lMax = (long) m_BuildStats.dwMaxAmplitude;
    long lTemp;
    for (dwIndex = 0; dwIndex < (dwLength << m_dwStereo); dwIndex++)
    {
        lTemp = pBuffer[dwIndex];
        lTemp <<= 1;
        if (lTemp < -32767) lTemp = -32767;
        if (lTemp > 32767) lTemp = 32767;
#pragma prefast (suppress: __WARNING_WRITE_OVERRUN, "DevDiv:321023")
        pBuffer[dwIndex] = (short) lTemp;
        if (lTemp > lMax)
        {
            lMax = lTemp;
        }
    }
    m_BuildStats.dwMaxAmplitude = lMax;
}

/*****************************************************************************
 * CSynth::Unload()
 *****************************************************************************
 * Unload a previous download.  Forward the request to the instrument manager.
 */
HRESULT CSynth::Unload(HANDLE hDownload,
                       HRESULT ( CALLBACK *lpFreeMemory)(HANDLE,HANDLE),
                       HANDLE hUserData)
{
    return m_Instruments.Unload( hDownload, lpFreeMemory, hUserData);
}

/*****************************************************************************
 * CSynth::Download()
 *****************************************************************************
 * Handle a download.  Forward the request to the instrument manager.
 */
HRESULT CSynth::Download(LPHANDLE phDownload, void * pdwData, LPBOOL bpFree)
{
    FLOATSAFE fs;

    return m_Instruments.Download( phDownload, (DWORD *) pdwData,  bpFree);
}

/*****************************************************************************
 * CSynth::PlayBuffer()
 *****************************************************************************
 * This receives one MIDI message in the form of a buffer of data and
 * ulCable, which indicates which Channel Group the message is addressed
 * to. Each channel group is implemented with an instance of a CControlLogic
 * object, so this chooses which CControlLogic object to send the message
 * to. If ulCable is 0, this is a broadcast message and should be sent to all
 * CControlLogics.
 *
 * PlayBuffer() analyzes the message and, depending on the size, either
 * sends to CControlLogic::RecordMIDI() or CControlLogic::RecordSysEx().
 *
 * In order to properly associate the time stamp of the MIDI
 * message in the buffer, the synth needs to convert from the
 * REFERENCE_TIME format to its internal sample based time. Since
 * the wave out stream is actually managed by IDirectMusicSynthSink,
 * the synth calls IDirectMusicSynthSink::RefTimeToSample
 * for each MIDI message to convert its time stamp into sample time.
 *
 * So, typically, the synthesizer pulls each MIDI message from the
 * buffer, stamps it in sample time, then places it in its own
 * internal queue. The queue is emptied later by the rendering
 * process, which is managed by CDmSynthStream::Render and
 * called by IDirectMusicSynthSink.
 */
HRESULT CSynth::PlayBuffer(IDirectMusicSynthSink *pSynthSink,REFERENCE_TIME rt,
                           LPBYTE lpBuffer, DWORD cbBuffer, ULONG ulCable)
{
    STIME stTime;

    ::EnterCriticalSection(&m_CriticalSection);

    if ( rt == 0 ) // Special case of time == 0.
    {
        stTime = m_stLastTime;
    }
    else
    {
        pSynthSink->RefTimeToSample(rt, &stTime);
    }

    if (cbBuffer <= sizeof(DWORD))
    {
        if (ulCable <= m_dwControlCount)
        {
            if (ulCable == 0) // Play all groups if 0.
            {
                for (; ulCable < m_dwControlCount; ulCable++)
                {
                    m_ppControl[ulCable]->RecordMIDI(stTime,lpBuffer[0],
                        lpBuffer[1], lpBuffer[2]);
                }
            }
            else
            {
                m_ppControl[ulCable - 1]->RecordMIDI(stTime,lpBuffer[0],
                lpBuffer[1], lpBuffer[2]);

            }
        }
        else
        {
            Trace(1,"MIDI event on channel group %ld is beyond range of %ld opened channel groups\n",
                ulCable, m_dwControlCount);
        }
    }
    else
    {
        if (ulCable <= m_dwControlCount)
        {
            if (ulCable == 0)
            {
                for (; ulCable < m_dwControlCount; ulCable++)
                {
                    m_ppControl[ulCable]->RecordSysEx(cbBuffer,
                        &lpBuffer[0], stTime);
                }
            }
            else
            {
                m_ppControl[ulCable-1]->RecordSysEx(cbBuffer,
                    &lpBuffer[0], stTime);
            }
        }
    }

    ::LeaveCriticalSection(&m_CriticalSection);
    return S_OK;
}

/*****************************************************************************
 * CSynth::SetStereoMode()
 *****************************************************************************
 * Set the stereo/mono mode for this synth.
 */
HRESULT CSynth::SetStereoMode(DWORD dwChannels)   // 1 for Mono, 2 for Stereo.
{
    HRESULT hr = S_OK;
    if ((m_dwStereo + 1) != dwChannels)
    {
        DWORD dwStereo;
        if (dwChannels > 1) dwStereo = 1;
        else dwStereo = 0;
        if (dwStereo != m_dwStereo)
        {
            m_dwStereo = dwStereo;
        }
    }
    return hr;
}

/*****************************************************************************
 * CSynth::ResetPerformanceStats()
 *****************************************************************************
 * Reset the running performance statistics.
 */
void CSynth::ResetPerformanceStats()
{
    m_BuildStats.dwNotesLost = 0;
    m_BuildStats.dwTotalTime = 0;
    m_BuildStats.dwVoices = 0;
    m_BuildStats.dwTotalSamples = 0;
    m_BuildStats.dwCPU = 0;
    m_BuildStats.dwMaxAmplitude = 0;
    m_CopyStats = m_BuildStats;
}

/*****************************************************************************
 * CSynth::AllNotesOff()
 *****************************************************************************
 * Stop all voices.
 */
HRESULT CSynth::AllNotesOff()
{
    CVoice *pVoice;
    ::EnterCriticalSection(&m_CriticalSection);
    pVoice = m_VoicesInUse.RemoveHead();
    while (pVoice != nullptr)
    {
        pVoice->ClearVoice();
        pVoice->m_fInUse = FALSE;
        m_VoicesFree.AddHead(pVoice);

        long lSamples;

        if (pVoice->m_stStartTime < m_stLastStats)
        {
            lSamples = (long) (pVoice->m_stStopTime - m_stLastStats);
        }
        else
        {
            lSamples = (long) (pVoice->m_stStopTime - pVoice->m_stStartTime);
        }
        if (lSamples < 0)
        {
            lSamples = 0;
        }
        m_BuildStats.dwTotalSamples += lSamples;

        pVoice = m_VoicesInUse.RemoveHead();
    }
    ::LeaveCriticalSection(&m_CriticalSection);
    return (S_OK);
}

/*****************************************************************************
 * CSynth::SetChannelPriority()
 *****************************************************************************
 * Set the priority for a given channel, to be used in voice stealing.
 */
HRESULT CSynth::SetChannelPriority(DWORD dwChannelGroup,DWORD dwChannel,
                                   DWORD dwPriority)
{
    HRESULT hr = S_OK;

    ::EnterCriticalSection(&m_CriticalSection);

    dwChannelGroup--;
    if ((dwChannelGroup >= m_dwControlCount) || (dwChannel > 15))
    {
        hr = E_INVALIDARG;
    }
    else
    {
        if (m_ppControl)
        {
            hr = m_ppControl[dwChannelGroup]->SetChannelPriority(dwChannel,dwPriority);
        }
    }
    ::LeaveCriticalSection(&m_CriticalSection);

    return hr;
}

/*****************************************************************************
 * CSynth::GetChannelPriority()
 *****************************************************************************
 * Retrieve the priority of a given channel/channel group, to be used to
 * facilitate correct voice stealing.
 */
HRESULT CSynth::GetChannelPriority(DWORD dwChannelGroup, DWORD dwChannel,
                                   LPDWORD pdwPriority)
{
    HRESULT hr = S_OK;

    ::EnterCriticalSection(&m_CriticalSection);

    dwChannelGroup--;
    if ((dwChannelGroup >= m_dwControlCount) || (dwChannel > 15))
    {
        hr = E_INVALIDARG;
    }
    else
    {
        if (m_ppControl)
        {
            hr = m_ppControl[dwChannelGroup]->GetChannelPriority(dwChannel,pdwPriority);
        }
    }
    ::LeaveCriticalSection(&m_CriticalSection);

    return hr;
}



Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS