Question

Je suis en train de reconstruire un vieux métronome application qui a été écrit à l'origine à l'aide de MFC en C++ pour être écrite .NET avec C#.L'une des questions que je suis en cours d'exécution dans est de mettre le minuteur de "cocher" avec suffisamment de précision.

Par exemple, en supposant un facile BPM (battements par minute) de 120, le minuteur doit cocher toutes .5 secondes (ou 500 millisecondes).L'utilisation de ce comme base pour les tiques, cependant, n'est pas tout à fait exacts .NET garantit seulement que le minuteur ne tique avant l'écoulement du temps a passé.

Actuellement, pour contourner cela pour la même 120 BPM exemple utilisé ci-dessus, je suis en train de tiques à quelque chose comme 100 millisecondes et de jouer le son de clic sur chaque 5e cycle d'horloge.Cela permet d'améliorer la précision tout à fait un peu, mais si se sent comme un peu un hack.

Alors, quelle est la meilleure façon d'obtenir exacts des tiques?Je sais qu'il y a plus de minuteries disponibles que les windows forms minuterie qui est facilement disponible dans Visual Studio, mais je ne suis pas vraiment familier avec eux.

Était-ce utile?

La solution

Il y a trois classes timer appelé "Timer" dans .NET.Il semble que vous êtes en utilisant les Fenêtres de Formes, mais en fait, vous pourriez trouver le Système.Le filetage.Classe Timer plus utile - mais être prudent, car il appelle de nouveau sur un pool de thread, donc vous ne pouvez pas interagir directement avec votre formulaire à partir de la fonction de rappel.

Une autre approche pourrait être de p/invoke pour Win32 multimédia minuteurs - timeGetTime, timeSetPeriod, etc.

Un rapide google a trouvé ce qui pourrait être utile http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'Multimédia' (timer) est le buzz-word pour la recherche dans ce contexte.

Autres conseils

Qu'est-ce que l'application C++ à l'aide?Vous pouvez toujours utiliser la même chose ou enveloppez la minuterie de code C++ en C++/CLI classe.

J'ai eu ce problème lors de l'élaboration d'un récent enregistrement de données de projet.Le problème avec la .NET des chronomètres ( windows.formes, système.le filetage et le système.timer ) est qu'ils ne sont précis à environ 10 milli-secondes, ce qui est dû à l'événement de la planification intégrée .NET je crois.( Je parle .NET 2 ici ).Ce n'était pas acceptable pour moi et j'ai donc dû utiliser la minuterie multimédia ( vous avez besoin d'importer de la dll ).J'ai aussi écrit une classe wrapper pour tous les compteurs à zéro et vous pouvez basculer entre eux, si nécessaire, en utilisant un minimum de changements de code.Check out my blog post ici:http://www.indigo79.net/archives/27

Une autre possibilité est qu'il y a un bug dans WPF mise en œuvre de la Minuterie (il y a un décalage entre quelques millisecondes et les tiques causant des inexactitudes possibles selon le processus exact de temps d'exécution) , comme démontré ci-dessous:

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,143

class DispatcherTimer
{
    public TimeSpan Interval
    {
        set
        {
            ...
            _interval = value;
            // Notice below bug: ticks1 + milliseconds [Bug1]
            _dueTimeInTicks = Environment.TickCount + (int)_interval.TotalMilliseconds;
        }
    }
}

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs

class Dispatcher
{
    private object UpdateWin32TimerFromDispatcherThread(object unused)
    {
        ...
        _dueTimeInTicks = timer._dueTimeInTicks;
        SetWin32Timer(_dueTimeInTicks);
    }

    private void SetWin32Timer(int dueTimeInTicks)
    {
        ...
        // Notice below bug: (ticks1 + milliseconds) - ticks2  [Bug2 - almost cancels Bug1, delta is mostly milliseconds not ticks]
        int delta = dueTimeInTicks - Environment.TickCount; 
        SafeNativeMethods.SetTimer( 
            new HandleRef(this, _window.Value.Handle),
            TIMERID_TIMERS,
            delta); // <-- [Bug3 - if delta is ticks, it should be divided by TimeSpan.TicksPerMillisecond = 10000]
    }
}

http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/SafeNativeMethodsCLR.cs,505

class SafeNativeMethodsPrivate
{
    ...
    [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
    public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, NativeMethods.TimerProc lpTimerFunc);
}

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx

uElapse [in]
Type: UINT
The time-out value, in milliseconds. // <-- milliseconds were needed eventually

Classes Timer peut commencer à se comporter étrangement lorsque le timer "cochez la case" code d'événement n'est pas fini son exécution par le temps, le suivant "cocher la case" se produit.Une manière de lutter contre cela est de désactiver le compteur au début de l'événement tick, puis le réactiver à la fin.

Cependant, cette approche n'est pas appropriée dans les cas où le temps d'exécution de la "cochez la case" code n'est pas acceptable d'erreur dans le calendrier de la tique, depuis le minuteur est désactivé (pas de comptage) pendant ce temps.

Si la désactivation de la minuterie est une option, alors vous pouvez également obtenir le même effet par la création d'un thread qui s'exécute, il dort x millisecondes, exécute, dort, etc...

System.Windows.Forms.Timer est limitée à une précision de 55 millisecondes...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top