xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CMtcSync.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 // ****************************************************************************
2 //
3 //		CMtcSync.cpp
4 //
5 //		Implementation of the CMtcSync class.
6 //		Used to sync to MIDI time code.
7 //
8 //		Set editor tabs to 3 for your viewing pleasure.
9 //
10 // ----------------------------------------------------------------------------
11 //
12 // This file is part of Echo Digital Audio's generic driver library.
13 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
14 // All rights reserved
15 // www.echoaudio.com
16 //
17 // This library is free software; you can redistribute it and/or
18 // modify it under the terms of the GNU Lesser General Public
19 // License as published by the Free Software Foundation; either
20 // version 2.1 of the License, or (at your option) any later version.
21 //
22 // This library is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25 // Lesser General Public License for more details.
26 //
27 // You should have received a copy of the GNU Lesser General Public
28 // License along with this library; if not, write to the Free Software
29 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30 //
31 // ****************************************************************************
32 
33 #include	"CEchoGals.h"
34 #include	"CMtcSync.h"
35 
36 
37 /****************************************************************************
38 
39 	MTC frames per second lookup
40 
41  ****************************************************************************/
42 
43 static DWORD dwMtcFpsLookup[] =
44 {
45 	24,
46 	25,
47 	30,	// 30 frames per second, drop frame
48 	30
49 };
50 
51 
52 /****************************************************************************
53 
54 	Construction, destruction and reset
55 
56  ****************************************************************************/
57 
58 CMtcSync::CMtcSync( CEchoGals *pEG )
59 {
60 	m_pEG = pEG;
61 
62 	pEG->GetAudioSampleRate( &m_dwBaseSampleRate );
63 
64 	//
65 	//	Buffer init
66 	//
67 	m_dwFill = 0;
68 	m_dwDrain = 0;
69 
70 	//
71 	// Reset the sync state info
72 	//
73 	Reset();
74 
75 }	// CMtcSync::CMtcSync()
76 
77 
78 CMtcSync::~CMtcSync()
79 {
80 }	// CMtcSync::~CMtcSync()
81 
82 
83 void CMtcSync::Reset()
84 {
85 	ECHO_DEBUGPRINTF(("\t **** CMtcSync::Reset\n"));
86 
87 	m_dwFramesPerSec = 30;
88 
89 	m_iSamplesPerDframe = (m_dwBaseSampleRate / m_dwFramesPerSec) * 2;
90 
91 	m_dwLastDframeTimestamp = 0;
92 	m_dwLastDframe = 0;
93 	m_dwCurrentDframe = 0;
94 	m_iNumDframesSynced = 0;
95 	m_iNumSamplesSynced = 0;
96 	m_dwNextQfType = 0;
97 	m_dwTemp = 0;
98 
99 }	// void CMtcSync::Reset()
100 
101 
102 /****************************************************************************
103 
104 	Methods for storing MTC data - called at interrupt time
105 
106  ****************************************************************************/
107 
108 //===========================================================================
109 //
110 // Store the upper 16 bits of the sample timestamp
111 //
112 //===========================================================================
113 
114 void CMtcSync::StoreTimestampHigh(DWORD dwData)
115 {
116 
117 	m_Buffer[ m_dwFill ].dwTimestamp = dwData << 16;
118 
119 }	// StoreTimestampHigh
120 
121 
122 //===========================================================================
123 //
124 // Store the lower 16 bits of the sample timestamp
125 //
126 //===========================================================================
127 
128 void CMtcSync::StoreTimestampLow(DWORD dwData)
129 {
130 
131 	m_Buffer[ m_dwFill ].dwTimestamp |= dwData;
132 
133 }	// StoreTimestampLow
134 
135 
136 //===========================================================================
137 //
138 // Store the MTC data byte
139 //
140 //===========================================================================
141 
142 void CMtcSync::StoreMtcData(DWORD dwData)
143 {
144 	//
145 	//	Store it
146 	//
147 	m_Buffer[ m_dwFill ].dwData = dwData;
148 
149 	//
150 	// Increment and wrap the pointer
151 	//
152 	m_dwFill++;
153 	m_dwFill &= ECHO_MTC_QUEUE_SZ - 1;
154 
155 }	// StoreMtcData
156 
157 
158 /****************************************************************************
159 
160   Sync to MIDI time code
161 
162  ****************************************************************************/
163 
164 void CMtcSync::Sync()
165 {
166 	BOOL fRateAdjusted;
167 
168 	//------------------------------------------------------------------------
169 	//
170 	// Process each MTC_DATA in the buffer until the buffer is emtpy
171 	// or the sample rate has been adjusted
172 	//
173 	//------------------------------------------------------------------------
174 
175 	fRateAdjusted = FALSE;
176 	while ( (FALSE == fRateAdjusted) && (m_dwFill != m_dwDrain) )
177 	{
178 		//---------------------------------------------------------------------
179 		//
180 		// Parse the quarter-frame message
181 		//
182 		//---------------------------------------------------------------------
183 
184 		DWORD dwData,dwTimestamp,dwQfType,dwQfData;
185 
186 		dwData = m_Buffer[ m_dwDrain ].dwData;
187 		dwTimestamp = m_Buffer[ m_dwDrain ].dwTimestamp;
188 
189 		ECHO_DEBUGPRINTF(("\n\tCMtcSync::Sync - data 0x%lx, timestamp 0x%lx\n",
190 								dwData,dwTimestamp));
191 
192 		dwQfType = (dwData >> 4) & 0xf;
193 		dwQfData = dwData & 0xf;
194 
195 		m_dwDrain++;
196 		m_dwDrain &= ECHO_MTC_QUEUE_SZ - 1;
197 
198 		//
199 		// Make sure the QFs arrive in sequence
200 		//
201 		if (dwQfType != m_dwNextQfType)
202 		{
203 			ECHO_DEBUGPRINTF(("\tCMtcSync::Sync - quarter-frames out of sequence\n"));
204 			Reset();
205 			continue;
206 		}
207 
208 		//
209 		// Process this QF - accumulate the current dframe number
210 		//
211 		switch (dwQfType)
212 		{
213 			case MTC_QF_FRAME_LSN :
214 				m_dwTemp = dwQfData;
215 
216 				ECHO_DEBUGPRINTF(("\t\tQF frame LSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
217 										dwQfData,m_dwTemp,m_dwCurrentDframe));
218 
219 				break;
220 
221 			case MTC_QF_FRAME_MSN :
222 				m_dwTemp |= dwQfData << 4;
223 				m_dwTemp &= 0x1f;
224 				m_dwCurrentDframe = m_dwTemp;
225 
226 				ECHO_DEBUGPRINTF(("\t\tQF frame MSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
227 										dwQfData,m_dwTemp,m_dwCurrentDframe));
228 				break;
229 
230 			case MTC_QF_SECOND_LSN :
231 				m_dwTemp = dwQfData;
232 
233 				ECHO_DEBUGPRINTF(("\t\tQF second LSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
234 										dwQfData,m_dwTemp,m_dwCurrentDframe));
235 				break;
236 
237 			case MTC_QF_SECOND_MSN :
238 				m_dwTemp |= dwQfData << 4;
239 				m_dwTemp &= 0x3f;
240 				m_dwCurrentDframe += m_dwTemp * m_dwFramesPerSec;
241 
242 				ECHO_DEBUGPRINTF(("\t\tQF second MSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
243 										dwQfData,m_dwTemp,m_dwCurrentDframe));
244 				break;
245 
246 			case MTC_QF_MINUTE_LSN :
247 				m_dwTemp = dwQfData;
248 
249 				ECHO_DEBUGPRINTF(("\t\tQF minute LSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
250 										dwQfData,m_dwTemp,m_dwCurrentDframe));
251 				break;
252 
253 			case MTC_QF_MINUTE_MSN :
254 				m_dwTemp |= dwQfData << 4;
255 				m_dwTemp &= 0x3f;
256 				m_dwCurrentDframe += m_dwTemp * m_dwFramesPerSec * 60;
257 
258 				ECHO_DEBUGPRINTF(("\t\tQF minute MSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
259 										dwQfData,m_dwTemp,m_dwCurrentDframe));
260 				break;
261 
262 			case MTC_QF_HOUR_LSN :
263 				m_dwTemp = dwQfData;
264 
265 				ECHO_DEBUGPRINTF(("\t\tQF hour LSN %ld  m_dwTemp %ld  m_dwCurrentDframe %ld\n",
266 										dwQfData,m_dwTemp,m_dwCurrentDframe));
267 				break;
268 
269 			case MTC_QF_HOUR_MSN :
270 				//
271 				//	Finish the dframe number
272 				//
273 				m_dwTemp |= dwQfData << 4;
274 				m_dwTemp &= 0x1f;
275 				m_dwCurrentDframe += m_dwTemp * m_dwFramesPerSec * 3600;
276 
277 				ECHO_DEBUGPRINTF(("\t\tQF hour MSN %ld  m_dwCurrentDframe %ld  m_dwCurrentDframe %ld\n",
278 										dwQfData,m_dwTemp,m_dwCurrentDframe));
279 
280 				//
281 				// Store the number of frames per second
282 				//
283 				DWORD dwFpsIndex,dwFps;
284 
285 				dwFpsIndex = (dwQfData >> 1) & 3;
286 				dwFps = dwMtcFpsLookup[ dwFpsIndex ];
287 				m_dwFramesPerSec = dwFps;
288 				m_iSamplesPerDframe = (m_dwBaseSampleRate / dwFps) * 2;
289 				break;
290 
291 			default :
292 				Reset();
293 				break;
294 
295 		}
296 
297 		//
298 		// If this is not the end of the dframe, go to the next MTC_DATA
299 		//
300 		if (MTC_QF_HOUR_MSN != dwQfType)
301 		{
302 			m_dwNextQfType = dwQfType + 1;
303 			continue;
304 		}
305 
306 		//---------------------------------------------------------------------
307 		//
308 		// End of doubleframe reached; see if the card is synced or not
309 		//
310 		//---------------------------------------------------------------------
311 
312 		INT32 iFrameDelta;
313 
314 		ECHO_DEBUGPRINTF(("\n\n\t\t\tCMtcSync::Sync - doubleframe %ld\n",m_dwCurrentDframe));
315 
316 
317 		//
318 	   // Make sure the doubleframes are arriving in sequence
319 		//
320 	   // Even though we are checking that doubleframes are in sequence, with drop-frame
321 	   // you can sometimes have doubleframes that differ by three, so make sure the
322 	   // frame diff is no more than 3
323 	   //
324 	   // Should probably check for drop-frame here and only allow diff of 3 for
325 		// drop-frame, but who really cares?
326 		//
327 		iFrameDelta = (INT32) (m_dwCurrentDframe - m_dwLastDframe);
328 		if ((iFrameDelta > 3) || (iFrameDelta < 0))
329 		{
330 			DWORD dwDframe;
331 
332 			ECHO_DEBUGPRINTF(("\t****** CMtcSync::Sync - double-frames out of sequence\n"));
333 
334 			dwDframe = m_dwCurrentDframe;
335 			Reset();
336 			m_dwLastDframe = dwDframe;
337 			continue;
338 		}
339 
340 		//
341 		// See if the timestamps have wrapped around
342 		//
343 		if (m_dwLastDframeTimestamp > dwTimestamp)
344 		{
345 			ECHO_DEBUGPRINTF(("\tCMtcSync::Sync - timestamps have wrapped around"));
346 			Reset();
347 			continue;
348 		}
349 
350 		//
351 		// See if the sample rate needs to be adjusted
352 		//
353 		INT32 iTimestampDelta,iActualTotalSamples,iExpectedTotalSamples,iVariance;
354 
355 		// Calculate the timestamp delta; limit the minimum and maximum
356 		iTimestampDelta = (INT32) (dwTimestamp - m_dwLastDframeTimestamp);
357 
358 		if (iTimestampDelta > ((INT32) (m_iSamplesPerDframe * 2)) )
359 			iTimestampDelta = m_iSamplesPerDframe * 2;
360 		else if (iTimestampDelta < ((INT32) (m_iSamplesPerDframe / 2)) )
361 			iTimestampDelta = m_iSamplesPerDframe / 2;
362 
363 		// How many sample times should have gone by?
364 		iExpectedTotalSamples = (m_iNumDframesSynced + 1)	* m_iSamplesPerDframe;
365 
366 		ECHO_DEBUGPRINTF(("\t\t\tdwTimestamp 0x%lx  m_dwLastDframeTimestamp 0x%lx\n",dwTimestamp,m_dwLastDframeTimestamp));
367 		ECHO_DEBUGPRINTF(("\t\t\tBase rate %ld   m_iSamplesPerDframe %ld  iTimestampDelta %ld\n",
368 								m_dwBaseSampleRate,m_iSamplesPerDframe,iTimestampDelta));
369 
370 		// iVariance represents the difference between expected and actual elapsed
371 		// sample time
372 		iActualTotalSamples = iTimestampDelta + m_iNumSamplesSynced;
373 		iVariance = iActualTotalSamples - iExpectedTotalSamples;
374 		if (iVariance < 0) iVariance = -iVariance;
375 
376 		ECHO_DEBUGPRINTF(("\t\t\tiExpectedTotalSamples %ld   iVariance %ld\n",
377 								iExpectedTotalSamples,iVariance));
378 
379 		if (iVariance > MTC_TOLERANCE)
380 		{
381 			DWORD dwSampleRate;
382 
383 			//------------------------------------------------------------------
384 			//
385 			// The clock needs to be adjusted by this ratio
386 			//
387 			//           iExpectedTotalSamples
388 			//			-----------------------------
389 			//			     iActualTotalSamples
390 			//
391 			// This math is done in a fixed point fractional format, with 12
392 			// bits after the decimal point
393 			//
394 			//------------------------------------------------------------------
395 
396 			INT32 iRatio;
397 
398 			iRatio = (iExpectedTotalSamples << 12) / iActualTotalSamples;
399 
400 			//
401 			// To avoid wild swings in the sample rate, apply damping to the ratio;
402 			// that is, only change by a small percentage of the ratio.  This is only
403 			// done if the ratio is not already small
404 			//
405 			if (	(iRatio < DAMPING_RATIO_LIMIT_LOW) ||
406 					(iRatio > DAMPING_RATIO_LIMIT_HIGH))
407 			{
408 				iRatio = (iRatio * (0x1000 - MTC_DAMPING)) >> 12;
409 				iRatio += MTC_DAMPING;
410 			}
411 
412 			ECHO_DEBUGPRINTF(("\t\t\tiRatio 0x%08lx\n",iRatio));
413 			ECHO_DEBUGPRINTF(("\t\t\tiExpectedTotalSamples %ld  iActualTotalSamples %ld\n",
414 									iExpectedTotalSamples,iActualTotalSamples));
415 			ECHO_DEBUGPRINTF(("\t\t\tiTimestampDelta %ld   m_iSamplesPerDframe %ld\n",
416 									iTimestampDelta,m_iSamplesPerDframe));
417 
418 
419 			//
420 			// Adjust the sample rate
421 			//
422 			dwSampleRate = m_pEG->GetDspCommObject()->GetSampleRate();
423 			dwSampleRate = (iRatio * dwSampleRate ) >> 12;
424 
425 			// Limit for single or double speed mode
426 			if (m_dwBaseSampleRate > 50000)
427 			{
428 				if (dwSampleRate > 100000)
429 					dwSampleRate = 100000;
430 				else if (dwSampleRate < 50000)
431 					dwSampleRate = 50000;
432 			}
433 			else
434 			{
435 				if (dwSampleRate > 50000)
436 					dwSampleRate = 50000;
437 				else if (dwSampleRate < MIN_MTC_1X_RATE)
438 					dwSampleRate = MIN_MTC_1X_RATE;
439 			}
440 
441 			ECHO_DEBUGPRINTF(("\t\t\tNew sample rate %ld\n",dwSampleRate));
442 
443 			m_pEG->GetDspCommObject()->SetSampleRate ( dwSampleRate );
444 
445 			//
446 			// Reset the sync counts
447 			//
448 			m_iNumDframesSynced = 0;
449 			m_iNumSamplesSynced = 0;
450 		}
451 		else
452 		{
453 			//------------------------------------------------------------------
454 			//
455 			// Sample rate is close enough
456 			//
457 			//------------------------------------------------------------------
458 
459 			ECHO_DEBUGPRINTF(("\t\t\tClose enough - iVariance %ld\n",iVariance));
460 
461 			m_iNumDframesSynced++;
462 			m_iNumSamplesSynced += iTimestampDelta;
463 
464 			if (m_iNumDframesSynced > MAX_DFRAME_SYNC_COUNT)
465 				m_iNumDframesSynced >>= 1;
466 
467 			ECHO_DEBUGPRINTF(("\t\t\tm_iNumDframesSynced %ld  m_iNumSamplesSynced %ld\n\n",
468 									m_iNumDframesSynced,m_iNumSamplesSynced));
469 		}
470 
471 		m_dwLastDframe = m_dwCurrentDframe;
472 		m_dwLastDframeTimestamp = dwTimestamp;
473 
474 		m_dwNextQfType = 0;
475 
476 	}
477 
478 
479 }	// Sync
480 
481 
482 
483 
484 /****************************************************************************
485 
486 	New & delete
487 
488  ****************************************************************************/
489 
490 PVOID CMtcSync::operator new( size_t Size )
491 {
492 	PVOID 		pMemory;
493 	ECHOSTATUS 	Status;
494 
495 	Status = OsAllocateNonPaged(Size,&pMemory);
496 
497 	if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory ))
498 	{
499 		ECHO_DEBUGPRINTF(("CMtcSync::operator new - memory allocation failed\n"));
500 
501 		pMemory = NULL;
502 	}
503 	else
504 	{
505 		memset( pMemory, 0, Size );
506 	}
507 
508 	return pMemory;
509 
510 }	// PVOID CMtcSync::operator new( size_t Size )
511 
512 
513 VOID  CMtcSync::operator delete( PVOID pVoid )
514 {
515 	if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) )
516 	{
517 		ECHO_DEBUGPRINTF( ("CMtcSync::operator delete memory free failed\n") );
518 	}
519 }	// VOID  CMtcSync::operator delete( PVOID pVoid )
520 
521 
522 // *** CMtcSync.cpp ***
523