//      Copyright (c) 1996-1999 Microsoft Corporation
//      MIDI.cpp
//

#ifdef DMSYNTH_MINIPORT
#include "common.h"
#else
#include "simple.h"
#include <mmsystem.h>
#include "synth.h"
#include "math.h"
#endif
 
CMIDIDataList    CMIDIRecorder::m_sFreeList;
CMIDIData        CMIDIRecorder::m_sEventBuffer[MAX_MIDI_EVENTS];

CMIDIData::CMIDIData() 
{
    m_stTime = 0;
    m_lData = 0;            
}


CMIDIRecorder::CMIDIRecorder()
{
    m_lCurrentData = 0;
    m_stCurrentTime = 0;
}

CMIDIRecorder::~CMIDIRecorder()

{
    ClearMIDI(0x7FFFFFFF);
}

VREL CMIDIRecorder::m_vrMIDIToVREL[128];

void CMIDIRecorder::Init()
{
    int nIndex;
    static BOOL fAlreadyDone = FALSE;
    if (!fAlreadyDone)
    {
        m_sFreeList.RemoveAll();
        for (nIndex = 0; nIndex < MAX_MIDI_EVENTS; nIndex++)
        {
            m_sFreeList.AddHead(&m_sEventBuffer[nIndex]);
        }
        fAlreadyDone = TRUE;
		for (nIndex = 1; nIndex < 128; nIndex++)
		{
			double   flTemp;
			flTemp = nIndex;
			flTemp /= 127.0;
			flTemp = pow(flTemp,4.0);
			flTemp = log10(flTemp);
			flTemp *= 1000.0;
			m_vrMIDIToVREL[nIndex] = (VREL) flTemp;
		}
		m_vrMIDIToVREL[0] = -9600;
    }
}


BOOL CMIDIRecorder::FlushMIDI(STIME stTime)

{
    CMIDIData *pMD;
    CMIDIData *pLast = NULL;
    for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
    {
        if (pMD->m_stTime >= stTime)
        {
            if (pLast == NULL)
            {
                m_EventList.RemoveAll();
            }
            else
            {
                pLast->SetNext(NULL);
            }
            m_sFreeList.Cat(pMD);
            break;
        }
        pLast = pMD;
    }
    return m_EventList.IsEmpty();
}

BOOL CMIDIRecorder::ClearMIDI(STIME stTime)

{
    CMIDIData *pMD;
    for (;pMD = m_EventList.GetHead();)
    {
        if (pMD->m_stTime < stTime)
        {
            m_EventList.RemoveHead();
            m_stCurrentTime = pMD->m_stTime;
            m_lCurrentData = pMD->m_lData;
            m_sFreeList.AddHead(pMD);
            
        }
        else break;
    }
    return m_EventList.IsEmpty();
}

VREL CMIDIRecorder::VelocityToVolume(WORD nVelocity)

{
    return (m_vrMIDIToVREL[nVelocity]);
}

BOOL CMIDIRecorder::RecordMIDI(STIME stTime, long lData)

{
    CMIDIData *pMD = m_sFreeList.RemoveHead();
	CMIDIData *pScan = m_EventList.GetHead();
	CMIDIData *pNext;
    if (pMD != NULL)
    {
        pMD->m_stTime = stTime;
        pMD->m_lData = lData;
		if (pScan == NULL)
		{
			m_EventList.AddHead(pMD);
		}
		else
		{
			if (pScan->m_stTime > stTime)
			{
				m_EventList.AddHead(pMD);
			}
			else
			{
				for (;pScan != NULL; pScan = pNext)
				{
					pNext = pScan->GetNext();
					if (pNext == NULL)
					{
						pScan->SetNext(pMD);
					}
					else
					{
						if (pNext->m_stTime > stTime)
						{
							pMD->SetNext(pNext);
							pScan->SetNext(pMD);
							break;
						}
					}
				}
			}
		}
        return (TRUE);
    }
    Trace(1,"MIDI Event pool empty.\n");
    return (FALSE);
}

long CMIDIRecorder::GetData(STIME stTime)

{
    CMIDIData *pMD = m_EventList.GetHead();
    long lData = m_lCurrentData;
    for (;pMD;pMD = pMD->GetNext())
    {
        if (pMD->m_stTime > stTime)
        {
            break;
        }
        lData = pMD->m_lData;
    }
    return (lData);
}

BOOL CNoteIn::RecordNote(STIME stTime, CNote * pNote)

