From 932522a0ca91a5bdaab9f7400392d8f85a4ce9d6 Mon Sep 17 00:00:00 2001
From: Razmoth <32140579+Razmoth@users.noreply.github.com>
Date: Thu, 24 Oct 2024 13:06:03 +0400
Subject: [PATCH] - GUI imporvments. - Bug fixes. - Conversion performance
improvments.
---
Audio.GUI.Desktop/Audio.GUI.Desktop.csproj | 6 +-
Audio.GUI/Audio.GUI.csproj | 4 +-
Audio.GUI/Behaviours/FileDropBehaviour.cs | 45 +++
...ScrollToEndOnCollectionChangedBehaviour.cs | 48 ---
Audio.GUI/Controls/VLCPlayer.axaml.cs | 175 -----------
Audio.GUI/Models/EntryTreeNode.cs | 1 +
Audio.GUI/ViewModels/EntryViewModel.cs | 10 +
Audio.GUI/ViewModels/LogViewModel.cs | 24 +-
Audio.GUI/ViewModels/MainViewModel.cs | 84 +++--
Audio.GUI/ViewModels/PlayerViewModel.cs | 123 ++++++++
Audio.GUI/ViewModels/TreeViewModel.cs | 75 ++---
Audio.GUI/Views/EntryView.axaml | 10 +-
Audio.GUI/Views/LogView.axaml | 33 +-
Audio.GUI/Views/MainView.axaml | 21 +-
.../PlayerView.axaml} | 18 +-
Audio.GUI/Views/PlayerView.axaml.cs | 11 +
Audio.GUI/Views/TreeView.axaml | 7 +-
Audio/AudioManager.cs | 10 +-
Audio/Chunks/STID.cs | 2 +-
.../HIRC/Utils/DecisionTree/DecisionTree.cs | 5 +-
.../Utils/DecisionTree/DecisionTreeNode.cs | 7 +
Audio/Conversion/Chunks/AKD.cs | 28 ++
Audio/Conversion/Chunks/EnvelopePoint.cs | 13 +
Audio/Conversion/Codecs/Vorbis.cs | 297 +++++++++---------
Audio/Conversion/Utils/BitHelper.cs | 45 ---
Audio/Conversion/Utils/BitStream.cs | 173 +++++++---
Audio/Conversion/Utils/BitValue.cs | 57 ----
Audio/Conversion/Utils/OGGStream.cs | 11 +-
Audio/Conversion/WWiseCodebook.cs | 153 +++++----
Audio/Entries/EmbeddedSound.cs | 2 +-
Audio/Entries/Folder.cs | 4 +-
.../{TaggedEntry.cs => TaggedEntry{T}.cs} | 0
Audio/FNVID.cs | 34 +-
33 files changed, 805 insertions(+), 731 deletions(-)
create mode 100644 Audio.GUI/Behaviours/FileDropBehaviour.cs
delete mode 100644 Audio.GUI/Behaviours/ScrollToEndOnCollectionChangedBehaviour.cs
delete mode 100644 Audio.GUI/Controls/VLCPlayer.axaml.cs
create mode 100644 Audio.GUI/ViewModels/PlayerViewModel.cs
rename Audio.GUI/{Controls/VLCPlayer.axaml => Views/PlayerView.axaml} (62%)
create mode 100644 Audio.GUI/Views/PlayerView.axaml.cs
create mode 100644 Audio/Conversion/Chunks/AKD.cs
create mode 100644 Audio/Conversion/Chunks/EnvelopePoint.cs
delete mode 100644 Audio/Conversion/Utils/BitHelper.cs
delete mode 100644 Audio/Conversion/Utils/BitValue.cs
rename Audio/Entries/{TaggedEntry.cs => TaggedEntry{T}.cs} (100%)
diff --git a/Audio.GUI.Desktop/Audio.GUI.Desktop.csproj b/Audio.GUI.Desktop/Audio.GUI.Desktop.csproj
index 65ba1bc..bf6f39c 100644
--- a/Audio.GUI.Desktop/Audio.GUI.Desktop.csproj
+++ b/Audio.GUI.Desktop/Audio.GUI.Desktop.csproj
@@ -13,13 +13,14 @@
-
+
+
@@ -31,6 +32,7 @@
+
@@ -39,7 +41,7 @@
-
+
diff --git a/Audio.GUI/Audio.GUI.csproj b/Audio.GUI/Audio.GUI.csproj
index 9adcf72..829a596 100644
--- a/Audio.GUI/Audio.GUI.csproj
+++ b/Audio.GUI/Audio.GUI.csproj
@@ -29,8 +29,8 @@
-
- VLCPlayer.axaml
+
+ PlayerView.axaml
EntryView.axaml
diff --git a/Audio.GUI/Behaviours/FileDropBehaviour.cs b/Audio.GUI/Behaviours/FileDropBehaviour.cs
new file mode 100644
index 0000000..b2c1641
--- /dev/null
+++ b/Audio.GUI/Behaviours/FileDropBehaviour.cs
@@ -0,0 +1,45 @@
+using Audio.GUI.ViewModels;
+using Avalonia.Input;
+using Avalonia.Platform.Storage;
+using Avalonia.Xaml.Interactions.DragAndDrop;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Audio.GUI.Behaviours;
+public class FileDropBehaviour : DropHandlerBase
+{
+ public override bool Execute(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state)
+ {
+ if (targetContext is MainViewModel mainViewModel)
+ {
+ List files = [];
+
+ foreach(IStorageItem? storageItem in e.Data.GetFiles() ?? [])
+ {
+ string? path = storageItem.TryGetLocalPath();
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ if (Directory.Exists(path))
+ {
+ files.AddRange(Directory.GetFiles(path, "*.*", SearchOption.AllDirectories));
+ }
+ else if (File.Exists(path))
+ {
+ files.Add(path);
+ }
+ }
+ }
+
+ _ = mainViewModel.LoadFiles(files);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public override bool Validate(object? sender, DragEventArgs e, object? sourceContext, object? targetContext, object? state)
+ {
+ return targetContext is MainViewModel && e.Data.Contains(DataFormats.Files);
+ }
+}
diff --git a/Audio.GUI/Behaviours/ScrollToEndOnCollectionChangedBehaviour.cs b/Audio.GUI/Behaviours/ScrollToEndOnCollectionChangedBehaviour.cs
deleted file mode 100644
index 3efe3c5..0000000
--- a/Audio.GUI/Behaviours/ScrollToEndOnCollectionChangedBehaviour.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Avalonia.Controls;
-using Avalonia.Threading;
-using Avalonia.Xaml.Interactivity;
-using System.Collections.Specialized;
-
-namespace Audio.GUI.Behaviours;
-public class ScrollToEndOnCollectionChangedBehaviour : Behavior
-{
- protected override void OnAttached()
- {
- base.OnAttached();
- if (AssociatedObject != null)
- {
- AssociatedObject.Loaded += AssociatedObject_Loaded;
- AssociatedObject.Unloaded += AssociatedObject_Unloaded;
- }
- }
- protected override void OnDetaching()
- {
- base.OnDetaching();
- if (AssociatedObject != null)
- {
- AssociatedObject.Loaded -= AssociatedObject_Loaded;
- AssociatedObject.Unloaded -= AssociatedObject_Unloaded;
- }
- }
- private void AssociatedObject_Loaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
- {
- if (AssociatedObject?.Items is INotifyCollectionChanged notifyCollectionChanged)
- {
- notifyCollectionChanged.CollectionChanged += NotifyCollectionChanged_CollectionChanged;
- }
- }
- private void AssociatedObject_Unloaded(object? sender, Avalonia.Interactivity.RoutedEventArgs e)
- {
- if (AssociatedObject?.Items is INotifyCollectionChanged notifyCollectionChanged)
- {
- notifyCollectionChanged.CollectionChanged -= NotifyCollectionChanged_CollectionChanged;
- }
- }
- private void NotifyCollectionChanged_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.Action == NotifyCollectionChangedAction.Add && AssociatedObject?.Items.Count > 0)
- {
- Dispatcher.UIThread.Post(() => AssociatedObject?.ScrollIntoView(AssociatedObject.Items.Count - 1));
- }
- }
-}
diff --git a/Audio.GUI/Controls/VLCPlayer.axaml.cs b/Audio.GUI/Controls/VLCPlayer.axaml.cs
deleted file mode 100644
index 4905db2..0000000
--- a/Audio.GUI/Controls/VLCPlayer.axaml.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using Audio.Entries;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Threading;
-using LibVLCSharp.Shared;
-using System;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace Audio.GUI.Controls;
-
-public partial class VLCPlayer : UserControl, IDisposable
-{
- private readonly MediaPlayer _mediaPlayer;
- private readonly LibVLC _context;
-
- private MemoryStream? _stream;
- private bool _isLoading = false;
- private bool _isSeeking = false;
-
- private Entry? _entry;
-
- public static readonly DirectProperty EntryProperty =
- AvaloniaProperty.RegisterDirect(nameof(Entry), o => o.Entry, (o, v) => o.Entry = v);
-
- public Entry? Entry
- {
- get => _entry;
- set => Task.Run(() => LoadAudio(value));
- }
-
- public VLCPlayer()
- {
- InitializeComponent();
-
- _context = new();
- _mediaPlayer = new(_context);
- _mediaPlayer.LengthChanged += MediaPlayer_LengthChanged;
- _mediaPlayer.TimeChanged += MediaPlayer_TimeChanged;
- _mediaPlayer.EndReached += MediaPlayer_EndReached;
-
- Thumb.DragCompletedEvent.AddClassHandler((o, e) => o.OnThumbDragCompleted(e));
- Thumb.DragStartedEvent.AddClassHandler((o, e) => o.OnThumbDragStarted(e));
- Button.IsCheckedChanged += Button_IsCheckedChanged;
- SeekBar.ValueChanged += SeekBar_ValueChanged;
- }
-
- private void MediaPlayer_EndReached(object? sender, EventArgs e)
- {
- Task.Run(() =>
- {
- _mediaPlayer.Stop();
- Dispatcher.UIThread.Post(() =>
- {
- SeekBar.Value = 0;
- Button.IsChecked = false;
- }, DispatcherPriority.Render);
- });
- }
-
- private void MediaPlayer_LengthChanged(object? sender, MediaPlayerLengthChangedEventArgs e)
- {
- Dispatcher.UIThread.Post(() => SeekBar.Maximum = _mediaPlayer.Length, DispatcherPriority.Render);
- }
-
- private void MediaPlayer_TimeChanged(object? sender, MediaPlayerTimeChangedEventArgs e)
- {
- Task.Run(() =>
- {
- if (_isLoading)
- {
- _mediaPlayer.Stop();
- }
-
- if (!_isSeeking)
- {
- Dispatcher.UIThread.Post(() =>
- {
- _isSeeking = true;
- SeekBar.Value = _mediaPlayer.Time;
- _isSeeking = false;
- }, DispatcherPriority.Render);
- }
- });
-
- }
- private void Button_IsCheckedChanged(object? sender, RoutedEventArgs e)
- {
- if (e.Source is ToggleButton toggleButton)
- {
- if (toggleButton.IsChecked == true)
- {
- _mediaPlayer.Play();
- }
- else
- {
- _mediaPlayer.Pause();
- }
- }
- }
- private void SeekBar_ValueChanged(object? sender, RangeBaseValueChangedEventArgs e)
- {
- if (!_isSeeking && e.Source is Slider slider)
- {
- Seek(slider);
- }
- }
- private void OnThumbDragStarted(VectorEventArgs e)
- {
- _isSeeking = true;
- }
- private void OnThumbDragCompleted(VectorEventArgs e)
- {
- _isSeeking = false;
- if (e.Source is Thumb thumb && thumb.TemplatedParent is Slider slider)
- {
- Seek(slider);
- }
- }
- private void Seek(Slider slider)
- {
- double scale = _mediaPlayer.Length / (slider.Maximum - slider.Minimum);
- _mediaPlayer.Time = (long)(slider.Value * scale);
- }
- private void LoadAudio(Entry? entry)
- {
- if (entry == null) return;
- else if (entry.Type == EntryType.Bank)
- {
- Logger.Warning("Playing Bank type is not supported !!");
- return;
- }
-
- Logger.Info($"Attempting to load audio {entry.Location}");
-
- Dispatcher.UIThread.Post(() =>
- {
- _isLoading = true;
- Button.IsChecked = false;
- }, DispatcherPriority.Render);
-
- MemoryStream memoryStream = new();
- if (entry.TryConvert(memoryStream, out _))
- {
- _stream?.Dispose();
- _stream = memoryStream;
-
- _mediaPlayer.Media = new Media(_context, new StreamMediaInput(_stream));
- _mediaPlayer.Play();
- _entry = entry;
-
- Logger.Info($"{entry.Location} loaded successfully");
-
- Dispatcher.UIThread.Post(() =>
- {
- _isLoading = false;
- Button.IsChecked = true;
- }, DispatcherPriority.Render);
- return;
- }
-
- Logger.Info($"Unable to load {entry.Location}");
- return;
- }
- public void Dispose()
- {
- _stream?.Dispose();
- _mediaPlayer.Dispose();
- _context.Dispose();
- GC.SuppressFinalize(this);
- }
-}
diff --git a/Audio.GUI/Models/EntryTreeNode.cs b/Audio.GUI/Models/EntryTreeNode.cs
index b6858fa..771ad76 100644
--- a/Audio.GUI/Models/EntryTreeNode.cs
+++ b/Audio.GUI/Models/EntryTreeNode.cs
@@ -31,6 +31,7 @@ public override bool HasMatch(string? searchText)
{
foreach (KeyValuePair, HashSet> evt in uintTag.Events)
{
+ match |= regex.IsMatch(evt.Key.ToString());
foreach (EventTag tag in evt.Value)
{
match |= regex.IsMatch(tag.Type.ToString());
diff --git a/Audio.GUI/ViewModels/EntryViewModel.cs b/Audio.GUI/ViewModels/EntryViewModel.cs
index e3218ee..da861cb 100644
--- a/Audio.GUI/ViewModels/EntryViewModel.cs
+++ b/Audio.GUI/ViewModels/EntryViewModel.cs
@@ -3,17 +3,26 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.Threading.Tasks;
namespace Audio.GUI.ViewModels;
public partial class EntryViewModel : ViewModelBase
{
+ private readonly PlayerViewModel _playerViewModel;
+
[ObservableProperty]
private string infoText = "";
[ObservableProperty]
private Entry? entry;
+ public PlayerViewModel PlayerViewModel => _playerViewModel;
+
+ public EntryViewModel()
+ {
+ _playerViewModel = new();
+ }
partial void OnEntryChanged(Entry? value)
{
if (value != null)
@@ -39,6 +48,7 @@ partial void OnEntryChanged(Entry? value)
}
InfoText = sb.ToString();
+ Task.Run(() => _playerViewModel.LoadAudio(value));
}
}
}
\ No newline at end of file
diff --git a/Audio.GUI/ViewModels/LogViewModel.cs b/Audio.GUI/ViewModels/LogViewModel.cs
index b00edb5..af36811 100644
--- a/Audio.GUI/ViewModels/LogViewModel.cs
+++ b/Audio.GUI/ViewModels/LogViewModel.cs
@@ -1,19 +1,41 @@
-using CommunityToolkit.Mvvm.ComponentModel;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.ObjectModel;
+using System.Threading.Tasks;
namespace Audio.GUI.ViewModels;
public partial class LogViewModel : ViewModelBase, ILogger, IDisposable
{
+ private bool _scrolling = false;
+
[ObservableProperty]
private ObservableCollection logs = [];
+
public LogViewModel()
{
Logger.TryRegister(this);
}
+ [RelayCommand]
+ public void Clear()
+ {
+ Logs.Clear();
+ }
+ [RelayCommand]
+ public async Task ScrollToEnd(ScrollChangedEventArgs e)
+ {
+ if (!_scrolling && e.Source is ScrollViewer scrollViewer && e.ExtentDelta != Avalonia.Vector.Zero)
+ {
+ _scrolling = true;
+ await Dispatcher.UIThread.InvokeAsync(scrollViewer.ScrollToEnd);
+ _scrolling = false;
+ }
+ }
public void Log(LogLevel logLevel, string message)
{
Logs.Add($"[{logLevel}]: {message}");
diff --git a/Audio.GUI/ViewModels/MainViewModel.cs b/Audio.GUI/ViewModels/MainViewModel.cs
index 825040d..9bb5085 100644
--- a/Audio.GUI/ViewModels/MainViewModel.cs
+++ b/Audio.GUI/ViewModels/MainViewModel.cs
@@ -73,15 +73,8 @@ public async Task LoadFile()
{
IReadOnlyList files = await PlatformServiceProvider.StorageProvider.OpenFilePickerAsync(new() { Title = "Pick file(s)", AllowMultiple = true });
IEnumerable paths = files.Select(x => x.TryGetLocalPath() ?? "");
- if (paths.Any())
- {
- _audioManager.Clear();
- int loaded = await Task.Run(() => _audioManager.LoadFiles(paths.ToArray()));
- if (loaded > 0)
- {
- _treeViewModel.Update();
- }
- }
+
+ await LoadFiles(paths);
}
}
@@ -102,15 +95,7 @@ public async Task LoadFolder()
}
}
- if (files.Count != 0)
- {
- _audioManager.Clear();
- int loaded = await Task.Run(() => _audioManager.LoadFiles([.. files]));
- if (loaded > 0)
- {
- _treeViewModel.Update();
- }
- }
+ await LoadFiles(files);
}
}
@@ -122,31 +107,6 @@ public async Task LoadFolder()
[RelayCommand]
public async Task ExportAll() => await ExportEntry([EntryType.Bank, EntryType.Sound, EntryType.EmbeddedSound, EntryType.External]);
-
- private async Task ExportEntry(IEnumerable types)
- {
- if (PlatformServiceProvider.StorageProvider != null)
- {
- IReadOnlyList folders = await PlatformServiceProvider.StorageProvider.OpenFolderPickerAsync(new() { AllowMultiple = false });
-
- IStorageFolder? folder = folders.FirstOrDefault();
- if (folder != null)
- {
- string? path = folder.TryGetLocalPath();
- if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
- {
- if (_treeViewModel.Checked.Any())
- {
- await Task.Run(() => _audioManager.DumpEntries(path, _treeViewModel.Checked.OfType().Select(x => x.Entry)));
- }
- else
- {
- await Task.Run(() => _audioManager.DumpEntries(path, types));
- }
- }
- }
- }
- }
[RelayCommand]
public async Task ExportHierarchy()
@@ -222,6 +182,44 @@ public async Task LoadEvents()
await Dispatcher.UIThread.InvokeAsync(_treeViewModel.Update);
}
+ public async Task LoadFiles(IEnumerable files)
+ {
+ if (files.Any())
+ {
+ _audioManager.Clear();
+ int loaded = await Task.Run(() => _audioManager.LoadFiles([.. files]));
+ if (loaded > 0)
+ {
+ await Dispatcher.UIThread.InvokeAsync(_treeViewModel.Update);
+ }
+ }
+ }
+
+ private async Task ExportEntry(IEnumerable types)
+ {
+ if (PlatformServiceProvider.StorageProvider != null)
+ {
+ IReadOnlyList folders = await PlatformServiceProvider.StorageProvider.OpenFolderPickerAsync(new() { AllowMultiple = false });
+
+ IStorageFolder? folder = folders.FirstOrDefault();
+ if (folder != null)
+ {
+ string? path = folder.TryGetLocalPath();
+ if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path))
+ {
+ if (_treeViewModel.Checked.Any())
+ {
+ await Task.Run(() => _audioManager.DumpEntries(path, _treeViewModel.Checked.OfType().Select(x => x.Entry)));
+ }
+ else
+ {
+ await Task.Run(() => _audioManager.DumpEntries(path, types));
+ }
+ }
+ }
+ }
+ }
+
partial void OnConvertChanged(bool value)
{
_audioManager.Convert = value;
diff --git a/Audio.GUI/ViewModels/PlayerViewModel.cs b/Audio.GUI/ViewModels/PlayerViewModel.cs
new file mode 100644
index 0000000..fc497f0
--- /dev/null
+++ b/Audio.GUI/ViewModels/PlayerViewModel.cs
@@ -0,0 +1,123 @@
+using Audio.Entries;
+using CommunityToolkit.Mvvm.ComponentModel;
+using LibVLCSharp.Shared;
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+
+namespace Audio.GUI.ViewModels;
+
+public partial class PlayerViewModel : ViewModelBase, IDisposable
+{
+ private readonly MediaPlayer _mediaPlayer;
+ private readonly LibVLC _context;
+
+ [ObservableProperty]
+ private float position;
+ [ObservableProperty]
+ private float volume;
+ [ObservableProperty]
+ private TimeSpan time;
+ [ObservableProperty]
+ private TimeSpan duration;
+ [ObservableProperty]
+ private bool isChecked;
+
+ private MemoryStream? _stream;
+
+ public PlayerViewModel()
+ {
+ _context = new();
+ _mediaPlayer = new(_context);
+ _mediaPlayer.PositionChanged += MediaPlayer_PositionChanged;
+ _mediaPlayer.LengthChanged += MediaPlayer_LengthChanged;
+ _mediaPlayer.TimeChanged += MediaPlayer_TimeChanged;
+ _mediaPlayer.EndReached += MediaPlayer_EndReached;
+
+ Volume = 100;
+ }
+
+ public void LoadAudio(Entry? entry)
+ {
+ if (entry == null) return;
+ else if (entry.Type == EntryType.Bank)
+ {
+ Logger.Warning("Playing Bank type is not supported !!");
+ return;
+ }
+
+ Logger.Info($"Attempting to load audio {entry.Location}");
+
+ IsChecked = false;
+ MemoryStream memoryStream = new();
+ if (entry.TryConvert(memoryStream, out _))
+ {
+ _stream?.Dispose();
+ _stream = memoryStream;
+
+ Position = 0;
+ IsChecked = true;
+ _mediaPlayer.Media = new Media(_context, new StreamMediaInput(_stream));
+ _mediaPlayer.Play();
+
+ Logger.Info($"{entry.Location} loaded successfully");
+ return;
+ }
+
+
+ Logger.Info($"Unable to load {entry.Location}");
+ return;
+ }
+ private void MediaPlayer_PositionChanged(object? sender, MediaPlayerPositionChangedEventArgs e)
+ {
+ position = e.Position * 100.0f;
+ OnPropertyChanged(new PropertyChangedEventArgs(nameof(Position)));
+ }
+ private void MediaPlayer_LengthChanged(object? sender, MediaPlayerLengthChangedEventArgs e)
+ {
+ Duration = TimeSpan.FromMilliseconds(e.Length == -1 ? 0 : e.Length);
+ }
+ private void MediaPlayer_TimeChanged(object? sender, MediaPlayerTimeChangedEventArgs e)
+ {
+ Time = TimeSpan.FromMilliseconds(e.Time == -1 ? 0 : e.Time);
+ }
+ private void MediaPlayer_EndReached(object? sender, EventArgs e)
+ {
+ Position = 0;
+ isChecked = false;
+ OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsChecked)));
+ }
+ partial void OnPositionChanged(float value)
+ {
+ _mediaPlayer.Position = value / 100.0f;
+ }
+ partial void OnVolumeChanged(float value)
+ {
+ _mediaPlayer.Volume = (int)value;
+ }
+ partial void OnIsCheckedChanged(bool value)
+ {
+ switch (_mediaPlayer.State)
+ {
+ case VLCState.Ended:
+ _mediaPlayer.Stop();
+ OnIsCheckedChanged(value);
+ break;
+ case VLCState.Paused:
+ case VLCState.Stopped:
+ _mediaPlayer.Play();
+ break;
+ case VLCState.Playing:
+ _mediaPlayer.Pause();
+ break;
+ }
+ }
+ public void Dispose()
+ {
+ _stream?.Dispose();
+ _mediaPlayer.Dispose();
+ _context.Dispose();
+ GC.SuppressFinalize(this);
+ }
+}
\ No newline at end of file
diff --git a/Audio.GUI/ViewModels/TreeViewModel.cs b/Audio.GUI/ViewModels/TreeViewModel.cs
index 4a7ed9a..a7d6b00 100644
--- a/Audio.GUI/ViewModels/TreeViewModel.cs
+++ b/Audio.GUI/ViewModels/TreeViewModel.cs
@@ -6,6 +6,7 @@
using Avalonia.Controls.Models.TreeDataGrid;
using Avalonia.Data;
using Avalonia.Input;
+using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
@@ -39,9 +40,6 @@ public partial class TreeViewModel : ViewModelBase
[ObservableProperty]
private ObservableCollection nodes;
- [ObservableProperty]
- private Entry? selectedEntry;
-
private IPlatformServiceProvider PlatformServiceProvider
{
get
@@ -66,7 +64,13 @@ public IEnumerable Checked
}
[DesignOnly(true)]
- public TreeViewModel() { }
+ public TreeViewModel()
+ {
+ _audioManager = new();
+ _entryViewModel = new();
+ Nodes = [];
+ Source = new([]);
+ }
public TreeViewModel(AudioManager audioManager, EntryViewModel entryViewModel)
{
@@ -94,17 +98,15 @@ public TreeViewModel(AudioManager audioManager, EntryViewModel entryViewModel)
),
},
};
+ Source.RowSelection!.SingleSelect = true;
+ Source.RowSelection!.SelectionChanged += TreeViewModel_SelectionChanged;
}
+
[RelayCommand]
public void Expand(TappedEventArgs e)
{
if (Source.RowSelection!.SelectedIndex > -1)
{
- if (Source.RowSelection!.SelectedItem is EntryTreeNode entryTreeNode)
- {
- _entryViewModel.Entry = entryTreeNode.Entry;
- }
-
if (Source.RowSelection!.SelectedItem?.IsExpanded == true)
{
Source.Collapse(Source.RowSelection!.SelectedIndex);
@@ -115,25 +117,14 @@ public void Expand(TappedEventArgs e)
}
}
}
-
- [RelayCommand]
- public void PreviewEntry(TappedEventArgs e)
- {
- if (e.Source is StyledElement styledElement && styledElement.DataContext is EntryTreeNode entryTreeNode)
- {
- SelectedEntry = entryTreeNode.Entry;
- }
- }
-
[RelayCommand]
- public void Refresh(KeyEventArgs e)
+ public async Task Refresh(KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
- Update();
+ await Dispatcher.UIThread.InvokeAsync(Update);
}
}
-
[RelayCommand]
public async Task Copy(KeyEventArgs e)
{
@@ -145,13 +136,18 @@ public async Task Copy(KeyEventArgs e)
}
}
}
-
public void Update()
{
Nodes.Clear();
-
BuildTree(_audioManager.Entries);
}
+ private void TreeViewModel_SelectionChanged(object? sender, Avalonia.Controls.Selection.TreeSelectionModelSelectionChangedEventArgs e)
+ {
+ if (e.SelectedItems.FirstOrDefault() is EntryTreeNode entryTreeNode)
+ {
+ _entryViewModel.Entry = entryTreeNode.Entry;
+ }
+ }
private void BuildTree(IEnumerable entries, TreeNode? parent = null, int index = 0)
{
foreach (IGrouping group in entries.Where(x => x.Location?.Split(_separators).Length > index).GroupBy(x => Path.ChangeExtension(x.Location?.Split(_separators)[index], null)))
@@ -167,31 +163,24 @@ private void BuildTree(IEnumerable entries, TreeNode? parent = null, int
else
{
node = new EntryTreeNode(group.First()) { Name = group.Key };
- if (!node.HasMatch(SearchText))
- {
- node = null;
- }
}
- if (node != null)
+ if (parent == null)
{
- if (parent == null)
- {
- Nodes.Add(node);
- }
- else
- {
- parent.Nodes.Add(node);
- }
+ Nodes.Add(node);
+ }
+ else
+ {
+ parent.Nodes.Add(node);
+ }
- BuildTree(group, node, index + 1);
+ BuildTree(group, node, index + 1);
- for (int i = node.Nodes.Count - 1; i >= 0; i--)
+ for (int i = node.Nodes.Count - 1; i >= 0; i--)
+ {
+ if (node.Nodes[i] is TreeNode child && !child.HasMatch(SearchText) && child.Nodes.Count == 0)
{
- if (node.Nodes[i] is TreeNode child && child is not EntryTreeNode && child.Nodes.Count == 0)
- {
- node.Nodes.RemoveAt(i);
- }
+ node.Nodes.RemoveAt(i);
}
}
}
diff --git a/Audio.GUI/Views/EntryView.axaml b/Audio.GUI/Views/EntryView.axaml
index 9fd81c5..a5b61f8 100644
--- a/Audio.GUI/Views/EntryView.axaml
+++ b/Audio.GUI/Views/EntryView.axaml
@@ -2,11 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
- xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
xmlns:vm="clr-namespace:Audio.GUI.ViewModels"
- xmlns:models="clr-namespace:Audio.GUI.Models"
- xmlns:controls="clr-namespace:Audio.GUI.Controls"
xmlns:views="clr-namespace:Audio.GUI.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Audio.GUI.Views.EntryView"
@@ -16,8 +12,8 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
-
-
-
+
+
+
diff --git a/Audio.GUI/Views/LogView.axaml b/Audio.GUI/Views/LogView.axaml
index c6a317b..b12395b 100644
--- a/Audio.GUI/Views/LogView.axaml
+++ b/Audio.GUI/Views/LogView.axaml
@@ -2,9 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:vm="clr-namespace:Audio.GUI.ViewModels"
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
- xmlns:vm="clr-namespace:Audio.GUI.ViewModels"
- xmlns:behaviors="clr-namespace:Audio.GUI.Behaviours"
+ xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Audio.GUI.Views.LogView"
x:DataType="vm:LogViewModel">
@@ -13,14 +13,29 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
-
+
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Audio.GUI/Views/MainView.axaml b/Audio.GUI/Views/MainView.axaml
index 8f0a518..4a8058e 100644
--- a/Audio.GUI/Views/MainView.axaml
+++ b/Audio.GUI/Views/MainView.axaml
@@ -2,12 +2,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
- xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
- xmlns:iac="clr-namespace:Avalonia.Xaml.Interactions.Custom;assembly=Avalonia.Xaml.Interactions.Custom"
- xmlns:views="clr-namespace:Audio.GUI.Views"
xmlns:vm="clr-namespace:Audio.GUI.ViewModels"
+ xmlns:views="clr-namespace:Audio.GUI.Views"
xmlns:behaviors="clr-namespace:Audio.GUI.Behaviours"
+ xmlns:idd="using:Avalonia.Xaml.Interactions.DragAndDrop"
+ xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Audio.GUI.Views.MainView"
x:DataType="vm:MainViewModel">
@@ -40,10 +39,18 @@
-
+
+
+
+
+
+
+
-
-
+
+
+
+
diff --git a/Audio.GUI/Controls/VLCPlayer.axaml b/Audio.GUI/Views/PlayerView.axaml
similarity index 62%
rename from Audio.GUI/Controls/VLCPlayer.axaml
rename to Audio.GUI/Views/PlayerView.axaml
index d61891f..5c5a0e0 100644
--- a/Audio.GUI/Controls/VLCPlayer.axaml
+++ b/Audio.GUI/Views/PlayerView.axaml
@@ -2,8 +2,15 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:vm="using:Audio.GUI.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Audio.GUI.Controls.VLCPlayer">
+ x:Class="Audio.GUI.Views.PlayerView"
+ x:DataType="vm:PlayerViewModel">
+
+
+
+