Brevet FR2768823A1 — publié le 19990326

La présente invention concerne de façon générale les systèmes de commande de mouvement. Elle se rapporte plus particulièrement à un système permettant de commander le mouvement d'un transport audio répondant au mouvement d'une entrée audio, afin de synchroniser l'enregistrement et la reproduction de supports. Récemment, on a utilisé des dispositifs de traitement de signaux pour commander le mouvement dans divers dispositifs. De façon générale, il existe une entrée qui indique au système dans quel état il doit être, par exemple une position, une pression, un courant circulant dans un bobinage de moteur, etc. Le signal d'entrée est envoyé dans un dispositif de commande de mouvement. II existe un système généralisé, placé sous commande, qui possède un état réel, par exemple une position, une pression, un courant, etc. Le système peut comporter un réseau et une boucle de réaction. Des retards peuvent s'introduire dans les états lors du traitement via le système. Les états peuvent être comparés dans un élément de comparaison et, s'ils sont identiques, le signal de sortie de l'élément de comparaison est alors nul. Si les états sont différents, il existe une erreur entre l'état où le système se trouve et celui où il devrait être, ce qui demande de s'attaquer à l'erreur. Les dispositifs classiques de commande de mouvement tentent de minimiser l'erreur qui existe entre l'entrée courante et l'état présent du système. Toutefois, dans un dispositif tel qu'un lecteur/enregistreur audio servant à reproduire et enregistrer des supports, lequel comporte un système servant à traiter le mouvement confer. par un utilisateur à une entrée audio afin de produire un signal de sortie de fréquence d'échantillonnage audio afin de commander le mouvement d'un transport audio, pour synchroniser le mouvement du transport audio sur l'entrée audio, les dispositifs classiques de commande de mouvement peuvent se révéler instables du fait que l'architecture du système introduit un retard notable entre le dispositif de commande de mouvement et les transports qui sont sous commande. De plus, la mise en oeuvre du système se complique du fait de la nécessité de suivre l'évolution des signaux d'entrée retardés qui n'ont pas exercé leur effet dans le système, d'accumuler ces informations et de déterminer quels signaux d'entrée il faut envoyer ensuite pour corriger les retards, en plus d'envoyer le nouveau signal d'entrée. Par conséquent, le besoin existe donc d'un système qui est en mesure de synchroniser le mouvement d'un transport audio servant à enregistrer des supports sur le mouvement d'une entrée audio servant à la reproduction de supports, et qui compense les retards apparaissant dans le système entre l'entrée et le transport. L'invention satisfait ces besoins. En résumé, et d'un point de vue général, l'invention propose un système permettant de minimiser l'erreur apparaissant entre un état futur du système suite à un retard et la valeur prédite de l'entrée à cet instant futur, par prédiction de l'entrée future et calcul de l'état futur attendu pour le système. Le système traite des échantillons audio. Il comporte un moyen servant à produire un signal d'entrée de fréquence d'échantillonnage audio à partir du mouvement d'une source audio qui produit les échantillons audio, et un moyen servant à produire un signal de sortie de fréquence d'échantillonnage audio à partir du signal d'entrée de fréquence d'échantillonnage audio, afin de commander le mouvement d'un transport audio. Le moyen de production de sortie comporte un réseau servant à traiter le signal d'entrée de fréquence d'échantillonnage audio afin de produire le signal de sortie de fréquence d'échantillonnage audio, lequel réseau introduit un retard dans le signal d'entrée de fréquence d'échantillonnage audio, ce qui produit un signal d'entrée retardé de fréquence d'échantillonnage audio. Le système comporte en outre un moyen servant à suivre la fréquence d'échantillonnage audio et à compenser le retard apparaissant dans le signal d'entrée de fréquence d'échantillonnage audio afin de synchroniser le signal de sortie de fréquence d'échantillonnage audio avec le signal d'entrée de fréquence d'échantillonnage audio. Le moyen de suivi comporte un moyen servant à produire une fréquence d'échantillonnage audio poeditc cn fonction du temps du signal d'entrée retardé, un moyen servant à produire une fréquence d'échantillonnage audio calculée en fonction du temps du signal d'entrée retardé, et un moyen servant à minimiser toute erreur entre la fréquence d'échantillonnage audio prédite et la fréquence d'échantillonnage audio calculée. Un aspect de l'invention est que le système compense toute erreur entre un état futur du système après un retard apparu dans un réseau et la valeur prédite de l'entrée à cet instant futur. Un autre aspect de l'invention est que le système synchronise le mouvement du transport audio sur le mouvement d'une entrée audio dont le signal a été retardé lors du traitement dans le réseau. La description suivante, conçue à titre d'illustration de l'invention, vise à donner une meilleure compréhension de ses caractéristiques et avantages; elle s'appuie sur les dessins annexés, panmi lesquels: - la figure 1 illustre, à un niveau élevé, la structure d'un système de traitement d'échantillons audio selon l'invention; - la figure 2 illustre à un niveau élevé la structure d'un dispositif de traitement d'un système de traitement d'échantillons audio, selon l'invention; - la figure 3 illustre à un niveau élevé la structure d'un dispositif de traitement d'un système de traitement d'échantillons audio, selon l'invention; - la figure 4 est un diagramme illustrant la fréquence d'échantillonnage dans un système de traitement d'échantillons audio, selon l'invention; - la figure 5 est un diagramme illustrant le nombre d'échantillons réel dans un système de traitement d'échantillons audio, selon Itinvention; - la figure 6 est un diagramme illustrant le nombre d'échantillons voulu dans un système de traitement d'échantillons audio, selon l'invention; et - la figure 7 est un diagramme illustrant un exemple des performances d'un système de traitement d'échantillons audio, selon l'invention. On se reporte maintenant aux dessins, et plus particulièrement à la figure 1, ainsi qu'aux figures 2 et 3. Sur ces figures, on peut voir une représentation, à un niveau élevé, de la structure employée dans un système 10 de traitement d'échantillons audio, qui comporte un dispositif 12 de commande de fréquence, contenant un dispositif 14 de suivi d'échantillons audio qui compense un retard 16 apparu dans un réseau 18 et synchronise une sortie audio 20 sur une entrée audio 22 qui a été retardée par le réseau 18. Le système 10 lit un signal d'entrée, tel qu'un code temporel externe fourni par une source d'entrée 22, dans un lecteur d'entrée 24. Le signal d'entrée est envoyé par une liaison série 26 à un dispositif de traitement 28, ainsi qu'à un multiplicateur 30 (qui multiplie par exemple les trames par les échantillons par trame) afin de produire un nombre voulu d'échantillons audio 32. Le nombre voulu d'échantillons audio 32 est ajouté, dans un additionneur 34, à un nombre réel d'échantillons 36 produit dans une horloge 38, par exemple une horloge audio AES, via un multiplicateur 40 qui multiplie le signal d'horloge 38 par les échantillons par trame afin de produire le nombre réel d'échantillons 36. La différence entre le nombre voulu et le nombre réel d'échantillons audio constitue l'erreur de retard 16. L'erreur 16 est appliquée en entrée au dispositif 14 de suivi d'échantillons audio, lequel produit une fréquence d'échantillonnage audio modifiée 42, laquelle est ajoutée, dans un additionneur 44, à la dernière fréquence 46, produite dans un bloc 48, afin de produire une nouvelle fréquence 50. La nouvelle fréquence 50 est envoyée via le réseau 18 à une pluralité de lecteurs/enregistreurs 52, ainsi qu'à un bloc 54 qui produit une fréquence 56 envoyée en réaction au multiplicateur 40. Le signal d'horloge 38 est également envoyé aux lecteurs/ enregistreurs 52. Le système 10 est conçu pour être synchronisé avec une entrée 22, telle qu'une source de code temporel externe, une roue à frottement manuelle, ou une souris d'ordinateur, en temps réel. L'entrée 22 peut comprendre une source basée sur les trames pour la synchronisation, par exemple un code temporel SMPTE (norme de la "Society of Moving Picture and Television Engineers"). Pour synchroniser le système 10 avec le dispositif 12 de commande de fréquence, la source d'entrée 22 est lue au niveau de l'entrée audio du système. L'entrée 22 peut présenter des changements internes importants, en fonction du mouvement conféré par l'utilisateur de la source audio, l'entrée 22 peut avoir un mouvement qui obéit à une fréquence stationnaire pendant une longue durée, et soudainement s'arrête ou change de fréquence. Si la source audio présente un mouvement rapide vers l'avant, le code temporel doit commencer à se déplacer à une vitesse rapide. Le système 10 prend un "instantané", tous les 30e de seconde par exemple, de là où il devrait être, puis envoie des instructions dans le réseau 18 pour amener la sortie audio là où elle devrait être. Le système 10 commande une pluralité de lecteurs/enregistreurs 52 raccordés sur le réseau 18, via le processeur de commande 28, lequel peut commander un nombre quelconque de lecteurs/enregistreurs 52, mais n'est pas luimême un lecteur/enregistreur. Le processeur 28 envoie aux lecteurs/enregistreurs 52 une fréquence, qui est celle à laquelle ils doivent reproduire. Le processeur 28 surveille également l'entrée courante 22 qui est appliquée, via la liaison série 26, depuis le lecteur d'entrée 24. Le signal d'entrée 22 est lu par le lecteur 24 et envoyé via la liaison série 26 au multiplicateur 30 du processeur 28, lequel multiplie le signal d'entrée 22 par les échantillons par trame, afin d'indiquer au système là où il devrait être sur la piste audio. Le processeur 28 possède un logiciel auquel il est indiqué là où il devrait être sur la piste audio, et qui envoie ensuite cette information via le réseau 18 aux lecteurs/enregistreurs 52 de façon qu'il puisse se trouver au bon endroit sur la piste audio. Ce qui est demandé est que les lecteurs/enregistreurs 52 soient en synchronisme avec l'entrée 22, indépendamment du fait que l'entrée s'est arrêtée ou se déplace à la fréquence (ou vitesse) de lecture ou bien en sens inverse ou en avance rapide. Puisqu'il apparaît un retard dans le système 10 du fait du réseau 18, il ne peut pas être toujours synchronisé, car, lorsque l'entrée externe 22 commence à changer de manière inattendue, il faut un certain temps pour que les lecteurs/enregistreurs 52 rattrapent, et, pendant ce temps, ils sont sous l'effet de l'erreur 38. Le dispositif 14 de suivi d'échantillons est conçu pour minimiser cette erreur 16, de façon à tenter de résoudre le problème de la commande du mouvement. Le réseau 18 peut avoir un retard d'environ 0,5 s par exemple entre l'envoi aux lecteurs/enregistreurs 52 d'une instruction correspondant à une nouvelle fréquence et l'instant où cette instruction produit réellement son effet et atteint via le réseau 18 tous les lecteurs/ enregistreurs 52. Si une seule instruction concernant une nouvelle fréquence est envoyée chaque 0,5 s, il faut alors un temps très long pour corriger des variations, même petites, de la fréquence suivant laquelle l'entrée externe 22 se déplace. Ainsi, le réseau 18 est destiné à envoyer des instructions de fréquence (ou de vitesse) plus rapidement que le retard du réseau. En effet, il pourrait y avoir plusieurs instructions concernant une nouvelle fréquence qui sont intercalées sur le réseau 18, mais qui n'ont pas encore exercé leur effet. Ces fréquences ne prendront pas effet avant un instant futur, mais la position courante du code temporel doit être mesurée et des vitesses envoyées de façon à minimiser l'erreur entre la position des lecteurs/ enregistreurs 52 et l'entrée externe 22. Le signal d'horloge audio d'entrée/sortie 36 obtenu par exemple à partir d'une référence AES fournit une marque temporelle afin de permettre que le retard soit déterminé entre le moment où une instruction se rapportant à une nouvelle vitesse est envoyée et le moment ou celle-ci prend effet, afin qu'on puisse observer quel est le temps courant qui est sur le processeur 28. Le dispositif 14 de suivi d'échantillons note par exemple le temps de référence AES courant, ajoute 0,5 s à celui-ci, ce qui peut constituer le temps le plus défavorable pour qu'une instruction traverse le réseau et prenne effet, marque temporellement l'instruction relative à la fréquence en utilisant le temps courant plus 0,5 s, puis l'envoie pour qu'elle arrive aux lecteurs/enregistreurs 52 à temps, mais non pour prendre effet avant que les 0,5 s de retard ne soient écoulés. Les paramètres relatifs au système 10 peuvent être calculés à l'aide des équations suivantes, par exemple, comme représenté sur la figure 3, où y, est le nombre voulu d'échantillons à l'instant nT, gn est la fréquence d'échantillonnage voulue estimée à l'instant nT, Yn+N est la valeur prédite de y à l'instant n7 + NT, xn est la valeur d'échantillonnage réelle à l'instant nx, xn+N est la valeur prédite de x à l'instant nx + NT, et fn est la fréquence d'échantillonnage réelle à l'instant nn. La valeur d'échantillonnage voulue est échantillonnée à des intervalles de temps T (typiquement, T ~ 0 1 s). La fréquence d'échantillonnage est mise à jour à chaque intervalle de temps #. L'exécution du changement de fréquence d'échantillonnage est retardée de NT. (Typiquement, N = 5). Le renvoi en réaction de l'erreur d'échantillonnage qui est utilisé pour mettre à jour la fréquence d'échantillonnage s'appuie sur des valeurs prédites de y et x à N# secondes dans le futur. La valeur prédite de y est basée sur la fréquence d'échantillonnage voulue estimée, laquelle est donnée par: Yn - Yn-1 gn = # Ainsi, Yn+N = Yn + NZg,. La valeur prédite de x est calculée à partir de la valeur connue à l'instant n#, et des valeurs connues de la fréquence d'échantillonnage au cours des N intervalles précédents. Ainsi, La valeur suivante de la fréquence d'échantillonnage est calculée à partir de l'erreur d'échantillonnage prédite, comme suit Dans le modèle présenté sur le schéma fonctionnel, la valeur d'échantillonnage réelle est calculée à partir de l'histoire passée des fréquences d'échantillonnage, comme suit Xn+1 = Xn + t#n La fonction de transfert de y, à xn, qui est désignée par H(z), se simplifie en: H(Z) = (N+2)Z-N+1-(N+1)Z-(N+2) où on peut faire usage de l'identité: La fonction de transfert de y, à f"+N, qui est désignée par G(z), est donnée par: G(z) = l/T[(N+2)-(2N+3)z-' + (N+1)z-2] = (1-z-1)[(N+2)-(N+1)z-1]. La fonction de transfert de F"+N à xn, qui est désignée par F(z), est donnée par: F(x) = zZ" (sur) z-1, et H(z) = F(z)G(z). Le système 10 est conçu pour programmer un retard temporel dans un système de compensation sensiblement fixe, comme cela est également représenté sur les diagrammes des figures 4 à 7. Par exemple, à Z-N, OÙ une nouvelle fréquence est envoyée à raison de 10 fois par seconde, mais demande 0,5 s pour traverser le réseau, N sera égal à 5. A l'intérieur du processeur qui fait fonctionner le dispositif de commande de fréquence, il y aura un retard d'échantillons de fréquence de 5. Vis-à-vis du problème du retard, le système considère le temps courant depuis une source externe, et compare la fréquence qui revient à la position du nombre réel d'échantillons. Le système compare une version retardée de la position où les lecteurs/enregistreurs devraient être avec la position courante du code temporel. Le système calcule un nombre d'échantillons prédit, par exemple comme représenté sur la figure 2. Le signal d'entrée se trouvant à 1 sur tao est l'erreur prédite, qui est basée sur l'hypothèse selon laquelle le code temporel entrant continuera à varier à la vitesse à laquelle il était en train de changer. Ceci fournit le terme plus de l'erreur, qui est la valeur prédite pour le code temporel entrant. Le terme moins de l'erreur est la position calculée que les lecteurs/enregistreurs occuperont après le retard. L'erreur est l'erreur retardée que l'on s'attend à voir à 5 échantillons par rapport au temps courant. Cette partie du dispositif de suivi de trames modélise le réseau et le système de lecteurs/enregistreurs, avec un retard qui est interne au dispositif de suivi de trame. Le système rend compte d'un retard du réseau qui est plus long que la durée de traitement de chaque échantillon à l'intérieur du dispositif de commande en faisant une prédiction de la source de code temporel entrante et en modélisant le retard interne au dispositif de suivi de trames. Un premier élément place un retard dans la source de code temporel entrante afin d'assurer une correspondance avec le retard intervenant dans le réseau, pour produire un nombre d'échantillons prédit dans l'avenir. L'autre élément modélise le retard du réseau à l'intérieur du filtre luimême par accumulation de toutes les fréquences précédentes. Le bloc effectue une sommation sur K de Z à moins K, ce qui additionne tous les changements qui ont été envoyés au système et qui attendent encore dans le réseau, mais qui n'ont pas encore pris effet, afin d'estimer où la boîte se trouvera dans le futur. Le moyen de prédiction et le moyen de modélisation en chevauchement retardé, visant où il sera, permettent le fonctionnement du système. Par exemple, le multiplicateur se trouvant en bas à gauche représente la tête de transport audio, qui modélise la tête d'un enregistreur sur bande, ou magnétophone. La fréquence nominale (fréquence de lecture normale) du système est typiquement de 48 000 échantillons par seconde, mais la fréquence de transport peut être fixée à n'importe quel endroit entre zéro et plus ou moins deux fois la fréquence nominale (idéalement, le système devrait être en mesure de traiter des fréquences non nulles allant d'environ 0,01 à environ 100 fois la fréquence nominale, positivement ou négativement, c'est-à-dire en sens avant ou arrière). Le lecteur de code temporel lit des nombres de trames dans un dispositif externe qui peut être arrêté ou se déplacer vers l'avant ou vers l'arrière à n'importe laquelle des fréquences ci-dessus indiquées. La fréquence du dispositif externe peut changer de façon très importante à tout instant, ou peut rester constante pendant de longues durées. Le cas particulier de la fréquence nominale peut être traité en tant qu'entrée séparée du filtre de compensation d'erreur. La partie du système se trouvant dans la boîte délimitée par un trait interrompu à gauche de la figure 2 est mise à jour avec une fréquence qui lui permet d'échantillonner des nombres de trames aussi vite que s'ils étaient mis à disposition par le dispositif externe. Les fréquences typiques de trames se trouvent entre 24 et 30 trames par seconde. Si le dispositif externe se déplace plus lentement, on peut voir le même nombre de trames se répéter de nombreuses fois, puis augmenter gauche de la figure 2 est mise à jour avec une fréquence qui lui permet d'échantillonner des nombres de trames aussi vite que s ils étaient mis à disposition par le dispositif externe. Les fréquences typiques de trames se trouvent entre 24 et 30trames par seconde. Si le dispositif externe se déplace plus lentement, on peut voir le même nombre de trames se répéter de nombreuses fois, puis augmenter d'une unité, se répéter de nombreuses fois, etc. S'il se déplace plus rapidement, on verra de grands sauts dans le nombre de trames d'une mise à jour à la suivante. La partie du système se trouvant dans la boîte délimitée par un trait interrompu située à la droite de la figure 2 est mise à jour à une fréquence plus lente du fait de la servitude qui consiste à diffuser des nouvelles fréquences à autres parties du système. Le temps qui sépare le début d'une telle diffusion et la prise d'effet réelle de la nouvelle fréquence est couramment d'environ 0,5 s, par exemple. Pour que l'erreur de décalage des échantillons soit minimisée, il faut que l'erreur soit maintenue au voisinage de zéro lorsque la fréquence de trames du code temporel d'entrée est constante, et soit amenée à zéro aussi vite que possible (tout en maintenant la stabilité du système) lorsqu'il apparaît un changement du type "fonction échelon" dans la fréquence de trames du code temporel d'entrée. L'invention concerne également un procédé de traitement d'échantillons audio dans un système tel que décrit précédemment, le procédé étant caractérisé en ce qu'il comprend les opérations suivantes activer le signal d'entrée audio afin de produire la fréquence d'échantillonnage audio; activer le moyen de production de fréquence d'échantillonnage audio prédite afin de produire une fréquence d'échantillonnage audio prédite, en fonction du temps du signal d'entrée retardé; activer le moyen de production de fréquence d'échantillonnage audio calculée afin de produire une fréquence d'échantillonnage audio calculée en fonction du temps du signal d'entrée retardé ; et activer le moyen de minimisation d'erreur afin de minimiser l'erreur existant entre la fréquence d'échantillonnage audio prédite et la fréquence d'échantillonnage audio calculée. Selon un aspect particulier, I'opération d'activation du moyen de production de fréquence d'échantillonnage audio prédite comprend l'opération consistant à activer un moyen de production de retard inclus dans le moyen de production de fréquence d'échantillonnage audio prédite afin de produire un retard, dans la fréquence d'échantillonnage audio, qui est sensiblement équivalent au retard du réseau. Selon un autre aspect particulier, l'opération d'activation du moyen de production de fréquence d'échantillonnage audio calculée comprend l'opération consistant à activer un moyen de modélisation du retard du réseau inclus dans le moyen de production de fréquence d'échantillonnage audio calculée. Dans ce cas, selon un mode particulier de réalisation, I'opération d'activation du moyen de modélisation comprend l'opération qui consiste à activer un moyen d'accumulation de fréquences afin d'accumuler les fréquences qui sont antérieures à la fréquence constituant le nouveau signal d'entrée et qui séjournent dans le système. Selon encore un autre aspect particulier, le procédé comprend en outre l'opération consistant à commander le mouvement du transport audio en réponse à un signal d'entrée audio venant de la source audio et à un signal de sortie audio délivré au transport audio et provenant du signal d'entrée audio, où le mouvement de la source audio produit les échantillons audio et le signal d'entrée de fréquence d'échantillonnage audio. Une description fonctionnelle du dispositif de suivi d'échantillons audio selon l'invention est incluse dans l'Appendice A annexé. Bien entendu, l'homme de l'art sera en mesure d'imaginer, à partir des systémes et des procédés dont la description vient d'être donnée à titre simplement illustratif et nullement limitatif, diverses variantes et modifications ne sortant pas du cadre de l'invention. APPENDICE A // NOM // rate.ccp // DESCRIPTION // Interface to the AES reference time. // COPYRIGHT // Copyright 1996 by Advanced Digital Systems Group, All rights reserved. // VERSION CONTROL // Sheader: G:/PVCS/ARCHIVES/TPRTDLL/rate.cpv 1.23 Aug 15 1997 15:52:40 jclaar S #include "precomp.h" &num;include #include &num;include &num;include #include #include // FOLLOWING IS THE NEW FILTER IMPLEMENTATION, WHICH IS BASED ON THIS PSEUDO-CODE // First stage (sample frame number) // frameSamplePeriod should be nominalFramPeriod ? // nominalFramePeriod : kWorstCaseHardwareUpdateTime // minForwardFilterDepth = log2((frameUpdatePeriod + minForwardFilterLength) / frameSamplePeriod) + 2 // minReverseFilterDepth = log2 ((frameUpdatePeriod + minReverseFilterLength) / frameSamplePeriod) + 2 // every frameSamplePeriod (can use waitTime as a delay) // read beforeSampleNumber // read inputFrameNumber // read afterSampleNumber // inputSampleNumber = capturerTimeVaries ? (beforeSampleNumber + afterSampleNumber) 1 : beforeSampleNumber // Second stage (lowpass filter) // static samplePeriods = 0 // static currentPosition = 0 // static filterDepthLimit = 1 starting at 0, spaced apart by lnitialFrameNumber // / Initialize samples starting at 0, spaced apart by frameSamplePeriod // filterRate [kMaxFilterDepth+1] // newSamplePeriods = (currentTime + (frameSamplePeriod 1) lastIterateTime / frameSamplePeriod // if (kExternalFrameClock) // if (newSamplePeriods > 0) // lastIterateTime = currentTime else // lastIterateTime += newSamplePeriods * frameSamplePeriod // waitTime = lastIterateTime + framesamplePerlod - currentTime // samplePeriods += newSamplePeriods // if (samplePeriods = = 0 || newSamplePeriods > 1) return waitTime // lastPoint = mFilterArray[currentPosition - 1] mFllterArray[mCurrentPositlon - 2] // for (j = 1: j the case j = = samplePeriods // filterArray(currentPosition).frame = filterArray(currentPosition l].frame + // lastPoint.frame + (j* (inputFrameNumber - lastPoint.frame)) / samplePeriods // filterArraylcurrentPosltion] sample = fllterArray[currentPosition l].sample + lastPoint.sample + (j * (inputSampleNumber - lastPoint.sample)) / samplePeriods // // determine the minimum filter depth based on whether we're going forward or backward // minFilterDepth =max(minForwardFitlerDepth, minReverseFilterDepth) // minFilterDepth = filterArray[currentPosition].frme + // filterArray[currentPosition - (1 minFilterDepth)].frame // (filterArray[currentPosition - (1 1) > = 0 ? // minForwardFilterDepth : minReverseFilterDepth // // calculate rate in frames/period (really use fixed point) // rate = filterArray[currentPosition] + fllterArray[currentPosition (1 minFilterDepth)] // (filterArray[currentPosition - (1 2 // for (currentFilterDepth = kMaxFilterDepth, i = minFilterDepth; i kMaxFilterDepth; i++) filterRate[i] = filterArray[currentPosition] + filterArray[currentPosition - (1 frameSamplePeriod to the variance in the input frame rate // // if the variance in frameSamplePeriod is significant, rate should be calculated by dividing the difference // // in frame numbers by the difference in sample numbers // if (currentFilterDepth = = kMaxFilterDepth & & abs(rate.frame - filterRate [i].frame) > (5 = = minFilterDepth // currentFilterDepth = i - 1 // rate - filterRate[i] // if (1 > > currentFilterDepth filterDepthLimit) // limit increase in depth to prevent usage of "stable" filter that actually // currentFilterDepth -- // has equal but opposite transients passing through it // if (filterDepthLimit filterArray[currentPosition - il CurrentFilterDepth)] + // (filterRate [currentFilterDeph) > currentFilterDepth, // filterRate [currentFilterDepth] * kDecimate > > (currentFilterDepth - 1 pass = 0 // currentPositio stop/cue/play to simulate CD-type shuttling // if (newRate > kMaxRate) newRate = kMaxRate // if (newRate rateTolerance = (kMaxUnityError / samplesPerFrame) * nominalFrameperiod (frameSamplePeriod * kDecimate) kSyncWindowFactor = 8 // Use tighter rate & position windows before locking into unity speed // if (newRate > 1.0 - rateTolerance & & newRate > kMaxUnityError) if (unityRateTimer 1.0 - rateTolerance / kSyncWindowFactor & & // newRate kMaxUnityError / kSyncWindowFactor) // unityRateTimer++ else // newRate = 1.0 else if (newRate -1.0 - rateTolerance & & error reverseSyncLocDelay) // if (newRate -1.o - rateTolerance / kSyncWindowFactor & & error else // newRate = -1.0 else // unityRateTimer = 0 // currentRateCueHint = (currentRate =0 ? kForward : 0) // if (error > kMaxChaseError & & abs(newRate) = = kMaxRate | cueHint I = kBidirectional & & currentRate CueHint & -cueHint) // cueHint = currentRateCueHint // cueSettleTime = kCueSettleTime (cueHint = = kBidirectional) // if (cueSettleTime lock position = newFrameNumber * samplesPerFrame - currentRate (cueTimeStamp - newSampleNumber) // cue transport to position at commandTimeStamp with cueHint // cuePosition = position // else if (commandTimeStamp + newRateFilterDelta > = cueTimeStamp) lock = (unityRateTimer = - forwardSyncLockDelayIlunityRateTimer = = reverseSyncLockDelay) & & abs(newRate) = = 1.0 // lf (newRate != lastActualRate | lock != lastActualLock) // set transport rate to newRate at rateTimeStamp with lock // cueTimeStamp = rateTimeStamp // if (cueHint != kBidirectional) // if (position > - cuePosition + kCueSettleTime) // cueHint | = kBackward // else if (position lastActualRateTimeStamp) // lastActualRate = cmd.mRate // lastActualRateTimeStamp = cmd.mTimeStamp // lastActualLock = md.mLock // upon receipt of a transport cue command // actualPosition = cmd.mCue.mTime // lastActualRateTimeStamp = cmd.mTimeStamp static const Int16 INFINITE~SYNLOCK~DELAY = Ox7FFF; SyNewRateControl::SyNewRateControl IEEE32 sampleRate, // Samples/second IEEE32 nominalFramePeriod, // Milliseconds/frame for this type of time code at unity rate Bool32 victTypeUpdate, // TRUE iff frame numbers change only on nominal frame period boundaries UInt32 forwardSyncLockDelay, // Time in ms over which timecode rate must stay at play speed before a play command is issued UInt32 reverseSyncLockDelay, // Time in ms over which timecode rate must stay at play reverse speed before a play reverse command is issued UInt32 minForwardFilterLenght, // Period in ms over which average timecode rate is stable in the forward direction UInt32 mlnReverseFilterLength, // Period in ms over which average timecode rate is stable in the reverse direction UInt32 frameSamplePeriod, // Period between calls to CalculateRate ), should be Bool32 externalFrameSampleLock, // TRUE iff period between calls to CalculateRate is driven by external dock IEEE32 maxFrameSamplePeriodvarlance, // Ideally less than half of frameSamplePeriod (see note in FilterFrameNumber IEEE32 captureDelay, // ms between H/W capture of frame number and middle of CurrentFrameNumber () call Bool32 captureTimeVaries, // TRUE iff call to currentFrameNumber Boll32 throwAwayLateFilterPoints, // TRUE iff late calls to CalculateRate can happen, FALSE if calls to CalculateRate // might be skipped but will not affect subsequent calls Bool32 correctForFilterDelay, // TRUE if the output should be adjusted to correct for the positional // (time) delay through the filter; generally TRUE for time code filters // (so they lock at the right position) and FALSE for jog filters UIntl6 decimationFactor, // Transport rate will be updated every decimationFactor*frameSamplePeriod ms Int32 priority, // Should be not higher than the thread that calls CalculateRate( ), or // accuracy of ()'s return value may be adversely affected IEEE32 worstCaseHardwareUpdateTime // Max time for hardware to update frame number when inputtime code changes IEEE32 maxRate, // Max +/- rate at which we can move the transport (default +12.5% varispeed) IEEE32 maxUnityError, // Maximum number of frames we can off by and still maintain "unity" rate IEEE32 maxChaseError, // Maximum number of frames we can be off while chasing before cueing to catch up UIntl6 maxFilterDepth, // Lenght of the longest FIR filter will be 1 maxFilterDepth UInt32 pipeDepth // Depth of sync pipe used to pass decimated filter points to output stage SyTask(NULL, NULL, 0, TRUE, INFINITE, TRUE, priority), kSamplesPerMillisecond(SampleRate / 1000.0), mFrameUpdatePeriod vitcTypeUpdate & & worstCaseHardWareUpdateTime nominalFramePeriod worstCaseHardwareUpdateTime maxFrameSamplePeriodVariance FrameSamplePeriod(frameSamplePeriod) , kExternalFrameSampleClock(externalFrameSampleClock), kCaptureDelay(captureDelay +sampleRate / 1000.f), kCaptureTimeVaries(captureTimeVaries), kThrowAwayLateFllterPoints(throwAwayLateFllterPoints), mCorrectForFilterDelay(correctForFilterDelay) kMaxFilterDepth(maxFilterDepth), kDecimate(decimationFactor), mFilterArray(new SyFllterPoint[l size of the array to simplify indexing mFllterRate(new SyFxlterPolntEmaxFilterDepth + 11), mFllterPlpe(plpeDepth), kMaxRate(maxRate), kRateTolerance((maxUnityError * nominalFramePeriod) / (frameSamplePeriod decimationFactor)). kMaxUnuityError (maxUnityError * nominalFramePeriod * sampleRate / 1000.0). xMaxChaseError (maxChaseError * nominalFramePeriod * sampleRate / 1000,0), kRatePrecisionThreshold (5 * nominalFramePeriod * sampleRate / 100.0). IterateFrameFilter ( & SyNewRateControl::InitIterateFrameFilter), mTimeCodeAnchor (kInvalidAESTime) // Set the minimum filter depths and sync lock delays thls- > UpdateForwardFilterLength(mlnForwardFllterLength); this- > UpdateReverseFilterLength(minReverseFilterLength); this- > UpdateForwardSyncLockDelay(forwardSyncLockDelay) his- > UpdateReverseSyncLockDelay (reverseSyncLockDelay); // Initialize the critical section ::SynInitializeCriticalSelection ( & mTransportCS) // Set system timer resolution for SyTimeGetTime() UInt 32 maxResolution; ::SyTimeGetDevCaps( & mTimerResolution, & maxResolution); ::SyTlmeBeginPeriod(mTimerResolution); // Add pipe empty semaphore to the base SyTask wait list and start the task this- > AddObject (((SySemaphoreSemaphore*)mFilterPipe. EmptySemphore ()) > GetHandle()); this- > Start(); //&num;define~DEBUG~BCD &num;ifdef~DEBUG~BCD extern UInt32 gBCDSize; extern UInt32 gBCDCount; extern UInt8 gBCD[ ] [4]; extern TimeCode gTimeCode[ Int32 gTimeStamp [1000]; &num;endif SyNewRateControl::-SyNewRateControl (void) // Shut down the transport control task this - > Kill(); &num;ifdef DEBUG BCD for (UInt32 i = gBCDCount; i SYRELTRACE(~T("%C%C:%C%C:%C%C:%C%C %164d"), (gBCD[i] [3] 4 & DxF) + ~T('O'), (gBCD[i] [3] & 0xF) + T('O'), (gBCD[i] [2] > 4 & DxF) + (gBCD[i] [2] & 0xF) + T('O'), (gBCD[i] [1] > > 4 & 0xF) + T('O'), (gBCD[1] [1] & 0xF) + ~T('O'), (gBCD[i] (gBCD[i] [0] & 0xF) + ~T('O'), gTimeCode[i]); SYRELTRACE(~T(" %ld\n"), gTimeStamp[i]); for (i = 0; i SYRELTRACE(~T("%C%C:%C%C:%C%C:%C%C %I64d"), (gBCD[i] [3] > > 4 & 0xF) + ~T('O'), (gBCD[i] [3] & 0xF) + ~T('O'), (gBCD[i] [2] > 4 & 0xF) + -T('O'), (gBCD[i] [2] & 0xF) + (gBCD[i] [1] > > 4 & 0xF) + ~T('O'), (gBCD[i] [1] & 0xF) + (gBCD[1] [0] > > 4 & 0xF) + ~T('O'), (gBCD[1] [0] & 0xF) + ~T('O'), gTimeCode[1]; SYRELTRACE(~T(" %ld\n"), gTimeStamp[1]); ) &num;endif // Undo timer resolution setting SyTimeEndPeriod(mTimerResolution); // Delete the critical section ::SyDeleterCriticalSection( & mTransportCS); // Free up the filter arrays delete [ ] mFilterRate; delete [ ] mFilterArray; UInt32 SyNewRateControl::CalculateRate (void) { // // call functions which perform the first stage (sample the time code) and second stage // (filter the time code) of rate processing, as well as send decimated data to the third stage return (this- > *IterateFrameFilter) (this- > GetFrameNumber ()); UInt32 SyNewRateControl::Iterate(Intl6) // Local variables for the third and fourth stages of the filter SyFilterPipeItem filterPoint; IEEE32 currentRate; IEEE32 newRate; TimeCode position; TimeStamp rateTimeStamp; TimeStamp commandTimeStamp; // Release the empty semaphore because we waited on it in order to Iterate (), thereby artificially decrementing it mFilterPipe.EmptySemaphore() - > Increment(): // Get the decimated data from the second stage and call functions to perform the third stage // (track the time code) and fourth stage (control transport rate and cueing) of rate processing mFilterPipe.GetItem(filterPoint); this- > TrackFrames(filterPoint, currentRate, newRate, position, rateTimeStamp, commandTimeStamp); this- > ControlTransport(filterPoint, currentRate, newRate, position, rateTimeStamp, commandTimeStamp); return 0; SyFilterPoint SyNewRateControl: :GetFrameNumber (void) // // Construct a filter point by reading the reference time, reading the time code, reading the // reference time again and averaging the two reference times SyFilterPoint filterPoint; filterPoint.mSample = this- > ExtendTimeStampXthis- > GetReferenceTime()); &num;ifdef ~TRACE~CAPTURE~TIME SYRELTRACE(~T("GFN: timeOffsetBefore = %ld\n"), ::SyTimeGetTime() (mLastIterateTime + kFrameSamplePeriod)); endif filterPoint .mFrame = this- > CurrentFrameNumber(); if (filterPoint.mSample ! = kInvalidAESTime) { &num;ifdef ~TRACE~CAPTURE~TIME SYRELTRACE(~T("GFN: timeOffsetAfter = %ld\n"), ::SyTimeGetTime() (mLastIterateTime + kFrameSamplePeriod)); endif if (kCaptureTimeVaries) TimeStamp timeStamp = this- > GetReferenceTime() if (timeStamp ! = kInvalidAESTime) filterPoint m.Sample += this- > ExtendTimeStamp(timeStamp); filterPoint.mSample =1; filterPoint.mSample -= this- > GetTimeStampleDelay() - kCaptureDelay; &num;ifdef ~DEBUG~BCD static TimeStamp last - kInvalidAESTime; if (last ! = kInvalidAESTime) gTimeStamp[gBCDCount++] = filterPoint.mSample - laster; if (gBCDCount = = gBCDSize( { /* for (UInt32 i = 0; 0#i SYRELTRACE(~T("%C%C:%C%C:%C%C:%C%C %I64d"), (gBCD[i] [3] > > 4 & 0xF) + T('O'), (gBCD[1] [3] & 0xF) + ~T('O'), (gBCD[i] [2] > > 4 & 0xF) + ~T('O'), (gBCD[i] [2] & 0xF) + ~T('O'), (gBCD[i] [1] > > 4 4 & 0xF) + (gBCD[i] [1] & 0xF) + ~T('O'), (gBCD[1] [0] > > 4 & 0xF) + ~T('O'), (gBCD[i] [0] & 0xF) + ~T('O'), gTimeCode[1]); SYRELTRACE(~T(" %ld\n"), gTimeStamp[i]); gBCDCount = 0; //::DebugBreak(); last = filterPoint.mSample; endif return filterpoint; UInt32 SyNewRateControl::InitInterateFrameFilter(const SyFilterPoint & filterPoint) // Check for valid reference time if (filterPoint.mSample = = kInvalidAESTime) return kFrameSamplePeriod; // Initialize filter variables mLastIterateTime = ::SyTimeGetTime() - kFrameSamplePeriod; mSamplePeriods = 0; mFilterDepthLimit = 1 mPass = 0; mCurrentPosition = 0; mFilterArray [mCurrentPostition].mFrame = 0; mFilterArray [mCurrentPostition].mSample = 0; mFilterArray [mCurrentPostition + (1 for (mCurrentPosition++; mCurrentPosition mCurrentPosition++) mFilterArray[mCurrentPosition] .mFrame mFilterArray[mCurrentPosition - 1].mFrame + filterPoint.mFrame; mFilterArray[mCurrentPosition] .mSample mFilterArray[mCurrentPosition - 1].mSample + filterPolnt.mSample - kFrameSamplePeriod kSamplePerMillisecond * ((1 mCurrentPosition); mFilterArray[mCurrentPosition + (1 kMaxFilterDepth)] = mFilterArray[mCurrentPosition]; // Initialize output stage variables here, also, so the timestamps will at least have reasonable values mCueTimeStamp = mLastActualRateTimeStamp = this - > GetReferenceTime(); mLastActualRate = 0.0; mLastActualLock = FALSE; mActualPosition = mCuePosition = this - > TransportTime(); //SYTRACE(~T("MAP set to %f\n"), (float) mActualPosition); mUnityRateTimer = 0; mCueHint = CUEHINT~BIDIRECTIONAL; // Pass control for this and all future iterations to DoIterateFrame Filter() this- > IterateFrameFilter = sSyNewRateControl: :DolterateFrameFilter; return this- > DoIterateFrameFilter(filterPoint); UInt32 SyNewRateControl::DoIterateFrameFilter(const SyFilterPoint & filterPoint) // Check for valid reference time if (filterPoint.mSample = = kInvalidAESTime) this- > IterateFrameFilter = & SyNewRateControl::InitIterateFrameFilter; return kFrameSamplePeriod; // See how much time there is before the next iteration, and see if we've missed any iterations UInt32 time = ::SyTimeGetTime(); UInt32 newSamplePeriods = (time + (kFrameSamplePeriod > > 1) mLastIterateTime)/ / kFrameSamplePerlod; if (kExternalFrameSampleClock) If (newSamplePeriods > 0) mLastIterateTime = time; else mLastIterateTime += newSamplePeriods + kFrameSamplePeriod; Int32 waitTime = mLastIterateTime + kFrameSamplePeriod - time; *ifdef TRACE ITERATE TIME SYRELTRACE(~T("DIFF: timeErr = %ld\n"), time - (mLastIterateTime + kFrameSamplePeriod)); *endif mSamplePeriods += newSamplePeriods; // If the last iteration was too short or too long, don't use the current filter point &num;ifdef DEBUG static UInt32 total; static UIntl6 nimber; static UIntl6 period; if (newSamplePeriods > 1) total += newsamplePeriods; number++; if (period = = 0) { if (number = = 1) SYRELTRACE ~T else SYRELTRACE(~T("SyNewRateControl::IterateFrameFilter: total of id newSamplePeriods = %ld\n"), number, total); total = 0; number = 0; period++; if (period ! = 0 & & period++ = = 100) period = 0; If (number ! = 0) If (number = = 1) SYRELTRACE(~T("SyNewRateControl::IterateFrameFilter: newSamplePeriods = %ld\n"), total); else SYRELTRACE(~T("SyNewRateControl::IterateFrameFllter: ~ total of %d newSamplePeriods = %ld\n"), number, total); total = 0; number = 0; endif if (mSamplePeriods = = 0 || (kThrowAwayLateFilterPoints & & newSamplePeriods > 1)) return waitTime; // Put the new filter point into the frame filter (and construct fake filter points if we missed iterations) SyFilterPoint lastPoint = mFilterArray[m.CurrentPosition - 1] mFilterArray[mCurrentPosition - 2]; for (Int32 i = 1; i // This loop should only execute if a previous iteration of getting the frame sample period // took too long. SyFilterPoint fp; fp.mFrame = lastPoint.mFrame + (i * (filterPoint.mFrame lastPoint.mFrame)) / / mSamplePeriods; fp.msample = lastPoint.mSample + ( filterPoint.mSample lastPoint.mSample)) this- > FilterFrameNumber(fp): } this- > FilterFrameNumber(filterPoint); msampleperiods = 0; return waitTime; #if defined(~DEBUG) II defined(NDEBUG) SyFilterPipeItem::SyFilterPipeItem(const SyFilterPoint & filterPoint, const SyFilterPoint & rate, UInt16 depth, UInt16 position) : SyFilterPoint(filterPoint), mRate(rate), mDepth(depth), mPosition(position) &num;else SyFilterPipeItem::SyFilterPipeItem(const SyFilterPoint & filterPolnt, const SyFilterPoint & rate) : SyFilterPoint(filterPoint), mRate(rate) { } &num;endif void SyNewRateControl::FilterFrameNumber(cons SyFilterPoint & filterPoint) // The values stored in mFilterArray) ) are the cumulative sums of frame and sample numbers. They are kept // this way so that the sum of the frame/sample numbers between any positions in the array can be // determined by taking the difference between those two points, which in turn allows the average frame or Il sample number over that interval to be determined. This means that the values in mFilterArray[ ]will // periodically overflow the Word size used to store the numbers, but this doesn't matter since we only // care about the difference between the values (provided the word sire is large enough to represent the // cumulative frame/sample delta over (1 // // The values stored in mFilterRatet ] are shifted left by (filterDepth 1 > > 1); where filterDepth is the // index into the array (e.g., mFilterRate mFilterRate[3].mSample are both sixteen times the // actual frame number rate and sample number rate that is actually represented). // Put the new filter point into the filter array mFilterArray[mCurrentPosition] = mFilterArray[mCurrentPosition - 1] + filterPoint; // Copy the member variable to a local variable in order to keep them thread-safe UIntl6 minForwardFilterDepth = mMinForwardFllterDepth; UIntl6 mlnReverseFllterDepth = mMinReverseFilterDepth: // Determine the minimum filter depth based on whether we're going forward or backward UInt16 minFilterDepth = SyMa:;(minForwardFllterDepth, mlnReverseFllterDepth); mlnFllterDepth = mFilterArray[mCurrentPosition].mFrame mFilterArray[mCurrentPosition - (l minFilterDepth ) .mFrame (mFilterArray[mCurrentPosition - (1 xi minFilterDepth - 1)].mFrame > = 0 ? minForwardFilterDepth minReverseFilterDepth // Process filter levels from minFilterDepth to kMaxFilterDepth, // and set currentFilterDepth depending on how fast the rate is changing SyFilterPoint rate mFilterArray[mCurrentPosition] + mFilterArray[mCurrentPosition - (1 > > minFilterDepth)] ~ (mFilterArray[mCurrentPosition - (1 > > minFilterDepth - 1)] > > 2; UIntl6 currentFilterDepth = kMaxFilterDepth; //&num;define ~TRACE~RATE~COMPARE //&num;define ~TRACE~BAD~RATE &num;if defined (TRACE~RATE~COMPARE) || defined (~TRACE~BAD~RATE) Bool8 rateCompare)8] = {2, 2, 2, 2, 2, 2, 2, 2); endif &num;ifdef ~TRACE~BAD~RATE static UInt32 rateStability[8] = (0, 0, 0, 0, 0, 0, 0, 0); endif for (UInt16 i = minFilterDepth; i mFilterRate[i] = mFilterArray[mCurrentPosition) mFilterArray[mCurrentPositlon - (1 i)] mFilterArray(mCurrentPosition - (1 // The following rate compare adds the variance in frameSamplePeriod to the variance in the input frame rate. // If the variance in frameSamplePeriod is significant, rate should be calculated by divinding the difference // in frame numbers by the difference in sample numbers. // Note that the compare never succeeds for i = = minFilterDepth (i.e., the first loop iteration). // // !!! - we could probably make a tighter check by saving rate limits at each depth, so that every shorter filter // could invalidate any longer filter, not just the next longer filter // &num;if defined(TRACE~RATE~COMPARE) || defined (~TRACE~BAD~RATE) rateCompare[i - 1] = SY::abs((rate.mFrame > > 2) mFilterRate[i].mFrame) 1) > > 2; Nendif &num;ifdef ~TRACE~BAD~RATE If (i > minFilterDepth + 1) if (!rateCompare[l rateStability(i - 1] = 0; else if (@rateCompare[i - 2] II rateStability[i - 1] ! = 0) if (++rateStability[i - 1] > = 1 i - 1) SYRELTRACE(~T("****** virtual rate error: minFilterDepth (now %d) should be at least %d******\n"), minFilterDepth, i 1); rateStabillty[l - 1] = 0; &num;endif if currentFilterDepth = = kMaxFilterDepth & & SY::abs((rate.mFrame (kRatePrecisionThreshold > 2 ) currentFilterDepth = 1 - 1; rate = mFilterRate[i]; // Limit any increase in depth to prevent usage of a "stable' filter that actually // has equal but opposite transients passing through it if (1 currentFilterDepth if (1 > 1) SYTRACE T("Depth truncated: curDepth=%d limit=%d ~ rate[curDepth] 0x%I64X\n"), currentFilterDepth, mFilterDepthLimit, mFilterRate[currentFilterDepth].mFrame (currentFilterDepth 1), mFilterRate[currentFilterDepth+1].mFrame 16 (currentFilterDepth 1), kRatePrecisionThreshold 15 - currentFilterDepth &num;endif mFilterDepthLimlt = 1 currentFilterDepth; &num;ifdef ~TRACE~DEPTH~TRUNCATION ) &num;endif while (1 (1 mFilterDepthLimit) currentFllterDepth--; if UmFilterDepthLimit mFilterDepthLimit++; //&num;define ~TRACE~INPUT~DELTA &num;ifdef ~TRACE~RATE COMPARE SYRELTRACE(~T("RC: %d%d%d%d%d%d%d%d"), rateCompare[0], rateCompare[1], rateCompare[2], rateCompare[3], rateCompare[4], rateCompare[5], rateCompare[6], rateCompare[7]); &num;ifndef TRACE INPUT DELTA ~ ~ ~ SYRELTRACE(~T("\n")); &num;else SYRELTRACE(~T(" ")); *endif endif &num;ifdef ~TRACE~INPUT~DELTA rate = mFilterArray[mCurrentPosition] + mFilterArray[mCurrentPosition - 2] - (mFilterArray[mCurrentPosition] - 1] 1); SYRELTRACE(~T("FFN: f=%I64d S=%I64d\n"), rate.mFrame, rate.mSample); endif &num;ifdef ~TRACE~BAD~RATE static Bool8 lastDeepCompare; if (lastDeepCompare & & IrateCompareI kMaxFilterDepth - 1]) SYRELTRACE(~T("* real rate change: minForwardFllterDepth = ~ %d, minReverseFilterDepth = %d ******\n"), mMinForwardFilterDepth, mMinReverseFilterDepth); lastDeepCompare = rateCompare[kMaxFilterDepth] - 1); nendlf // Every kDecimate passes, send a filter point o the frame tracker if 8++mPass = = kDecimate) mFilterPipe.PutItem SyFilterPipeItem mCorrectForFilterDelay mFilterArray[mCurrentPosition] mFilterArray[mCurrentPosition - (1 currentFilterDepth)] + (mFilterRate[currentFllterDepth] 1) currentFilterDepth mFilterArray[mCurrentPosition] mFilterArray[mCurrentPosition - (1 currentFilterDepth currentFilterDepth, mFilterRate[currentFilterDepth] * kDecimate (currentFilterDepth - 1 currentFilterDepth, mCurrentPosition &num;endif &num;ifdef TRACE PIPE INPUT SyFilterPipeItem pi mCorrectForFilterFilterDelay mFilterArray[mCurrentposition] mFilterArray[mCurrentPosition - (1 currentFilterDepth)] + tmFilterRate[currentFilterDepth] 1) currentFilterDepth, : mFilterArray[mCurrentPosition] - (1 mFilterAray[mCurrentPosition - (1 > > currentFilterDepth, mFilterRate[currentFilterDepth] * kDecimate (currentDilterDepth - 1 ); SYTRACE (~T(''PI: F= %164d S=%164d RF= %164d RS=%164d depth= %d curPos= %d\n''), pi.mFrame, pi.mSample, pi.mRate.mFrame, pi.mRate.mSample, currentFilterDepth, mCurrentPosition); &num;endif mPass = 0; // Store the filter point again, since the array size has been doubled to simplify indexing mFllterArray[mCurrentPosltlon - (1 a kMaxFilterDepth)] = mFllterArray[mCurrentPosition - 1] + filterPoint; // Move to the net position in the filter array mCurrentPosition++; mCurrentPosition & = (1 kmaxFilterDepth) - 1; mCurrentPosition I= 1 kMaxFilterDepth; void SyNewRateControl: :TrackFrames SyFilterPipeItem & newFilterPoint, // newFilterPoint is an input to the function, all following args are outputs IEEE32 & currentRate, IEEE326 newRate, TimeCode & position, TimeStamp & rateTimeStamp, TlmeStamp & commandTimeStamp // This is the frame tracker, which calculates the rates and timestamps needed by the transport controller commandTimeStamp = this- > GetReferenceTime rateTimeStamp = commandTimeStamp; if (rateTimeStamp rateTimeStamp = mCueTimeStamp; ASSERT ((Int32)newFilterPoint.mRate.mSAmple > 0); TimeStamp nextRateTimeStamp = commandTimeStamp + (TimeStamp)newFilterPoint.mRate.mSample; currentRate = (IEEE32) (newFilterPoint.mRate.mFrame) / newFilterPolnt.mRate.mRate.mSample; //SYTRACE(~(T"cr: %f, mFrame: %I64d, mSample: %I164d\n"), (float) currentRate, // newFilterPoint.mRate.mFrame, newFilterPoint.mRate.mSample); ::SyEnterCriticalSection( & mTransportCS); position = mActualPosition + mLastActualRate * (rateTimeStamp mLastActualRateTimeStamp); ::SyLeaveCriticalSection( & mTransportCS); TimeCode desiredNextPosition = newFilterPoint.mFrame + (TimeCode) (currentRate * (nextRateTimeStamp (TimesTamp)newFilterPoint.mSample)); newRate = (IEEE32) (desiredNextPosition - position / (nextRateTimeStamp rateTimeStamp); void else newRate = 1.0; else if newRate newRate > -1.0 - kRateTolerance & & error if (reverseSyncLockDelay = = INFINITE SYNCLOCK DELAY) mUnityRateTimer = 0; else if (mUnityRateTimer > reverseSyncLockDelay) // Use a tighter rate window before actually locking into reverse play speed if newRate newRate > -1.0 - kRateTolerance / 8 & & error mUnityRateTimer--; else newRate = -1.0; else mUnityRateTimer = 0; &num;ifdef ~TRACE~OUTPUT~DELTA static SyFilterPipeItem lastFilterPoint; SYRELTRACE T("FPD: F=%I64d S=%I64d\n"), newFilterPoint.mFrame - lastFilterPoint.mFrame, newFilterPoint.mSample - lastFilterPoint.mSample SYRELTRACE ~T("FRP: F=%I64d S=%I64d\n"), newFilterPoint.mRate.mFrame - lastFilterPoint.mRate.mFrame, newFilterPoint . mRate.mSample - lastFilterPoint.mRate.mSample lastFilterPoint = newFilterPoint; tèndif &num;if defined(~DEBUG) || defined(NDEBUG) //&num;define ~TRACE &num;ifdef TRACE SYRELTRACE T("CT: fp=%I64u, %I64u fr=I64d,%I64d curRate=%f newRate=%f pos=%I64d rateTS=%lu cmdTS=%lu\n"), newFilterPoint.mFrame, newFilterPoint.mSample, newFilterPoint.mRate.mFrame, newFilterPolnt.mRate.mSample, currentRate, newRate, position, rateTimeStamp, commandTimeStamp SYRELTRACE T(" actPos=%lf desPos=%I64u error=%s%I64d depth=%d curPos=%d\n"), mActualPosition, desiredPosition, position > desiredPosition ? ~T("-") : ~T(""), error, newFilterPoint.mDepth, newFilterPoint.mPosition ): &num;endif &num;endif // If the error is more than kMaxChaseError and the rate is maxed out, cue the transport If error > kMaxChaseError & & SY::abs(newRate) = = kMaxRate this - > CueHint(currentRate) & -mCueHlnt mCueHint = this - > CueHint(currentRate); TimeStamp cueDelay = this - > GetCueDelay() (int) (mCueHint = = CUEHINT ~BIDIRECTIONAL) ; if ((Int32)cueDelay cueDelay = newFilterPoint.mRate.mSample; mCueTimeStamp = commandTimeStamp + cueDelay; newRate = 0.0; if (newRate i= mLastActualRate) this- > SetTransportRate (newRate, commandTimeStamp); // SYTRACE(~T("nfp.mFrame: %I64d, cr: %f, cts: %d, nfp.mSample: %I64d\n"), // newFilterPoint.mFrame, (float) currentRate, (Int32) mCueTimeStamp, // newFilterPoint.mSample); position = newFilterPoint.mFrame + (TimeCode) (currentRate * (mCueTimeStamp - TimeStamp)newFilterPoint.mSample)); if (SY::abs(currentRate) this- > CueTransport(position, commandTimeStamp, mCueHint); else // The following call to Cue used to be called to handle the case // where CalculateRate wasn't called for a long time. However, // it could cause the rate controller to get out of sync with // the transports it is controlling. // Set position just to keep mActualPosition and mLastActualRateTimeStamp reasonable // this- > Cue(position, commandTimeStamp); // Clear mCueHint to force a cue before any new rates are sent mCueHint = 0; mCuePosition = position; // Otherwise, if we're not cueing, set the transport rate else if (TimeStamp) (commandTimeStamp + (TimeStamp)newFilterPoint.mRate.mSample) > = mCueTimeStamp) Bool32 lock mUnityRateTimer = = forwardSyncLockDelay mUnityRateTimer = = reverseSyncLockDelay & & SY::abs(newRate) = = 1.0; if (newRate ! = mLastActualRate | lock ! = mLastActualLock) thls- > SetTransportRate(newRate, rateTlmeStamp, lock); mCueTlmeStamp = rateTimeStamp; if (mCueHint ! = CUEHINT~BIDIRECTIONAL) { Int32 cueDelay = this- > GetCueDelay( ); if (position > = mCuePosition + CueDelay) mCueHint ! = CUEHINT~BACKWARD; else if (position mCueHint ! = CUEHINT~FORWARD; } } } void SyNewRateControl::SetRate(IEEE32 rate, TimeStamp timeStamp, Bool32 lock) { ::SyEnterCriticalSection( & mTransportCS); mActualPosition += mLastActualRate * (timeStamp mLastActualRateTimeStamp); //SYTRACE( T("SR MAP %f\n"), (float) mActualPosition); mLastActualRate = rate mLastActualRateTimeStamp = tiemStamp; mLastActualLock = lock; ::SyLeaveCriticalSection( & mTransportCS); &num;ifdef TRACE TimeStamp cur = tis- > GetReferenceTime( ); SYRELTRACE(~T("SetRate(%f, %lu, %d), cur=%lu, delta=%ld\n"), rate, timeStamp, lock, cur, timeStamp - cur); &num;endif void SyNewRateControl::Cue(TimeCode time, TimeStamp timeStamp) ::SyEnterCriticalSection( & mTransportCS); mActualPosition = time; //SYTRACES T("Cu MAP: %F\n"), (float) mActualPosition); mLastActualRateTimeStamp = timeStamp; ::SyLeaveCriticalSection( & mTransportCS); &num;ifdef TRACE TimeStamp cur = this - > GetReferenceTime SYRELTRACE(~T("Cue(%I64d, %lu), cur=%lu, delta=%ld\n"), time, timeStamp, cur, timeStamp - cur); &num;endif void SyNewRateControl::UpdateForwardSyncLockDelay(UInt32 forwardSyncLockDelay) if (forwardSyncLockDelay = = INFINITE) mForwardSyncLockDelay = INFINITE SYNCLOCK DELAY; else UInt32 SyncLockDelay = (forwardSyncLockDelay (kFrameSamplePeriod * kDecimate > > 1)) / (kFrameSamplePeriod * kDecimate); mForwardSyncLockDelay = syncLockDelay > 0x7FFF ? 0x7FFF : syncLockDelay; &num;ifdef TRACE SYRELTRACE(~T("mForwardSyncLockDelay set to %d\n"), mForwardSyncLockDelay); &num;endif void SyNewRateControl::UpdateReverseSyncLockDelay(UInt32 reverseSyncLockDelay) if (reverseSyncLockDelay = = INFINITE) mReverseSyncLockDelay = INFINITE~SYNCLOCK~DELAY; else UInt32 syncLockDelay = (reverseSyncLockDelay + (kFrameSamplePeriod * kDecimat > > 1)) / (kFrameSamplePeriod * kDecimate); mReverseSyncLockDelay = synctockoelay > 0x7FFF ? -0x7FFF: -syncLockDelay; } #ifdef ~TRACE SYRELTRACE(~T(""mReverseSyncLockDelay set to %d\n"), mReverseSyncLokDelay); #endif ) void SyNewRateControl::UpdateForwardFilterLength(UInt32 minForwardFilterLength) ( UInt32 twoToTheMlnFllterDepthMinusTwo = (mFrameUpdatePeriod + minForwardFilterLength) / kFrameSamplePeriod; for UIntl6 minForwardFilterDepth = 1; // For thread protection, use a local variable while calculating filter depth twoToTheMinFilterDepthMinusTwo ! = 0 & & minForwardFilteroepth kMaxFilterDepth; twoToTheMinFilterDepthMinusTwo = 1, mlnForwardFilterDepth++ mMinForwardFilterDepth = minForwardFilterDepth > kMaxFilterDepth ? kMaxFilterDepth . minForwardFilterDepth; &num;ifdef TRACE SYRELTRACE UInt32 twoToTheMinFilterDepthMinusTwo = (mFrameUpdatePeriod + minReverseFilterLength) / kFrameSamplePeriod; for UIntl6 minReverseFllterDepth = 1; // For thread protection, use a local variable while calculating filter depth twoToTheMinFilterDepthMinusTwo ! = 0 & & minReverseFilterDepth kMaxFilterDepth ; twoToTheMinFilterDepthMinusTwo = 1, minReverseFilterDepth++ mMinReverseFilterDepth - minReverseFilterDepth > kMaxFilterDepth ? kMaxFilterDepth : min ReverseFilterDepth; &num;ifdef TRACE SYRELTRACE(~T("minReverseFilterDepht set to %d\n"), minReverseFilterDepth) &num;endif SyNewRateControl::FilterPipe::FilterPipe(UInt32 length) SySigPipe length, new SyNullMutex, new SyNullMutex, new SySemaphoreSemaphore(length, length), new SySemaphoreSemaphore(0, length) SyNewRateControl::FilterPipe::~FilterPipe(void) { delete this- > EmptySempahore(); delete this- > FullSemaphore(); delete this- > SinkMutes(); delete this- > SourceMutes(); } REVENDICATIONS 1. Système de traitement d'échantillons audio, caractérisé en ce qu'il comprend: un moyen (24) servant à produire un signal d'entrée de fréquence d'échantillonnage audio à partir du mouvement d'une source audio (22) qui produit les échantillons audio un moyen servant à produire un signal de sortie de fréquence d'échantillonnage audio à partir du signal d'entrée de fréquence d'échantillonnage audio, afin de commander le mouvement d'un transport audio (52), comportant: un réseau (18) servant à traiter le signal d'entrée de fréquence d'échantillonnage audio afin de produire le signal de sortie de fréquence d'échantillonnage audio, lequel réseau introduit un retard dans le signal d'entrée de fréquence d'échantillonnage audio, produisant un signal d'entrée de fréquence d'échantillonnage audio retardé; et un moyen (14) permettant de suivre la fréquence d'échantillonnage audio et de compenser le retard présent dans le signal d'entrée de fréquence d'échantillonnage audio afin de synchroniser le signal de sortie de fréquence d'échantillonnage audio avec le signal d'entrée de fréquence d'échantillonnage audio, comprenant: un moyen servant à produire une fréquence d'échantillonnage audio prédite, en fonction du temps du signal d'entrée retardé un moyen servant à produire une fréquence d'échantillonnage audio calculée, en fonction du temps du signal d'entrée retardé ; et un moyen servant à minimiser toute erreur existant entre la fréquence d'échantillonnage audio prédite et la fréquence d'échantillonnage audio calculée. 2. Système selon la revendication 1, caractérisé en ce que le moyen de production de fréquence d'échantillonnage audio prédite comprend un moyen servant à produire un retard dans le retard de la fréquence d'échantillonnage audio, qui est sensiblement équivalent au retard du réseau. 3. Système selon la revendication 1, caractérisé en ce que le moyen de production de fréquence d'échantillonnage audio calculée comprend un moyen servant à modéliser le retard du réseau. 4. Système selon la revendication 1, caractérisé en ce qu'il comprend en outre un moyen servant à commander le mouvement du transport audio en réponse à un signal d'entrée audio venant de la source audio (22) et à un signal de sortie audio délivré au transport audio (52) et obtenu à partir du signal d'entrée audio, où le mouvement de la source audio produit les échantillons audio et le signal d'entrée de fréquence d'échantillonnage audio. 5. Système selon la revendication 3, caractérisé en ce que le moyen de modélisation comprend un moyen servant à accumuler les fréquences antérieures à la fréquence nouvellement fournie en entrée et séjoumant dans le système. 6. Procédé de traitement d'échantillons audio dans un système tel que décrit dans la revendication 1, le procédé étant caractérisé en ce qu'il comprend les opérations suivantes activer le signal d'entrée audio afin de produire la fréquence d'échantillonnage audio; activer le moyen de production de fréquence d'échantillonnage audio prédite afin de produire une fréquence d'échantillonnage audio prédite, en fonction du temps du signal d'entrée retardé activer le moyen de production de fréquence d'échantillonnage audio calculée afin de produire une fréquence d'échantillonnage audio calculée en fonction du temps du signal d'entrée retardé; et activer le moyen de minimisation d'erreur afin de minimiser l'erreur existant entre la fréquence d'échantillonnage audio prédite et la fréquence d'échantillonnage audio calculée. 7. Procédé selon la revendication 6, caractérisé en ce que l'opération d'activation du moyen de production de fréquence d'échantillonnage audio prédite comprend l'opération consistant à activer un moyen de production de retard inclus dans le moyen de production de fréquence d'échantillonnage audio prédite afin de produire un retard, dans la fréquence d'échantillonnage audio, qui est sensiblement équivalent au retard du réseau. 8. Procédé selon la revendication 6, caractérisé en ce que l'opération d'activation du moyen de production de fréquence d'échantillonnage audio calculée comprend l'opération consistant à activer un moyen de modélisation du retard du réseau inclus dans le moyen de production de fréquence d'échantillonnage audio calculée. 9. Procédé selon la revendication 6, caractérisé en ce qu'il comprend en outre l'opération consistant à commander le mouvement du transport audio en réponse à un signal d'entrée audio venant de la source audio et à un signal de sortie audio délivré au transport audio et provenant du signal d'entrée audio, où le mouvement de la source audio produit les échantillons audio et le signal d'entrée de fréquence d'échantillonnage audio. 10. Procédé selon la revendication 8, caractérisé en ce que l'opération d'activation du moyen de modélisation comprend l'opération qui consiste à activer un moyen d'accumulation de fréquences afin d'accumuler les fréquences qui sont antérieures à la fréquence constituant le nouveau signal d'entrée et qui séjournent dans le système.