From 469510fa73ea4c37b6170631c3eea07a0f1759c9 Mon Sep 17 00:00:00 2001 From: joekolodz Date: Wed, 6 Nov 2024 18:39:14 -0600 Subject: [PATCH] Switch audio playback to NAudio; clarify Mode function names; fix json reader/writer to suport float type --- src/CustomSierraJsonConverter.cs | 2 +- src/Models/HOTASAxis.cs | 2 +- src/Models/HOTASDevice.cs | 9 ++--- src/Models/HOTASQueue.cs | 4 +-- src/Models/IHOTASQueue.cs | 4 +-- src/Models/IMediaPlayer.cs | 9 ++--- src/Models/MediaPlayerWrapper.cs | 41 +++++++++------------- src/Models/Segment.cs | 4 +++ src/Serializer/Read.cs | 22 ++++++++---- src/Serializer/Write.cs | 10 ++++++ src/SierraHOTAS.csproj | 3 ++ src/ViewModels/AxisMapViewModel.cs | 19 ++++------ src/ViewModels/HOTASCollectionViewModel.cs | 2 -- tests/AxisMapViewModelTests.cs | 7 ++-- tests/HOTASQueueTests.cs | 2 +- 15 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/CustomSierraJsonConverter.cs b/src/CustomSierraJsonConverter.cs index 0e95ee9..96d288c 100644 --- a/src/CustomSierraJsonConverter.cs +++ b/src/CustomSierraJsonConverter.cs @@ -152,7 +152,7 @@ private static void SerializeAxis(HOTASAxis axis) if (prop.Name == nameof(axis.IsDirectional) && (bool)prop.GetValue(axis) == true) continue; if (prop.Name == nameof(axis.IsMultiAction) && (bool)prop.GetValue(axis) == false) continue; if (prop.Name == nameof(axis.SoundFileName) && string.IsNullOrEmpty((string)prop.GetValue(axis))) continue; - if (prop.Name == nameof(axis.SoundVolume) && Math.Abs((double)prop.GetValue(axis) - 1.0d) < 0.01) continue; + if (prop.Name == nameof(axis.SoundVolume) && Math.Abs((float)prop.GetValue(axis) - 1.0d) < 0.01) continue; Serializer.WriteKeyValue(prop.Name, value); } diff --git a/src/Models/HOTASAxis.cs b/src/Models/HOTASAxis.cs index 07266e8..3ba4687 100644 --- a/src/Models/HOTASAxis.cs +++ b/src/Models/HOTASAxis.cs @@ -20,7 +20,7 @@ public class HOTASAxis : IHotasBaseMap public bool IsDirectional { get; set; } = true; public bool IsMultiAction { get; set; } = false; public string SoundFileName { get; set; } - public double SoundVolume { get; set; } = 1.0d; + public float SoundVolume { get; set; } = 1.0f; public ObservableCollection Segments { get; set; } [SierraJsonIgnore] diff --git a/src/Models/HOTASDevice.cs b/src/Models/HOTASDevice.cs index a52011d..387f2c6 100644 --- a/src/Models/HOTASDevice.cs +++ b/src/Models/HOTASDevice.cs @@ -285,7 +285,7 @@ public void OverlayAllModesToDevice() } Modes = mergedModes; - _hotasQueue?.SetModes(Modes); + _hotasQueue?.SetModesCollection(Modes); } /// @@ -305,7 +305,7 @@ public void OverlayAllModesToDevice(Dictionary @@ -366,7 +366,7 @@ public void SetMode(int mode) return; } ButtonMap = Modes[mode]; - _hotasQueue.SetMode(mode); + _hotasQueue.ActivateMode(mode); } public void SetModeActivation(Dictionary modeActivationButtons) @@ -562,7 +562,8 @@ public void Reset() _modeActivationButtons.Clear(); //Modes = new Dictionary>(); - _hotasQueue.SetModes(Modes); + _hotasQueue.SetModesCollection(Modes); + _hotasQueue.ActivateMode(1); } private void ClearButtonMap() diff --git a/src/Models/HOTASQueue.cs b/src/Models/HOTASQueue.cs index f70cf17..1a660e3 100644 --- a/src/Models/HOTASQueue.cs +++ b/src/Models/HOTASQueue.cs @@ -523,13 +523,13 @@ public void SetButtonMap(ObservableCollection buttonMap) _buttonMap = buttonMap; } - public void SetMode(int mode) + public void ActivateMode(int mode) { _mode = mode; _buttonMap = _modes[_mode]; } - public void SetModes(Dictionary> modes) + public void SetModesCollection(Dictionary> modes) { _modes = modes; } diff --git a/src/Models/IHOTASQueue.cs b/src/Models/IHOTASQueue.cs index 156a29a..2a23de5 100644 --- a/src/Models/IHOTASQueue.cs +++ b/src/Models/IHOTASQueue.cs @@ -23,7 +23,7 @@ public interface IHOTASQueue void Stop(); IHotasBaseMap GetMap(int buttonOffset); void SetButtonMap(ObservableCollection buttonMap); - void SetMode(int mode); - void SetModes(Dictionary> modes); + void ActivateMode(int mode); + void SetModesCollection(Dictionary> modes); } } \ No newline at end of file diff --git a/src/Models/IMediaPlayer.cs b/src/Models/IMediaPlayer.cs index ad73d1f..e2ace8f 100644 --- a/src/Models/IMediaPlayer.cs +++ b/src/Models/IMediaPlayer.cs @@ -1,5 +1,4 @@ using System; -using System.Windows.Threading; namespace SierraHOTAS.Models { @@ -7,10 +6,8 @@ public interface IMediaPlayer { void Play(); void Close(); - void Open(Uri source); - Dispatcher Dispatcher { get; } - bool IsMuted { get; set; } - double Volume { get; set; } - TimeSpan Position { get; set; } + void Open(string sourceFilePath); + float Volume { get; set; } + long Position { get; set; } } } diff --git a/src/Models/MediaPlayerWrapper.cs b/src/Models/MediaPlayerWrapper.cs index 4795613..a25d8b5 100644 --- a/src/Models/MediaPlayerWrapper.cs +++ b/src/Models/MediaPlayerWrapper.cs @@ -1,54 +1,45 @@ -using System; +using NAudio.Wave; using System.Diagnostics.CodeAnalysis; -using System.Windows.Media; -using System.Windows.Threading; namespace SierraHOTAS.Models { [ExcludeFromCodeCoverage] public class MediaPlayerWrapper : IMediaPlayer { - private readonly MediaPlayer _mediaPlayer; + private readonly WaveOutEvent _outputDevice; + private AudioFileReader _audioFile; - public MediaPlayerWrapper(MediaPlayer mediaPlayer) + public MediaPlayerWrapper(WaveOutEvent outputDevice) { - _mediaPlayer = mediaPlayer; + _outputDevice = outputDevice; } public void Play() { - _mediaPlayer.Play(); + _outputDevice.Play(); } public void Close() { - _mediaPlayer.Close(); + _audioFile.Close(); } - public void Open(Uri source) + public void Open(string sourceFilePath) { - _mediaPlayer.Open(source); + _audioFile = new AudioFileReader(sourceFilePath); + _outputDevice.Init(_audioFile); } - public Dispatcher Dispatcher => _mediaPlayer.Dispatcher; - - - public bool IsMuted - { - get => _mediaPlayer.IsMuted; - set => _mediaPlayer.IsMuted = value; - } - - public double Volume + public float Volume { - get => _mediaPlayer.Volume; - set => _mediaPlayer.Volume = value; + get => _outputDevice.Volume; + set => _outputDevice.Volume = value; } - public TimeSpan Position + public long Position { - get => _mediaPlayer.Position; - set => _mediaPlayer.Position = value; + get => _audioFile.Position; + set => _audioFile.Position = value; } } } diff --git a/src/Models/Segment.cs b/src/Models/Segment.cs index b0046a3..b028415 100644 --- a/src/Models/Segment.cs +++ b/src/Models/Segment.cs @@ -20,6 +20,10 @@ public int Value } } + public Segment() + { + } + public Segment(int id, int value) { Id = id; diff --git a/src/Serializer/Read.cs b/src/Serializer/Read.cs index 0f4d8d5..4c14208 100644 --- a/src/Serializer/Read.cs +++ b/src/Serializer/Read.cs @@ -214,7 +214,7 @@ private static string TokenizeString() return s.ToString(); } - private static Int32 TokenizeNumber() + private static string TokenizeNumber() { StringBuilder s = new StringBuilder(); @@ -226,7 +226,7 @@ private static Int32 TokenizeNumber() } index--; //roll back one index otherwise the for loop will advance over the comma which is being pointed at currently - return Convert.ToInt32(s.ToString()); + return s.ToString(); } private static void TokenizeBoolTrue() @@ -261,7 +261,7 @@ private static void TokenizeNull() } - static object ParseValue(JsonToken token) + static object ParseValue(JsonToken token, Type type) { switch (token.Token) { @@ -284,7 +284,9 @@ static object ParseValue(JsonToken token) break; case Tokens.NUMBER: - return Convert.ToInt32(token.Value); + if(int.TryParse(token.Value, out var i)) return i; + if(float.TryParse(token.Value, out var f)) return f; + throw new InvalidCastException($"ParseValue not implemented to handle type {type}."); case Tokens.COMMA: break; @@ -311,7 +313,15 @@ private static object ParseObject(Type type) } //treat as class - var obj = Activator.CreateInstance(type); + object obj = null; + try + { + obj = Activator.CreateInstance(type); + } + catch (Exception e) + { + Debug.WriteLine("can't instantiate"); + } return ParseObject(obj); } @@ -398,7 +408,7 @@ private static object ParseObject(object obj) value = TryCustomConverter(propInfo.PropertyType, tok.Value); if (value == null) { - value = ParseValue(tok); + value = ParseValue(tok, propInfo.PropertyType); } } diff --git a/src/Serializer/Write.cs b/src/Serializer/Write.cs index fc04d6c..9730833 100644 --- a/src/Serializer/Write.cs +++ b/src/Serializer/Write.cs @@ -104,6 +104,9 @@ public static void WriteValue(object obj) case double d: WriteDouble(d); break; + case float f: + WriteFloat(f); + break; case bool b: WriteBool(b); break; @@ -135,6 +138,8 @@ private static bool HideValue(object value) return i == 0; case double d: return Math.Abs(d - 1.0d) < 0.01; + case float f: + return Math.Abs(f - 1.0d) < 0.01; case bool b: return b == false; case string s: @@ -169,6 +174,11 @@ private static void WriteDouble(double d) _resultJSON.Append(d); } + private static void WriteFloat(float f) + { + _resultJSON.Append(f); + } + private static void WriteBool(bool b) { _resultJSON.Append(b.ToString().ToLower()); diff --git a/src/SierraHOTAS.csproj b/src/SierraHOTAS.csproj index 2bde5c7..ef0e1aa 100644 --- a/src/SierraHOTAS.csproj +++ b/src/SierraHOTAS.csproj @@ -411,6 +411,9 @@ 1.0.8 + + 2.2.1 + 4.6.7 diff --git a/src/ViewModels/AxisMapViewModel.cs b/src/ViewModels/AxisMapViewModel.cs index cf42f72..b9cce84 100644 --- a/src/ViewModels/AxisMapViewModel.cs +++ b/src/ViewModels/AxisMapViewModel.cs @@ -118,7 +118,7 @@ public string SoundFileName } } - public double SoundVolume + public float SoundVolume { get => _hotasAxis.SoundVolume; set @@ -176,13 +176,9 @@ public AxisMapViewModel(IDispatcher dispatcher, MediaPlayerFactory mediaPlayerFa _mediaPlayer = mediaPlayerFactory.CreateMediaPlayer(); _mediaPlayer.Volume = 0f; - if (string.IsNullOrWhiteSpace(SoundFileName)) + if (!string.IsNullOrWhiteSpace(SoundFileName)) { - _mediaPlayer.IsMuted = true; - } - else - { - _mediaPlayer.Open(new Uri(map.SoundFileName, UriKind.Relative)); + _mediaPlayer.Open(map.SoundFileName); } RebuildAllButtonMapViewModels(); } @@ -341,9 +337,9 @@ private void OnAxisSegmentChanged(object sender, AxisSegmentChangedEventArgs e) { _appDispatcher?.Invoke(() => { - _mediaPlayer.Volume = axisMap.SoundVolume; + _mediaPlayer.Volume = axisMap.SoundVolume > 1.0f ? 1.0f : axisMap.SoundVolume; + _mediaPlayer.Position = 0; _mediaPlayer.Play(); - _mediaPlayer.Position = TimeSpan.Zero; }); } } @@ -428,14 +424,13 @@ private void LoadNewSound() if (string.IsNullOrWhiteSpace(soundFileName)) return; SoundFileName = soundFileName; _mediaPlayer.Close(); - _mediaPlayer.Open(new Uri(SoundFileName, UriKind.Relative)); - _mediaPlayer.IsMuted = false; + _mediaPlayer.Open(SoundFileName); + _mediaPlayer.Play(); } private void RemoveSound() { SoundFileName = string.Empty; - _mediaPlayer.IsMuted = true; } } } diff --git a/src/ViewModels/HOTASCollectionViewModel.cs b/src/ViewModels/HOTASCollectionViewModel.cs index 2a4e270..c2c1ee8 100644 --- a/src/ViewModels/HOTASCollectionViewModel.cs +++ b/src/ViewModels/HOTASCollectionViewModel.cs @@ -1,5 +1,4 @@ using SierraHOTAS.Annotations; -using SierraHOTAS.Controls; using SierraHOTAS.Factories; using SierraHOTAS.Models; using SierraHOTAS.ViewModels.Commands; @@ -7,7 +6,6 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; -using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Windows.Input; diff --git a/tests/AxisMapViewModelTests.cs b/tests/AxisMapViewModelTests.cs index 05dd083..76846b3 100644 --- a/tests/AxisMapViewModelTests.cs +++ b/tests/AxisMapViewModelTests.cs @@ -1,12 +1,11 @@ namespace SierraHOTAS.Tests { using NSubstitute; - using System; - using System.Collections.ObjectModel; - using System.Windows.Threading; using SierraHOTAS.Factories; using SierraHOTAS.Models; using SierraHOTAS.ViewModels; + using System; + using System.Collections.ObjectModel; using Xunit; public class AxisMapViewModelTests @@ -158,7 +157,7 @@ public void constructor_test_no_sound_file() public void constructor_test_valid_sound_file_from_deserialization() { var map = new HOTASAxis(); - map.SoundFileName = "file name"; + map.SoundFileName = "D:\\Development\\SierraHOTAS\\src\\Sounds\\click05.mp3"; CreateAxisMapViewModel(out var subMediaPlayer, map); map.SoundFileName = "bob"; diff --git a/tests/HOTASQueueTests.cs b/tests/HOTASQueueTests.cs index ba6c0b7..d19b7ed 100644 --- a/tests/HOTASQueueTests.cs +++ b/tests/HOTASQueueTests.cs @@ -363,7 +363,7 @@ public void button_pressed_use_inherited_parent() queue.Listen(joystick, modes, activationList); //inherited key should be active - queue.SetMode(2); + queue.ActivateMode(2); var mre = new ManualResetEventSlim(); queue.KeystrokeDownSent += (sender, e) =>