1 // ****************************************************************************
5 // Implementation file for the CMidiInQ class.
6 // Use a simple fixed size queue for managing MIDI data.
7 // Fill & drain pointers are maintained automatically whenever
8 // an Add or Get function succeeds.
10 // Set editor tabs to 3 for your viewing pleasure.
12 // ----------------------------------------------------------------------------
14 // This file is part of Echo Digital Audio's generic driver library.
15 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
16 // All rights reserved
19 // This library is free software; you can redistribute it and/or
20 // modify it under the terms of the GNU Lesser General Public
21 // License as published by the Free Software Foundation; either
22 // version 2.1 of the License, or (at your option) any later version.
24 // This library is distributed in the hope that it will be useful,
25 // but WITHOUT ANY WARRANTY; without even the implied warranty of
26 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 // Lesser General Public License for more details.
29 // You should have received a copy of the GNU Lesser General Public
30 // License along with this library; if not, write to the Free Software
31 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 // ****************************************************************************
35 #include "CEchoGals.h"
38 /****************************************************************************
40 Construction and destruction, clean up and init
42 ****************************************************************************/
44 //============================================================================
46 // Construction & destructor
48 //============================================================================
54 m_ullLastActivityTime
= 0;
56 m_wMtcState
= MIDI_IN_STATE_NORMAL
;
61 } // CMidiInQ::CMidiInQ()
69 } // CMidiInQ::~CMidiInQ()
72 //============================================================================
76 //============================================================================
78 ECHOSTATUS
CMidiInQ::Init( CEchoGals
*pEG
)
80 ECHO_DEBUGPRINTF(("CMidiInQ::Init\n"));
85 m_dwBufferSizeMask
= MIDI_IN_Q_SIZE
- 1; // MIDI_IN_Q_SIZE must be a power of 2
92 //============================================================================
96 //============================================================================
98 void CMidiInQ::Cleanup()
101 // Free the MIDI input buffer
105 OsFreeNonPaged( m_pBuffer
);
110 // Free the MTC sync object if it exists
122 /****************************************************************************
124 Queue management - add and remove MIDI data
126 ****************************************************************************/
128 //============================================================================
130 // GetMidi - get oldest MIDI input byte from the Q
132 //============================================================================
134 ECHOSTATUS
CMidiInQ::GetMidi
136 ECHOGALS_MIDI_IN_CONTEXT
*pContext
,
138 LONGLONG
&llTimestamp
143 if (NULL
== m_pBuffer
)
144 return ECHOSTATUS_CHANNEL_NOT_OPEN
;
146 dwDrain
= pContext
->dwDrain
;
147 if ( m_dwFill
== dwDrain
)
148 return ECHOSTATUS_NO_DATA
;
150 dwMidiByte
= m_pBuffer
[ dwDrain
].dwMidi
;
151 llTimestamp
= m_pBuffer
[ dwDrain
].llTimestamp
;
153 //ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%lx\n", dwMidiByte) );
156 dwDrain
&= m_dwBufferSizeMask
;
157 pContext
->dwDrain
= dwDrain
;
159 return ECHOSTATUS_OK
;
161 } // ECHOSTATUS CMidiInQ::GetMidi
164 //============================================================================
166 // AddMidi - add new MIDI input byte to the Q
168 //============================================================================
170 ECHOSTATUS
CMidiInQ::AddMidi
178 //ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%lx\n", dwMidiByte) );
181 m_pBuffer
[ dwFill
].dwMidi
= dwMidiByte
;
182 m_pBuffer
[ dwFill
].llTimestamp
= llTimestamp
;
185 dwFill
&= m_dwBufferSizeMask
;
188 return ECHOSTATUS_OK
;
190 } // ECHOSTATUS CMidiInQ::AddMidi
193 //============================================================================
197 //============================================================================
199 void CMidiInQ::Reset(ECHOGALS_MIDI_IN_CONTEXT
*pContext
)
201 pContext
->dwDrain
= m_dwFill
;
203 } // void CMidiInQ::Reset()
208 /****************************************************************************
212 ****************************************************************************/
215 //============================================================================
217 // Arm - resets the Q and enables the Q to start receiving MIDI input data
219 //============================================================================
221 ECHOSTATUS
CMidiInQ::Arm(ECHOGALS_MIDI_IN_CONTEXT
*pContext
)
225 pContext
->fOpen
= FALSE
;
228 // Return if there is no Echogals pointer
232 return ECHOSTATUS_CANT_OPEN
;
235 //-------------------------------------------------------------------------
237 // Set up the MIDI input buffer
239 //-------------------------------------------------------------------------
241 if (NULL
== m_pBuffer
)
244 // Reset the buffer pointers
251 Status
= OsAllocateNonPaged( MIDI_IN_Q_SIZE
* sizeof(MIDI_DATA
),
252 (PPVOID
) &m_pBuffer
);
253 if (Status
!= ECHOSTATUS_OK
)
255 ECHO_DEBUGPRINTF(("CMidiInQ::Arm - Could not allocate MIDI input buffer\n"));
263 //-------------------------------------------------------------------------
267 //-------------------------------------------------------------------------
269 m_pEG
->GetDspCommObject()->SetMidiOn( TRUE
);
271 pContext
->fOpen
= TRUE
;
272 return ECHOSTATUS_OK
;
277 //============================================================================
279 // Disarm - surprisingly, does the opposite of Arm
281 //============================================================================
283 ECHOSTATUS
CMidiInQ::Disarm(ECHOGALS_MIDI_IN_CONTEXT
*pContext
)
286 // Did this client open the MIDI input?
288 if (FALSE
== pContext
->fOpen
)
290 ECHO_DEBUGPRINTF(("CMidiInQ::Disarm - trying to disarm with client that isn't open\n"));
291 return ECHOSTATUS_CHANNEL_NOT_OPEN
;
294 pContext
->fOpen
= FALSE
;
299 if (0 == m_dwNumClients
)
300 return ECHOSTATUS_OK
;
304 if (0 == m_dwNumClients
)
307 // Tell the DSP to disable MIDI input
310 m_pEG
->GetDspCommObject()->SetMidiOn( FALSE
);
313 // Free the MIDI input buffer
317 OsFreeNonPaged( m_pBuffer
);
322 return ECHOSTATUS_OK
;
327 //============================================================================
329 // ArmMtcSync - turns on MIDI time code sync
331 //============================================================================
333 ECHOSTATUS
CMidiInQ::ArmMtcSync()
336 return ECHOSTATUS_DSP_DEAD
;
338 if (NULL
!= m_pMtcSync
)
339 return ECHOSTATUS_OK
;
342 // Create the MTC sync object
344 m_pMtcSync
= new CMtcSync( m_pEG
);
345 if (NULL
== m_pMtcSync
)
346 return ECHOSTATUS_NO_MEM
;
349 // Tell the DSP to enable MIDI input
351 m_pEG
->GetDspCommObject()->SetMidiOn( TRUE
);
353 return ECHOSTATUS_OK
;
358 //============================================================================
360 // DisarmMtcSync - turn off MIDI time code sync
362 //============================================================================
364 ECHOSTATUS
CMidiInQ::DisarmMtcSync()
366 if (NULL
== m_pMtcSync
)
367 return ECHOSTATUS_OK
;
370 return ECHOSTATUS_DSP_DEAD
;
373 // Tell the DSP to disable MIDI input
375 m_pEG
->GetDspCommObject()->SetMidiOn( FALSE
);
380 CMtcSync
*pTemp
; // Use temp variable to avoid killing the object
381 // while the ISR is using it
387 return ECHOSTATUS_OK
;
394 /****************************************************************************
396 Detect MIDI input activity - see if the driver has recently received
399 ****************************************************************************/
401 BOOL
CMidiInQ::IsActive()
403 ULONGLONG ullCurTime
,ullDelta
;
406 // See if anything has happened recently
408 m_pEG
->m_pOsSupport
->OsGetSystemTime( &ullCurTime
);
409 ullDelta
= ullCurTime
- m_ullLastActivityTime
;
411 if (ullDelta
> MIDI_ACTIVITY_TIMEOUT_USEC
)
421 /****************************************************************************
425 ****************************************************************************/
427 //===========================================================================
429 // Get and set the base MTC sample rate
431 //===========================================================================
434 ECHOSTATUS
CMidiInQ::GetMtcBaseRate(DWORD
*pdwBaseRate
)
440 *pdwBaseRate
= m_pMtcSync
->m_dwBaseSampleRate
;
441 Status
= ECHOSTATUS_OK
;
446 Status
= ECHOSTATUS_NO_DATA
;
454 ECHOSTATUS
CMidiInQ::SetMtcBaseRate(DWORD dwBaseRate
)
460 m_pMtcSync
->m_dwBaseSampleRate
= dwBaseRate
;
461 Status
= ECHOSTATUS_OK
;
465 Status
= ECHOSTATUS_NO_DATA
;
473 //===========================================================================
475 // Run the state machine for MIDI input data
477 // You need this state machine to parse the incoming
478 // MIDI data stream. Every time the DSP sees a 0xF1 byte come in,
479 // it adds the DSP sample position to the MIDI data stream.
480 // The DSP sample position is represented as a 32 bit unsigned
481 // value, with the high 16 bits first, followed by the low 16 bits.
483 // The following logic parses the incoming MIDI data.
485 //===========================================================================
487 DWORD
CMidiInQ::MtcProcessData( DWORD dwMidiData
)
493 switch ( m_wMtcState
)
496 case MIDI_IN_STATE_NORMAL
:
498 if ( dwMidiData
== 0xF1L
)
500 m_wMtcState
= MIDI_IN_STATE_TS_HIGH
;
505 case MIDI_IN_STATE_TS_HIGH
:
508 m_pMtcSync
->StoreTimestampHigh( dwMidiData
);
510 m_wMtcState
= MIDI_IN_STATE_TS_LOW
;
511 dwRval
= MIDI_IN_SKIP_DATA
;
515 case MIDI_IN_STATE_TS_LOW
:
518 m_pMtcSync
->StoreTimestampLow( dwMidiData
);
520 m_wMtcState
= MIDI_IN_STATE_F1_DATA
;
521 dwRval
= MIDI_IN_SKIP_DATA
;
525 case MIDI_IN_STATE_F1_DATA
:
528 m_pMtcSync
->StoreMtcData( dwMidiData
);
530 m_wMtcState
= MIDI_IN_STATE_NORMAL
;
537 } // DWORD CMidiInQ::MtcProcessData
540 //===========================================================================
544 //===========================================================================
546 void CMidiInQ::ServiceMtcSync()
556 /****************************************************************************
560 ****************************************************************************/
562 ECHOSTATUS
CMidiInQ::ServiceIrq()
567 CDspCommObject
*pDCO
;
568 LONGLONG llTimestamp
;
571 // Store the time for the activity detector
573 m_pEG
->m_pOsSupport
->OsGetSystemTime( &m_ullLastActivityTime
);
576 // Get the MIDI count
578 pDCO
= m_pEG
->GetDspCommObject();
579 pDCO
->ReadMidi( 0, dwMidiCount
); // The count is at index 0
582 //ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) );
583 if ( dwMidiCount
== 0 )
592 llTimestamp
= m_pEG
->m_pOsSupport
->GetMidiInTimestamp();
595 // Get the MIDI data from the comm page
597 wIndex
= 1; // First MIDI byte is at index 1
598 while ( dwMidiCount
-- > 0 )
605 Status
= pDCO
->ReadMidi( wIndex
++, dwMidiByte
);
606 if ( ECHOSTATUS_OK
!= Status
)
608 ECHO_DEBUGPRINTF(("Failed on ReadMidi\n"));
609 ECHO_DEBUGBREAK(); // should never happen...
614 // Parse the incoming MIDI stream. The incoming MIDI data consists
615 // of MIDI bytes and timestamps for the MIDI time code 0xF1 bytes.
616 // MtcProcessData is a little state machine that parses the stream.
618 // If you get MIDI_IN_SKIP_DATA back, then this is a timestamp byte,
619 // not a MIDI byte, so don't store it in the MIDI input buffer.
621 if ( MIDI_IN_SKIP_DATA
== MtcProcessData( dwMidiByte
) )
625 // Only store the MIDI data if there is at least one
628 if (0 != m_dwNumClients
)
631 // Stash the MIDI data and check for overflow
633 if ( ECHOSTATUS_BUFFER_OVERFLOW
== AddMidi( dwMidiByte
, llTimestamp
) )
639 } // while there is more MIDI data to read
641 return ECHOSTATUS_OK
;
646 // *** CMidiInQ.cpp ***