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
CMtcSync(CEchoGals * pEG)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
~CMtcSync()78 CMtcSync::~CMtcSync()
79 {
80 } // CMtcSync::~CMtcSync()
81
82
Reset()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
StoreTimestampHigh(DWORD dwData)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
StoreTimestampLow(DWORD dwData)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
StoreMtcData(DWORD dwData)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
Sync()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
operator new(size_t Size)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
operator delete(PVOID pVoid)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