From e3e343320cb96d1ad629849a926f998b76b6baa5 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sun, 19 Jan 2025 22:23:06 +0300 Subject: [PATCH] [GUI] AudioClip improvements - Increased loading speed of AudioClip preview - Optimized memory consumption of AudioClip preview - Fixed incorrect length detection for some sound types - Added channel count info (audio channels) --- AssetStudio/ResourceReader.cs | 7 +- AssetStudioCLI/ParallelExporter.cs | 69 +++++++++--------- AssetStudioGUI/AssetStudioGUIForm.Designer.cs | 46 ++++++++---- AssetStudioGUI/AssetStudioGUIForm.cs | 31 ++++++-- AssetStudioGUI/ParallelExport.cs | 71 +++++++++---------- AssetStudioUtility/AudioClipConverter.cs | 11 +-- AssetStudioUtility/Texture2DConverter.cs | 2 +- 7 files changed, 131 insertions(+), 106 deletions(-) diff --git a/AssetStudio/ResourceReader.cs b/AssetStudio/ResourceReader.cs index e44f753..9c97ebb 100644 --- a/AssetStudio/ResourceReader.cs +++ b/AssetStudio/ResourceReader.cs @@ -80,17 +80,18 @@ namespace AssetStudio lock (binaryReader) { binaryReader.BaseStream.Position = Offset; - return binaryReader.ReadBytes((int) size); + return binaryReader.ReadBytes((int)size); } } - public void GetData(byte[] buff, int startIndex = 0) + public void GetData(byte[] buff, out int read, int startIndex = 0) { + read = -1; var binaryReader = GetReader(); lock (binaryReader) { binaryReader.BaseStream.Position = Offset; - binaryReader.Read(buff, startIndex, (int) size); + read = binaryReader.Read(buff, startIndex, (int)size); } } diff --git a/AssetStudioCLI/ParallelExporter.cs b/AssetStudioCLI/ParallelExporter.cs index 0ee6fc8..45af2b4 100644 --- a/AssetStudioCLI/ParallelExporter.cs +++ b/AssetStudioCLI/ParallelExporter.cs @@ -102,8 +102,8 @@ namespace AssetStudioCLI var m_AudioData = BigArrayPool.Shared.Rent(m_AudioClip.m_AudioData.Size); try { - m_AudioClip.m_AudioData.GetData(m_AudioData); - if (m_AudioData == null || m_AudioData.Length == 0) + m_AudioClip.m_AudioData.GetData(m_AudioData, out var read); + if (read <= 0) { Logger.Error($"Export error. \"{item.Text}\": AudioData was not found"); return false; @@ -116,24 +116,8 @@ namespace AssetStudioCLI if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug) { - var sb = new StringBuilder(); - sb.AppendLine($"Converting {item.TypeString} \"{m_AudioClip.m_Name}\" to wav.."); - if (m_AudioClip.version >= (2, 6)) - { - sb.AppendLine(m_AudioClip.version < 5 - ? $"AudioClip type: {m_AudioClip.m_Type}" - : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); - } - else - { - sb.AppendLine($"Is raw AudioClip: {m_AudioClip.m_Format != 0x05}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - } - debugLog += sb.ToString(); + debugLog += $"Converting {item.TypeString} \"{m_AudioClip.m_Name}\" to wav..\n"; + debugLog += GenerateAudioClipInfo(m_AudioClip); } var debugLogConverter = ""; @@ -155,23 +139,8 @@ namespace AssetStudioCLI if (CLIOptions.o_logLevel.Value <= LoggerEvent.Debug) { - var sb = new StringBuilder(); - sb.AppendLine($"Exporting non-fmod {item.TypeString} \"{m_AudioClip.m_Name}\".."); - if (m_AudioClip.version >= (2, 6)) - { - sb.AppendLine(m_AudioClip.version < 5 - ? $"AudioClip type: {m_AudioClip.m_Type}" - : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); - } - else - { - sb.AppendLine($"Is raw AudioClip: {m_AudioClip.m_Format != 0x05}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - } - debugLog += sb.ToString(); + debugLog += $"Exporting non-fmod {item.TypeString} \"{m_AudioClip.m_Name}\"..\n"; + debugLog += GenerateAudioClipInfo(m_AudioClip); } using (var file = File.OpenWrite(exportFullPath)) { @@ -187,6 +156,32 @@ namespace AssetStudioCLI } } + private static string GenerateAudioClipInfo(AudioClip m_AudioClip) + { + var sb = new StringBuilder(); + if (m_AudioClip.version >= (2, 6)) + { + sb.AppendLine(m_AudioClip.version < 5 + ? $"AudioClip type: {m_AudioClip.m_Type}" + : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); + if (m_AudioClip.version >= 5) + { + sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); + sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); + sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); + } + } + else + { + var isRawWav = m_AudioClip.m_Format != 0x05; + sb.AppendLine($"Is raw wav data: {isRawWav}"); + if (isRawWav) + sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); + sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); + } + return sb.ToString(); + } + private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath) { var fileName = FixFileName(item.Text); diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index 462cfc3..1c42831 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -124,7 +124,8 @@ this.previewPanel = new System.Windows.Forms.PictureBox(); this.assetInfoLabel = new System.Windows.Forms.Label(); this.FMODpanel = new System.Windows.Forms.Panel(); - this.FMODcopyright = new System.Windows.Forms.Label(); + this.FMODaudioChannelsLabel = new System.Windows.Forms.Label(); + this.FMODcopyrightLabel = new System.Windows.Forms.Label(); this.FMODinfoLabel = new System.Windows.Forms.Label(); this.FMODtimerLabel = new System.Windows.Forms.Label(); this.FMODstatusLabel = new System.Windows.Forms.Label(); @@ -1077,7 +1078,8 @@ // FMODpanel // this.FMODpanel.BackColor = System.Drawing.SystemColors.ControlDark; - this.FMODpanel.Controls.Add(this.FMODcopyright); + this.FMODpanel.Controls.Add(this.FMODaudioChannelsLabel); + this.FMODpanel.Controls.Add(this.FMODcopyrightLabel); this.FMODpanel.Controls.Add(this.FMODinfoLabel); this.FMODpanel.Controls.Add(this.FMODtimerLabel); this.FMODpanel.Controls.Add(this.FMODstatusLabel); @@ -1094,18 +1096,31 @@ this.FMODpanel.TabIndex = 2; this.FMODpanel.Visible = false; // - // FMODcopyright + // FMODaudioChannelsLabel // - this.FMODcopyright.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.FMODcopyright.AutoSize = true; - this.FMODcopyright.BackColor = System.Drawing.Color.Transparent; - this.FMODcopyright.ForeColor = System.Drawing.Color.White; - this.FMODcopyright.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.FMODcopyright.Location = new System.Drawing.Point(214, 365); - this.FMODcopyright.Name = "FMODcopyright"; - this.FMODcopyright.Size = new System.Drawing.Size(283, 13); - this.FMODcopyright.TabIndex = 9; - this.FMODcopyright.Text = "Audio Engine supplied by FMOD by Firelight Technologies."; + this.FMODaudioChannelsLabel.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.FMODaudioChannelsLabel.AutoSize = true; + this.FMODaudioChannelsLabel.BackColor = System.Drawing.Color.Transparent; + this.FMODaudioChannelsLabel.ForeColor = System.Drawing.Color.White; + this.FMODaudioChannelsLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.FMODaudioChannelsLabel.Location = new System.Drawing.Point(368, 255); + this.FMODaudioChannelsLabel.Name = "FMODaudioChannelsLabel"; + this.FMODaudioChannelsLabel.Size = new System.Drawing.Size(38, 13); + this.FMODaudioChannelsLabel.TabIndex = 10; + this.FMODaudioChannelsLabel.Text = "Stereo"; + // + // FMODcopyrightLabel + // + this.FMODcopyrightLabel.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.FMODcopyrightLabel.AutoSize = true; + this.FMODcopyrightLabel.BackColor = System.Drawing.Color.Transparent; + this.FMODcopyrightLabel.ForeColor = System.Drawing.Color.White; + this.FMODcopyrightLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.FMODcopyrightLabel.Location = new System.Drawing.Point(214, 365); + this.FMODcopyrightLabel.Name = "FMODcopyrightLabel"; + this.FMODcopyrightLabel.Size = new System.Drawing.Size(283, 13); + this.FMODcopyrightLabel.TabIndex = 9; + this.FMODcopyrightLabel.Text = "Audio Engine supplied by FMOD by Firelight Technologies."; // // FMODinfoLabel // @@ -1126,7 +1141,7 @@ this.FMODtimerLabel.BackColor = System.Drawing.Color.Transparent; this.FMODtimerLabel.ForeColor = System.Drawing.Color.White; this.FMODtimerLabel.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.FMODtimerLabel.Location = new System.Drawing.Point(457, 253); + this.FMODtimerLabel.Location = new System.Drawing.Point(457, 255); this.FMODtimerLabel.Name = "FMODtimerLabel"; this.FMODtimerLabel.Size = new System.Drawing.Size(102, 13); this.FMODtimerLabel.TabIndex = 7; @@ -1649,7 +1664,7 @@ private System.Windows.Forms.ColumnHeader columnHeader1; private System.Windows.Forms.TextBox classTextBox; private System.Windows.Forms.ToolStripMenuItem exportClassStructuresMenuItem; - private System.Windows.Forms.Label FMODcopyright; + private System.Windows.Forms.Label FMODcopyrightLabel; private OpenTK.GLControl glControl1; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ToolStripMenuItem showOriginalFileToolStripMenuItem; @@ -1735,6 +1750,7 @@ private System.Windows.Forms.ToolStripMenuItem colorThemeAutoToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem colorThemeLightToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem colorThemeDarkToolStripMenuItem; + private System.Windows.Forms.Label FMODaudioChannelsLabel; } } diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index fcffc5e..42e1dd2 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -43,6 +43,7 @@ namespace AssetStudioGUI private FMOD.Channel channel; private FMOD.SoundGroup masterSoundGroup; private FMOD.MODE loopMode = FMOD.MODE.LOOP_OFF; + private byte[] soundBuff; private uint FMODlenms; private uint FMODloopstartms; private uint FMODloopendms; @@ -1101,17 +1102,16 @@ namespace AssetStudioGUI break; } } - - var m_AudioData = m_AudioClip.m_AudioData.GetData(); - if (m_AudioData == null || m_AudioData.Length == 0) + soundBuff = BigArrayPool.Shared.Rent(m_AudioClip.m_AudioData.Size); + m_AudioClip.m_AudioData.GetData(soundBuff, out var read); + if (read <= 0) return; var exinfo = new FMOD.CREATESOUNDEXINFO(); - exinfo.cbsize = Marshal.SizeOf(exinfo); exinfo.length = (uint)m_AudioClip.m_Size; - var result = system.createSound(m_AudioData, FMOD.MODE.OPENMEMORY | loopMode, ref exinfo, out sound); + var result = system.createStream(soundBuff, FMOD.MODE.OPENMEMORY | FMOD.MODE.LOWMEM | FMOD.MODE.IGNORETAGS | FMOD.MODE.ACCURATETIME | loopMode, ref exinfo, out sound); if (result != FMOD.RESULT.OK) { if (m_AudioClip.version < (2, 6) || m_AudioClip.version >= 5) @@ -1141,7 +1141,6 @@ namespace AssetStudioGUI } sound.getNumSubSounds(out var numsubsounds); - if (numsubsounds > 0) { result = sound.getSubSound(0, out var subsound); @@ -1172,6 +1171,20 @@ namespace AssetStudioGUI FMODinfoLabel.Text = frequency + " Hz"; FMODtimerLabel.Text = $"00:00.00 / {(FMODlenms / 1000 / 60):00}:{(FMODlenms / 1000 % 60):00}.{(FMODlenms / 10 % 100):00}"; + + sound.getFormat(out _, out _, out var audioChannels, out _); + switch (audioChannels) + { + case 1: + FMODaudioChannelsLabel.Text = "Mono"; + break; + case 2: + FMODaudioChannelsLabel.Text = "Stereo"; + break; + default: + FMODaudioChannelsLabel.Text = $"{audioChannels}-Channel"; + break; + } } private void PreviewVideoClip(AssetItem assetItem, VideoClip m_VideoClip) @@ -2589,6 +2602,7 @@ namespace AssetStudioGUI FMODtimerLabel.Text = "00:00.00 / 00:00.00"; FMODstatusLabel.Text = "Stopped"; FMODinfoLabel.Text = ""; + FMODaudioChannelsLabel.Text = ""; if (sound.hasHandle()) { @@ -2596,6 +2610,11 @@ namespace AssetStudioGUI ERRCHECK(result); sound.clearHandle(); } + if (soundBuff != null) + { + BigArrayPool.Shared.Return(soundBuff, clearArray: true); + soundBuff = null; + } } private void FMODplayButton_Click(object sender, EventArgs e) diff --git a/AssetStudioGUI/ParallelExport.cs b/AssetStudioGUI/ParallelExport.cs index 7748cd2..9274b39 100644 --- a/AssetStudioGUI/ParallelExport.cs +++ b/AssetStudioGUI/ParallelExport.cs @@ -101,8 +101,8 @@ namespace AssetStudioGUI var m_AudioData = BigArrayPool.Shared.Rent(m_AudioClip.m_AudioData.Size); try { - m_AudioClip.m_AudioData.GetData(m_AudioData); - if (m_AudioData == null || m_AudioData.Length == 0) + m_AudioClip.m_AudioData.GetData(m_AudioData, out var read); + if (read <= 0) { Logger.Warning($"Failed to export \"{item.Text}\": AudioData was not found"); return false; @@ -115,25 +115,8 @@ namespace AssetStudioGUI if (GUILogger.ShowDebugMessage) { - var sb = new StringBuilder(); - sb.AppendLine($"Converting {item.TypeString} \"{m_AudioClip.m_Name}\" to wav.."); - if (m_AudioClip.version >= (2, 6)) - { - sb.AppendLine(m_AudioClip.version < 5 - ? $"AudioClip type: {m_AudioClip.m_Type}" - : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); - } - else - { - sb.AppendLine($"Is raw AudioClip: {m_AudioClip.m_Format != 0x05}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - } - - debugLog += sb.ToString(); + debugLog += $"Converting {item.TypeString} \"{m_AudioClip.m_Name}\" to wav..\n"; + debugLog += GenerateAudioClipInfo(m_AudioClip); } var debugLogConverter = ""; @@ -155,24 +138,8 @@ namespace AssetStudioGUI if (GUILogger.ShowDebugMessage) { - var sb = new StringBuilder(); - sb.AppendLine($"Exporting non-fmod {item.TypeString} \"{m_AudioClip.m_Name}\".."); - if (m_AudioClip.version >= (2, 6)) - { - sb.AppendLine(m_AudioClip.version < 5 - ? $"AudioClip type: {m_AudioClip.m_Type}" - : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); - sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); - } - else - { - sb.AppendLine($"Is raw AudioClip: {m_AudioClip.m_Format != 0x05}"); - sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); - } - - debugLog += sb.ToString(); + debugLog += $"Exporting non-fmod {item.TypeString} \"{m_AudioClip.m_Name}\"..\n"; + debugLog += GenerateAudioClipInfo(m_AudioClip); } using (var file = File.OpenWrite(exportFullPath)) { @@ -188,6 +155,32 @@ namespace AssetStudioGUI } } + private static string GenerateAudioClipInfo(AudioClip m_AudioClip) + { + var sb = new StringBuilder(); + if (m_AudioClip.version >= (2, 6)) + { + sb.AppendLine(m_AudioClip.version < 5 + ? $"AudioClip type: {m_AudioClip.m_Type}" + : $"AudioClip compression format: {m_AudioClip.m_CompressionFormat}"); + if (m_AudioClip.version >= 5) + { + sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); + sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); + sb.AppendLine($"AudioClip bit depth: {m_AudioClip.m_BitsPerSample}"); + } + } + else + { + var isRawWav = m_AudioClip.m_Format != 0x05; + sb.AppendLine($"Is raw wav data: {isRawWav}"); + if (isRawWav) + sb.AppendLine($"AudioClip channel count: {m_AudioClip.m_Channels}"); + sb.AppendLine($"AudioClip sample rate: {m_AudioClip.m_Frequency}"); + } + return sb.ToString(); + } + private static bool TryExportFile(string dir, AssetItem item, string extension, out string fullPath) { var fileName = FixFileName(item.Text); diff --git a/AssetStudioUtility/AudioClipConverter.cs b/AssetStudioUtility/AudioClipConverter.cs index b0d2e5a..1998d83 100644 --- a/AssetStudioUtility/AudioClipConverter.cs +++ b/AssetStudioUtility/AudioClipConverter.cs @@ -38,7 +38,7 @@ namespace AssetStudio var exinfo = new CREATESOUNDEXINFO(); exinfo.cbsize = Marshal.SizeOf(exinfo); exinfo.length = (uint)m_AudioClip.m_Size; - var result = system.createSound(m_AudioData, MODE.OPENMEMORY, ref exinfo, out var sound); + var result = system.createSound(m_AudioData, MODE.OPENMEMORY | MODE.LOWMEM | MODE.ACCURATETIME, ref exinfo, out var sound); if (ErrorCheck(result, out debugLog)) return null; result = sound.getNumSubSounds(out var numsubsounds); @@ -106,19 +106,20 @@ namespace AssetStudio public byte[] RawAudioClipToWav(out string debugLog) { - var audioSize = m_AudioClip.m_Size; + var audioSize = (uint)m_AudioClip.m_Size; var channels = m_AudioClip.m_Channels; var sampleRate = m_AudioClip.m_Frequency; var bits = 16; debugLog = "[Legacy wav converter] Generating wav header..\n"; var buffer = new byte[audioSize + 44]; - m_AudioClip.m_AudioData.GetData(buffer, 44); - WriteWavHeader(buffer, audioSize, sampleRate, channels, bits); + m_AudioClip.m_AudioData.GetData(buffer, out var read, 44); + if (read > 0) + WriteWavHeader(buffer, audioSize, sampleRate, channels, bits); return buffer; } - private static void WriteWavHeader(byte[] buffer, long size, int sampleRate, int channels, int bits) + private static void WriteWavHeader(byte[] buffer, uint size, int sampleRate, int channels, int bits) { Encoding.ASCII.GetBytes("RIFF").CopyTo(buffer, 0); BitConverter.GetBytes(size + 36).CopyTo(buffer, 4); diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index 3df0956..1f3bdb8 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -89,7 +89,7 @@ namespace AssetStudio var buff = BigArrayPool.Shared.Rent(reader.Size); try { - reader.GetData(buff); + reader.GetData(buff, out _); if (switchSwizzled) { var unswizzledData = BigArrayPool.Shared.Rent(reader.Size);