RaspberryIO_26/Unosquare.RaspberryIO/Computer/AudioSettings.cs

105 lines
5.2 KiB
C#
Raw Normal View History

2019-12-06 23:12:34 +01:00
using System;
using System.Linq;
using System.Threading.Tasks;
using Swan;
namespace Unosquare.RaspberryIO.Computer {
/// <summary>
/// Settings for audio device.
/// </summary>
public class AudioSettings : SingletonBase<AudioSettings> {
private const String DefaultControlName = "PCM";
private const Int32 DefaultCardNumber = 0;
private readonly String[] _errorMess = { "Invalid", "Unable" };
2019-12-04 18:57:18 +01:00
/// <summary>
2019-12-06 23:12:34 +01:00
/// Gets the current audio state.
2019-12-04 18:57:18 +01:00
/// </summary>
2019-12-06 23:12:34 +01:00
/// <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(Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) {
String volumeInfo = await ProcessRunner.GetProcessOutputAsync("amixer", $"-c {cardNumber} get {controlName}").ConfigureAwait(false);
String[] lines = volumeInfo.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
if(!lines.Any()) {
throw new InvalidOperationException("Invalid command.");
}
if(this._errorMess.Any(x => lines[0].Contains(x))) {
throw new InvalidOperationException(lines[0]);
}
String volumeLine = lines.FirstOrDefault(x => x.Trim().StartsWith("Mono:", StringComparison.OrdinalIgnoreCase));
if(volumeLine == null) {
throw new InvalidOperationException("Unexpected output from 'amixer'.");
}
String[] sections = volumeLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
Int32 level = Int32.Parse(sections[3][1..^2], System.Globalization.NumberFormatInfo.InvariantInfo);
Single decibels = Single.Parse(sections[4][1..^3], System.Globalization.NumberFormatInfo.InvariantInfo);
Boolean 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(Int32 level, Int32 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(Single decibels, Int32 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(Single decibels, Int32 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(Boolean mute, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand(mute ? "mute" : "unmute", cardNumber, controlName);
private static async Task<String> SetAudioCommand(String command, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) {
String 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;
}
}
2019-12-04 18:57:18 +01:00
}