Skip to content

Commit

Permalink
Audio Preview
Browse files Browse the repository at this point in the history
  • Loading branch information
Razmoth committed Dec 5, 2023
1 parent aced30b commit 7556ca8
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 31 deletions.
3 changes: 3 additions & 0 deletions Audio.Desktop/Audio.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

<ItemGroup>
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" />
<PackageReference Include="VideoLAN.LibVLC.iOS" Version="3.3.18" />
<PackageReference Include="VideoLAN.LibVLC.Mac" Version="3.1.3.1" />
<PackageReference Include="VideoLAN.LibVLC.Windows" Version="3.0.20" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions Audio/Audio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
<PackageReference Include="Avalonia.ReactiveUI" Version="11.0.5" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.5" />
<PackageReference Include="LibVLCSharp" Version="3.8.2" />
</ItemGroup>
</Project>
190 changes: 176 additions & 14 deletions Audio/ViewModels/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,64 @@
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Security.Cryptography;
using System.Reactive;
using LibVLCSharp.Shared;
using System.Reactive.Linq;
using System.Threading;

namespace Audio.ViewModels;

public partial class MainViewModel : ViewModelBase
{
private ReadOnlyObservableCollection<Entry> _filteredEntries;
private MediaPlayer _mediaPlayer;
private LibVLC _vlcLib;
private Entry _selectedEntry;
private string _searchText;
private string _statusText;
private double _progressValue;
private double _duration;
private double _time;
private bool _isPlay;

public string WWiserPath { get; set; }
public string VGMStreamPath { get; set; }
public FileInfo PreviewInput { get; set; }
public FileInfo PreviewOutput { get; set; }
public List<Package> Packages { get; set; }
public SourceList<Entry> Entries { get; set; }
public ObservableCollection<Folder> Folders { get; set; }
public FlatTreeDataGridSource<Entry> EntrySource { get; set; }
public List<Entry> SelectedEntries { get; set; }
public string ClipboardText { get; set; }
public bool IsExportAudio { get; set; }
public bool AllowBanks { get; set; }
public bool EnableTXTH { get; set; }
public bool AllowDupes { get; set; }
public bool IsPlay
{
get => _isPlay;
set => this.RaiseAndSetIfChanged(ref _isPlay, value);
}
public ReactiveCommand<Unit, Unit> AudioPreviewCommand { get; }
public ReadOnlyObservableCollection<Entry> FilteredEntries => _filteredEntries;
public List<Entry> SelectedEntries => EntrySource.RowSelection!.SelectedItems.ToList();
public IObservable<bool> CanPreviewAudio => this.WhenAnyValue(x => x.SelectedEntry, y => y != null && y.Type != EntryType.Bank);
public MediaPlayer MediaPlayer
{
get
{
if (_mediaPlayer == null)
{
_vlcLib ??= new LibVLC();
_mediaPlayer = new MediaPlayer(_vlcLib);
_mediaPlayer.EndReached += MediaPlayer_EndReached;
_mediaPlayer.LengthChanged += MediaPlayer_LengthChanged;
_mediaPlayer.TimeChanged += MediaPlayer_TimeChanged;
}

return _mediaPlayer;
}
}
public string SearchText
{
get => _searchText;
Expand All @@ -49,6 +96,11 @@ public string SearchText
this.RaiseAndSetIfChanged(ref _searchText, value);
}
}
public Entry SelectedEntry
{
get => _selectedEntry;
set => this.RaiseAndSetIfChanged(ref _selectedEntry, value);
}
public string StatusText
{
get => _statusText;
Expand All @@ -59,26 +111,33 @@ public double ProgressValue
get => _progressValue;
set => this.RaiseAndSetIfChanged(ref _progressValue, value);
}
public List<Package> Packages { get; set; }
public SourceList<Entry> Entries { get; set; }
public ObservableCollection<Folder> Folders { get; set; }
public FlatTreeDataGridSource<Entry> EntrySource { get; set; }
public string ClipboardText { get; set; }
public bool IsExportAudio { get; set; }
public bool AllowBanks { get; set; }
public bool EnableTXTH { get; set; }
public bool AllowDupes { get; set; }
public double Duration
{
get => _duration;
set => this.RaiseAndSetIfChanged(ref _duration, value);
}
public double Time
{
get => _time;
set => this.RaiseAndSetIfChanged(ref _time, value);
}
public MainViewModel()
{
SearchText = "";
ClipboardText = "";
StatusText = "";
IsExportAudio = true;
Duration = 1;
Time = 0;

PreviewInput = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "preview.wem"));
PreviewOutput = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "preview.wav"));

ProgressHelper.Instance = new Progress<double>(value => ProgressValue = value);

Packages = new List<Package>();
Entries = new SourceList<Entry>();
SelectedEntries = new List<Entry>();
Folders = new ObservableCollection<Folder>();
EntrySource = new FlatTreeDataGridSource<Entry>(Array.Empty<Entry>())
{
Expand All @@ -94,7 +153,39 @@ public MainViewModel()
}
};
EntrySource.RowSelection!.SingleSelect = false;
EntrySource.RowSelection!.SelectionChanged += EntrySource_SelectionChanged;

AudioPreviewCommand = ReactiveCommand.Create(PreviewAudio, CanPreviewAudio);
}

private void EntrySource_SelectionChanged(object? sender, Avalonia.Controls.Selection.TreeSelectionModelSelectionChangedEventArgs<Entry> e)
{
foreach(var entry in e.DeselectedItems)
{
SelectedEntries.Remove(entry);
}
foreach(var entry in e.SelectedItems)
{
SelectedEntries.Add(entry);
}
if (SelectedEntries.Count > 0)
{
SelectedEntry = SelectedEntries[0];
}
}
public void Dispose()
{
if (PreviewInput.Exists)
{
PreviewInput.Delete();
}
if (PreviewOutput.Exists)
{
PreviewOutput.Delete();
}

_mediaPlayer?.Dispose();
_vlcLib?.Dispose();
}
public async void LoadFiles(string[] files)
{
Expand All @@ -110,7 +201,7 @@ public async void LoadFolder(string folder)
public async void ExportBanks(string outputDir) => await Task.Run(() => Export(Entries.Items.Where(x => x is Bank).ToList(), outputDir));
public async void ExportAll(string outputDir) => await Task.Run(() => Export(Entries.Items.ToList(), outputDir));
public async void LoadVO(string path) => await Task.Run(() => LoadVOInternal(path));
public async void GenerateTXTP(string wwiser, string file) => await Task.Run(() => GenerateTXTPInternal(wwiser, file));
public async void GenerateTXTP(string file) => await Task.Run(() => GenerateTXTPInternal(file));
public async void LoadDIFF(string src, string dst) => await Task.Run(() => LoadDIFFInternal(src, dst));
public async void DumpInfo(string output) => await Task.Run(() => DumpInfoInternal(output));
public void SelectAll()
Expand All @@ -125,6 +216,77 @@ public async void Search()
await Task.Run(Refresh);
StatusText = "Updated !!";
}
public void Seek(double value)
{
MediaPlayer.Time = (long)(value * 1000.0d);
MediaPlayer.Play();
}
public void LoadAudio()
{
IsPlay = false;

if (SelectedEntry.Type == EntryType.Bank)
{
StatusText = "Previewing Bank type is not supported !!";
return;
}

if (string.IsNullOrEmpty(VGMStreamPath))
{
StatusText = "VGMStream path must be set first !!";
return;
}

DumpEntry(SelectedEntry, PreviewInput.FullName);

StatusText = "Invoking vgmstream...";

var startInfo = new ProcessStartInfo();
startInfo.FileName = VGMStreamPath;
startInfo.ArgumentList.Add("-o");
startInfo.ArgumentList.Add(PreviewOutput.FullName);
startInfo.ArgumentList.Add(PreviewInput.FullName);
startInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
using var process = Process.Start(startInfo);
process.WaitForExit();

if (PreviewOutput.Exists)
{
MediaPlayer.Media = new Media(_vlcLib, PreviewOutput.FullName);
Duration = 1;
Time = 0;
}

StatusText = $"Loaded {SelectedEntry.Name}";
}
private void PreviewAudio()
{
if (IsPlay)
{
MediaPlayer.Play();
}
else
{
MediaPlayer.Pause();
}
}
private void MediaPlayer_EndReached(object? sender, EventArgs e)
{
IsPlay = false;
ThreadPool.QueueUserWorkItem(state => MediaPlayer.Stop());
Duration = 1;
Time = 0;
}
private void MediaPlayer_LengthChanged(object? sender, MediaPlayerLengthChangedEventArgs e)
{
Duration = MediaPlayer.Length / 1000.0d;
}
private void MediaPlayer_TimeChanged(object? sender, MediaPlayerTimeChangedEventArgs e)
{
Time = MediaPlayer.Time / 1000.0d;
}
private async void LoadPaths(string[] paths)
{
Entries.Clear();
Expand Down Expand Up @@ -381,7 +543,7 @@ private void Refresh()
var parsed = await Task.Run(() => Package.Parse(path, out package));
return (parsed, package);
}
private void GenerateTXTPInternal(string wwiser, string file)
private void GenerateTXTPInternal(string file)
{
var outputDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "output");
var banksDir = Path.Combine(outputDir, "banks");
Expand Down Expand Up @@ -419,7 +581,7 @@ private void GenerateTXTPInternal(string wwiser, string file)

