diff --git a/Editors/Audio/AudioEditor/Commands/PlayAudioFileCommand.cs b/Editors/Audio/AudioEditor/Commands/PlayAudioFileCommand.cs deleted file mode 100644 index b8c3483b9..000000000 --- a/Editors/Audio/AudioEditor/Commands/PlayAudioFileCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Editors.Audio.Shared.Utilities; -using Shared.Core.Events; -using Shared.Core.Misc; -using Shared.Core.PackFiles; - -namespace Editors.Audio.AudioEditor.Commands -{ - public class PlayAudioFileCommand(IPackFileService packFileService, SoundPlayer soundPlyer) : IUiCommand - { - private readonly IPackFileService _packFileService = packFileService; - private readonly SoundPlayer _soundPlayer = soundPlyer; - - public void Execute(string fileName, string filePath) - { - var wavFile = _packFileService.FindFile(filePath); - - _soundPlayer.ExportFileToAEFolder(fileName, wavFile.DataSource.ReadData()); - - var audioFolderName = $"{DirectoryHelper.Temp}\\Audio"; - var wavFilePath = $"{audioFolderName}\\{fileName}"; - - _soundPlayer.PlayWav(wavFilePath); - } - } -} diff --git a/Editors/Audio/AudioEditor/Commands/SetAudioFilesCommand.cs b/Editors/Audio/AudioEditor/Commands/SetAudioFilesCommand.cs index 232369ee5..b78454c6e 100644 --- a/Editors/Audio/AudioEditor/Commands/SetAudioFilesCommand.cs +++ b/Editors/Audio/AudioEditor/Commands/SetAudioFilesCommand.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using Editors.Audio.AudioEditor.Core; using Editors.Audio.AudioEditor.Events; using Editors.Audio.AudioEditor.Presentation.Shared; @@ -17,7 +16,7 @@ public class SetAudioFilesCommand(IAudioEditorStateService audioEditorStateServi private readonly IEventHub _eventHub = eventHub; private readonly IAudioRepository _audioRepository = audioRepository; - public void Execute(ObservableCollection selectedAudioFiles, bool addToExistingAudioFiles) + public void Execute(List selectedAudioFiles, bool addToExistingAudioFiles) { var usedSourceIds = new HashSet(); var audioProject = _audioEditorStateService.AudioProject; diff --git a/Editors/Audio/AudioEditor/Events/AudioFilesExplorerNodeSelectedEvent.cs b/Editors/Audio/AudioEditor/Events/AudioFilesExplorerNodeSelectedEvent.cs new file mode 100644 index 000000000..5656490f8 --- /dev/null +++ b/Editors/Audio/AudioEditor/Events/AudioFilesExplorerNodeSelectedEvent.cs @@ -0,0 +1,6 @@ +using System.Collections.Generic; + +namespace Editors.Audio.AudioEditor.Events +{ + public record AudioFilesExplorerNodeSelectedEvent(List WavFilePaths); +} diff --git a/Editors/Audio/AudioEditor/Events/AddToWaveformCacheRequestedEvent.cs b/Editors/Audio/AudioEditor/Events/CacheWaveformRequestedEvent.cs similarity index 52% rename from Editors/Audio/AudioEditor/Events/AddToWaveformCacheRequestedEvent.cs rename to Editors/Audio/AudioEditor/Events/CacheWaveformRequestedEvent.cs index 5917df83c..9bf6be29a 100644 --- a/Editors/Audio/AudioEditor/Events/AddToWaveformCacheRequestedEvent.cs +++ b/Editors/Audio/AudioEditor/Events/CacheWaveformRequestedEvent.cs @@ -2,5 +2,5 @@ namespace Editors.Audio.AudioEditor.Events { - public record AddToWaveformCacheRequestedEvent(List FilePaths); + public record CacheWaveformRequestedEvent(List FilePaths); } diff --git a/Editors/Audio/AudioEditor/Events/RemoveFromWaveformCacheRequestedEvent.cs b/Editors/Audio/AudioEditor/Events/DecacheWaveformRequestedEvent.cs similarity index 51% rename from Editors/Audio/AudioEditor/Events/RemoveFromWaveformCacheRequestedEvent.cs rename to Editors/Audio/AudioEditor/Events/DecacheWaveformRequestedEvent.cs index b41766488..c60c610c9 100644 --- a/Editors/Audio/AudioEditor/Events/RemoveFromWaveformCacheRequestedEvent.cs +++ b/Editors/Audio/AudioEditor/Events/DecacheWaveformRequestedEvent.cs @@ -2,5 +2,5 @@ namespace Editors.Audio.AudioEditor.Events { - public record RemoveFromWaveformCacheRequestedEvent(List FilePaths); + public record DecacheWaveformRequestedEvent(List FilePaths); } diff --git a/Editors/Audio/AudioEditor/Events/DisplayWaveformVisualiserRequestedEvent.cs b/Editors/Audio/AudioEditor/Events/DisplayWaveformVisualiserRequestedEvent.cs deleted file mode 100644 index 02fdc577d..000000000 --- a/Editors/Audio/AudioEditor/Events/DisplayWaveformVisualiserRequestedEvent.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Editors.Audio.AudioEditor.Events -{ - public record DisplayWaveformVisualiserRequestedEvent(string FilePath); -} diff --git a/Editors/Audio/AudioEditor/Events/PlayAudioRequestedEvent.cs b/Editors/Audio/AudioEditor/Events/PlayAudioRequestedEvent.cs new file mode 100644 index 000000000..2f099cc33 --- /dev/null +++ b/Editors/Audio/AudioEditor/Events/PlayAudioRequestedEvent.cs @@ -0,0 +1,6 @@ +using System.Collections.Generic; + +namespace Editors.Audio.AudioEditor.Events +{ + public record PlayAudioRequestedEvent(List WavFilePaths); +} diff --git a/Editors/Audio/AudioEditor/Presentation/AudioEditorView.xaml b/Editors/Audio/AudioEditor/Presentation/AudioEditorView.xaml index 56d413ebe..b532ec75a 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioEditorView.xaml +++ b/Editors/Audio/AudioEditor/Presentation/AudioEditorView.xaml @@ -108,11 +108,10 @@ Height="Auto"/> - + - + + + DataContext="{Binding WaveformVisualiserViewModel}"/> diff --git a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerView.xaml.cs b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerView.xaml.cs index 3c4be5eb5..93b14b3fc 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerView.xaml.cs +++ b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerView.xaml.cs @@ -1,7 +1,8 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; -using Editors.Audio.AudioEditor.Presentation.AudioFilesExplorer; +using System.Windows.Media; +using Editors.Audio.AudioEditor.Presentation.Shared; namespace Editors.Audio.AudioEditor.Presentation.AudioFilesExplorer { @@ -16,6 +17,21 @@ public AudioFilesExplorerView() private void OnClearButtonClick(object sender, RoutedEventArgs e) => FilterTextBoxItem.Focus(); - private void OnNodeDoubleClick(object sender, MouseButtonEventArgs e) => ViewModel.PlayWav(); + private void OnNodeDoubleClick(object sender, MouseButtonEventArgs e) + { + var source = e.OriginalSource as DependencyObject; + while (source != null && source is not TreeViewItem) + source = VisualTreeHelper.GetParent(source); + + var treeViewItem = source as TreeViewItem; + if (treeViewItem?.DataContext is not AudioFilesTreeNode node) + return; + + if (node.Type == AudioFilesTreeNodeType.WavFile) + { + ViewModel.PlayWav(); + e.Handled = true; + } + } } } diff --git a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs index 23098c0de..8fe9e7754 100644 --- a/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/AudioFilesExplorer/AudioFilesExplorerViewModel.cs @@ -75,7 +75,7 @@ public AudioFilesExplorerViewModel( AudioFilesTree = _audioFilesTreeBuilder.BuildTree(editablePack); SetupIsExpandedHandlers(AudioFilesTree); - CacheRootWavFilesInWaveformVisualiser(); + CacheRootWaveformVisualisations(); } private void SetupIsExpandedHandlers(ObservableCollection nodes) @@ -90,7 +90,7 @@ private void SetupIsExpandedHandlers(ObservableCollection no } } - private void CacheRootWavFilesInWaveformVisualiser() + private void CacheRootWaveformVisualisations() { var wavFilePaths = new List(); foreach (var node in AudioFilesTree) @@ -100,7 +100,7 @@ private void CacheRootWavFilesInWaveformVisualiser() } if (wavFilePaths.Count > 0) - _eventHub.Publish(new AddToWaveformCacheRequestedEvent(wavFilePaths)); + _eventHub.Publish(new CacheWaveformRequestedEvent(wavFilePaths)); } private void OnNodeIsExpandedChanged(object sender, bool isExpanded) @@ -119,9 +119,9 @@ private void OnNodeIsExpandedChanged(object sender, bool isExpanded) return; if (isExpanded) - _eventHub.Publish(new AddToWaveformCacheRequestedEvent(wavFilePaths)); + _eventHub.Publish(new CacheWaveformRequestedEvent(wavFilePaths)); else - _eventHub.Publish(new RemoveFromWaveformCacheRequestedEvent(wavFilePaths)); + _eventHub.Publish(new DecacheWaveformRequestedEvent(wavFilePaths)); } } @@ -145,64 +145,56 @@ private void OnPackFileContainerSetAsMainEditable(PackFileContainer packFileCont private void RefreshAudioFilesTree(PackFileContainer packFileContainer) { AudioFilesTree = _audioFilesTreeBuilder.BuildTree(packFileContainer); - CacheRootWavFilesInWaveformVisualiser(); + CacheRootWaveformVisualisations(); } private void OnSelectedTreeNodesChanged(object sender, NotifyCollectionChangedEventArgs e) { - SetSelectedTreeNodes(e); + var selectedWavNodes = GetSelectedWavNodes(); - if (SelectedTreeNodes.Count == 1) + if (selectedWavNodes != null && selectedWavNodes.Count == 1) { - var selectedNode = e.NewItems[0] as AudioFilesTreeNode; - if (selectedNode.Type == AudioFilesTreeNodeType.WavFile) - { - var selectedAudioFile = SelectedTreeNodes[0]; - _eventHub.Publish(new DisplayWaveformVisualiserRequestedEvent(selectedAudioFile.FilePath)); - } + var wavFilePaths = new List { selectedWavNodes[0].FilePath }; + _eventHub.Publish(new AudioFilesExplorerNodeSelectedEvent(wavFilePaths)); } SetButtonEnablement(); } - private void SetSelectedTreeNodes(NotifyCollectionChangedEventArgs e) + private List GetSelectedWavNodes() { - if (e.Action == NotifyCollectionChangedAction.Add) - { - foreach (AudioFilesTreeNode addedNode in e.NewItems) - { - if (addedNode.Type != AudioFilesTreeNodeType.WavFile) - SelectedTreeNodes.Remove(addedNode); - } - } + if (SelectedTreeNodes == null || SelectedTreeNodes.Count == 0) + return []; + + var result = new List(); + foreach (var node in SelectedTreeNodes) + if (node.Type == AudioFilesTreeNodeType.WavFile) + result.Add(node); + return result; } private void SetButtonEnablement() { - IsPlayAudioButtonEnabled = SelectedTreeNodes.Count == 1; + var selectedWavNodes = GetSelectedWavNodes(); + IsPlayAudioButtonEnabled = selectedWavNodes.Count == 1; var selectedAudioProjectExplorerNode = _audioEditorStateService.SelectedAudioProjectExplorerNode; if (selectedAudioProjectExplorerNode == null) return; - if (SelectedTreeNodes.Count > 0) + if (selectedWavNodes.Count > 0) { if (selectedAudioProjectExplorerNode.Type == AudioProjectTreeNodeType.ActionEventType || selectedAudioProjectExplorerNode.Type == AudioProjectTreeNodeType.DialogueEvent) { IsSetAudioFilesButtonEnabled = true; - - if (_audioEditorStateService.AudioFiles.Count > 0) - IsAddAudioFilesButtonEnabled = true; - else - IsAddAudioFilesButtonEnabled = false; + IsAddAudioFilesButtonEnabled = _audioEditorStateService.AudioFiles.Count > 0; + return; } } - else - { - IsSetAudioFilesButtonEnabled = false; - IsAddAudioFilesButtonEnabled = false; - } + + IsSetAudioFilesButtonEnabled = false; + IsAddAudioFilesButtonEnabled = false; } partial void OnFilterQueryChanged(string value) => DebounceFilterAudioFilesTreeForFilterQuery(); @@ -250,17 +242,32 @@ private static void ToggleNodeExpansion(AudioFilesTreeNode node, bool shouldExpa ToggleNodeExpansion(child, shouldExpand); } - [RelayCommand] public void SetAudioFiles() => _uiCommandFactory.Create().Execute(SelectedTreeNodes, false); + [RelayCommand] public void SetAudioFiles() + { + var selectedWavs = GetSelectedWavNodes(); + if (selectedWavs.Count == 0) + return; - [RelayCommand] public void AddToAudioFiles() => _uiCommandFactory.Create().Execute(SelectedTreeNodes, true); + _uiCommandFactory.Create().Execute(selectedWavs, false); + } + + [RelayCommand] public void AddToAudioFiles() + { + var selectedWavs = GetSelectedWavNodes(); + if (selectedWavs.Count == 0) + return; + + _uiCommandFactory.Create().Execute(selectedWavs, true); + } [RelayCommand] public void PlayWav() { - if (!IsPlayAudioButtonEnabled) + var selectedWavNodes = GetSelectedWavNodes(); + if (selectedWavNodes == null || selectedWavNodes.Count != 1) return; - var selectedAudioFile = SelectedTreeNodes[0]; - _uiCommandFactory.Create().Execute(selectedAudioFile.FileName, selectedAudioFile.FilePath); + var wavFilePaths = new List { selectedWavNodes[0].FilePath }; + _eventHub.Publish(new PlayAudioRequestedEvent(wavFilePaths)); } [RelayCommand] public void ClearText() => FilterQuery = string.Empty; diff --git a/Editors/Audio/AudioEditor/Presentation/Settings/SettingsViewModel.cs b/Editors/Audio/AudioEditor/Presentation/Settings/SettingsViewModel.cs index e2045972f..1f74ffa5d 100644 --- a/Editors/Audio/AudioEditor/Presentation/Settings/SettingsViewModel.cs +++ b/Editors/Audio/AudioEditor/Presentation/Settings/SettingsViewModel.cs @@ -4,7 +4,6 @@ using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Editors.Audio.AudioEditor.Commands; using Editors.Audio.AudioEditor.Core; using Editors.Audio.AudioEditor.Events; using Editors.Audio.AudioEditor.Presentation.Shared; @@ -517,7 +516,8 @@ private void SetAudioFilesFromViewerItem(List audioFiles, bool isRowE [RelayCommand] public void PlayWav(AudioFile audioFile) { - _uiCommandFactory.Create().Execute(audioFile.WavPackFileName, audioFile.WavPackFilePath); + var wavFilePaths = new List { audioFile.WavPackFilePath }; + _eventHub.Publish(new PlayAudioRequestedEvent(wavFilePaths)); } private void SetInitialSettings() diff --git a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformRendererService.cs b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformRendererService.cs new file mode 100644 index 000000000..4d080c946 --- /dev/null +++ b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformRendererService.cs @@ -0,0 +1,124 @@ +using System; +using System.Drawing.Imaging; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Media.Imaging; +using NAudio.Wave; +using NAudio.WaveFormRenderer; +using Shared.Core.PackFiles; +using Color = System.Drawing.Color; +using DrawingImage = System.Drawing.Image; + +namespace Editors.Audio.AudioEditor.Presentation.WaveformVisualiser +{ + public record WaveformRenderResult(WaveformVisualisation Visualisation, TimeSpan TotalTime, int PixelWidth); + + public interface IWaveformRendererService + { + Task RenderAsync(string filePathKey, int targetWidth, CancellationToken cancellationToken); + } + + public sealed class WaveformRendererService(IPackFileService packFileService) : IWaveformRendererService + { + private readonly IPackFileService _packFileService = packFileService; + + public static int DefaultPixelsPerPeak { get; set; } = 2; + public static int DefaultSpacerPixels { get; set; } = 1; + + public async Task RenderAsync(string filePathKey, int targetWidth, CancellationToken cancellationToken) + { + if (string.IsNullOrWhiteSpace(filePathKey)) + throw new ArgumentNullException(nameof(filePathKey)); + + var packFile = _packFileService.FindFile(filePathKey); + var data = packFile.DataSource.ReadData(); + + var baseSettings = CreateBaseWaveformSettings(targetWidth); + var overlaySettings = CreateOverlayWaveformSettings(targetWidth); + + return await Task.Run(() => RenderWaveformFromBytes(data, packFile.Extension, baseSettings, overlaySettings), cancellationToken).ConfigureAwait(false); + } + + private static WaveformRenderResult RenderWaveformFromBytes(byte[] data, string extension, WaveFormRendererSettings baseSettings, WaveFormRendererSettings overlaySettings) + { + using var memoryStream = new MemoryStream(data, writable: false); + using var waveStream = new WaveFileReader(memoryStream); + using var alignedWaveStream = new BlockAlignReductionStream(waveStream); + + var waveFormRenderer = new WaveFormRenderer(); + + using var baseImageDrawing = waveFormRenderer.Render(alignedWaveStream, baseSettings); + alignedWaveStream.Position = 0; + using var overlayImageDrawing = waveFormRenderer.Render(alignedWaveStream, overlaySettings); + + var baseBitmap = ToBitmapImage(baseImageDrawing); + var overlayBitmap = ToBitmapImage(overlayImageDrawing); + + var waveformVisualisation = WaveformVisualisation.Create(baseBitmap, overlayBitmap); + var totalTime = waveStream.TotalTime; + var pixelWidth = baseBitmap.PixelWidth; + + return new WaveformRenderResult(waveformVisualisation, totalTime, pixelWidth); + } + + private static SoundCloudBlockWaveFormSettings CreateBaseWaveformSettings(int width) + { + return new SoundCloudBlockWaveFormSettings( + Color.FromArgb(196, 230, 230, 230), // top peak + Color.FromArgb(64, 220, 220, 220), // top spacer + Color.FromArgb(196, 210, 210, 210), // bottom peak + Color.FromArgb(64, 190, 190, 190)) // bottom spacer + { + Width = width, + PixelsPerPeak = DefaultPixelsPerPeak, + SpacerPixels = DefaultSpacerPixels, + TopSpacerGradientStartColor = Color.FromArgb(64, 220, 220, 220), + BackgroundColor = Color.Transparent + }; + } + + private static SoundCloudBlockWaveFormSettings CreateOverlayWaveformSettings(int width) + { + return new SoundCloudBlockWaveFormSettings( + Color.FromArgb(255, 255, 68, 0), // top peak + Color.FromArgb(64, 255, 68, 0), // top spacer + Color.FromArgb(255, 255, 191, 153), // bottom peak + Color.FromArgb(128, 255, 191, 153)) // bottom spacer + { + Width = width, + PixelsPerPeak = DefaultPixelsPerPeak, + SpacerPixels = DefaultSpacerPixels, + TopSpacerGradientStartColor = Color.FromArgb(64, 255, 68, 0), + BackgroundColor = Color.Transparent + }; + } + + private static BitmapImage ToBitmapImage(DrawingImage drawingImage) + { + using var memoryStream = new MemoryStream(); + + try + { + drawingImage.Save(memoryStream, ImageFormat.Png); + } + catch (ArgumentNullException) + { + // Sometimes the encoder isn't initialised at the start so we delay then retry. + Thread.Sleep(50); + drawingImage.Save(memoryStream, ImageFormat.Png); + } + + memoryStream.Position = 0; + + var image = new BitmapImage(); + image.BeginInit(); + image.CacheOption = BitmapCacheOption.OnLoad; + image.StreamSource = memoryStream; + image.EndInit(); + image.Freeze(); + return image; + } + + } +} diff --git a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualisationCacheService.cs b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualisationCacheService.cs new file mode 100644 index 000000000..e2964bf33 --- /dev/null +++ b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualisationCacheService.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Editors.Audio.AudioEditor.Presentation.WaveformVisualiser +{ + public interface IWaveformVisualisationCacheService + { + WaveformRenderResult GetWaveformVisualisation(string filePath, int targetWidth); + void Store(string filePath, WaveformRenderResult waveformRenderResult); + void Remove(string filePath); + Task PreloadWaveformVisualisationsAsync(IEnumerable filePaths, int targetWidth, IWaveformRendererService renderService, CancellationToken cancellationToken); + } + + public sealed class WaveformVisualisationCacheService : IWaveformVisualisationCacheService + { + private readonly ConcurrentDictionary _visualisationByFilePath = new(); + private readonly ConcurrentDictionary _preloadInProgressByFilePath = new(); + private readonly ConcurrentDictionary _removedDuringPreloadByFilePath = new(); + + public WaveformRenderResult GetWaveformVisualisation(string filePath, int targetWidth) + { + if (string.IsNullOrWhiteSpace(filePath)) + return null; + + if (_visualisationByFilePath.TryGetValue(filePath, out var cached) && cached.PixelWidth == targetWidth) + return cached; + + return null; + } + + public void Store(string filePath, WaveformRenderResult waveformRenderResult) + { + _visualisationByFilePath[filePath] = waveformRenderResult; + } + + public void Remove(string filePath) + { + _removedDuringPreloadByFilePath[filePath] = 0; + _visualisationByFilePath.TryRemove(filePath, out _); + _preloadInProgressByFilePath.TryRemove(filePath, out _); + } + + public async Task PreloadWaveformVisualisationsAsync(IEnumerable filePaths, int targetWidth, IWaveformRendererService renderService, CancellationToken cancellationToken) + { + var uniqueFilePaths = (filePaths ?? []) + .Where(filePath => !string.IsNullOrWhiteSpace(filePath)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .Where(filePath => !_visualisationByFilePath.TryGetValue(filePath, out var existingfilePath) || existingfilePath.PixelWidth != targetWidth) + .Where(filePath => _preloadInProgressByFilePath.TryAdd(filePath, 0)) + .ToArray(); + + if (uniqueFilePaths.Length == 0) + return; + + var options = new ParallelOptions + { + MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount - 1), + CancellationToken = cancellationToken + }; + + try + { + await Parallel.ForEachAsync(uniqueFilePaths, options, async (filePath, cancellationToken) => + { + try + { + _removedDuringPreloadByFilePath.TryRemove(filePath, out _); + + var waveformRenderResult = await renderService.RenderAsync(filePath, targetWidth, cancellationToken).ConfigureAwait(false); + + if (_removedDuringPreloadByFilePath.ContainsKey(filePath)) + return; + + _visualisationByFilePath[filePath] = waveformRenderResult; + } + finally + { + _preloadInProgressByFilePath.TryRemove(filePath, out _); + } + }).ConfigureAwait(false); + } + catch (OperationCanceledException) { } + } + } +} diff --git a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserHelpers.cs b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserHelpers.cs index f8ba47604..f4fc28050 100644 --- a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserHelpers.cs +++ b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserHelpers.cs @@ -1,73 +1,7 @@ -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Windows.Media.Imaging; -using NAudio.Wave; -using NAudio.WaveFormRenderer; -using DrawingImage = System.Drawing.Image; - -namespace Editors.Audio.AudioEditor.Presentation.WaveformVisualiser +namespace Editors.Audio.AudioEditor.Presentation.WaveformVisualiser { public class WaveformVisualiserHelpers { - public static int DefaultPixelsPerPeak { get; set; } = 2; - public static int DefaultSpacerPixels { get; set; } = 1; - - public static SoundCloudBlockWaveFormSettings CreateBaseWaveformSettings(int width) - { - return new SoundCloudBlockWaveFormSettings( - Color.FromArgb(196, 230, 230, 230), // top peak - Color.FromArgb(64, 220, 220, 220), // top spacer - Color.FromArgb(196, 210, 210, 210), // bottom peak - Color.FromArgb(64, 190, 190, 190)) // bottom spacer - { - Width = width, - PixelsPerPeak = DefaultPixelsPerPeak, - SpacerPixels = DefaultSpacerPixels, - TopSpacerGradientStartColor = Color.FromArgb(64, 220, 220, 220), - BackgroundColor = Color.Transparent - }; - } - - public static SoundCloudBlockWaveFormSettings CreateOverlayWaveformSettings(int width) - { - return new SoundCloudBlockWaveFormSettings( - Color.FromArgb(255, 255, 68, 0), // top peak - Color.FromArgb(64, 255, 68, 0), // top spacer - Color.FromArgb(255, 255, 191, 153), // bottom peak - Color.FromArgb(128, 255, 191, 153)) // bottom spacer - { - Width = width, - PixelsPerPeak = DefaultPixelsPerPeak, - SpacerPixels = DefaultSpacerPixels, - TopSpacerGradientStartColor = Color.FromArgb(64, 255, 68, 0), - BackgroundColor = Color.Transparent - }; - } - - public static BitmapImage ToBitmapImage(DrawingImage drawingImage) - { - using var memoryStream = new MemoryStream(); - drawingImage.Save(memoryStream, ImageFormat.Png); - memoryStream.Position = 0; - - var image = new BitmapImage(); - image.BeginInit(); - image.CacheOption = BitmapCacheOption.OnLoad; - image.StreamSource = memoryStream; - image.EndInit(); - image.Freeze(); - return image; - } - public static WaveStream CreateWaveStream(Stream stream, string fileExtension) - { - return fileExtension switch - { - ".wav" => new WaveFileReader(stream), - _ => throw new NotSupportedException("File type not supported."), - }; - } } } diff --git a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserView.xaml b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserView.xaml index 7ff84309b..869f5ead4 100644 --- a/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserView.xaml +++ b/Editors/Audio/AudioEditor/Presentation/WaveformVisualiser/WaveformVisualiserView.xaml @@ -7,77 +7,84 @@ d:DataContext="{d:DesignInstance Type=local:WaveformVisualiserViewModel}" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> - - - - - - - + + + + + + + - - -