xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CMidiInQ.cpp (revision 7120e97489acbf17d86d3f33e3b2e68974fd4b23)
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.
11 //
12 //		Copyright Echo Digital Audio Corporation (c) 1998 - 2002
13 //		All rights reserved
14 //		www.echoaudio.com
15 //
16 //		Permission is hereby granted, free of charge, to any person obtaining a
17 //		copy of this software and associated documentation files (the
18 //		"Software"), to deal with the Software without restriction, including
19 //		without limitation the rights to use, copy, modify, merge, publish,
20 //		distribute, sublicense, and/or sell copies of the Software, and to
21 //		permit persons to whom the Software is furnished to do so, subject to
22 //		the following conditions:
23 //
24 //		- Redistributions of source code must retain the above copyright
25 //		notice, this list of conditions and the following disclaimers.
26 //
27 //		- Redistributions in binary form must reproduce the above copyright
28 //		notice, this list of conditions and the following disclaimers in the
29 //		documentation and/or other materials provided with the distribution.
30 //
31 //		- Neither the name of Echo Digital Audio, nor the names of its
32 //		contributors may be used to endorse or promote products derived from
33 //		this Software without specific prior written permission.
34 //
35 //		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36 //		EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37 //		MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
38 //		IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
39 //		ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
40 //		TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
41 //		SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
42 //
43 // ****************************************************************************
44 
45 #include	"CEchoGals.h"
46 
47 
48 /****************************************************************************
49 
50 	Construction and destruction, clean up and init
51 
52  ****************************************************************************/
53 
54 //============================================================================
55 //
56 // Construction & destructor
57 //
58 //============================================================================
59 
60 CMidiInQ::CMidiInQ()
61 {
62 
63 	Reset();
64 
65 	m_pBuffer = NULL;
66 	m_ullLastActivityTime = 0;
67 
68 	m_wMtcState = MTC_STATE_NORMAL;
69 
70 	m_pEG = NULL;
71 
72 	m_fArmed = FALSE;
73 
74 }	// CMidiInQ::CMidiInQ()
75 
76 
77 CMidiInQ::~CMidiInQ()
78 {
79 
80 	Cleanup();
81 
82 }	// CMidiInQ::~CMidiInQ()
83 
84 
85 //============================================================================
86 //
87 // Init
88 //
89 //============================================================================
90 
91 ECHOSTATUS CMidiInQ::Init( CEchoGals *pEG )
92 {
93 	ECHOSTATUS Status;
94 
95 	ECHO_DEBUGPRINTF(("CMidiInQ::Init\n"));
96 
97 	m_pEG = pEG;
98 
99 	Status = OsAllocateNonPaged(	sizeof(MIDI_DATA) * ECHO_MIDI_QUEUE_SZ,
100 											(PPVOID) &m_pBuffer);
101 
102 	if (ECHOSTATUS_OK == Status)
103 		Reset();
104 
105 	return Status;
106 
107 }	// Init
108 
109 
110 //============================================================================
111 //
112 // Cleanup
113 //
114 //============================================================================
115 
116 void CMidiInQ::Cleanup()
117 {
118 	//
119 	// Free the MIDI input buffer
120 	//
121 	if (NULL != m_pBuffer)
122 	{
123 		if (ECHOSTATUS_OK != OsFreeNonPaged( m_pBuffer ))
124 		{
125 			ECHO_DEBUGPRINTF(("CMidiInQ::Cleanup - Failed to free MIDI input bufer\n"));
126 		}
127 	}
128 
129 	m_pBuffer = NULL;
130 }
131 
132 
133 
134 
135 /****************************************************************************
136 
137 	Queue management - add and remove MIDI data
138 
139  ****************************************************************************/
140 
141 //============================================================================
142 //
143 // GetMidi - get oldest MIDI input byte from the Q
144 //
145 //============================================================================
146 
147 ECHOSTATUS CMidiInQ::GetMidi
148 (
149 	MIDI_DATA &Midi
150 )
151 {
152 	if (NULL == m_pBuffer)
153 		return ECHOSTATUS_CHANNEL_NOT_OPEN;
154 
155 	if ( m_pFill == m_pDrain )
156 		return( ECHOSTATUS_NO_DATA );
157 
158 	Midi = *m_pDrain;
159 
160 	ECHO_DEBUGPRINTF( ("CMidiInQ::GetMidi 0x%x\n", Midi) );
161 
162 	m_pDrain = ComputeNext( m_pDrain );
163 
164 	return ECHOSTATUS_OK;
165 
166 }	// ECHOSTATUS CMidiInQ::GetMidi
167 
168 
169 //============================================================================
170 //
171 // AddMidi - add new MIDI input byte to the Q
172 //
173 //============================================================================
174 
175 ECHOSTATUS CMidiInQ::AddMidi
176 (
177 	MIDI_DATA Midi
178 )
179 {
180 	if ( ComputeNext( m_pFill ) == m_pDrain )
181 	{
182 		ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi buffer overflow\n") );
183 		ECHO_DEBUGBREAK();
184 		return ECHOSTATUS_BUFFER_OVERFLOW;
185 	}
186 
187 	ECHO_DEBUGPRINTF( ("CMidiInQ::AddMidi 0x%x\n", Midi) );
188 
189 	*m_pFill = Midi;
190 	m_pFill = ComputeNext( m_pFill );
191 
192 	return ECHOSTATUS_OK;
193 
194 }	// ECHOSTATUS CMidiInQ::AddMidi
195 
196 
197 //============================================================================
198 //
199 // ComputeNext - protected routine used to wrap the read and write pointers
200 // around the circular buffer
201 //
202 //============================================================================
203 
204 PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur )
205 {
206 	if ( ++pCur >= m_pEnd )
207 		pCur = m_pBuffer;
208 
209 	return pCur;
210 
211 }	// PMIDI_DATA CMidiInQ::ComputeNext( PMIDI_DATA pCur )
212 
213 
214 //============================================================================
215 //
216 // Reset
217 //
218 //============================================================================
219 
220 void CMidiInQ::Reset()
221 {
222 	//
223 	//	Midi buffer pointer initialization
224 	//
225 	m_pEnd = m_pBuffer + ECHO_MIDI_QUEUE_SZ;
226 	m_pFill = m_pBuffer;
227 	m_pDrain = m_pBuffer;
228 
229 }	// void CMidiInQ::Reset()
230 
231 
232 
233 
234 /****************************************************************************
235 
236 	Arm and disarm
237 
238  ****************************************************************************/
239 
240 
241 //============================================================================
242 //
243 // Arm - resets the Q and enables the Q to start receiving MIDI input data
244 //
245 //============================================================================
246 
247 ECHOSTATUS CMidiInQ::Arm()
248 {
249 	//
250 	// Return if the MIDI input buffer is not allocated
251 	//
252 	if ( 	(NULL == m_pBuffer) ||
253 			(NULL == m_pEG)
254 		)
255 		return ECHOSTATUS_CANT_OPEN;
256 
257 	//
258 	// Reset the buffer pointers
259 	//
260 	Reset();
261 
262 	//
263 	// Tell the DSP to enable MIDI input
264 	//
265 	m_pEG->GetDspCommObject()->SetMidiOn( TRUE );
266 
267 	//
268 	// Set the flag
269 	//
270 	m_fArmed = TRUE;
271 
272 	return ECHOSTATUS_OK;
273 
274 } // Arm
275 
276 
277 //============================================================================
278 //
279 // Disarm - surprisingly, does the opposite of Arm
280 //
281 //============================================================================
282 
283 void CMidiInQ::Disarm()
284 {
285 	//
286 	// Tell the DSP to disable MIDI input
287 	//
288 	if (m_pEG)
289 		m_pEG->GetDspCommObject()->SetMidiOn( FALSE );
290 
291 	//
292 	// Clear the flag
293 	//
294 	m_fArmed = FALSE;
295 
296 }	// Disarm
297 
298 
299 
300 
301 /****************************************************************************
302 
303 	Detect MIDI input activity - see if the driver has recently received
304 	any MIDI input
305 
306  ****************************************************************************/
307 
308 BOOL CMidiInQ::IsActive()
309 {
310 	ULONGLONG	ullCurTime,ullDelta;
311 
312 	//
313 	// See if anything has happened recently
314 	//
315 	m_pEG->m_pOsSupport->OsGetSystemTime( &ullCurTime );
316 	ullDelta = ullCurTime - m_ullLastActivityTime;
317 
318 	if (ullDelta > MIDI_ACTIVITY_TIMEOUT_USEC)
319 		return FALSE;
320 
321 	return TRUE;
322 
323 }	// IsActive
324 
325 
326 
327 
328 /****************************************************************************
329 
330 	MIDI time code
331 
332  ****************************************************************************/
333 
334 
335 //===========================================================================
336 //
337 // Run the state machine for MIDI input data
338 //
339 // MIDI time code sync isn't supported by this code right now,
340 // but you still need this state machine to parse the incoming
341 // MIDI data stream.  Every time the DSP sees a 0xF1 byte come in,
342 // it adds the DSP sample position to the MIDI data stream.
343 // The DSP sample position is represented as a 32 bit unsigned
344 // value, with the high 16 bits first, followed by the low 16 bits.
345 //
346 // Since these aren't real MIDI bytes, the following logic is
347 // needed to skip them.
348 //
349 //===========================================================================
350 
351 DWORD CMidiInQ::MtcProcessData( DWORD dwMidiData )
352 {
353 	switch ( m_wMtcState )
354 	{
355 		case MTC_STATE_NORMAL :
356 			if ( dwMidiData == 0xF1L )
357 				m_wMtcState = MTC_STATE_TS_HIGH;
358 			break;
359 		case MTC_STATE_TS_HIGH :
360 			m_wMtcState = MTC_STATE_TS_LOW;
361 			return MTC_SKIP_DATA;
362 			break;
363 		case MTC_STATE_TS_LOW :
364 			m_wMtcState = MTC_STATE_F1_DATA;
365 			return MTC_SKIP_DATA;
366 			break;
367 		case MTC_STATE_F1_DATA :
368 			m_wMtcState = MTC_STATE_NORMAL;
369 			break;
370 	}
371 	return 0;
372 
373 }	// DWORD CMidiInQ::MtcProcessData
374 
375 
376 
377 
378 /****************************************************************************
379 
380 	Interrupt service
381 
382  ****************************************************************************/
383 
384 ECHOSTATUS CMidiInQ::ServiceIrq()
385 {
386 	DWORD				dwMidiCount;
387 	WORD				wIndex;
388 	ECHOSTATUS 		Status;
389 	CDspCommObject	*pDCO;
390 
391 	//
392 	// Store the time for the activity detector
393 	//
394 	m_pEG->m_pOsSupport->OsGetSystemTime( &m_ullLastActivityTime );
395 
396 	//
397 	// Get the MIDI count
398 	//
399 	pDCO = m_pEG->GetDspCommObject();
400 	pDCO->ReadMidi( 0, dwMidiCount );	  // The count is at index 0
401 
402 #ifdef ECHO_DEBUG
403 	ECHO_DEBUGPRINTF( ("\tMIDI interrupt (%ld MIDI bytes)\n", dwMidiCount) );
404 	if ( dwMidiCount == 0 )
405 	{
406 		ECHO_DEBUGBREAK();
407 	}
408 #endif
409 
410 	//
411 	// Get the MIDI data from the comm page
412 	//
413 	wIndex = 1;		// First MIDI byte is at index 1
414 	while ( dwMidiCount-- > 0 )
415 	{
416 		DWORD	dwMidiByte;
417 
418 		//
419 		// Get the MIDI byte
420 		//
421 		Status = pDCO->ReadMidi( wIndex++, dwMidiByte );
422 		if ( ECHOSTATUS_OK != Status )
423 		{
424 			ECHO_DEBUGPRINTF(("Failed on ReadMidi\n"));
425 			ECHO_DEBUGBREAK();	// should never happen...
426 			break;
427 		}
428 
429 		//
430 		// Parse the incoming MIDI stream.  The incoming MIDI data consists
431 		// of MIDI bytes and timestamps for the MIDI time code 0xF1 bytes.
432 		// MtcProcessData is a little state machine that parses the strem.
433 		//
434 		// If you get MTC_SKIP_DATA back, then this is a timestamp byte,
435 		// not a MIDI byte, so don't store it in the MIDI input buffer.
436 		//
437 		if ( MTC_SKIP_DATA == MtcProcessData( dwMidiByte ) )
438 			continue;
439 
440 		//
441 		// Only store the MIDI data if MIDI input is armed
442 		//
443 		if (m_fArmed)
444 		{
445 			//
446 			// Stash the MIDI and timestamp data and check for overflow
447 			//
448 			if ( ECHOSTATUS_BUFFER_OVERFLOW == AddMidi( (MIDI_DATA) dwMidiByte ) )
449 			{
450 				break;
451 			}
452 
453 		}
454 	}		// while there is more MIDI data to read
455 
456 	return ECHOSTATUS_OK;
457 
458 } // ServiceIrq
459 
460 
461 // *** CMidiInQ.cpp ***
462