Skip to content

Instantly share code, notes, and snippets.

@RickDB
Created January 8, 2017 15:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickDB/f2e2b7f723ef2c974250334b0415f799 to your computer and use it in GitHub Desktop.
Save RickDB/f2e2b7f723ef2c974250334b0415f799 to your computer and use it in GitHub Desktop.
#region Copyright (C) 2005-2013 Team MediaPortal
// Copyright (C) 2005-2013 Team MediaPortal
// http://www.team-mediaportal.com
//
// MediaPortal is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// MediaPortal is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MediaPortal. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using AudioSwitcher.AudioApi;
using AudioSwitcher.AudioApi.CoreAudio;
using MediaPortal.ExtensionMethods;
using MediaPortal.GUI.Library;
using MediaPortal.Profile;
using Microsoft.Win32;
using Action = MediaPortal.GUI.Library.Action;
namespace MediaPortal.Player
{
/// <summary>
/// Looks like this class is notably taking care of broadcasting various volume change events.
/// This is intended to be used as a singleton.
/// The singleton instance should be created from MP main thread.
/// </summary>
public class VolumeHandler
{
#region Vars
static HideVolumeOSD.HideVolumeOSDLib VolumeOSD;
private CoreAudioController _controller;
private CoreAudioDevice _device;
#endregion
#region Constructors
public VolumeHandler() : this(LoadFromRegistry()) {}
public VolumeHandler(int[] volumeTable)
{
if (GUIGraphicsContext.DeviceAudioConnected > 0)
{
bool isDigital;
bool hideWindowsOSD;
using (Settings reader = new MPSettings())
{
int levelStyle = reader.GetValueAsInt("volume", "startupstyle", 0);
if (levelStyle == 0)
{
_startupVolume = Math.Max(0, Math.Min(65535, reader.GetValueAsInt("volume", "lastknown", 52428)));
}
if (levelStyle == 1)
{
}
if (levelStyle == 2)
{
_startupVolume = Math.Max(0, Math.Min(65535, reader.GetValueAsInt("volume", "startuplevel", 52428)));
}
isDigital = reader.GetValueAsBool("volume", "digital", false);
_showVolumeOSD = reader.GetValueAsBool("volume", "defaultVolumeOSD", true);
hideWindowsOSD = reader.GetValueAsBool("volume", "hideWindowsOSD", false);
}
_controller = new CoreAudioController();
_device = _controller.GetDefaultDevice(DeviceType.Playback, Role.Multimedia);
if (OSInfo.OSInfo.Win8OrLater() && hideWindowsOSD)
{
try
{
bool tempShowVolumeOSD = _showVolumeOSD;
_showVolumeOSD = false;
VolumeOSD = new HideVolumeOSD.HideVolumeOSDLib(IsMuted);
VolumeOSD.HideOSD();
_showVolumeOSD = tempShowVolumeOSD;
}
catch { }
}
}
else
{
_volumeTable = volumeTable;
}
}
#endregion Constructors
#region Methods
/// <summary>
/// Create our volume handler singleton.
/// </summary>
public static void CreateInstance()
{
if (_instance==null)
{
_instance = Create();
}
}
/// <summary>
/// Create a volume handler.
/// </summary>
/// <returns>A newly created volume handler.</returns>
private static VolumeHandler Create()
{
if (GUIGraphicsContext.DeviceAudioConnected > 0)
{
using (Settings reader = new MPSettings())
{
int volumeStyle = reader.GetValueAsInt("volume", "handler", 1);
switch (volumeStyle)
{
// classic volume table
case 0:
return new VolumeHandler(new[] {0, 6553, 13106, 19659, 26212, 32765, 39318, 45871, 52424, 58977, 65535});
// windows default from registry
case 1:
return new VolumeHandler();
// logarithmic
case 2:
return new VolumeHandler(new[]
{
0, 1039, 1234, 1467, 1744, 2072, 2463, 2927, 3479, 4135, 4914, 5841, 6942, 8250,
9806
, 11654, 13851, 16462, 19565, 23253, 27636, 32845, 39037, 46395, 55141, 65535
});
// custom user setting
case 3:
return new VolumeHandlerCustom();
// defaults to vista safe "0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, 32767, 36863, 40959, 45055, 49151, 53247, 57343, 61439, 65535"
// Vista recommended values
case 4:
return new VolumeHandler(new[]
{
0, 4095, 8191, 12287, 16383, 20479, 24575, 28671, 32767, 36863, 40959, 45055,
49151,
53247, 57343, 61439, 65535
});
default:
return new VolumeHandlerCustom();
}
}
}
return new VolumeHandlerCustom();
}
public static void Dispose()
{
if (_instance == null)
{
return;
}
if (_instance._device != null)
{
using (Settings writer = new MPSettings())
{
writer.SetValue("volume", "lastknown", _instance._device.Volume);
}
_instance._device.SafeDispose();
_instance._device = null;
}
_instance = null;
GUIGraphicsContext.VolumeHandler = null;
}
public virtual void UnMute()
{
_device.Mute(false);
}
private static int[] LoadFromRegistry()
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Multimedia\Audio\VolumeControl"))
{
if (key == null)
{
return SystemTable;
}
if (Equals(key.GetValue("EnableVolumeTable", 0), 0))
{
return SystemTable;
}
var buffer = (byte[])key.GetValue("VolumeTable", null);
if (buffer == null)
{
return SystemTable;
}
// Windows documentation states that a volume table must consist of between 11 and 201 entries
if ((buffer.Length / 4) < 11 || (buffer.Length / 4) > 201)
{
return SystemTable;
}
// create an array large enough to hold the system's volume table
var volumeTable = new int[buffer.Length / 4];
for (int index = 0, offset = 0; index < volumeTable.Length; index++, offset += 4)
{
volumeTable[index] = Marshal.ReadInt32(buffer, offset);
}
return volumeTable;
}
}
protected virtual void SetVolume(double volume)
{
if (_device != null)
{
if (_device.IsMuted)
{
_device.Mute(false);
}
_device.Volume = volume;
}
}
protected virtual void SetVolume(bool isMuted)
{
if (_device != null)
{
_device.Mute(isMuted);
}
}
private void HandleGUIOnControlChange()
{
try
{
var msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_AUDIOVOLUME_CHANGED, 0, 0, 0, 0, 0, 0)
{
Label = Instance.Step.ToString(CultureInfo.InvariantCulture),
Label2 = Instance.StepMax.ToString(CultureInfo.InvariantCulture),
Label3 = Instance.IsMuted.ToString(CultureInfo.InvariantCulture)
};
GUIGraphicsContext.SendMessage(msg);
var showVolume = new Action(Action.ActionType.ACTION_SHOW_VOLUME, 0, 0);
GUIWindowManager.OnAction(showVolume);
}
catch (Exception e)
{
Log.Info("VolumeHandler.HandleGUIOnControlChange: {0}", e.ToString());
}
}
private static void mixer_ControlChanged(object sender, Mixer.MixerEventArgs e)
{
Instance.HandleGUIOnControlChange();
GUIGraphicsContext.VolumeOverlay = true;
GUIGraphicsContext.VolumeOverlayTimeOut = DateTime.Now;
Instance.UpdateVolumeProperties();
}
public void UpdateVolumeProperties()
{
float fRange = (float)(Instance.Maximum - Instance.Minimum);
float fPos = (float)(Instance.Volume - Instance.Minimum);
float fPercent = (fPos / fRange) * 100.0f;
GUIPropertyManager.SetProperty("#volume.percent", ((int)Math.Round(fPercent)).ToString());
GUIPropertyManager.SetProperty("#volume.mute", Instance.IsMuted.ToString().ToLowerInvariant());
}
#endregion Methods
#region Properties
public virtual double Volume
{
get
{
if (_device != null)
{
return _device.Volume;
}
return 0;
}
set { SetVolume(value); }
}
public virtual bool IsMuted
{
get
{
if (_device != null)
{
return _device.IsMuted;
}
return false;
}
set { SetVolume(value); }
}
public virtual bool IsEnabledVolumeOSD
{
get { return _showVolumeOSD; }
set { _showVolumeOSD = value; }
}
public virtual int Next
{
get
{
if (_volumeTable != null)
{
lock (_volumeTable)
foreach (int vol in _volumeTable.Where(vol => Volume < vol))
{
return vol;
}
return Maximum;
}
return 0;
}
}
public virtual int Maximum
{
get { return 65535; }
}
public virtual int Minimum
{
get { return 0; }
}
public virtual int Step
{
get
{
lock (_volumeTable)
for (int index = 0; index < _volumeTable.Length; ++index)
{
if (Volume <= _volumeTable[index])
{
return index;
}
}
return _volumeTable.Length;
}
}
public virtual int StepMax
{
get { lock (_volumeTable) return _volumeTable.Length; }
}
public virtual int Previous
{
get
{
if (_volumeTable != null)
{
lock (_volumeTable)
for (int index = _volumeTable.Length - 1; index >= 0; --index)
{
if (Volume > _volumeTable[index])
{
return _volumeTable[index];
}
}
return Minimum;
}
return 0;
}
}
protected virtual int[] Table
{
set { lock (_volumeTable) _volumeTable = value; }
}
/// <summary>
/// Provide our instance singleton.
/// Can be null until explicitly created through CreateInstance.
/// </summary>
public static VolumeHandler Instance
{
get { return _instance; }
}
#endregion Properties
#region Fields
private static VolumeHandler _instance;
private static readonly int[] SystemTable = new[]
{
0, 1039, 1234, 1467, 1744, 2072,
2463, 2927, 3479, 4135, 4914, 5841,
6942, 8250, 9806, 11654, 13851, 16462,
19565, 23253, 27636, 32845, 39037, 46395,
55141, 65535
};
private int[] _volumeTable;
private int _startupVolume;
private static bool _showVolumeOSD;
#endregion Fields
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment