// **************************************************************************** // // 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. // // Copyright Echo Digital Audio Corporation (c) 1998 - 2002 // All rights reserved // www.echoaudio.com // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal with the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // - Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimers. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimers in the // documentation and/or other materials provided with the distribution. // // - Neither the name of Echo Digital Audio, nor the names of its // contributors may be used to endorse or promote products derived from // this Software without specific prior written permission. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE // SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. // // **************************************************************************** #include "CEchoGals.h" /**************************************************************************** Construction and destruction, clean up and init ****************************************************************************/ //============================================================================ // // Construction & destructor // //============================================================================ CMidiInQ::CMidiInQ() { Reset(); m_pBuffer = NULL; m_ullLastActivityTime = 0; m_wMtcState = MTC_STATE_NORMAL; m_pEG = NULL; m_fArmed = FALSE; } // CMidiInQ::CMidiInQ() CMidiInQ::~CMidiInQ() { Cleanup(); } // CMidiInQ::~CMidiInQ() //============================================================================ // // Init // //============================================================================ ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG ) { ECHOSTATUS Status; ECHO_DEBUGPRINTF(("CMidiInQ::Init\n")); m_pEG = pEG; Status = OsAllocateNonPaged( sizeof(MIDI_DATA) * ECHO_MIDI_QUEUE_SZ, (PPVOID) &m_pBuffer); if (ECHOSTATUS_OK == Status) Reset(); return Status; } // Init //============================================================================ // // Cleanup // //============================================================================ void CMidiInQ::Cleanup() { // // Free the MIDI input buffer // if (NULL != m_pBuffer) { if (ECHOSTATUS_OK != OsFreeNonPaged( m_pBuffer )) { ECHO_DEBUGPRINTF(("CMidiInQ::Cleanup - Failed to free MIDI input bufer\n")); } } m_pBuffer = NULL; } /**************************************************************************** Queue management - add and remove MIDI data ****************************************************************************/ //============================================================================ // // GetMidi - get oldest MIDI input byte from the Q // //============================================================================ ECHOSTATUS CMidiInQ::GetMidi ( MIDI_DATA &Midi ) { if (NULL == m_pBuffer) return ECHOSTATUS_CHANNEL_NOT_OPEN; if ( m_pFill == m_pDrain ) return( ECHOSTATUS_NO_DATA ); Midi = *m_pDrain; ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%x\n", Midi) ); m_pDrain = ComputeNext( m_pDrain ); return ECHOSTATUS_OK; } // ECHOSTATUS CMidiInQ::GetMidi //============================================================================ // // AddMidi - add new MIDI input byte to the Q // //============================================================================ ECHOSTATUS CMidiInQ::AddMidi ( MIDI_DATA Midi ) { if ( ComputeNext( m_pFill ) == m_pDrain ) { ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi buffer overflow\n") ); ECHO_DEBUGBREAK(); return ECHOSTATUS_BUFFER_OVERFLOW; } ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%x\n", Midi) ); *m_pFill = Midi; m_pFill = ComputeNext( m_pFill ); return ECHOSTATUS_OK; } // ECHOSTATUS CMidiInQ::AddMidi //============================================================================ // // ComputeNext - protected routine used to wrap the read and write pointers // around the circular buffer // //============================================================================ PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur ) { if ( ++pCur >= m_pEnd ) pCur = m_pBuffer; return pCur; } // PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur ) //============================================================================ // // Reset // //============================================================================ void CMidiInQ::Reset() { // // Midi buffer pointer initialization // m_pEnd = m_pBuffer + ECHO_MIDI_QUEUE_SZ; m_pFill = m_pBuffer; m_pDrain = m_pBuffer; } // void CMidiInQ::Reset() /**************************************************************************** Arm and disarm ****************************************************************************/ //============================================================================ // // Arm - resets the Q and enables the Q to start receiving MIDI input data // //============================================================================ ECHOSTATUS CMidiInQ::Arm() { // // Return if the MIDI input buffer is not allocated // if ( (NULL == m_pBuffer) || (NULL == m_pEG) ) return ECHOSTATUS_CANT_OPEN; // // Reset the buffer pointers // Reset(); // // Tell the DSP to enable MIDI input // m_pEG->GetDspCommObject()->SetMidiOn( TRUE ); // // Set the flag // m_fArmed = TRUE; return ECHOSTATUS_OK; } // Arm //============================================================================ // // Disarm - surprisingly, does the opposite of Arm // //============================================================================ void CMidiInQ::Disarm() { // // Tell the DSP to disable MIDI input // if (m_pEG) m_pEG->GetDspCommObject()->SetMidiOn( FALSE ); // // Clear the flag // m_fArmed = FALSE; } // Disarm /**************************************************************************** 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 ****************************************************************************/ //=========================================================================== // // Run the state machine for MIDI input data // // MIDI time code sync isn't supported by this code right now, // but you still 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. // // Since these aren't real MIDI bytes, the following logic is // needed to skip them. // //=========================================================================== DWORD CMidiInQ::MtcProcessData( DWORD dwMidiData ) { switch ( m_wMtcState ) { case MTC_STATE_NORMAL : if ( dwMidiData == 0xF1L ) m_wMtcState = MTC_STATE_TS_HIGH; break; case MTC_STATE_TS_HIGH : m_wMtcState = MTC_STATE_TS_LOW; return MTC_SKIP_DATA; break; case MTC_STATE_TS_LOW : m_wMtcState = MTC_STATE_F1_DATA; return MTC_SKIP_DATA; break; case MTC_STATE_F1_DATA : m_wMtcState = MTC_STATE_NORMAL; break; } return 0; } // DWORD CMidiInQ::MtcProcessData /**************************************************************************** Interrupt service ****************************************************************************/ ECHOSTATUS CMidiInQ::ServiceIrq() { DWORD dwMidiCount; WORD wIndex; ECHOSTATUS Status; CDspCommObject *pDCO; // // 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 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 strem. // // If you get MTC_SKIP_DATA back, then this is a timestamp byte, // not a MIDI byte, so don't store it in the MIDI input buffer. // if ( MTC_SKIP_DATA == MtcProcessData( dwMidiByte ) ) continue; // // Only store the MIDI data if MIDI input is armed // if (m_fArmed) { // // Stash the MIDI and timestamp data and check for overflow // if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( (MIDI_DATA) dwMidiByte ) ) { break; } } } // while there is more MIDI data to read return ECHOSTATUS_OK; } // ServiceIrq // *** CMidiInQ.cpp ***