BPicture: Fix archive constructor.
[haiku.git] / src / add-ons / kernel / drivers / audio / echo / generic / CMidiInQ.cpp
blob604b8409dbfdef15606ebe4b0c3dbb601f723eac
1 // ****************************************************************************
2 //
3 // CMidiInQ.cpp
4 //
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.
9 //
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
17 // www.echoaudio.com
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 //============================================================================
50 CMidiInQ::CMidiInQ()
53 m_pBuffer = NULL;
54 m_ullLastActivityTime = 0;
56 m_wMtcState = MIDI_IN_STATE_NORMAL;
58 m_pEG = NULL;
59 m_pMtcSync = NULL;
61 } // CMidiInQ::CMidiInQ()
64 CMidiInQ::~CMidiInQ()
67 Cleanup();
69 } // CMidiInQ::~CMidiInQ()
72 //============================================================================
74 // Init
76 //============================================================================
78 ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG )
80 ECHO_DEBUGPRINTF(("CMidiInQ::Init\n"));
82 m_pEG = pEG;
84 m_dwFill = 0;
85 m_dwBufferSizeMask = MIDI_IN_Q_SIZE - 1; // MIDI_IN_Q_SIZE must be a power of 2
87 return ECHOSTATUS_OK;
89 } // Init
92 //============================================================================
94 // Cleanup
96 //============================================================================
98 void CMidiInQ::Cleanup()
101 // Free the MIDI input buffer
103 if (m_pBuffer)
105 OsFreeNonPaged( m_pBuffer );
106 m_pBuffer = NULL;
110 // Free the MTC sync object if it exists
112 if (m_pMtcSync)
114 delete m_pMtcSync;
115 m_pMtcSync = NULL;
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,
137 DWORD &dwMidiByte,
138 LONGLONG &llTimestamp
141 DWORD dwDrain;
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) );
155 dwDrain++;
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
172 DWORD dwMidiByte,
173 LONGLONG llTimestamp
176 DWORD dwFill;
178 //ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%lx\n", dwMidiByte) );
180 dwFill = m_dwFill;
181 m_pBuffer[ dwFill ].dwMidi = dwMidiByte;
182 m_pBuffer[ dwFill ].llTimestamp = llTimestamp;
184 dwFill++;
185 dwFill &= m_dwBufferSizeMask;
186 m_dwFill = dwFill;
188 return ECHOSTATUS_OK;
190 } // ECHOSTATUS CMidiInQ::AddMidi
193 //============================================================================
195 // Reset
197 //============================================================================
199 void CMidiInQ::Reset(ECHOGALS_MIDI_IN_CONTEXT *pContext)
201 pContext->dwDrain = m_dwFill;
203 } // void CMidiInQ::Reset()
208 /****************************************************************************
210 Arm and disarm
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)
223 ECHOSTATUS Status;
225 pContext->fOpen = FALSE;
228 // Return if there is no Echogals pointer
230 if (NULL == m_pEG)
232 return ECHOSTATUS_CANT_OPEN;
235 //-------------------------------------------------------------------------
237 // Set up the MIDI input buffer
239 //-------------------------------------------------------------------------
241 if (NULL == m_pBuffer)
244 // Reset the buffer pointers
246 m_dwFill = 0;
249 // Allocate
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"));
256 return Status;
260 Reset(pContext);
261 m_dwNumClients++;
263 //-------------------------------------------------------------------------
265 // Enable MIDI input
267 //-------------------------------------------------------------------------
269 m_pEG->GetDspCommObject()->SetMidiOn( TRUE );
271 pContext->fOpen = TRUE;
272 return ECHOSTATUS_OK;
274 } // Arm
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;
297 // Last client?
299 if (0 == m_dwNumClients)
300 return ECHOSTATUS_OK;
302 m_dwNumClients--;
304 if (0 == m_dwNumClients)
307 // Tell the DSP to disable MIDI input
309 if (m_pEG)
310 m_pEG->GetDspCommObject()->SetMidiOn( FALSE );
313 // Free the MIDI input buffer
315 if (m_pBuffer)
317 OsFreeNonPaged( m_pBuffer );
318 m_pBuffer = NULL;
322 return ECHOSTATUS_OK;
324 } // Disarm
327 //============================================================================
329 // ArmMtcSync - turns on MIDI time code sync
331 //============================================================================
333 ECHOSTATUS CMidiInQ::ArmMtcSync()
335 if (NULL == m_pEG)
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;
355 } // ArmMtcSync
358 //============================================================================
360 // DisarmMtcSync - turn off MIDI time code sync
362 //============================================================================
364 ECHOSTATUS CMidiInQ::DisarmMtcSync()
366 if (NULL == m_pMtcSync)
367 return ECHOSTATUS_OK;
369 if (NULL == m_pEG)
370 return ECHOSTATUS_DSP_DEAD;
373 // Tell the DSP to disable MIDI input
375 m_pEG->GetDspCommObject()->SetMidiOn( FALSE );
378 // Free m_pMtcSync
380 CMtcSync *pTemp; // Use temp variable to avoid killing the object
381 // while the ISR is using it
383 pTemp = m_pMtcSync;
384 m_pMtcSync = NULL;
385 delete pTemp;
387 return ECHOSTATUS_OK;
389 } // DisarmMtcSync
394 /****************************************************************************
396 Detect MIDI input activity - see if the driver has recently received
397 any MIDI input
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)
412 return FALSE;
414 return TRUE;
416 } // IsActive
421 /****************************************************************************
423 MIDI time code
425 ****************************************************************************/
427 //===========================================================================
429 // Get and set the base MTC sample rate
431 //===========================================================================
434 ECHOSTATUS CMidiInQ::GetMtcBaseRate(DWORD *pdwBaseRate)
436 ECHOSTATUS Status;
438 if (m_pMtcSync)
440 *pdwBaseRate = m_pMtcSync->m_dwBaseSampleRate;
441 Status = ECHOSTATUS_OK;
443 else
445 *pdwBaseRate = 0;
446 Status = ECHOSTATUS_NO_DATA;
449 return Status;
451 } // GetMtcBaseRate
454 ECHOSTATUS CMidiInQ::SetMtcBaseRate(DWORD dwBaseRate)
456 ECHOSTATUS Status;
458 if (m_pMtcSync)
460 m_pMtcSync->m_dwBaseSampleRate = dwBaseRate;
461 Status = ECHOSTATUS_OK;
463 else
465 Status = ECHOSTATUS_NO_DATA;
468 return Status;
470 } // SetMtcBaseRate
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 )
489 DWORD dwRval;
491 dwRval = 0;
493 switch ( m_wMtcState )
496 case MIDI_IN_STATE_NORMAL :
498 if ( dwMidiData == 0xF1L )
500 m_wMtcState = MIDI_IN_STATE_TS_HIGH;
502 break;
505 case MIDI_IN_STATE_TS_HIGH :
507 if (m_pMtcSync)
508 m_pMtcSync->StoreTimestampHigh( dwMidiData );
510 m_wMtcState = MIDI_IN_STATE_TS_LOW;
511 dwRval = MIDI_IN_SKIP_DATA;
512 break;
515 case MIDI_IN_STATE_TS_LOW :
517 if (m_pMtcSync)
518 m_pMtcSync->StoreTimestampLow( dwMidiData );
520 m_wMtcState = MIDI_IN_STATE_F1_DATA;
521 dwRval = MIDI_IN_SKIP_DATA;
522 break;
525 case MIDI_IN_STATE_F1_DATA :
527 if (m_pMtcSync)
528 m_pMtcSync->StoreMtcData( dwMidiData );
530 m_wMtcState = MIDI_IN_STATE_NORMAL;
531 break;
535 return dwRval;
537 } // DWORD CMidiInQ::MtcProcessData
540 //===========================================================================
542 // ServiceMtcSync
544 //===========================================================================
546 void CMidiInQ::ServiceMtcSync()
548 if (m_pMtcSync)
549 m_pMtcSync->Sync();
551 } // ServiceMtcSync
556 /****************************************************************************
558 Interrupt service
560 ****************************************************************************/
562 ECHOSTATUS CMidiInQ::ServiceIrq()
564 DWORD dwMidiCount;
565 WORD wIndex;
566 ECHOSTATUS Status;
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
581 #ifdef ECHO_DEBUG
582 //ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) );
583 if ( dwMidiCount == 0 )
585 ECHO_DEBUGBREAK();
587 #endif
590 // Get the timestamp
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 )
600 DWORD dwMidiByte;
603 // Get the MIDI byte
605 Status = pDCO->ReadMidi( wIndex++, dwMidiByte );
606 if ( ECHOSTATUS_OK != Status )
608 ECHO_DEBUGPRINTF(("Failed on ReadMidi\n"));
609 ECHO_DEBUGBREAK(); // should never happen...
610 break;
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 ) )
622 continue;
625 // Only store the MIDI data if there is at least one
626 // client
628 if (0 != m_dwNumClients)
631 // Stash the MIDI data and check for overflow
633 if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( dwMidiByte, llTimestamp ) )
635 break;
639 } // while there is more MIDI data to read
641 return ECHOSTATUS_OK;
643 } // ServiceIrq
646 // *** CMidiInQ.cpp ***