//
// syslink.cpp
//
/*
  Copyright (c) 1998-2000 Microsoft Corporation.  All Rights Reserved.
*/

#include "common.h"
#include "private.h"

#define STR_MODULENAME "DDKSynth.sys:SysLink: "

/*****************************************************************************
 *****************************************************************************
 * CDmSynthStream-- ISynthSinkDMus implementation
 *****************************************************************************
 ****************************************************************************/


#pragma code_seg()
/*****************************************************************************
 * CDmSynthStream::Render()
 *****************************************************************************
 * Render is called from the port driver, to fill the given buffer.  This is 
 * in turn forwarded to the synth (which -- roughly -- goes to the different 
 * voices, which goes to the DigitalAudios, which goes to the mix functions).
 *
 * Typically, a synthesizer manages converting messages into
 * rendered wave data in two processes. First, it time stamps the MIDI 
 * messages it receives from the application via calls to 
 * PlayBuffer and places them in its own internal queue. 
 * 
 * Then, in response to Render, it generates audio by pulling MIDI 
 * messages from the queue and synthesizing the appropriate tones within
 * the time span of the requested render buffer.
 * 
 * As the synthesizer renders the MIDI messages into the buffer, it
 * calls RefTimeToSample to translate the MIDI time stamps into sample 
 * positions. This guarantees extremely accurate timing.
 */
void CDmSynthStream::Render(
                            IN  PBYTE       pBuffer,
                            IN  DWORD       dwLength,
                            IN  LONGLONG    llPosition)
{
    PAGED_CODE();

    _DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::Render"));
    ASSERT(pBuffer);

    m_pSynth->Mix((short*)pBuffer, dwLength, llPosition);

    m_llLastPosition = llPosition + dwLength;
}


/*****************************************************************************
 * CDmSynthStream::SyncToMaster()
 *****************************************************************************
 * Sync this stream to the master clock, using the given slave time, and
 * whether we are starting now.
 */
STDMETHODIMP 
CDmSynthStream::SyncToMaster(IN REFERENCE_TIME  rtSlaveTime,
                             IN BOOL            fStart)
{
    PAGED_CODE();

	_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::SyncToMaster"));

    REFERENCE_TIME rtMasterTime;
	m_pMasterClock->GetTime(&rtMasterTime);

    if (!fStart)
    {
        m_SampleClock.SyncToMaster(rtSlaveTime, rtMasterTime);
    }
    else
    {
        m_llStartPosition = ((rtSlaveTime / 1000) * m_PortParams.SampleRate) / 10000;

        m_SampleClock.Start(m_pMasterClock, m_PortParams.SampleRate, m_llStartPosition);
    }


    return S_OK;
}

/*****************************************************************************
 * CDmSynthStream::SampleToRefTime()
 *****************************************************************************
 * Translate between sample time and reference clock time.
 */
STDMETHODIMP
CDmSynthStream::SampleToRefTime(IN  LONGLONG         llSampleTime,
                                OUT REFERENCE_TIME * prtTime)
{
	_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::SampleToRefTime"));
    ASSERT(prtTime);

    m_SampleClock.SampleToRefTime(llSampleTime + m_llStartPosition, prtTime);

    return S_OK;
}


/*****************************************************************************
 * CDmSynthStream::RefTimeToSample()
 *****************************************************************************
 * Translate between sample time and reference clock time.
 */
STDMETHODIMP
CDmSynthStream::RefTimeToSample(IN  REFERENCE_TIME  rtTime,
                                OUT LONGLONG *      pllSampleTime)
{
	_DbgPrintF(DEBUGLVL_BLAB, ("CDmSynthStream::RefTimeToSample"));
    ASSERT(pllSampleTime);

    *pllSampleTime = m_SampleClock.RefTimeToSample(rtTime) - m_llStartPosition;
  
    return S_OK;
}