1

I'm trying to build my own Metronome UWP application, here's what i've done so far:

  1. I design a Class that will handle all the desired operations for my app, with a DispatcherTimer

    ...
    public MetronomeContructor(int upperNum, int lowerNum, int bpm, System.Uri baseURI, MainPage page, MediaElement mediaTick, MediaElement mediaTock)
    {
        UpperNum = upperNum;
        LowerNum = lowerNum;
        Bpm = bpm;
        BaseURI = baseURI;
        CalculateBpm(Bpm);
        Page = page;
        Tick = mediaTick;
        Tock = mediaTock;
        DispTimer = new DispatcherTimer();
        DispTimer.Tick += new EventHandler<object>(timer_Tick);
        Tick.AutoPlay = false;
        Tock.AutoPlay = false;
        Tick.Source = new Uri(Page.BaseUri, Page.Sounds.Where(X => X.Name == "Tick").FirstOrDefault().AudioFile.ToString());
        Tock.Source = new Uri(Page.BaseUri, Page.Sounds.Where(X => X.Name == "Click").FirstOrDefault().AudioFile.ToString());
        StartInterval();
    }
    
    //*************************************************************
    //Methods 
    public void CalculateBpm(int bpms)
    {            
        this.TimerMSecs = (1000 * 60) / bpms;            
    }
    
    //*********************************
    //Dispatcher Methods and Events
    public void StartInterval()
    {            
        DispTimer.Interval = TimeSpan.FromMilliseconds(TimerMSecs);         
    }
    
    async void timer_Tick(object sender, object e)
    {
        await Page.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () =>
        {
            Tick.Play();                
        });
    }
    
  2. My MainPage look like this:

    public sealed partial class MainPage : Page
    {        
    public ObservableCollection<Sound> Sounds;
    private MetronomeContructor novo;
    
    public MainPage()
    {
        this.InitializeComponent();                    
    
        Sounds = new ObservableCollection<Sound>();
        SoundManager.GetAllSounds(Sounds);
    
        novo = new MetronomeContructor(4, 4, 120, this.BaseUri, this, MediaElemTick, MediaElemTock);
    }
    private void sliderBpm_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
    {           
            if (novo != null)
            {
                novo.Bpm = (int)sliderBpm.Value;
                novo.CalculateBpm(novo.Bpm);
                DisplayTxtBlock.Text = novo.Bpm.ToString();
                novo.StartInterval();
            }                     
    }
    
    private void StartButton_Click(object sender, RoutedEventArgs e)
    {
        if (novo!=null)
        {
            novo.DispTimer.Start();
        }
    }
    
    private void StopButton_Click(object sender, RoutedEventArgs e)
    {
        if (novo != null)
        {
            novo.DispTimer.Stop();
        }
    }
    

I'm still beggining to implement the functionality, but as a musician i realized that the Dispatcher is not running effectively at a given TimeSpan, so I tried to make it Async operation. Eventhough is not accurate.

I read that with a Dispatcher we only have for sure that will not run before the TimeSpan, but there is the chance it can run after the TimeSpan without control. My question is this a Design issue? Does anyone know an effective Timer in UWP? What can be done to implement a simple metronome that can be effective?

Considerations: I'm building a UWP windows 10 App using VisualStudio 2015 I'm a musician that like's to Code in my free time, any design or code improvement ideias are wellcome. Also English is not my native language. TY

  • Async won't help here - everything goes on UI thread, so it can get lagged I think. Maybe you can try [System.Threading.Timer](https://msdn.microsoft.com/en-us/library/2x96zfy7(v=vs.110).aspx), that will run on separate thread, though you will have to use dispatcher to access UI elements. – Romasz Nov 20 '15 at 15:45
  • Creating your own timer in C# is never accurate, see http://stackoverflow.com/questions/11588337/how-to-make-an-accurate-decimal-timer – Bart Nov 20 '15 at 16:24
  • @Romasz I only tried to make it Async because the media Player naturally delay the execucion of the Method. I think you're right about the use of System.Threading.Timer, I will read about how it works and how i can use that Class. Thank you. – Lino Lisboa Nov 20 '15 at 18:24
  • @LinoLisboa But probably, even if you change the timer, there still will be problems. You are using *MediaPlayer* and it's not probably designed to play sounds in short intervals. Maybe you can try to use *DirectSound*. – Romasz Nov 20 '15 at 19:05
  • @Romasz Ok, Ty one more time. I go read about DirectSound also. Although i'm convinced it should work with MediaPlayer just because the media files used have just a couple miliseconds and should not interfere if I keep an Async Method. Also i'm loading the MediaFile objects at MainPage load and not during the event Tick. I will hard test that. Regards – Lino Lisboa Nov 20 '15 at 19:48
  • The best you can do with anything on the dispatcher is to call it using the invoke method with a priority of Send, if that doesn't help you, then it means there's too much work going on for it to keep time correctly. Remember the GUI dispatcher thread also has to render all the content. Here's how you marshall onto the GUI thread using send option MyControl.Dispatcher.Invoke(()=>{//do stuff here}, DispatcherPrority.Send); This will allow you to set up any type of timer you want and on the callback, perform the invoke directly to the control. – JWP Nov 22 '15 at 17:32

0 Answers0