Sample Code

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

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

#include "common.h"

#define STR_MODULENAME "DDKSynth.sys:Midi: "

#include "math.h"

#pragma code_seg()
#pragma data_seg()

CMIDIDataList * CMIDIRecorder::m_sFreeList = (CMIDIDataList *) NULL;
ULONG CMIDIRecorder::sm_cRefCnt = 0;

//
// Array for converting MIDI to volume.
// value = log10((index/127)^4)*1000 where index = 0..127
//
const VREL vrMIDIToVREL[] = {
//  0       1       2       3       4       5       6       7
    -9600,  -8415,  -7211,  -6506,  -6006,  -5619,  -5302,  -5034,
    -4802,  -4598,  -4415,  -4249,  -4098,  -3959,  -3830,  -3710,
    -3598,  -3493,  -3394,  -3300,  -3211,  -3126,  -3045,  -2968,
    -2894,  -2823,  -2755,  -2689,  -2626,  -2565,  -2506,  -2449,
    -2394,  -2341,  -2289,  -2238,  -2190,  -2142,  -2096,  -2050,
    -2006,  -1964,  -1922,  -1881,  -1841,  -1802,  -1764,  -1726,
    -1690,  -1654,  -1619,  -1584,  -1551,  -1518,  -1485,  -1453,
    -1422,  -1391,  -1361,  -1331,  -1302,  -1273,  -1245,  -1217,
    -1190,  -1163,  -1137,  -1110,  -1085,  -1059,  -1034,  -1010,
    -985,   -961,   -938,   -914,   -891,   -869,   -846,   -824,
    -802,   -781,   -759,   -738,   -718,   -697,   -677,   -657,
    -637,   -617,   -598,   -579,   -560,   -541,   -522,   -504,
    -486,   -468,   -450,   -432,   -415,   -397,   -380,   -363,
    -347,   -330,   -313,   -297,   -281,   -265,   -249,   -233,
    -218,   -202,   -187,   -172,   -157,   -142,   -127,   -113,
    -98,    -84,    -69,    -55,    -41,    -27,    -13,    0
};

/*****************************************************************************
 * Constructor for CMidiData object
 *****************************************************************************/
/*****************************************************************************
 * CMIDIData::CMIDIData()
 *****************************************************************************
 * Constructor for this object.
 */
CMIDIData::CMIDIData()
{
    m_stTime = 0;
    m_lData = 0;
}

/*****************************************************************************
 * CMIDIRecorder::CMIDIRecorder()
 *****************************************************************************
 * Constructor for this object.
 */
CMIDIRecorder::CMIDIRecorder()
{
    m_lCurrentData = 0;
    m_stCurrentTime = 0;
    KeInitializeSpinLock(&m_SpinLock);
    ++sm_cRefCnt;
}

/*****************************************************************************
 * CMIDIRecorder::~CMIDIRecorder()
 *****************************************************************************
 * Destructor for this object.
 */
CMIDIRecorder::~CMIDIRecorder()
{
    ClearMIDI(0x7FFFFFFF);

    --sm_cRefCnt;
    if(sm_cRefCnt == 0)
    {
        if(CMIDIRecorder::m_sFreeList != NULL)
        {
            while(!CMIDIRecorder::m_sFreeList->IsEmpty())
            {
                CMIDIData *event;
                event = CMIDIRecorder::m_sFreeList->RemoveHead();
                delete event;
            }
            delete CMIDIRecorder::m_sFreeList;
            CMIDIRecorder::m_sFreeList = NULL;
        }
    }
}

/*****************************************************************************
 * CMIDIRecorder::Init()
 *****************************************************************************
 * Initialize the CMIDIRecorder object.  Make a list of CMIDIData objects for later.
 */
