RaspberryIO_26/Unosquare.RaspberryIO/Computer/AudioSettings.cs

114 lines
5.6 KiB
C#
Raw Normal View History

2019-12-04 18:57:18 +01:00
namespace Unosquare.RaspberryIO.Computer
{
using Swan;
using System;
using System.Linq;
using System.Threading.Tasks;
/// <summary>
/// Settings for audio device.
/// </summary>
public class AudioSettings : SingletonBase<AudioSettings>
{
private const string DefaultControlName = "PCM";
private const int DefaultCardNumber = 0;
private readonly string[] _errorMess = { "Invalid", "Unable" };
/// <summary>
/// Gets the current audio state.
/// </summary>
/// <param name="cardNumber">The card number.</param>
/// <param name="controlName">Name of the control.</param>
/// <returns>An <see cref="AudioState"/> object.</returns>
/// <exception cref="InvalidOperationException">Invalid command, card number or control name.</exception>
public async Task<AudioState> GetState(int cardNumber = DefaultCardNumber, string controlName = DefaultControlName)
{
var volumeInfo = await ProcessRunner.GetProcessOutputAsync("amixer", $"-c {cardNumber} get {controlName}").ConfigureAwait(false);
var lines = volumeInfo.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (!lines.Any())
throw new InvalidOperationException("Invalid command.");
if (_errorMess.Any(x => lines[0].Contains(x)))
throw new InvalidOperationException(lines[0]);
var volumeLine = lines
.FirstOrDefault(x => x.Trim()
.StartsWith("Mono:", StringComparison.OrdinalIgnoreCase));
if (volumeLine == null)
throw new InvalidOperationException("Unexpected output from 'amixer'.");
var sections = volumeLine.Split(new[] { ' ' },
StringSplitOptions.RemoveEmptyEntries);
var level = int.Parse(sections[3].Substring(1, sections[3].Length - 3),
System.Globalization.NumberFormatInfo.InvariantInfo);
var decibels = float.Parse(sections[4].Substring(1, sections[4].Length - 4),
System.Globalization.NumberFormatInfo.InvariantInfo);
var isMute = sections[5].Equals("[off]",
StringComparison.CurrentCultureIgnoreCase);
return new AudioState(cardNumber, controlName, level, decibels, isMute);
}
/// <summary>
/// Sets the volume percentage.
/// </summary>
/// <param name="level">The percentage level.</param>
/// <param name="cardNumber">The card number.</param>
/// <param name="controlName">Name of the control.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Invalid card number or control name.</exception>
public Task SetVolumePercentage(int level, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
SetAudioCommand($"{level}%", cardNumber, controlName);
/// <summary>
/// Sets the volume by decibels.
/// </summary>
/// <param name="decibels">The decibels.</param>
/// <param name="cardNumber">The card number.</param>
/// <param name="controlName">Name of the control.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Invalid card number or control name.</exception>
public Task SetVolumeByDecibels(float decibels, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
SetAudioCommand($"{decibels}dB", cardNumber, controlName);
/// <summary>
/// Increments the volume by decibels.
/// </summary>
/// <param name="decibels">The decibels to increment or decrement.</param>
/// <param name="cardNumber">The card number.</param>
/// <param name="controlName">Name of the control.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Invalid card number or control name.</exception>
public Task IncrementVolume(float decibels, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
SetAudioCommand($"{decibels}dB{(decibels < 0 ? "-" : "+")}", cardNumber, controlName);
/// <summary>
/// Toggles the mute state.
/// </summary>
/// <param name="mute">if set to <c>true</c>, mutes the audio.</param>
/// <param name="cardNumber">The card number.</param>
/// <param name="controlName">Name of the control.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Invalid card number or control name.</exception>
public Task ToggleMute(bool mute, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName) =>
SetAudioCommand(mute ? "mute" : "unmute", cardNumber, controlName);
private static async Task<string> SetAudioCommand(string command, int cardNumber = DefaultCardNumber, string controlName = DefaultControlName)
{
var taskResult = await ProcessRunner.GetProcessOutputAsync("amixer", $"-q -c {cardNumber} -- set {controlName} {command}").ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(taskResult))
throw new InvalidOperationException(taskResult.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).First());
return taskResult;
}
}
}