2008-08-12 10 views
14

Tôi đang cố gắng xây dựng lại một ứng dụng nhịp cũ đã được viết bằng MFC trong C++ được viết bằng .NET bằng C#. Một trong những vấn đề tôi đang gặp phải là nhận được bộ đếm thời gian để "đánh dấu" đủ chính xác.Nhận các dấu chính xác từ bộ đếm thời gian trong C#

Ví dụ: giả sử BPM dễ dàng (nhịp mỗi phút) là 120, bộ hẹn giờ sẽ đánh dấu mỗi giây là 0,5 giây (hoặc 500 mili giây). Sử dụng điều này làm cơ sở cho các ve, tuy nhiên, không hoàn toàn chính xác như .NET chỉ đảm bảo rằng bộ hẹn giờ của bạn sẽ không đánh dấu trước khi thời gian trôi qua trôi qua.

Hiện tại, để thực hiện việc này cho cùng một ví dụ 120 BPM được sử dụng ở trên, tôi đặt các dấu tích cho khoảng 100 mili giây và chỉ phát âm thanh nhấp vào mỗi lần đếm thứ 5. Điều này cải thiện độ chính xác khá nhiều, nhưng nếu cảm thấy như một chút của một hack.

Vì vậy, cách tốt nhất để có được bọ ve chính xác là gì? Tôi biết có nhiều bộ tính giờ có sẵn hơn so với các cửa sổ hình thức bộ đếm thời gian đó là có sẵn trong Visual Studio, nhưng tôi không thực sự quen thuộc với họ.

Trả lời

9

Có ba lớp bộ hẹn giờ được gọi là 'Bộ hẹn giờ' trong .NET. Có vẻ như bạn đang sử dụng Windows Forms, nhưng thực sự bạn có thể thấy lớp System.Threading.Timer hữu ích hơn - nhưng hãy cẩn thận vì nó gọi lại trên một thread pool, vì vậy bạn không thể trực tiếp tương tác với biểu mẫu của bạn từ gọi lại.

cách tiếp cận khác có thể là p/gọi đến giờ đa phương tiện Win32 - timeGetTime, timeSetPeriod vv

Một google nhanh chóng tìm thấy này, trong đó có thể có ích http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'Multimedia' (timer) là từ buzz để tìm kiếm trong ngữ cảnh này.

1

Ứng dụng C++ sử dụng là gì? Bạn luôn có thể sử dụng cùng một thứ hoặc bọc mã bộ đếm thời gian từ C++ vào một lớp C++/CLI.

0

Các lớp hẹn giờ có thể bắt đầu hoạt động lạ khi mã sự kiện 'đánh dấu' tính giờ không được hoàn thành khi thực hiện lần 'đánh dấu' tiếp theo. Một cách để chống lại điều này là tắt bộ hẹn giờ ở đầu sự kiện đánh dấu, sau đó kích hoạt lại nó ở cuối. Tuy nhiên, cách tiếp cận này không phù hợp trong trường hợp thời gian thực thi mã 'đánh dấu' không được chấp nhận trong thời điểm đánh dấu, vì bộ hẹn giờ sẽ bị tắt (không tính) trong thời gian đó.

Nếu vô hiệu hóa bộ đếm thời gian là một lựa chọn, sau đó bạn cũng có thể đạt được hiệu quả tương tự bằng cách tạo ra một chủ đề riêng biệt mà thực hiện, ngủ cho mili giây x, thực hiện, ngủ, vv ...

+0

Nhưng sau đó bạn chỉ có thể chắc chắn rằng thread ngủ ít nhất x millioseconds; bộ lập lịch trình không xác nhận rằng luồng sẽ chạy với số millesond chính xác – Wilhelm

+0

Đúng. Tôi đồng ý với những gì bạn nói về việc không thể xác định thời điểm đánh dấu tiếp theo. Điểm của những gì tôi nói là bạn không muốn thực hiện mã sự kiện đánh dấu từ một đánh dấu trước đó khi đánh dấu tiếp theo của bạn xảy ra. –

0

System.Windows.Forms.Timer được giới hạn trong một độ chính xác của 55 mili giây ...

+1

Có một số tài liệu chính thức về nó không? – MajesticRa

+0

@MajesticRa: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx Xem văn bản trong hộp màu vàng –

+0

Cảm ơn bạn! Khi tôi viết nó, tôi đã bỏ lỡ điểm mà yazanpro có nghĩa là chính xác hình thức bộ đếm thời gian. Và nghĩ rằng anh ấy đang nói về bộ đếm thời gian nói chung. Nhưng cảm ơn bạn rất nhiều vì đã trả lời! – MajesticRa

1

Tôi đã gặp sự cố này khi phát triển dự án ghi dữ liệu gần đây. Vấn đề với bộ đếm thời gian .NET (windows.forms, system.threading và system.timer) là chúng chỉ chính xác xuống khoảng 10 milli giây do sự kiện lập lịch trình được tích hợp vào .NET Tôi tin. (Tôi đang nói về .NET 2 ở đây). Điều này đã không được chấp nhận đối với tôi và vì vậy tôi đã phải sử dụng bộ đếm thời gian đa phương tiện (bạn cần phải nhập dll). Tôi cũng đã viết một lớp bao bọc cho tất cả các bộ định giờ và vì vậy bạn có thể chuyển đổi giữa chúng nếu cần thiết bằng cách sử dụng các thay đổi mã tối thiểu.Kiểm tra bài viết trên blog của tôi ở đây: http://www.indigo79.net/archives/27

1

Một khả năng khác là có một lỗi trong việc thực hiện WPF của DispatcherTimer (có một không phù hợp giữa mili giây và bọ ve gây thiếu chính xác tiềm năng phụ thuộc vào chính xác thời gian quá trình thực hiện), được minh chứng dưới đây :

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