void CMIDIRecorder::Init()
{
    int nIndex;

    if(CMIDIRecorder::m_sFreeList == NULL)
    {
        CMIDIRecorder::m_sFreeList = new(NonPagedPool,'LSmD') CMIDIDataList;    //  DmSL
        if(CMIDIRecorder::m_sFreeList != NULL)
        {
            CMIDIRecorder::m_sFreeList->RemoveAll();
            for(nIndex = 0; nIndex < MAX_MIDI_EVENTS; nIndex++)
            {
                CMIDIData* pEvent;

                pEvent = new(NonPagedPool,'MSmD') CMIDIData;         //  DmSM
                if(pEvent != NULL)
                {
                    CMIDIRecorder::m_sFreeList->AddHead(pEvent);
                }
            }
        }
    }
}

/*****************************************************************************
 * CMIDIRecorder::FlushMIDI()
 *****************************************************************************
 * Remove all events from the event list, and put them back on the free list.
 */
BOOL CMIDIRecorder::FlushMIDI(STIME stTime)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

    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);
            }
            if (CMIDIRecorder::m_sFreeList)
            {
                CMIDIRecorder::m_sFreeList->Cat(pMD);
            }
            break;
        }
        pLast = pMD;
    }

    BOOL fIsEmpty = m_EventList.IsEmpty();

    KeReleaseSpinLock(&m_SpinLock, irql);

    return fIsEmpty;
}

/*****************************************************************************
 * CMIDIRecorder::ClearMIDI()
 *****************************************************************************
 * Clear out any MIDI that is before the given time.
 */
BOOL CMIDIRecorder::ClearMIDI(STIME stTime)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

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

    BOOL fIsEmpty = m_EventList.IsEmpty();

    KeReleaseSpinLock(&m_SpinLock, irql);

    return fIsEmpty;
}

/*****************************************************************************
 * CMIDIRecorder::VelocityToVolume()
 *****************************************************************************
 * Translate from 16-bit velocity to a VREL volume (dB).
 */
VREL CMIDIRecorder::VelocityToVolume(WORD nVelocity)
{
    return (::vrMIDIToVREL[nVelocity]);
}

/*****************************************************************************
 * CMIDIRecorder::RecordMIDI()
 *****************************************************************************
 * Queue up a given note at a given time.  Use an element from the free pool,
 * or allocate a new one if the pool is exhausted.
 */
#pragma prefast (suppress: __WARNING_UNEXPECTED_IRQL_CHANGE, "Suppressing 28167: SAL cannot tell throwing operator new from non-throwing operator new, so the code analysis tool will bypass the line 'pMD = new(NonPagedPool,'MSmD') CMIDIData;', resulting in warning 28167")
BOOL CMIDIRecorder::RecordMIDI(STIME stTime, long lData)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

    CMIDIData *pMD = NULL;
    if (CMIDIRecorder::m_sFreeList)
    {
        pMD = CMIDIRecorder::m_sFreeList->RemoveHead();
    }

    if (pMD == NULL)
    {
        pMD = new(NonPagedPool,'MSmD') CMIDIData;   //  DmSM
    }

    if (pMD != NULL)
    {
        CMIDIData *pScan = m_EventList.GetHead();
        CMIDIData *pNext;

        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;
                        }
                    }
                }
            }
        }
        KeReleaseSpinLock(&m_SpinLock, irql);
        return (TRUE);
    }

    KeReleaseSpinLock(&m_SpinLock, irql);
    Trace(1,"MIDI Event pool empty.\n");
    return (FALSE);
}

/*****************************************************************************
 * CMIDIRecorder::GetData()
 *****************************************************************************
 * Retrieve any data that is at or before the given time.
 */
long CMIDIRecorder::GetData(STIME stTime)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

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

    KeReleaseSpinLock(&m_SpinLock, irql);

    return (lData);
}

/*****************************************************************************
 * CNoteIn::RecordNote()
 *****************************************************************************
 * Record the given note object into the NoteIn receptor.
 */
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));
}

/*****************************************************************************
 * CNoteIn::RecordEvent()
 *****************************************************************************
 * Record the given note object into the NoteIn receptor.
 */
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));
}

/*****************************************************************************
 * CNoteIn::GetNote()
 *****************************************************************************
 * Retrieve the first note at or before the given time.
 */
