1. 枚举所有MIDI In设备
UINT nInCount = midiInGetNumDevs();
for(int i = 0; i < (int)nInCount; i++)
{
MIDIINCAPS midiInCaps = { 0 };
midiInGetDevCaps(i,&midiInCaps,sizeof(MIDIINCAPS));
int nIndex = m_midiInCombo.InsertString(i,midiInCaps.szPname);
if(i == 0 )
m_midiInCombo.SetCurSel(0);
}
2. 打开设备
MMRESULT mmResult = midiInOpen(&m_hMidiIn,nID,(DWORD_PTR)IDPMidiInProc,(DWORD_PTR)m_hWnd,CALLBACK_FUNCTION);
3. 准备
int nSize = sizeof(MIDIHDR);
memset(&m_midiInHdr,0,nSize);
m_midiInHdr.dwBytesRecorded = m_midiInHdr.dwBufferLength = 20;
m_midiInHdr.lpData = new CHAR[20];
midiInPrepareHeader( m_hMidiIn, &m_midiInHdr, nSize);
mmResult = midiInAddBuffer(m_hMidiIn,&m_midiInHdr,sizeof(MIDIHDR));
if(mmResult != 0)
{
TCHAR szError[MAX_PATH] = { 0 };
midiInGetErrorText( mmResult, szError, MAX_PATH);
TRACE(_T("Error:%s"), szError);
}
//midiInUnprepareHeader(m_hMidiIn,&m_midiInHdr,sizeof(MIDIHDR));
midiInStart(m_hMidiIn);
4. 回调函数
void CALLBACK IDPMidiInProc(HMIDIIN hMidiIn, UINT wMsg,DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
TRACE(_T("Msg: %d Data: 0x%08x, %d\n"),wMsg,dwParam1,dwParam2);
HWND hWnd = (HWND)dwInstance;
switch(wMsg)
{
case MIM_OPEN:
break;
case MIM_CLOSE:
break;
case MIM_DATA: // Quarter Time code
{
PostMessage(hWnd,WM_CHANGE_MIDI_MTC,dwParam1,0);
}
break;
case MIM_LONGDATA: // Full Time Code
{
// dwParam1 is the lpMidiHdr
// dwParam2 is the event time in ms since the start of midi in
LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
// sysex: (byte*)hdr->lpData, (int)hdr->dwBytesRecorded
PostMessage(hWnd,WM_CHANGE_MIDI_MTC,dwParam1,1); // Full TimeCode
MMRESULT res = ::midiInAddBuffer(hMidiIn, hdr, sizeof(MIDIHDR));
if(res != 0)
{
TCHAR szError[MAX_PATH] = { 0 };
midiInGetErrorText( res, szError, MAX_PATH);
TRACE(_T("Error:%s"), szError);
}
int a = 0;
}
break;
case MIM_ERROR:
break;
case MIM_LONGERROR:
break;
case MIM_MOREDATA:
break;
}
}
5. 处理消息
LRESULT CMIDITestDlg::OnMidiInChange(WPARAM wParam, LPARAM lParam)
{
DWORD dwMidiMessage = wParam;
DWORD dwCmd = lParam;
if(dwCmd == 0)
{
BYTE bState = BYTE(dwMidiMessage);
if(bState == 0xf1)
{
BYTE bData1 = (dwMidiMessage & 0x0000ff00) >> 8;
BYTE bPos = (bData1 & 0xf0);
BYTE bNum = (bData1 & 0x0f);
DWORD dwNum = bNum;
switch(bPos)
{
case TC_FRAME_LS:
m_dwMIDIIn = (m_dwMIDIIn & 0xfffffff0) | dwNum;
break;
case TC_FRAME_MS:
m_dwMIDIIn = (m_dwMIDIIn & 0xffffff0f) | (dwNum << 4);
break;
case TC_SECOND_LS:
m_dwMIDIIn = (m_dwMIDIIn & 0xfffff0ff) | (dwNum << 8);
break;
case TC_SECOND_MS:
m_dwMIDIIn = (m_dwMIDIIn & 0xffff0fff) | (dwNum << 12);
break;
case TC_MINUTE_LS:
m_dwMIDIIn = (m_dwMIDIIn & 0xfff0ffff) | (dwNum << 16);
break;
case TC_MINUTE_MS:
m_dwMIDIIn = (m_dwMIDIIn & 0xff0fffff) | (dwNum << 20);
break;
case TC_HOUR_LS:
m_dwMIDIIn = (m_dwMIDIIn & 0xf0ffffff) | (dwNum << 24);
break;
case TC_HOUR_MS:
{
BYTE bType = (bNum & 0x06 );
if(bType == FRAME24)
m_strType = _T("24fps");
else if(bType == FRAME25)
m_strType = _T("25fps");
else if(bType == FRAME30D)
m_strType = _T("29.97fps");
else if(bType == FRAME30)
m_strType = _T("30fps");
bNum = bNum & 0x01;
dwNum = bNum;
m_dwMIDIIn = (m_dwMIDIIn & 0x0fffffff) | (dwNum << 28);
m_strTimeCodeIn.Format(_T("%02d:%02d:%02d:%02d"),
((m_dwMIDIIn &0xff000000)>> 24),((m_dwMIDIIn & 0x00ff0000) >> 16),
((m_dwMIDIIn & 0x0000ff00) >> 8),((m_dwMIDIIn & 0x000000ff) >> 0));
GetDlgItem(IDC_MIDI_MTC)->SetWindowText(m_strTimeCodeIn);
GetDlgItem(IDC_MIDI_MTC2)->SetWindowText(m_strType);
}
break;
}
}
}
else if(dwCmd == 1)
{
LPMIDIHDR hdr = (LPMIDIHDR)wParam;
UINT nHour = 0, nMinute = 0, nSecond = 0, nFrame = 0, nType = 0;
if(hdr->dwBytesRecorded >= 10 && CMIDICtrl::ParseCmdTimeCode((BYTE*)hdr->lpData, hdr->dwBytesRecorded, nHour, nMinute, nSecond, nFrame, nType))
{
int a = 0;
m_dwMIDIIn = (nHour << 24) | (nMinute << 16) | (nSecond << 8) | (nFrame << 0);
if(nType == FRAME24)
m_strType = _T("24fps");
else if(nType == FRAME25)
m_strType = _T("25fps");
else if(nType == FRAME30D)
m_strType = _T("29.97fps");
else if(nType == FRAME30)
m_strType = _T("30fps");
m_strTimeCodeIn.Format(_T("%02d:%02d:%02d:%02d"),
((m_dwMIDIIn &0xff000000)>> 24),((m_dwMIDIIn & 0x00ff0000) >> 16),
((m_dwMIDIIn & 0x0000ff00) >> 8),((m_dwMIDIIn & 0x000000ff) >> 0));
GetDlgItem(IDC_MIDI_MTC)->SetWindowText(m_strTimeCodeIn);
GetDlgItem(IDC_MIDI_MTC2)->SetWindowText(m_strType);
}
}
return 1;
}
#define SOX 0xF0 // Command Head Flag
#define EOX 0xF7 // Command End Flag
#define SUBID1_NON_RT 0x7E // Non-Realtime SUBID1
#define SUBID1_RT 0x7F // RealTime SUBID1
// Frame Format for Timecode hr mn sc fr ff
#define FRAME24 0x00
#define FRAME25 0x20
#define FRAME30D 0x40
#define FRAME30 0x60
BOOL ParseCmdTimeCode(BYTE * pData, UINT nSize, UINT &nHour, UINT &nMinute, UINT &nSecond, UINT &nFrame, UINT &nType)
{
// SOX = 0xF0
// EOX = 0xF7
if(pData[0] == SOX && pData[1] == SUBID1_RT && pData[3] == 0x01 && pData[4] == 0x01 && pData[9] == EOX)
{
nType = (pData[5] & 0x60);
nHour = pData[5] & 0x1f;
nMinute = pData[6];
nSecond = pData[7];
nFrame = pData[8];
return TRUE;
}
return FALSE;
}