{
	long lData = pNote->m_bPart << 16;
	lData |= pNote->m_bKey << 8;
	lData |= pNote->m_bVelocity;
	return (RecordMIDI(stTime,lData));
}

BOOL CNoteIn::RecordEvent(STIME stTime, DWORD dwPart, DWORD dwCommand, BYTE bData)

{
	long lData = dwPart;
	lData <<= 8;
	lData |= dwCommand;
	lData <<= 8;
	lData |= bData;
	return (RecordMIDI(stTime,lData));	
}

BOOL CNoteIn::GetNote(STIME stTime, CNote * pNote)

{
    CMIDIData *pMD = m_EventList.GetHead();
	if (pMD != NULL)
	{
		if (pMD->m_stTime <= stTime)
		{
			pNote->m_stTime = pMD->m_stTime;
			pNote->m_bPart = (BYTE) (pMD->m_lData >> 16);
			pNote->m_bKey = (BYTE) (pMD->m_lData >> 8) & 0xFF;
			pNote->m_bVelocity = (BYTE) pMD->m_lData & 0xFF;
            m_EventList.RemoveHead();
            m_sFreeList.AddHead(pMD);
			return (TRUE);
		}
	}
	return (FALSE);
}

void CNoteIn::FlushMIDI(STIME stTime)

{
    CMIDIData *pMD;
    for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
    {
        if (pMD->m_stTime >= stTime)
        {
            pMD->m_stTime = stTime;     // Play now.
            pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
        }
    }
}


void CNoteIn::FlushPart(STIME stTime, BYTE bChannel)

{
    CMIDIData *pMD;
    for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
    {
        if (pMD->m_stTime >= stTime)
        {
			if (bChannel == (BYTE) (pMD->m_lData >> 16))
			{
				pMD->m_stTime = stTime;     // Play now.
				pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
			}
		}
    }
}

DWORD CModWheelIn::GetModulation(STIME stTime)

{
    DWORD nResult = CMIDIRecorder::GetData(stTime);
    return (nResult);
}

CPitchBendIn::CPitchBendIn()

{
    m_lCurrentData = 0x2000;	// initially at midpoint, no bend
    m_prRange = 200;           // whole tone range by default.
}

// note (davidmay 8/14/96): we don't keep a time-stamped range.
// if people are changing the pitch bend range often, this won't work right,
// but that didn't seem likely enough to warrant a new list.
PREL CPitchBendIn::GetPitch(STIME stTime)

{
    PREL prResult = (PREL) CMIDIRecorder::GetData(stTime);
    prResult -= 0x2000;         // Subtract MIDI Midpoint.
    prResult *= m_prRange;	// adjust by current range
    prResult >>= 13;
    return (prResult);
}

CVolumeIn::CVolumeIn()

{
    m_lCurrentData = 100;
}

VREL CVolumeIn::GetVolume(STIME stTime)

{
    long lResult = CMIDIRecorder::GetData(stTime);
    return (m_vrMIDIToVREL[lResult]);
}

CExpressionIn::CExpressionIn()

{
    m_lCurrentData = 127;
}

VREL CExpressionIn::GetVolume(STIME stTime)

{
    long lResult = CMIDIRecorder::GetData(stTime);
    return (m_vrMIDIToVREL[lResult]);
}

CPanIn::CPanIn()

{
    m_lCurrentData = 64;
}

long CPanIn::GetPan(STIME stTime)

{
    long lResult = (long) CMIDIRecorder::GetData(stTime);
    return (lResult);
}

/*CProgramIn::CProgramIn()

{
	m_lCurrentData = 0;
	m_bBankH = 0;
	m_bBankL = 0;
}

DWORD CProgramIn::GetProgram(STIME stTime)

{
	DWORD dwProgram = (DWORD) CMIDIRecorder::GetData(stTime);
    dwProgram |= (m_bBankH << 16) | (m_bBankL << 8);
	return (dwProgram);
}

BOOL CProgramIn::RecordBankH(BYTE bBankH)

{
	m_bBankH = bBankH;
    return (TRUE);
}

BOOL CProgramIn::RecordBankL(BYTE bBankL)

{
	m_bBankL = bBankL;
    return (TRUE);
}

BOOL CProgramIn::RecordProgram(STIME stTime, BYTE bProgram)

{
	DWORD dwProgram = bProgram;
	return (RecordMIDI(stTime,(long) dwProgram));
}*/

