// ****************************************************************************//// CMidiInQ.cpp//// Implementation file for the CMidiInQ class.// Use a simple fixed size queue for managing MIDI data.// Fill & drain pointers are maintained automatically whenever// an Add or Get function succeeds.//// Set editor tabs to 3 for your viewing pleasure.//// ----------------------------------------------------------------------------//// This file is part of Echo Digital Audio's generic driver library.// Copyright Echo Digital Audio Corporation (c) 1998 - 2005// All rights reserved// www.echoaudio.com//// This library is free software; you can redistribute it and/or// modify it under the terms of the GNU Lesser General Public// License as published by the Free Software Foundation; either// version 2.1 of the License, or (at your option) any later version.//// This library is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU// Lesser General Public License for more details.//// You should have received a copy of the GNU Lesser General Public// License along with this library; if not, write to the Free Software// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA//// ****************************************************************************#include "CEchoGals.h"/****************************************************************************Construction and destruction, clean up and init****************************************************************************///============================================================================//// Construction & destructor////============================================================================CMidiInQ::CMidiInQ(){m_pBuffer = NULL;m_ullLastActivityTime = 0;m_wMtcState = MIDI_IN_STATE_NORMAL;m_pEG = NULL;m_pMtcSync = NULL;} // CMidiInQ::CMidiInQ()CMidiInQ::~CMidiInQ(){Cleanup();} // CMidiInQ::~CMidiInQ()//============================================================================//// Init////============================================================================ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG ){ECHO_DEBUGPRINTF(("CMidiInQ::Init\n"));m_pEG = pEG;m_dwFill = 0;m_dwBufferSizeMask = MIDI_IN_Q_SIZE - 1; // MIDI_IN_Q_SIZE must be a power of 2return ECHOSTATUS_OK;} // Init//============================================================================//// Cleanup////============================================================================void CMidiInQ::Cleanup(){//// Free the MIDI input buffer//if (m_pBuffer){OsFreeNonPaged( m_pBuffer );m_pBuffer = NULL;}//// Free the MTC sync object if it exists//if (m_pMtcSync){delete m_pMtcSync;m_pMtcSync = NULL;}}/****************************************************************************Queue management - add and remove MIDI data****************************************************************************///============================================================================//// GetMidi - get oldest MIDI input byte from the Q////============================================================================ECHOSTATUS CMidiInQ::GetMidi(ECHOGALS_MIDI_IN_CONTEXT *pContext,DWORD &dwMidiByte,LONGLONG &llTimestamp){DWORD dwDrain;if (NULL == m_pBuffer)return ECHOSTATUS_CHANNEL_NOT_OPEN;dwDrain = pContext->dwDrain;if ( m_dwFill == dwDrain)return ECHOSTATUS_NO_DATA;dwMidiByte = m_pBuffer[ dwDrain ].dwMidi;llTimestamp = m_pBuffer[ dwDrain ].llTimestamp;//ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%lx\n", dwMidiByte) );dwDrain++;dwDrain &= m_dwBufferSizeMask;pContext->dwDrain = dwDrain;return ECHOSTATUS_OK;} // ECHOSTATUS CMidiInQ::GetMidi//============================================================================//// AddMidi - add new MIDI input byte to the Q////============================================================================ECHOSTATUS CMidiInQ::AddMidi(DWORD dwMidiByte,LONGLONG llTimestamp){DWORD dwFill;//ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%lx\n", dwMidiByte) );dwFill = m_dwFill;m_pBuffer[ dwFill ].dwMidi = dwMidiByte;m_pBuffer[ dwFill ].llTimestamp = llTimestamp;dwFill++;dwFill &= m_dwBufferSizeMask;m_dwFill = dwFill;return ECHOSTATUS_OK;} // ECHOSTATUS CMidiInQ::AddMidi//============================================================================//// Reset////============================================================================void CMidiInQ::Reset(ECHOGALS_MIDI_IN_CONTEXT *pContext){pContext->dwDrain = m_dwFill;} // void CMidiInQ::Reset()/****************************************************************************Arm and disarm****************************************************************************///============================================================================//// Arm - resets the Q and enables the Q to start receiving MIDI input data////============================================================================ECHOSTATUS CMidiInQ::Arm(ECHOGALS_MIDI_IN_CONTEXT *pContext){ECHOSTATUS Status;pContext->fOpen = FALSE;//// Return if there is no Echogals pointer//if (NULL == m_pEG){return ECHOSTATUS_CANT_OPEN;}//-------------------------------------------------------------------------//// Set up the MIDI input buffer////-------------------------------------------------------------------------if (NULL == m_pBuffer){//// Reset the buffer pointers//m_dwFill = 0;//// Allocate//Status = OsAllocateNonPaged( MIDI_IN_Q_SIZE * sizeof(MIDI_DATA),(PPVOID) &m_pBuffer);if (Status != ECHOSTATUS_OK){ECHO_DEBUGPRINTF(("CMidiInQ::Arm - Could not allocate MIDI input buffer\n"));return Status;}}Reset(pContext);m_dwNumClients++;//-------------------------------------------------------------------------//// Enable MIDI input////-------------------------------------------------------------------------m_pEG->GetDspCommObject()->SetMidiOn( TRUE );pContext->fOpen = TRUE;return ECHOSTATUS_OK;} // Arm//============================================================================//// Disarm - surprisingly, does the opposite of Arm////============================================================================ECHOSTATUS CMidiInQ::Disarm(ECHOGALS_MIDI_IN_CONTEXT *pContext){//// Did this client open the MIDI input?//if (FALSE == pContext->fOpen){ECHO_DEBUGPRINTF(("CMidiInQ::Disarm - trying to disarm with client that isn't open\n"));return ECHOSTATUS_CHANNEL_NOT_OPEN;}pContext->fOpen = FALSE;//// Last client?//if (0 == m_dwNumClients)return ECHOSTATUS_OK;m_dwNumClients--;if (0 == m_dwNumClients){//// Tell the DSP to disable MIDI input//if (m_pEG)m_pEG->GetDspCommObject()->SetMidiOn( FALSE );//// Free the MIDI input buffer//if (m_pBuffer){OsFreeNonPaged( m_pBuffer );m_pBuffer = NULL;}}return ECHOSTATUS_OK;} // Disarm//============================================================================//// ArmMtcSync - turns on MIDI time code sync////============================================================================ECHOSTATUS CMidiInQ::ArmMtcSync(){if (NULL == m_pEG)return ECHOSTATUS_DSP_DEAD;if (NULL != m_pMtcSync)return ECHOSTATUS_OK;//// Create the MTC sync object//m_pMtcSync = new CMtcSync( m_pEG );if (NULL == m_pMtcSync)return ECHOSTATUS_NO_MEM;//// Tell the DSP to enable MIDI input//m_pEG->GetDspCommObject()->SetMidiOn( TRUE );return ECHOSTATUS_OK;} // ArmMtcSync//============================================================================//// DisarmMtcSync - turn off MIDI time code sync////============================================================================ECHOSTATUS CMidiInQ::DisarmMtcSync(){if (NULL == m_pMtcSync)return ECHOSTATUS_OK;if (NULL == m_pEG)return ECHOSTATUS_DSP_DEAD;//// Tell the DSP to disable MIDI input//m_pEG->GetDspCommObject()->SetMidiOn( FALSE );//// Free m_pMtcSync//CMtcSync *pTemp; // Use temp variable to avoid killing the object// while the ISR is using itpTemp = m_pMtcSync;m_pMtcSync = NULL;delete pTemp;return ECHOSTATUS_OK;} // DisarmMtcSync/****************************************************************************Detect MIDI input activity - see if the driver has recently receivedany MIDI input****************************************************************************/BOOL CMidiInQ::IsActive(){ULONGLONG ullCurTime,ullDelta;//// See if anything has happened recently//m_pEG->m_pOsSupport->OsGetSystemTime( &ullCurTime );ullDelta = ullCurTime - m_ullLastActivityTime;if (ullDelta > MIDI_ACTIVITY_TIMEOUT_USEC)return FALSE;return TRUE;} // IsActive/****************************************************************************MIDI time code****************************************************************************///===========================================================================//// Get and set the base MTC sample rate////===========================================================================ECHOSTATUS CMidiInQ::GetMtcBaseRate(DWORD *pdwBaseRate){ECHOSTATUS Status;if (m_pMtcSync){*pdwBaseRate = m_pMtcSync->m_dwBaseSampleRate;Status = ECHOSTATUS_OK;}else{*pdwBaseRate = 0;Status = ECHOSTATUS_NO_DATA;}return Status;} // GetMtcBaseRateECHOSTATUS CMidiInQ::SetMtcBaseRate(DWORD dwBaseRate){ECHOSTATUS Status;if (m_pMtcSync){m_pMtcSync->m_dwBaseSampleRate = dwBaseRate;Status = ECHOSTATUS_OK;}else{Status = ECHOSTATUS_NO_DATA;}return Status;} // SetMtcBaseRate//===========================================================================//// Run the state machine for MIDI input data//// You need this state machine to parse the incoming// MIDI data stream. Every time the DSP sees a 0xF1 byte come in,// it adds the DSP sample position to the MIDI data stream.// The DSP sample position is represented as a 32 bit unsigned// value, with the high 16 bits first, followed by the low 16 bits.//// The following logic parses the incoming MIDI data.////===========================================================================DWORD CMidiInQ::MtcProcessData( DWORD dwMidiData ){DWORD dwRval;dwRval = 0;switch ( m_wMtcState ){case MIDI_IN_STATE_NORMAL :if ( dwMidiData == 0xF1L ){m_wMtcState = MIDI_IN_STATE_TS_HIGH;}break;case MIDI_IN_STATE_TS_HIGH :if (m_pMtcSync)m_pMtcSync->StoreTimestampHigh( dwMidiData );m_wMtcState = MIDI_IN_STATE_TS_LOW;dwRval = MIDI_IN_SKIP_DATA;break;case MIDI_IN_STATE_TS_LOW :if (m_pMtcSync)m_pMtcSync->StoreTimestampLow( dwMidiData );m_wMtcState = MIDI_IN_STATE_F1_DATA;dwRval = MIDI_IN_SKIP_DATA;break;case MIDI_IN_STATE_F1_DATA :if (m_pMtcSync)m_pMtcSync->StoreMtcData( dwMidiData );m_wMtcState = MIDI_IN_STATE_NORMAL;break;}return dwRval;} // DWORD CMidiInQ::MtcProcessData//===========================================================================//// ServiceMtcSync////===========================================================================void CMidiInQ::ServiceMtcSync(){if (m_pMtcSync)m_pMtcSync->Sync();} // ServiceMtcSync/****************************************************************************Interrupt service****************************************************************************/ECHOSTATUS CMidiInQ::ServiceIrq(){DWORD dwMidiCount;WORD wIndex;ECHOSTATUS Status;CDspCommObject *pDCO;LONGLONG llTimestamp;//// Store the time for the activity detector//m_pEG->m_pOsSupport->OsGetSystemTime( &m_ullLastActivityTime );//// Get the MIDI count//pDCO = m_pEG->GetDspCommObject();pDCO->ReadMidi( 0, dwMidiCount ); // The count is at index 0#ifdef ECHO_DEBUG//ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) );if ( dwMidiCount == 0 ){ECHO_DEBUGBREAK();}#endif//// Get the timestamp//llTimestamp = m_pEG->m_pOsSupport->GetMidiInTimestamp();//// Get the MIDI data from the comm page//wIndex = 1; // First MIDI byte is at index 1while ( dwMidiCount-- > 0 ){DWORD dwMidiByte;//// Get the MIDI byte//Status = pDCO->ReadMidi( wIndex++, dwMidiByte );if ( ECHOSTATUS_OK != Status ){ECHO_DEBUGPRINTF(("Failed on ReadMidi\n"));ECHO_DEBUGBREAK(); // should never happen...break;}//// Parse the incoming MIDI stream. The incoming MIDI data consists// of MIDI bytes and timestamps for the MIDI time code 0xF1 bytes.// MtcProcessData is a little state machine that parses the stream.//// If you get MIDI_IN_SKIP_DATA back, then this is a timestamp byte,// not a MIDI byte, so don't store it in the MIDI input buffer.//if ( MIDI_IN_SKIP_DATA == MtcProcessData( dwMidiByte ) )continue;//// Only store the MIDI data if there is at least one// client//if (0 != m_dwNumClients){//// Stash the MIDI data and check for overflow//if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( dwMidiByte, llTimestamp ) ){break;}}} // while there is more MIDI data to readreturn ECHOSTATUS_OK;} // ServiceIrq// *** CMidiInQ.cpp ***