// **************************************************************************** // // 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 2 return 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 it pTemp = m_pMtcSync; m_pMtcSync = NULL; delete pTemp; return ECHOSTATUS_OK; } // DisarmMtcSync /**************************************************************************** Detect MIDI input activity - see if the driver has recently received any 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; } // GetMtcBaseRate ECHOSTATUS 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 1 while ( 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 read return ECHOSTATUS_OK; } // ServiceIrq // *** CMidiInQ.cpp ***