var startInfo = new ProcessStartInfo();
startInfo.FileName = "python";
startInfo.ArgumentList.Add(wwiser);
startInfo.ArgumentList.Add(WWiserPath);
startInfo.ArgumentList.Add(Path.Combine(banksDir, "**/*.bnk"));
startInfo.ArgumentList.Add("-g");
startInfo.ArgumentList.Add("-te");
Expand Down Expand Up @@ -457,7 +619,7 @@ private void GenerateTXTPInternal(string wwiser, string file)

var startInfo = new ProcessStartInfo();
startInfo.FileName = "python";
startInfo.ArgumentList.Add(wwiser);
startInfo.ArgumentList.Add(WWiserPath);
startInfo.ArgumentList.Add(Path.Combine(banksDir, "**/*.bnk"));
startInfo.ArgumentList.Add("-g");
startInfo.ArgumentList.Add("-te");
Expand Down
43 changes: 37 additions & 6 deletions Audio/Views/MainView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="ToggleButton PathIcon.Play">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="ToggleButton:checked PathIcon.Play">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="ToggleButton PathIcon.Pause">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="ToggleButton:checked PathIcon.Pause">
<Setter Property="IsVisible" Value="True"/>
</Style>
</UserControl.Styles>
<UserControl.Resources>
<StreamGeometry x:Key="PlayIcon">M37.728,328.12c2.266,1.256,4.77,1.88,7.272,1.88c2.763,0,5.522-0.763,7.95-2.28l240-149.999 c4.386-2.741,7.05-7.548,7.05-12.72c0-5.172-2.664-9.979-7.05-12.72L52.95,2.28c-4.625-2.891-10.453-3.043-15.222-0.4 C32.959,4.524,30,9.547,30,15v300C30,320.453,32.959,325.476,37.728,328.12z</StreamGeometry>
<StreamGeometry x:Key="PauseIcon">M224,435.8V76.1c0-6.7-5.4-12.1-12.2-12.1h-71.6c-6.8,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6 C218.6,448,224,442.6,224,435.8z M371.8,64h-71.6c-6.7,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6c6.7,0,12.2-5.4,12.2-12.2V76.1 C384,69.4,378.6,64,371.8,64z</StreamGeometry>
</UserControl.Resources>

