[UWP]: Alarm notification audio when system volume is set to off

谁说我不能喝 提交于 2019-12-11 08:33:50

问题


Hi all :) We are coding an app for the local fire departements since they plan to remove one of two pager frequencies and replace it with an app (they have two alert channels then, classic pager and mobile phone). In case of an incoming alert, the phone creates an alarm-scenario ToastNotification with audio. So far so good, the problem is that the audio file isn't played if the user has set the system master volume to zero. I know it's not possible so far to change the system volume from an app, but for my scenario I need to play the audio notification regardless of the phones volume settings etc.

If somebody has an idea how to overcome that Problem I would greatly appreciate that :)


回答1:


I know it's not what you want to hear but Win10Mobile devices are consumer devices. How would you feel if you bought a phone, muted it, and it still made noises?

When a person mutes their phone they're saying it shouldn't make a noise. You're saying you want to overwrite the users setting and that's not what a device should do.

Where you have specialist requirements a specialist device may be required. I haven't checked but there may be devices (not UWP) available that don't have user accessible hardware volume settings.

If you can't control the device being used then I'd suggest getting (or making?) a case that hides access to the volume buttons.

At the end of the day if a person is accountable for answering a phone when a call/message is received then there is only so much you can do in code on a device with a fixed set of abilities.




回答2:


I ran into exactly the same problem. Fortunately I found a working solution by using the IAudioEndpointVolume from C# interface using P/Invoke.

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Windows.Media.Devices;

namespace AudioUtils
{
    public static class VolumeControl
    {
        public static void ChangeVolumeToMinLevel(double level)
        {
            if (level > 1)
                level = 1;
            else if (level < 0)
                level = 0;

            try
            {
                var masterVol = GetAudioEndpointVolume();

                if (masterVol == null)
                    return;

                var hr = masterVol.GetMute(out var muted);

                if (hr != (uint)HResult.S_OK)
                    return;

                if (muted)
                {
                    masterVol.SetMute(false, Guid.Empty);
                }

                // Only adapt volume if the current level is below the specified minumum level
                hr = masterVol.GetMasterVolumeLevelScalar(out float currentAudioValue);
                float newAudioValue = Convert.ToSingle(level);
                if (currentAudioValue > newAudioValue)
                    return;

                masterVol.SetMasterVolumeLevelScalar(newAudioValue, Guid.Empty);
            }
            catch { }
        }

        private static IAudioEndpointVolume GetAudioEndpointVolume()
        {
            var speakerId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
            var completionHandler = new ActivateAudioInterfaceCompletionHandler<IAudioEndpointVolume>();

            var hr = ActivateAudioInterfaceAsync(
                speakerId,
                typeof(IAudioEndpointVolume).GetTypeInfo().GUID,
                IntPtr.Zero,
                completionHandler,
                out var activateOperation);

            Debug.Assert(hr == (uint)HResult.S_OK);

            return completionHandler.WaitForCompletion();
        }

        [DllImport("Mmdevapi.dll", ExactSpelling = true, PreserveSig = false)]
        [return: MarshalAs(UnmanagedType.Error)]
        private static extern uint ActivateAudioInterfaceAsync(
                [In, MarshalAs(UnmanagedType.LPWStr)]string deviceInterfacePath,
                [In, MarshalAs(UnmanagedType.LPStruct)]Guid riid,
                [In] IntPtr activationParams,
                [In] IActivateAudioInterfaceCompletionHandler completionHandler,
                out IActivateAudioInterfaceAsyncOperation activationOperation);
    }

    internal class ActivateAudioInterfaceCompletionHandler<T> : IActivateAudioInterfaceCompletionHandler
    {
        private AutoResetEvent _completionEvent;
        private T _result;

        public ActivateAudioInterfaceCompletionHandler()
        {
            _completionEvent = new AutoResetEvent(false);
        }

        public void ActivateCompleted(IActivateAudioInterfaceAsyncOperation operation)
        {
            operation.GetActivateResult(out var hr, out var activatedInterface);

            Debug.Assert(hr == (uint)HResult.S_OK);

            _result = (T)activatedInterface;

            var setResult = _completionEvent.Set();
            Debug.Assert(setResult != false);
        }

        public T WaitForCompletion()
        {
            var waitResult = _completionEvent.WaitOne();
            Debug.Assert(waitResult != false);

            return _result;
        }
    }

    internal enum HResult : uint
    {
        S_OK = 0
    }

    [ComImport]
    [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IAudioEndpointVolume
    {
        [PreserveSig]
        int NotImpl1();

        [PreserveSig]
        int NotImpl2();

        [PreserveSig]
        int GetChannelCount([Out] [MarshalAs(UnmanagedType.U4)] out uint channelCount);

        [PreserveSig]
        int SetMasterVolumeLevel(
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int SetMasterVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int GetMasterVolumeLevel([Out] [MarshalAs(UnmanagedType.R4)] out float level);

        [PreserveSig]
        int GetMasterVolumeLevelScalar([Out] [MarshalAs(UnmanagedType.R4)] out float level);

        [PreserveSig]
        int SetChannelVolumeLevel(
            [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelNumber,
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int SetChannelVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [In] [MarshalAs(UnmanagedType.R4)] float level,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        int GetChannelVolumeLevel(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [Out] [MarshalAs(UnmanagedType.R4)] out float level);

        [PreserveSig]
        int GetChannelVolumeLevelScalar(
            [In] [MarshalAs(UnmanagedType.U4)] uint channelNumber,
            [Out] [MarshalAs(UnmanagedType.R4)] out float level);

        [PreserveSig]
        int SetMute(
            [In] [MarshalAs(UnmanagedType.Bool)] bool isMuted,
            [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int GetMute([Out] [MarshalAs(UnmanagedType.Bool)] out bool isMuted);

        [PreserveSig]
        int GetVolumeStepInfo(
            [Out] [MarshalAs(UnmanagedType.U4)] out uint step,
            [Out] [MarshalAs(UnmanagedType.U4)] out uint stepCount);

        [PreserveSig]
        int VolumeStepUp([In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int VolumeStepDown([In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);

        [PreserveSig]
        int QueryHardwareSupport([Out] [MarshalAs(UnmanagedType.U4)] out uint hardwareSupportMask);

        [PreserveSig]
        int GetVolumeRange(
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeMin,
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeMax,
            [Out] [MarshalAs(UnmanagedType.R4)] out float volumeStep);
    }

    [ComImport]
    [Guid("72A22D78-CDE4-431D-B8CC-843A71199B6D")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IActivateAudioInterfaceAsyncOperation
    {
        void GetActivateResult(
            [MarshalAs(UnmanagedType.Error)]out uint activateResult,
            [MarshalAs(UnmanagedType.IUnknown)]out object activatedInterface);
    }

    [ComImport]
    [Guid("41D949AB-9862-444A-80F6-C261334DA5EB")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IActivateAudioInterfaceCompletionHandler
    {
        void ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation);
    }
}

Credits

  • Reddit user sunius. See this thread and this code.
  • GitHub user vannatech. See this code.

The code provided by sunius had two major drawbacks:

  • It uses unsafe code
  • It did not work in Release mode (the app crashes)

To get rid of the unsafe code and the crash, more explicit marshalling attributes has been used and signatures are preserved (see the vannatech source code)



来源:https://stackoverflow.com/questions/41636179/uwp-alarm-notification-audio-when-system-volume-is-set-to-off

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!