📄 midistrm.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
// Portions Copyright (c) Texas Instruments. All rights reserved.
//
//------------------------------------------------------------------------------
//
#include "wavemain.h"
//------------------------------------------------------------------------------
//
// Function: GainChange()
//
//
void
CMidiStream::GainChange()
{
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);
pCNote->GainChange();
pListEntry = pListEntry->Flink;
}
}
//------------------------------------------------------------------------------
//
// Function: MapNoteGain
//
//
DWORD
CMidiStream::MapNoteGain(DWORD NoteGain)
{
DWORD TotalGain = NoteGain & 0xFFFF;
DWORD StreamGain = m_dwGain & 0xFFFF;
TotalGain *= StreamGain; // Calc. aggregate gain
TotalGain += 0xFFFF; // Force to round up
TotalGain >>= 16;
return MapGain(TotalGain);
}
//------------------------------------------------------------------------------
//
// Function: Open
//
//
HRESULT CMidiStream::Open(DeviceContext *pDeviceContext, LPWAVEOPENDESC lpWOD, DWORD dwFlags)
{
HRESULT Result = E_FAIL;
LPWAVEFORMAT_MIDI pwfxmidi = (LPWAVEFORMAT_MIDI) lpWOD->lpFormat;
if (pwfxmidi->wfx.cbSize!=WAVEFORMAT_MIDI_EXTRASIZE)
{
return E_FAIL;
}
m_USecPerQuarterNote = pwfxmidi->USecPerQuarterNote;
m_TicksPerQuarterNote = pwfxmidi->TicksPerQuarterNote;
UpdateTempo();
m_DeltaSampleCount=0;
// Add all notes to free list
InitializeListHead(&m_NoteList);
InitializeListHead(&m_FreeList);
for (int i=0;i<NUMNOTES;i++)
{
InsertTailList(&m_FreeList,&m_MidiNote[i].m_Link);
}
Result = StreamContext::Open(pDeviceContext, lpWOD, dwFlags);
if (Result==MMSYSERR_NOERROR)
{
// Note: Output streams should be initialized in the run state.
Run();
}
return Result;
}
//------------------------------------------------------------------------------
//
// Function: Reset
//
//
DWORD
CMidiStream::Reset()
{
DWORD dwResult = StreamContext::Reset();
if (dwResult==MMSYSERR_NOERROR)
{
AllNotesOff(0);
// Note: Output streams should be reset to the run state.
Run();
}
return dwResult;
}
//------------------------------------------------------------------------------
//
// Function: Close
//
DWORD
CMidiStream::Close()
{
DWORD dwResult = StreamContext::Close();
if (dwResult==MMSYSERR_NOERROR)
{
AllNotesOff(0);
}
return dwResult;
}
//------------------------------------------------------------------------------
//
// Function: UpdateTempo
//
//
HRESULT
CMidiStream::UpdateTempo()
{
if (m_USecPerQuarterNote==0)
{
m_USecPerQuarterNote = 500000; // If not specified, assume 500000usec = 1/2 sec per quarter note
}
if (m_TicksPerQuarterNote==0)
{
m_TicksPerQuarterNote = 96; // If not specified, assume 96 ticks/quarter note
}
UINT64 Num = SAMPLERATE;
Num *= m_USecPerQuarterNote;
UINT64 Den = 1000000;
Den *= m_TicksPerQuarterNote;
UINT64 SamplesPerTick = Num/Den;
m_SamplesPerTick = (UINT32)SamplesPerTick;
return S_OK;
}
//------------------------------------------------------------------------------
//
// Function: ProcessMidiStream
//
// Return the delta # of samples until the next midi event
// or 0 if no midi events are left in the queue
//
UINT32
CMidiStream::ProcessMidiStream()
{
WAVEFORMAT_MIDI_MESSAGE *pMsg;
WAVEFORMAT_MIDI_MESSAGE *pMsgEnd;
UINT32 ThisMidiEventDelta;
// Process all midi messages up to and including the current sample
pMsg = (WAVEFORMAT_MIDI_MESSAGE *)m_lpCurrData;
pMsgEnd = (WAVEFORMAT_MIDI_MESSAGE *)m_lpCurrDataEnd;
for (;;)
{
if (pMsg>=pMsgEnd)
{
pMsg = (WAVEFORMAT_MIDI_MESSAGE *)GetNextBuffer();
if (!pMsg)
{
// DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::ProcessMidiStream no more events\r\n")));
return 0;
}
pMsgEnd = (WAVEFORMAT_MIDI_MESSAGE *)m_lpCurrDataEnd;
}
_try
{
ThisMidiEventDelta = DeltaTicksToSamples(pMsg->DeltaTicks);
if (ThisMidiEventDelta > m_DeltaSampleCount)
{
m_lpCurrData = (PBYTE)pMsg;
INT32 Delta = ThisMidiEventDelta-m_DeltaSampleCount;
// DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::ProcessMidiStream next event @delta %d\r\n"),Delta));
return Delta;
}
// DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::ProcessMidiStream sending midi message 0x%x\r\n"),pMsg->MidiMsg));
InternalMidiMessage(pMsg->MidiMsg);
m_DeltaSampleCount=0;
pMsg++;
}
_except (EXCEPTION_EXECUTE_HANDLER)
{
DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::EXCEPTION IN IST for stream 0x%x, buffer 0x%x!!!!\r\n"), this, m_lpCurrData));
pMsg = pMsgEnd; // Pretend we finished reading the application buffer
}
}
return 0;
}
//------------------------------------------------------------------------------
//
// Function:
//
//
PBYTE
CMidiStream::Render(PBYTE pBuffer, PBYTE pBufferEnd, PBYTE pBufferLast)
{
// DEBUGMSG(ZONE_MIDI, (TEXT("Entering CMidiStream::Render, pBuffer=0x%x, current delta = %d\r\n"), pBuffer, m_DeltaSampleCount));
// If we're not running, or we don't have any buffers queued and the note list is empty,
// just return
if ( (!m_bRunning) || (!StillPlaying() && IsListEmpty(&m_NoteList)) )
{
// DEBUGMSG(ZONE_MIDI, (TEXT("CMidiStream::Render nothing to do\r\n")));
return pBuffer;
}
while (pBuffer<pBufferEnd)
{
// Process pending midi messages and get relative sample # of next midi event
UINT32 NextMidiEvent;
NextMidiEvent = ProcessMidiStream();
PBYTE pBufferEndEvent; // Where to stop on this pass
// If NextMidiEvent returns 0, it means there are no more midi messages left in the queue.
if (NextMidiEvent==0)
{
// Just process the rest of this buffer
pBufferEndEvent=pBufferEnd;
}
// NextMidiEvent is non-zero, and represents the delta sample value of the next midi event
else
{
// Convert to be a pointer in this buffer
pBufferEndEvent = pBuffer + (NextMidiEvent * (sizeof(HWSAMPLE) * OUTCHANNELS));
// If the next event occurs after this buffer, just finish processing this buffer
if (pBufferEndEvent>pBufferEnd)
{
pBufferEndEvent=pBufferEnd;
}
}
// Update the delta for the samples we're about to process
m_DeltaSampleCount += ((pBufferEndEvent-pBuffer)/(sizeof(HWSAMPLE) * OUTCHANNELS));
// Process existing notes
PLIST_ENTRY pListEntry;
pListEntry = m_NoteList.Flink;
while (pListEntry != &m_NoteList)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -