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
43static 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
58CMtcSync::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
78CMtcSync::~CMtcSync()
79{
80}	// CMtcSync::~CMtcSync()
81
82
83void 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
114void 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
128void 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
142void 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
164void 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
490PVOID 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
513VOID  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