105 lines
5.2 KiB
C#
105 lines
5.2 KiB
C#
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" };
|
|
|
|
/// <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(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;
|
|
}
|
|
}
|
|
}
|