823 lines
24 KiB
C++
823 lines
24 KiB
C++
//
|
|
// Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
|
|
// CControlLogic.cpp
|
|
//
|
|
|
|
#define STR_MODULENAME "DDKSynth.sys:Control: "
|
|
|
|
#include "common.h"
|
|
|
|
#pragma code_seg()
|
|
/*****************************************************************************
|
|
* CControlLogic::CControlLogic()
|
|
*****************************************************************************
|
|
* Constructor for CControlLogic object.
|
|
*/
|
|
CControlLogic::CControlLogic()
|
|
{
|
|
m_fCSInitialized = FALSE;
|
|
InitializeCriticalSection(&m_CriticalSection);
|
|
m_fCSInitialized = TRUE;
|
|
|
|
m_pSynth = NULL;
|
|
m_pInstruments = NULL;
|
|
|
|
DWORD nIndex;
|
|
GMReset();
|
|
m_fGSActive = FALSE;
|
|
m_fXGActive = FALSE;
|
|
for (nIndex = 0;nIndex < 16;nIndex++)
|
|
{
|
|
m_fSustain[nIndex] = FALSE;
|
|
m_dwProgram[nIndex] = 0;
|
|
}
|
|
m_fEmpty = TRUE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::~CControlLogic()
|
|
*****************************************************************************
|
|
* Destructor for CControlLogic object.
|
|
*/
|
|
CControlLogic::~CControlLogic()
|
|
{
|
|
if (m_fCSInitialized)
|
|
{
|
|
DeleteCriticalSection(&m_CriticalSection);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::GMReset()
|
|
*****************************************************************************
|
|
* Performs a General MIDI reset, including resetting all the voice priorities,
|
|
* GS, XG, master volume, etc.
|
|
*/
|
|
void CControlLogic::GMReset()
|
|
{
|
|
static const int nPartToChannel[16] = {
|
|
9,0,1,2,3,4,5,6,7,8,10,11,12,13,14,15
|
|
};
|
|
int nX;
|
|
for (nX = 0; nX < 16; nX++)
|
|
{
|
|
int nY;
|
|
m_nData[nX] = 0;
|
|
m_prFineTune[nX] = 0;
|
|
m_bDrums[nX] = 0;
|
|
for (nY = 0; nY < 12; nY++)
|
|
{
|
|
m_prScaleTune[nX][nY] = 0;
|
|
}
|
|
m_nCurrentRPN[nX] = (short) 0x3FFF;
|
|
m_prCoarseTune[nX] = 0;
|
|
m_bPartToChannel[nX] = (BYTE)nPartToChannel[nX];
|
|
m_fMono[nX] = FALSE;
|
|
m_dwPriority[nX] = DAUD_STANDARD_VOICE_PRIORITY +
|
|
((16 - nX) * DAUD_CHAN15_VOICE_PRIORITY_OFFSET);
|
|
m_bBankH[nX] = 0;
|
|
m_bBankL[nX] = 0;
|
|
}
|
|
m_bDrums[0] = 1;
|
|
m_vrMasterVolume = 0;
|
|
m_fGSActive = FALSE;
|
|
m_fXGActive = FALSE;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::Init()
|
|
*****************************************************************************
|
|
* Initialize the CControlLogic object, and initialize all the child objects --
|
|
* Note recorders, ModWheels, Volumes, Pan, Expression, PitchBend.
|
|
*/
|
|
HRESULT CControlLogic::Init(CInstManager *pInstruments, CSynth *pSynth)
|
|
{
|
|
m_pSynth = pSynth;
|
|
m_pInstruments = pInstruments;
|
|
m_vrGainAdjust = 0;
|
|
|
|
m_Notes.Init();
|
|
|
|
int iRecIdx;
|
|
for (iRecIdx = 0; iRecIdx < sizeof(m_ModWheel) / sizeof(*m_ModWheel); iRecIdx++)
|
|
{
|
|
m_ModWheel[iRecIdx].Init();
|
|
}
|
|
for (iRecIdx = 0; iRecIdx < sizeof(m_PitchBend) / sizeof(*m_PitchBend); iRecIdx++)
|
|
{
|
|
m_PitchBend[iRecIdx].Init();
|
|
}
|
|
for (iRecIdx = 0; iRecIdx < sizeof(m_Volume) / sizeof(*m_Volume); iRecIdx++)
|
|
{
|
|
m_Volume[iRecIdx].Init();
|
|
}
|
|
for (iRecIdx = 0; iRecIdx < sizeof(m_Expression) / sizeof(*m_Expression); iRecIdx++)
|
|
{
|
|
m_Expression[iRecIdx].Init();
|
|
}
|
|
for (iRecIdx = 0; iRecIdx < sizeof(m_Pan) / sizeof(*m_Pan); iRecIdx++)
|
|
{
|
|
m_Pan[iRecIdx].Init();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::ClearMIDI()
|
|
*****************************************************************************
|
|
* Clears MIDI up to a certain time, and updates m_fEmpty.
|
|
*/
|
|
void CControlLogic::ClearMIDI(STIME stEndTime)
|
|
{
|
|
DWORD dwIndex;
|
|
::EnterCriticalSection(&m_CriticalSection);
|
|
if (!m_fEmpty)
|
|
{
|
|
m_fEmpty = TRUE;
|
|
for (dwIndex = 0;dwIndex < 16; dwIndex++)
|
|
{
|
|
m_fEmpty = m_ModWheel[dwIndex].ClearMIDI(stEndTime)
|
|
&& m_PitchBend[dwIndex].ClearMIDI(stEndTime)
|
|
&& m_Volume[dwIndex].ClearMIDI(stEndTime)
|
|
&& m_Expression[dwIndex].ClearMIDI(stEndTime)
|
|
&& m_Pan[dwIndex].ClearMIDI(stEndTime);
|
|
}
|
|
}
|
|
::LeaveCriticalSection(&m_CriticalSection);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::SetGainAdjust()
|
|
*****************************************************************************
|
|
* Sets the overall gain for this CControlLogic.
|
|
*/
|
|
void CControlLogic::SetGainAdjust(VREL vrGainAdjust)
|
|
{
|
|
m_vrGainAdjust = vrGainAdjust;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::QueueNotes()
|
|
*****************************************************************************
|
|
* Retrieves notes from the note recorders, and dispatches them appropriately.
|
|
* Called during the Mix.
|
|
*/
|
|
void CControlLogic::QueueNotes(STIME stEndTime)
|
|
{
|
|
CNote note;
|
|
|
|
while (m_Notes.GetNote(stEndTime,¬e))
|
|
{
|
|
if (note.m_bKey > 0x7F) // Special command events.
|
|
{
|
|
long lTemp;
|
|
DWORD dwPart = note.m_bPart;
|
|
DWORD dwCommand = note.m_bKey;
|
|
BYTE bData = note.m_bVelocity;
|
|
switch (dwCommand)
|
|
{
|
|
case NOTE_PROGRAMCHANGE:
|
|
m_dwProgram[dwPart] = bData |
|
|
(m_bBankH[dwPart] << 16) |
|
|
(m_bBankL[dwPart] << 8);
|
|
break;
|
|
case NOTE_CC_BANKSELECTH:
|
|
m_bBankH[dwPart] = bData;
|
|
break;
|
|
case NOTE_CC_BANKSELECTL:
|
|
m_bBankL[dwPart] = bData;
|
|
break;
|
|
case NOTE_CC_POLYMODE:
|
|
m_fMono[dwPart] = FALSE;
|
|
break;
|
|
case NOTE_CC_MONOMODE:
|
|
m_fMono[dwPart] = TRUE;
|
|
break;
|
|
case NOTE_CC_RPN_MSB:
|
|
m_nCurrentRPN[dwPart] = (m_nCurrentRPN[dwPart] & 0x7f) + (bData << 7);
|
|
break;
|
|
case NOTE_CC_RPN_LSB:
|
|
m_nCurrentRPN[dwPart] = (m_nCurrentRPN[dwPart] & 0x3f80) + bData;
|
|
break;
|
|
case NOTE_CC_NRPN:
|
|
m_nCurrentRPN[dwPart] = 0x3FFF;
|
|
break;
|
|
case NOTE_CC_DATAENTRYLSB:
|
|
m_nData[dwPart] &= ~0x7F;
|
|
m_nData[dwPart] |= bData;
|
|
switch (m_nCurrentRPN[dwPart])
|
|
{
|
|
case RPN_PITCHBEND: // Don't do anything, Roland ignores lsb
|
|
break;
|
|
case RPN_FINETUNE:
|
|
lTemp = m_nData[dwPart];
|
|
lTemp -= 8192;
|
|
lTemp *= 100;
|
|
lTemp /= 8192;
|
|
m_prFineTune[dwPart] = lTemp;
|
|
break;
|
|
case RPN_COARSETUNE: // Ignore lsb
|
|
break;
|
|
}
|
|
break;
|
|
case NOTE_CC_DATAENTRYMSB:
|
|
m_nData[dwPart] &= ~(0x7F << 7);
|
|
m_nData[dwPart] |= bData << 7;
|
|
switch (m_nCurrentRPN[dwPart])
|
|
{
|
|
case RPN_PITCHBEND:
|
|
m_PitchBend[dwPart].m_prRange = bData * 100;
|
|
break;
|
|
case RPN_FINETUNE:
|
|
lTemp = m_nData[dwPart];
|
|
lTemp -= 8192;
|
|
lTemp *= 100;
|
|
lTemp /= 8192;
|
|
m_prFineTune[dwPart] = lTemp;
|
|
break;
|
|
case RPN_COARSETUNE:
|
|
m_prCoarseTune[dwPart] = 100 * (bData - 64);
|
|
break;
|
|
}
|
|
break;
|
|
case NOTE_SUSTAIN: // special sustain marker
|
|
m_fSustain[dwPart] = (BOOL) bData;
|
|
if (bData == FALSE)
|
|
{
|
|
CVoice * pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
for (;pVoice != NULL;pVoice = pVoice->GetNext())
|
|
{
|
|
if (pVoice->m_fSustainOn &&
|
|
(pVoice->m_nPart == dwPart) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
pVoice->StopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NOTE_ALLOFF:
|
|
{
|
|
CVoice *pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
for (;pVoice != NULL; pVoice = pVoice->GetNext())
|
|
{
|
|
if (pVoice->m_fNoteOn && !pVoice->m_fSustainOn &&
|
|
(pVoice->m_nPart == dwPart) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
if (m_fSustain[dwPart])
|
|
{
|
|
pVoice->m_fSustainOn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pVoice->StopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NOTE_SOUNDSOFF:
|
|
{
|
|
CVoice *pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
for (;pVoice != NULL; pVoice = pVoice->GetNext())
|
|
{
|
|
if (pVoice->m_fNoteOn &&
|
|
(pVoice->m_nPart == dwPart) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
pVoice->StopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case NOTE_ASSIGNRECEIVE:
|
|
m_bPartToChannel[dwPart] = (BYTE) bData;
|
|
break;
|
|
case NOTE_MASTERVOLUME:
|
|
m_vrMasterVolume = CMIDIRecorder::VelocityToVolume(bData);
|
|
break;
|
|
}
|
|
}
|
|
else if (note.m_bVelocity == 0) // Note Off.
|
|
{
|
|
CVoice * pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
WORD nPart = note.m_bPart;
|
|
for (;pVoice != NULL;pVoice = pVoice->GetNext())
|
|
{
|
|
if (pVoice->m_fNoteOn && !pVoice->m_fSustainOn &&
|
|
(pVoice->m_nKey == (WORD) note.m_bKey) &&
|
|
(pVoice->m_nPart == nPart) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
if (m_fSustain[nPart])
|
|
{
|
|
pVoice->m_fSustainOn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pVoice->StopVoice(note.m_stTime);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else // Note On.
|
|
{
|
|
DWORD dwProgram = m_dwProgram[note.m_bPart];
|
|
if (m_bDrums[note.m_bPart])
|
|
{
|
|
dwProgram |= F_INSTRUMENT_DRUMS;
|
|
}
|
|
if (m_fMono[note.m_bPart])
|
|
{
|
|
CVoice * pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
WORD nPart = note.m_bPart;
|
|
for (;pVoice != NULL;pVoice = pVoice->GetNext())
|
|
{
|
|
if (pVoice->m_fNoteOn && (pVoice->m_nPart == nPart) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
pVoice->StopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
// While we are working with the instrument, including copying
|
|
// the data over from the region, we have to make sure it
|
|
// can not be removed from the instrument list.
|
|
EnterCriticalSection(&m_pInstruments->m_CriticalSection);
|
|
CInstrument * pInstrument =
|
|
m_pInstruments->GetInstrument(dwProgram,note.m_bKey);
|
|
if (!pInstrument)
|
|
{
|
|
if (dwProgram & F_INSTRUMENT_DRUMS)
|
|
{
|
|
dwProgram = F_INSTRUMENT_DRUMS;
|
|
pInstrument =
|
|
m_pInstruments->GetInstrument(dwProgram,note.m_bKey);
|
|
}
|
|
else if (m_fXGActive)
|
|
{
|
|
if ((dwProgram & 0x7F0000) == 0x7F0000) // Drum?
|
|
{
|
|
dwProgram &= 0x7F007F; // Enforce 0 LSB
|
|
pInstrument =
|
|
m_pInstruments->GetInstrument(dwProgram,note.m_bKey);
|
|
if (!pInstrument)
|
|
{
|
|
dwProgram = 0x7F0000;
|
|
pInstrument =
|
|
m_pInstruments->GetInstrument(dwProgram,note.m_bKey);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwProgram &= 0x7F; // Fall back to GM set.
|
|
pInstrument =
|
|
m_pInstruments->GetInstrument(dwProgram,note.m_bKey);
|
|
}
|
|
}
|
|
}
|
|
if (pInstrument != NULL)
|
|
{
|
|
CSourceRegion * pRegion =
|
|
pInstrument->ScanForRegion(note.m_bKey);
|
|
if (pRegion != NULL)
|
|
{
|
|
WORD nPart = note.m_bPart;
|
|
CVoice * pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
if (!pRegion->m_bAllowOverlap)
|
|
{
|
|
for (;pVoice != NULL; pVoice = pVoice->GetNext())
|
|
{
|
|
if ((pVoice->m_nPart == nPart) &&
|
|
(pVoice->m_nKey == note.m_bKey) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
pVoice->QuickStopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pRegion->m_bGroup != 0)
|
|
{
|
|
pVoice = m_pSynth->m_VoicesInUse.GetHead();
|
|
for (;pVoice != NULL;pVoice = pVoice->GetNext())
|
|
{
|
|
if ((pVoice->m_dwGroup == pRegion->m_bGroup) &&
|
|
(pVoice->m_nPart == nPart) &&
|
|
(pVoice->m_dwProgram == dwProgram) &&
|
|
(pVoice->m_pControl == this))
|
|
{
|
|
pVoice->QuickStopVoice(note.m_stTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
pVoice = m_pSynth->m_VoicesFree.RemoveHead();
|
|
if (pVoice == NULL)
|
|
{
|
|
pVoice = m_pSynth->m_VoicesExtra.RemoveHead();
|
|
}
|
|
|
|
if (pVoice == NULL)
|
|
{
|
|
pVoice = m_pSynth->StealVoice(m_dwPriority[nPart]);
|
|
}
|
|
|
|
if (pVoice != NULL)
|
|
{
|
|
PREL prPitch = m_prFineTune[nPart] + m_prScaleTune[nPart][note.m_bKey % 12];
|
|
if (!m_bDrums[nPart])
|
|
{
|
|
if (m_fXGActive)
|
|
{
|
|
if ((dwProgram & 0x7F0000) != 0x7F0000)
|
|
{
|
|
prPitch += m_prCoarseTune[nPart];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prPitch += m_prCoarseTune[nPart];
|
|
}
|
|
}
|
|
pVoice->m_nKey = note.m_bKey;
|
|
pVoice->m_nPart = nPart;
|
|
pVoice->m_dwProgram = dwProgram;
|
|
pVoice->m_dwPriority = m_dwPriority[nPart];
|
|
pVoice->m_pControl = this;
|
|
|
|
if (pVoice->StartVoice( m_pSynth,
|
|
pRegion, note.m_stTime,
|
|
&m_ModWheel[nPart],
|
|
&m_PitchBend[nPart],
|
|
&m_Expression[nPart],
|
|
&m_Volume[nPart],
|
|
&m_Pan[nPart],
|
|
(WORD)note.m_bKey,
|
|
(WORD)note.m_bVelocity,
|
|
m_vrMasterVolume,
|
|
prPitch))
|
|
{
|
|
pVoice->m_fInUse = TRUE;
|
|
|
|
m_pSynth->QueueVoice(pVoice);
|
|
}
|
|
else
|
|
{
|
|
m_pSynth->m_VoicesFree.AddHead(pVoice);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pSynth->m_BuildStats.dwNotesLost++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pSynth->m_BuildStats.dwNotesLost++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace(1, "No instrument/region was found for patch # %lx, note %ld\n",
|
|
dwProgram, (long) note.m_bKey);
|
|
}
|
|
LeaveCriticalSection(&m_pInstruments->m_CriticalSection);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::Flush()
|
|
*****************************************************************************
|
|
* Clears events and notes up to a given time.
|
|
*/
|
|
void CControlLogic::Flush(STIME stTime)
|
|
{
|
|
DWORD dwIndex;
|
|
::EnterCriticalSection(&m_CriticalSection);
|
|
if (!m_fEmpty)
|
|
{
|
|
m_fEmpty = TRUE;
|
|
for (dwIndex = 0;dwIndex < 16; dwIndex++)
|
|
{
|
|
m_fEmpty = m_ModWheel[dwIndex].FlushMIDI(stTime)
|
|
&& m_PitchBend[dwIndex].FlushMIDI(stTime)
|
|
&& m_Volume[dwIndex].FlushMIDI(stTime)
|
|
&& m_Expression[dwIndex].FlushMIDI(stTime)
|
|
&& m_Pan[dwIndex].FlushMIDI(stTime);
|
|
}
|
|
m_Notes.FlushMIDI(stTime);
|
|
}
|
|
::LeaveCriticalSection(&m_CriticalSection);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::RecordMIDI()
|
|
*****************************************************************************
|
|
* Record a MIDI short message into this channel group.
|
|
* This dispatches the different MIDI
|
|
* messages to the different receptor objects.
|
|
*/
|
|
BOOL CControlLogic::RecordMIDI(STIME stTime,BYTE bStatus, BYTE bData1, BYTE bData2)
|
|
{
|
|
WORD nPreChannel = bStatus & 0xF;
|
|
CNote note;
|
|
bStatus = bStatus & 0xF0;
|
|
BOOL bReturn = TRUE;
|
|
WORD nPart;
|
|
|
|
::EnterCriticalSection(&m_CriticalSection);
|
|
|
|
for (nPart = 0;nPart < 16; nPart++)
|
|
{
|
|
if (nPreChannel == m_bPartToChannel[nPart])
|
|
{
|
|
switch (bStatus)
|
|
{
|
|
case MIDI_NOTEOFF :
|
|
bData2 = 0;
|
|
case MIDI_NOTEON :
|
|
note.m_bPart = (BYTE) nPart;
|
|
note.m_bKey = bData1;
|
|
note.m_bVelocity = bData2;
|
|
bReturn = m_Notes.RecordNote(stTime,¬e);
|
|
break;
|
|
case MIDI_CCHANGE :
|
|
switch (bData1)
|
|
{
|
|
case CC_BANKSELECTH :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_BANKSELECTH, bData2);
|
|
break;
|
|
case CC_MODWHEEL :
|
|
m_fEmpty = FALSE;
|
|
bReturn = m_ModWheel[nPart].RecordMIDI(stTime,(long) bData2);
|
|
break;
|
|
case CC_VOLUME :
|
|
m_fEmpty = FALSE;
|
|
bReturn = m_Volume[nPart].RecordMIDI(stTime,(long) bData2);
|
|
break;
|
|
case CC_PAN :
|
|
m_fEmpty = FALSE;
|
|
bReturn = m_Pan[nPart].RecordMIDI(stTime,(long) bData2);
|
|
break;
|
|
case CC_EXPRESSION :
|
|
m_fEmpty = FALSE;
|
|
bReturn = m_Expression[nPart].RecordMIDI(stTime,(long)bData2);
|
|
break;
|
|
case CC_BANKSELECTL :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_BANKSELECTL, bData2);
|
|
break;
|
|
case CC_RESETALL:
|
|
m_fEmpty = FALSE;
|
|
if (bData2)
|
|
{
|
|
bReturn = bReturn && m_Volume[nPart].RecordMIDI(stTime, 100);
|
|
bReturn = bReturn && m_Pan[nPart].RecordMIDI(stTime, 64);
|
|
}
|
|
bReturn = bReturn && m_Expression[nPart].RecordMIDI(stTime, 127);
|
|
bReturn = bReturn && m_PitchBend[nPart].RecordMIDI(stTime, 0x2000);
|
|
bReturn = m_ModWheel[nPart].RecordMIDI(stTime, 0);
|
|
bData2 = 0;
|
|
// fall through into Sustain Off case....
|
|
|
|
case CC_SUSTAIN :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_SUSTAIN, bData2);
|
|
break;
|
|
case CC_ALLSOUNDSOFF:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_SOUNDSOFF, 0);
|
|
break;
|
|
case CC_ALLNOTESOFF:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_ALLOFF, 0);
|
|
break;
|
|
case CC_DATAENTRYMSB:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_DATAENTRYMSB, bData2);
|
|
break;
|
|
case CC_DATAENTRYLSB:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_DATAENTRYLSB, bData2);
|
|
break;
|
|
case CC_NRPN_LSB :
|
|
case CC_NRPN_MSB :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_NRPN, bData2);
|
|
break;
|
|
case CC_RPN_LSB:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_RPN_LSB, bData2);
|
|
break;
|
|
case CC_RPN_MSB:
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_RPN_MSB, bData2);
|
|
break;
|
|
case CC_MONOMODE :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_MONOMODE, bData2);
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_SOUNDSOFF, 0);
|
|
break;
|
|
case CC_POLYMODE :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_CC_POLYMODE, bData2);
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_SOUNDSOFF, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case MIDI_PCHANGE :
|
|
bReturn = m_Notes.RecordEvent(stTime, nPart, NOTE_PROGRAMCHANGE, bData1);
|
|
break;
|
|
|
|
case MIDI_PBEND :
|
|
m_fEmpty = FALSE;
|
|
{
|
|
WORD nBend;
|
|
nBend = bData2 << 7;
|
|
nBend |= bData1;
|
|
bReturn = m_PitchBend[nPart].RecordMIDI(stTime,(long)nBend);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
::LeaveCriticalSection(&m_CriticalSection);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::RecordSysEx()
|
|
*****************************************************************************
|
|
* Record a MIDI SysEx message into this channel group.
|
|
* This dispatches the different MIDI
|
|
* messages to the different receptor objects.
|
|
*/
|
|
HRESULT CControlLogic::RecordSysEx(DWORD dwSysExLength,BYTE *pSysExData, STIME stTime)
|
|
{
|
|
HRESULT hSuccess = S_OK;
|
|
int nPart;
|
|
int nTune;
|
|
DWORD dwAddress;
|
|
|
|
BOOL fClearAll = FALSE;
|
|
BOOL fResetPatches = FALSE;
|
|
if (dwSysExLength < 6)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
switch (pSysExData[1]) // ID number
|
|
{
|
|
case 0x7E : // General purpose ID
|
|
if (pSysExData[3] == 0x09)
|
|
{
|
|
GMReset();
|
|
fClearAll = TRUE;
|
|
fResetPatches = TRUE;
|
|
}
|
|
break;
|
|
case 0x7F : // Real time ID
|
|
if (pSysExData[3] == 0x04)
|
|
{
|
|
if (pSysExData[4] == 1) // Master Volume
|
|
{
|
|
m_Notes.RecordEvent(stTime, 0, NOTE_MASTERVOLUME, pSysExData[6]);
|
|
}
|
|
}
|
|
break;
|
|
case 0x41 : // Roland
|
|
if (dwSysExLength < 11)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
if (pSysExData[3] != 0x42) break;
|
|
if (pSysExData[4] != 0x12) break;
|
|
nPart = pSysExData[6] & 0xF;
|
|
dwAddress = (pSysExData[5] << 16) |
|
|
((pSysExData[6] & 0xF0) << 8) | pSysExData[7];
|
|
switch (dwAddress)
|
|
{
|
|
case 0x40007F : // GS Reset.
|
|
GMReset();
|
|
m_fXGActive = FALSE;
|
|
fClearAll = TRUE;
|
|
m_fGSActive = TRUE;
|
|
fResetPatches = TRUE;
|
|
break;
|
|
case 0x401002 : // Set Receive Channel.
|
|
if (m_fGSActive)
|
|
{
|
|
if (pSysExData[8])
|
|
{
|
|
m_Notes.RecordEvent(stTime, nPart, NOTE_ASSIGNRECEIVE, pSysExData[8] - 1);
|
|
}
|
|
}
|
|
break;
|
|
case 0x401015 : // Use for Rhythm.
|
|
if (m_fGSActive)
|
|
{
|
|
m_bDrums[nPart] = pSysExData[8];
|
|
fClearAll = TRUE;
|
|
}
|
|
break;
|
|
case 0x401040 : // Scale Tuning.
|
|
if (m_fGSActive)
|
|
{
|
|
for (nTune = 0;nTune < 12; nTune++)
|
|
{
|
|
if (pSysExData[9 + nTune] & 0x80) break;
|
|
m_prScaleTune[nPart][nTune] =
|
|
(PREL) pSysExData[8 + nTune] - (PREL) 64;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case 0x43 : // Yamaha
|
|
if ((pSysExData[3] == 0x4C) &&
|
|
(pSysExData[4] == 0) &&
|
|
(pSysExData[5] == 0) &&
|
|
(pSysExData[6] == 0x7E) &&
|
|
(pSysExData[7] == 0))
|
|
{ // XG System On
|
|
m_fXGActive = TRUE;
|
|
m_fGSActive = FALSE;
|
|
GMReset();
|
|
m_fXGActive = TRUE;
|
|
m_bDrums[0] = 0;
|
|
m_bBankH[0] = 127;
|
|
fClearAll = TRUE;
|
|
fResetPatches = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
if (fClearAll)
|
|
{
|
|
m_pSynth->AllNotesOff();
|
|
Flush(0);
|
|
for (nPart = 0;nPart < 16;nPart++)
|
|
{
|
|
m_Notes.RecordEvent(stTime, nPart, NOTE_SUSTAIN, 0);
|
|
m_Volume[nPart].RecordMIDI(stTime, 100);
|
|
m_Pan[nPart].RecordMIDI(stTime, 64);
|
|
m_Expression[nPart].RecordMIDI(stTime, 127);
|
|
m_PitchBend[nPart].RecordMIDI(stTime, 0x2000);
|
|
m_ModWheel[nPart].RecordMIDI(stTime, 0);
|
|
}
|
|
}
|
|
if (fResetPatches)
|
|
{
|
|
for (nPart = 0;nPart < 16;nPart++)
|
|
{
|
|
if ((nPart == 0) && (m_fXGActive))
|
|
{
|
|
m_Notes.RecordEvent(stTime-1, nPart, NOTE_CC_BANKSELECTH, 127);
|
|
}
|
|
else
|
|
{
|
|
m_Notes.RecordEvent(stTime-1, nPart, NOTE_CC_BANKSELECTH, 0);
|
|
}
|
|
m_Notes.RecordEvent(stTime-1, nPart, NOTE_CC_BANKSELECTL, 0);
|
|
m_Notes.RecordEvent(stTime, nPart, NOTE_PROGRAMCHANGE, 0);
|
|
}
|
|
}
|
|
|
|
return hSuccess;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::SetChannelPriority()
|
|
*****************************************************************************
|
|
* Set the priority for a given channel, within this channel group.
|
|
*/
|
|
HRESULT CControlLogic::SetChannelPriority(DWORD dwChannel,DWORD dwPriority)
|
|
{
|
|
DWORD dwPart;
|
|
for (dwPart = 0;dwPart < 16; dwPart++)
|
|
{
|
|
if (m_bPartToChannel[dwPart] == dwChannel)
|
|
{
|
|
m_dwPriority[dwPart] = dwPriority;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CControlLogic::GetChannelPriority()
|
|
*****************************************************************************
|
|
* Retrieve the priority for a given channel, within this channel group.
|
|
*/
|
|
HRESULT CControlLogic::GetChannelPriority(DWORD dwChannel,LPDWORD pdwPriority)
|
|
{
|
|
DWORD dwPart;
|
|
for (dwPart = 0;dwPart < 16; dwPart++)
|
|
{
|
|
if (m_bPartToChannel[dwPart] == dwChannel)
|
|
{
|
|
*pdwPriority = m_dwPriority[dwPart];
|
|
break;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|