<DockPanel>
<Menu DockPanel.Dock="Top">
Expand All @@ -29,6 +47,9 @@
<MenuItem Header="Options">
<MenuItem Header="Load VO" Click="LoadVO_Click"/>
<Separator/>
<MenuItem Header="Set WWiser Path" Click="SetWWiserPath_Click"/>
<MenuItem Header="Set VGMStream Path" Click="SetVGMStreamPath_Click"/>
<Separator/>
<MenuItem Header="Generate TXTP" Click="GenerateTXTP_Click"/>
<CheckBox Content="Export audio" IsChecked="{Binding IsExportAudio}" ToolTip.Tip="Check to export audio files after invoking wwiser."/>
<CheckBox Content="Allow banks" IsChecked="{Binding AllowBanks}" ToolTip.Tip="Check to use internal banks and keep them after invoking wwiser."/>
Expand All @@ -44,22 +65,32 @@
</ItemsControl>
</MenuItem>
</Menu>
<Grid RowDefinitions="Auto,*,25" DragDrop.AllowDrop="True">
<TextBox Grid.Row="0" Text="{Binding SearchText}" Watermark="Search" KeyDown="SearchText_KeyDown"/>
<TreeDataGrid Grid.Row="1" Source="{Binding EntrySource}" KeyDown="EntryDataGrid_KeyDown" PointerPressed="EntryDataGrid_PointerPressed">
<Grid RowDefinitions="Auto,5*,100" ColumnDefinitions="*,*" VerticalAlignment="Stretch" DragDrop.AllowDrop="True">
<TextBox Grid.Row="0" Grid.ColumnSpan="2" VerticalAlignment="Center" Text="{Binding SearchText}" Watermark="Search" KeyDown="SearchText_KeyDown"/>
<TreeDataGrid Grid.Row="1" Grid.ColumnSpan="2" Source="{Binding EntrySource}" KeyDown="EntryDataGrid_KeyDown" PointerPressed="EntryDataGrid_PointerPressed" DoubleTapped="TreeDataGrid_DoubleTapped">
<TreeDataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy text" Click="EntryDataGridCopyText_Click"/>
<MenuItem Header="Export selected entries" Click="EntryDataGridExportSeleted_Click"/>
</ContextMenu>
</TreeDataGrid.ContextMenu>
</TreeDataGrid>
<Grid Grid.Row="2" ColumnDefinitions="*,*">
<ProgressBar Grid.Column="0" Minimum="0" Maximum="100" Value="{Binding ProgressValue}" ShowProgressText="True" VerticalAlignment="Stretch"/>
<Border Grid.Column="1" BorderThickness="1" BorderBrush="White">
<Grid Grid.Row="2" Grid.ColumnSpan="2" RowDefinitions="3*,*,*">
<Grid Grid.Row="0" ColumnDefinitions="40,*" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="5,0,15,0">
<ToggleButton Grid.Column="0" Background="Transparent" IsChecked="{Binding IsPlay}" Command="{Binding AudioPreviewCommand}" Margin="5,0">
<Panel>
<PathIcon Classes="Play" Data="{StaticResource PlayIcon}"/>
<PathIcon Classes="Pause" Data="{StaticResource PauseIcon}"/>
</Panel>
</ToggleButton>
<Slider Grid.Column="1" HorizontalAlignment="Stretch" Minimum="0" Maximum="{Binding Duration}" Value="{Binding Time}" PointerPressed="Slider_PointerPressed"/>
</Grid>
<ProgressBar Grid.Row="1" Minimum="0" Maximum="100" Value="{Binding ProgressValue}" ShowProgressText="True" VerticalAlignment="Stretch"/>
<Border Grid.Row="2" BorderThickness="1" BorderBrush="White">
<SelectableTextBlock Text="{Binding StatusText}"/>
</Border>
</Grid>
</Grid>
</DockPanel>

</UserControl>
Loading

0 comments on commit 7556ca8

Please sign in to comment.