using System;
using System.Linq;
using System.Threading.Tasks;
using Swan;
namespace Unosquare.RaspberryIO.Computer {
///
/// Settings for audio device.
///
public class AudioSettings : SingletonBase {
private const String DefaultControlName = "PCM";
private const Int32 DefaultCardNumber = 0;
private readonly String[] _errorMess = { "Invalid", "Unable" };
///
/// Gets the current audio state.
///
/// The card number.
/// Name of the control.
/// An object.
/// Invalid command, card number or control name.
public async Task 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);
}
///
/// Sets the volume percentage.
///
/// The percentage level.
/// The card number.
/// Name of the control.
/// A representing the asynchronous operation.
/// Invalid card number or control name.
public Task SetVolumePercentage(Int32 level, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{level}%", cardNumber, controlName);
///
/// Sets the volume by decibels.
///
/// The decibels.
/// The card number.
/// Name of the control.
/// A representing the asynchronous operation.
/// Invalid card number or control name.
public Task SetVolumeByDecibels(Single decibels, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{decibels}dB", cardNumber, controlName);
///
/// Increments the volume by decibels.
///
/// The decibels to increment or decrement.
/// The card number.
/// Name of the control.
/// A representing the asynchronous operation.
/// Invalid card number or control name.
public Task IncrementVolume(Single decibels, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand($"{decibels}dB{(decibels < 0 ? "-" : "+")}", cardNumber, controlName);
///
/// Toggles the mute state.
///
/// if set to true, mutes the audio.
/// The card number.
/// Name of the control.
/// A representing the asynchronous operation.
/// Invalid card number or control name.
public Task ToggleMute(Boolean mute, Int32 cardNumber = DefaultCardNumber, String controlName = DefaultControlName) => SetAudioCommand(mute ? "mute" : "unmute", cardNumber, controlName);
private static async Task 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;
}
}
}