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