BOOL CNoteIn::GetNote(STIME stTime, CNote * pNote)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

    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();
            if (CMIDIRecorder::m_sFreeList)
            {
                CMIDIRecorder::m_sFreeList->AddHead(pMD);
            }
            else
            {
                delete pMD;
            }
            KeReleaseSpinLock(&m_SpinLock, irql);
            return (TRUE);
        }
    }

    KeReleaseSpinLock(&m_SpinLock, irql);
    return (FALSE);
}

/*****************************************************************************
 * CNoteIn::FlushMIDI()
 *****************************************************************************
 * Flush out any MIDI after the given time, with attention given
 * to special events.
 */
void CNoteIn::FlushMIDI(STIME stTime)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

    CMIDIData *pMD;
    for (pMD = m_EventList.GetHead();pMD != NULL;pMD = pMD->GetNext())
    {
        if (pMD->m_stTime >= stTime)
        {
            pMD->m_stTime = stTime;     // Play now.

            BYTE command = (BYTE) ((pMD->m_lData & 0x0000FF00) >> 8);
            if (command < NOTE_PROGRAMCHANGE)
            {
                pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
            }
            //  otherwise it is a special command
            //  so don't mess with the velocity
        }
    }

    KeReleaseSpinLock(&m_SpinLock, irql);
}


/*****************************************************************************
 * CNoteIn::FlushPart()
 *****************************************************************************
 * Flush the given channel, with attention given to special events.
 */
void CNoteIn::FlushPart(STIME stTime, BYTE bChannel)
{
    KIRQL irql;
    KeAcquireSpinLock(&m_SpinLock, &irql);

    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.

                BYTE command = (BYTE) ((pMD->m_lData & 0x0000FF00) >> 8);
                if (command < NOTE_PROGRAMCHANGE)
                {
                    pMD->m_lData &= 0xFFFFFF00; // Clear velocity to make note off.
                }
                //  otherwise it is a special command
                //  so don't mess with the velocity
            }
        }
    }

    KeReleaseSpinLock(&m_SpinLock, irql);
}

/*****************************************************************************
 * CModWheelIn::GetModulation()
 *****************************************************************************
 * Get the modulation data.
 */
DWORD CModWheelIn::GetModulation(STIME stTime)
{
    DWORD nResult = CMIDIRecorder::GetData(stTime);
    return (nResult);
}

/*****************************************************************************
 * CPitchBendIn::CPitchBendIn()
 *****************************************************************************
 * Constructor for this object.
 */
CPitchBendIn::CPitchBendIn()
{
    m_lCurrentData = 0x2000;    // initially at midpoint, no bend
    m_prRange = 200;           // whole tone range by default.
}

/*****************************************************************************
 * CPitchBendIn::GetPitch()
 *****************************************************************************
 * Get the pitch data.
 * Note: 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()
 *****************************************************************************
 * Constructor for this object.
 */
CVolumeIn::CVolumeIn()
{
    m_lCurrentData = 100;
}

/*****************************************************************************
 * CVolumeIn::GetVolume()
 *****************************************************************************
 * Get the volume data.
 */
VREL CVolumeIn::GetVolume(STIME stTime)
{
    long lResult = CMIDIRecorder::GetData(stTime);
    return (::vrMIDIToVREL[lResult]);
}

/*****************************************************************************
 * CExpressionIn::CExpressionIn()
 *****************************************************************************
 * Constructor for this object.
 */
CExpressionIn::CExpressionIn()
{
    m_lCurrentData = 127;
}

/*****************************************************************************
 * CExpressionIn::GetVolume()
 *****************************************************************************
 * Get the volume data.
 */
VREL CExpressionIn::GetVolume(STIME stTime)
{
    long lResult = CMIDIRecorder::GetData(stTime);
    return (::vrMIDIToVREL[lResult]);
}

/*****************************************************************************
 * CPanIn::CPanIn()
 *****************************************************************************
 * Constructor for this object.
 */
CPanIn::CPanIn()
{
    m_lCurrentData = 64;
}

/*****************************************************************************
 * CPanIn::GetPan()
 *****************************************************************************
 * Get the pan data.
 */
long CPanIn::GetPan(STIME stTime)
{
    long lResult = (long) CMIDIRecorder::GetData(stTime);
    return (lResult);
}

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