📄 midistrm.cpp
字号:
CMidiNote *pCNote;
// Get a pointer to the stream context
pCNote = CONTAINING_RECORD(pListEntry,CMidiNote,m_Link);
// Get next list entry, since Render may cause note to go away
pListEntry = pListEntry->Flink;
PBYTE pBufferLastThis;
pBufferLastThis = pCNote->Render(pBuffer, pBufferEndEvent, pBufferLast);
if (pBufferLast < pBufferLastThis)
{
pBufferLast = pBufferLastThis;
}
}
pBuffer = pBufferEndEvent;
}
// We need to make sure we clear any unwritten section of the buffer to make sure the DMA controller doesn't stop
StreamContext::ClearBuffer(pBufferLast,pBufferEnd);
pBufferLast=pBufferEnd;
// DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::Render returning, pBufferLast=0x%x, pBufferEnd=0x%x\r\n"),pBufferLast,pBufferEnd));
return pBufferLast;
}
//------------------------------------------------------------------------------
//
// Function:
//
//
DWORD
CMidiStream::MidiMessage(UINT32 dwMessage)
{
HRESULT Result;
Result = InternalMidiMessage(dwMessage);
// If we're running, and the notelist has notes to render, make sure DMA is enabled
if ( (m_bRunning) && (m_NoteList.Flink != &m_NoteList) )
{
m_pDeviceContext->StreamReadyToRender(this);
}
return (Result==S_OK) ? MMSYSERR_NOERROR : MMSYSERR_ERROR;
}
//------------------------------------------------------------------------------
//
// Function: InternalMidiMessage
//
// Assumes lock is taken, and we're already positioned at the correct
// point in the stream
//
HRESULT
CMidiStream::InternalMidiMessage(UINT32 dwData)
{
UINT32 OpCode = dwData & 0xF0000000;
switch (OpCode)
{
case 0:
return MidiData(dwData);
case MIDI_MESSAGE_UPDATETEMPO:
m_USecPerQuarterNote = (dwData & 0xFFFFFF);
return UpdateTempo();
case MIDI_MESSAGE_FREQGENON:
case MIDI_MESSAGE_FREQGENOFF:
{
UINT32 dwNote = ((dwData) & 0xffff);
UINT32 dwVelocity = ((dwData >> 16) & 0x7f) ;
if ((OpCode==MIDI_MESSAGE_FREQGENON) && (dwVelocity>0))
{
return NoteOn(dwNote, dwVelocity, FREQCHANNEL);
}
else
{
return NoteOff(dwNote, dwVelocity, FREQCHANNEL);
}
}
}
return E_NOTIMPL;
}
//------------------------------------------------------------------------------
//
// Function: MidiData
//
//
HRESULT
CMidiStream::MidiData(UINT32 dwData)
{
HRESULT Result=E_NOTIMPL;
UINT32 dwChannel;
UINT32 dwNote;
UINT32 dwVelocity;
if (dwData & 0x80)
{
m_RunningStatus = dwData&0xFF; // status byte...
}
else
{
dwData = (dwData<<8) | m_RunningStatus;
}
dwChannel = (dwData & 0x0f) ;
dwNote = ((dwData >> 8) & 0x7f) ;
dwVelocity = ((dwData >> 16) & 0x7f) ;
switch (dwData & 0xf0)
{
case 0x90: // Note on
if (dwVelocity!=0)
{
Result = NoteOn(dwNote, dwVelocity, dwChannel);
break;
}
// If dwVelocity is 0, this is really a note off message, so fall through
case 0x80: // Note off
Result = NoteOff(dwNote, dwVelocity, dwChannel);
break;
case 0xB0: // Control change
{
switch (dwNote)
{
case 123: // turns all notes off
{
Result = AllNotesOff(0);
break;
}
}
break;
}
}
return Result;
}
//------------------------------------------------------------------------------
//
// Function: FindNote
//
//
CMidiNote *
CMidiStream::FindNote(UINT32 dwNote, UINT32 dwChannel)
{
PLIST_ENTRY pListEntry;
CMidiNote *pCNote;
pListEntry = m_NoteList.Flink;
while (pListEntry != &m_NoteList)
{
// Get a pointer to the stream context
pCNote = CONTAINING_RECORD(pListEntry,CMidiNote,m_Link);
if (pCNote->NoteVal()==dwNote && pCNote->NoteChannel()==dwChannel)
{
return pCNote;
}
pListEntry = pListEntry->Flink;
}
return NULL;
}
//------------------------------------------------------------------------------
//
// Function: NoteOn
//
// Assumes lock is taken, and we're already positioned at the correct
// point in the stream
//
HRESULT
CMidiStream::NoteOn(UINT32 dwNote, UINT32 dwVelocity, UINT32 dwChannel)
{
CMidiNote *pCNote=NULL;
PLIST_ENTRY pListEntry;
// First try to find the same note already being played
pCNote = FindNote(dwNote, dwChannel);
if (pCNote)
{
// If so, just set its velocity to the new velocity
// This allows us to change volume while a note is being
// played without any chance of glitching
pCNote->SetVelocity(dwVelocity);
}
else
{
// Try to allocate a note from the free list
pListEntry = m_FreeList.Flink;
if (pListEntry != &m_FreeList)
{
pCNote = CONTAINING_RECORD(pListEntry,CMidiNote,m_Link);
// If we got a note from the free list, do an AddRef on this stream context
AddRef();
}
else
{
// Note: if we every support multiple instruments, here we should try to steal the oldest
// note with the same channel before just trying to steal the oldest note.
// Steal the oldest note (which is the note at the head of the note list)
// Note: This should _never_ fail, since there must be a note on one of the lists!
pListEntry = m_NoteList.Flink;
pCNote = CONTAINING_RECORD(pListEntry,CMidiNote,m_Link);
}
pCNote->NoteOn(this,dwNote,dwVelocity,dwChannel);
}
// Move the note from whichever list it was on to the note list at the end.
// This ensures that if we reused an existing note, its age gets reset.
NoteMoveToNoteList(pCNote);
return S_OK;
}
//------------------------------------------------------------------------------
//
// Function: NoteOff
//
// Assumes lock is taken, and we're already positioned at the correct
// point in the stream
//
HRESULT
CMidiStream::NoteOff(UINT32 dwNote, UINT32 dwVelocity, UINT32 dwChannel)
{
CMidiNote *pCNote = FindNote(dwNote, dwChannel);
if (pCNote)
{
pCNote->NoteOff(dwVelocity);
}
return S_OK;
}
//------------------------------------------------------------------------------
//
// Function: AllNotesOff
//
//
HRESULT
CMidiStream::AllNotesOff(UINT32 dwVelocity)
{
PLIST_ENTRY pListEntry;
CMidiNote *pCNote;
pListEntry = m_NoteList.Flink;
while (pListEntry != &m_NoteList)
{
// Get the note
pCNote = CONTAINING_RECORD(pListEntry,CMidiNote,m_Link);
// Get the next link, since NoteOff may remove the note from the queue depeding on the implementation
pListEntry = pListEntry->Flink;
// Turn the note off
pCNote->NoteOff(dwVelocity);
}
return S_OK;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -