Search code examples
c#uwpbackground-process

Keep MiDi output playing when UWP goes into background


I'm trying to build a metronome application that has the capability to run in the background. As a starting point I decided to create something simple, a class that has a timer (the metronome itself), a class responsible for obtaining the MIDI output device and a class to play the sound. I'm having difficulty with how to make this run in the background. Additionally, another problem is the fact that the metronome needs to be executed when clicking an application button (in the main process).

Metronome Class:

public class Metronome
{
    private DispatcherTimer timer = new DispatcherTimer();

    private MidiDeviceSelector deviceSelector = new MidiDeviceSelector();

    private void TimerStart()
    {
        timer.Start();
        timer.Tick += timer_Tick;
    }

    private void timer_Tick(object sender, object e)
    {
        AudioPlayback.Beep1();
    }

    public void Start(int bpm)
    {
        double interval = (double)60.000f / (bpm);
        timer.Interval = TimeSpan.FromSeconds(interval);

        TimerStart();
    }

    public void Stop()
    {
        timer.Stop();
    }
}

MidiDeviceSelector:

class MidiDeviceSelector
{
    public MidiDeviceSelector()
    {
        GetOutputMidiDevice();
    }

    public async void GetOutputMidiDevice()
    {
        IMidiOutPort currentMidiOutputDevice;

        DeviceInformation devInfo;
        DeviceInformationCollection devInfoCollection;

        string devInfoId;

        devInfoCollection = await DeviceInformation.FindAllAsync(MidiOutPort.GetDeviceSelector());

        if (devInfoCollection == null)
        {
            //notify the user that any device was found.
            System.Diagnostics.Debug.WriteLine("Any device was found.");
        }

        devInfo = devInfoCollection[0];

        if (devInfo == null)
        {
            //Notify the User that the device not found
            System.Diagnostics.Debug.WriteLine("Device not found.");
        }


        devInfoId = devInfo.Id.ToString();
        currentMidiOutputDevice = await MidiOutPort.FromIdAsync(devInfoId);

        if (currentMidiOutputDevice == null)
        {
            //Notify the User that wasn't possible to create MidiOutputPort for the device.
            System.Diagnostics.Debug.WriteLine("It was not possible to create the OutPort for the device.");
        }

        MidiDevice.midiDevice = currentMidiOutputDevice;
    }

Class to Holds the MidiDevice:

class MidiDevice
{
    public static IMidiOutPort midiDevice; //Bad practice i know.
}

Class to play the "toc" sound:

class AudioPlayback
{
    static IMidiMessage beep1 = new MidiNoteOnMessage(9, 76, 90);

    //static IMidiOutPort midiOutputDevice = (IMidiOutPort)MidiDeviceSelector.GetOutputMidiDevice();


    public static void Beep1()
    {
        try
        {
            MidiDevice.midiDevice.SendMessage(beep1);
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.Message);
        }
    }
}

Each class is contained in a different file. As you can see, it is a very simple code, if you see any bad programming practice, I apologise, I do not have much experience.

I was looking at the documentation, however, I did not succeed. How do I register an activity in the background and that there is interaction with the application's user interface (a button to stop and start the metronome).

My apologies for bad English, not my native language.

Thank you.


Solution

  • Two things you need to add to make this scenario work:

    1. add the "backgroundMediaPlayback" capability as documented here: https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/background-audio

    2. since you are using the MiDI APIs, you need to explicitly integrate with the SystemMediaTransportControls to prevent getting muted on minimize

    I have update your repro sample and verified that it works correctly after adding those two things.

    Sharing it here for your reference: https://1drv.ms/u/s!AovTwKUMywTNl9QJTeecnDzCf0WWyQ