Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@
using Shared.Core.PackFiles.Models;
using Shared.Core.Services;
using Shared.Core.Settings;
using Shared.GameFormats.Audio.Formats.Pcm;
using Shared.GameFormats.Wwise;
using Shared.GameFormats.Wwise.Enums;
using Shared.GameFormats.Wwise.Hirc;
using Shared.GameFormats.Wwise.Wem.V132;
using Shared.GameFormats.Wwise.Wem.V132.Decoding;
using Shared.GameFormats.Wwise.Wem.V132.Encoding;
using Wav = Shared.GameFormats.Audio.Wav.WavFile;
using Wav = Shared.GameFormats.Audio.Containers.Wav.WavFile;

namespace Editors.Audio.AudioProjectConverter
{
Expand Down Expand Up @@ -504,9 +502,6 @@ private void ProcessWavFiles(
string voActor,
HashSet<uint> usedSourceIds)
{
var codebookLibrary = new WwiseCodebookLibrary();
var decoder = new WemVorbisDecoder(codebookLibrary);

var voActorSegment = voActor.Substring(voActor.IndexOf("vo_actor_") + "vo_actor_".Length).ToLower();

foreach (var wavFile in statePathWavs)
Expand Down Expand Up @@ -540,8 +535,8 @@ private void ProcessWavFiles(

var wavPackOutputPath = $"{OutputDirectoryPath}\\vo\\{voActorSegment}\\{wavFile.FileName}".ToLower();
var wemFileBytes = File.ReadAllBytes(wemFilePath);
var vorbisAudio = decoder.Decode(WemFile.CreateFromBytes(wemFileBytes));
var wavFileBytes = new Wav { Audio = vorbisAudio.ToPcm() }.WriteData();
var wav = new Wav { Audio = PcmAudio.CreateFromWemBytes(wemFileBytes) };
var wavFileBytes = wav.WriteData();
_fileSaveService.Save(wavPackOutputPath, wavFileBytes, false);
}
}
Expand Down
37 changes: 37 additions & 0 deletions Editors/Audio/ContextMenu/ExportCAVp8AsIvfCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.IO;
using Editors.Audio.Shared.Utilities;
using Shared.Core.Misc;
using Shared.Core.Services;
using Shared.Ui.BaseDialogs.PackFileTree;
using Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands;

namespace Editors.Audio.ContextMenu
{
public class ExportCAVp8AsIvfCommand(IStandardDialogs standardDialogs, IFileSystemAccess fileSystemAccess) : IContextMenuCommand
{
private readonly IStandardDialogs _standardDialogs = standardDialogs;
private readonly IFileSystemAccess _fileSystemAccess = fileSystemAccess;

public string GetDisplayName(TreeNode node) => "Export as IVF";
public bool ShouldAdd(TreeNode node) => node.NodeType == NodeType.File && node.Item != null;
public bool IsEnabled(TreeNode node) => node.Item != null && node.Item.Name.EndsWith(".ca_vp8", StringComparison.OrdinalIgnoreCase);

public void Execute(TreeNode selectedNode)
{
var packFile = selectedNode.Item;
if (packFile == null)
return;

var dialogResult = _standardDialogs.ShowSystemFolderBrowserDialog();
if (!dialogResult.Result || string.IsNullOrWhiteSpace(dialogResult.FolderPath))
return;

DirectoryHelper.EnsureCreated(dialogResult.FolderPath);

var ivfPath = Path.Combine(dialogResult.FolderPath, Path.ChangeExtension(packFile.Name, ".ivf"));
var ivfBytes = CAVp8Exporter.ExportToIvf(packFile);
_fileSystemAccess.FileWriteAllBytes(ivfPath, ivfBytes);
}
}
}
52 changes: 52 additions & 0 deletions Editors/Audio/ContextMenu/ExportCAVp8AsWebMCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.IO;
using Editors.Audio.Shared.GameInformation.Warhammer3;
using Editors.Audio.Shared.Storage;
using Editors.Audio.Shared.Utilities;
using Shared.Core.Misc;
using Shared.Core.PackFiles;
using Shared.Core.Services;
using Shared.Ui.BaseDialogs.PackFileTree;
using Shared.Ui.BaseDialogs.PackFileTree.ContextMenu.Commands;

namespace Editors.Audio.ContextMenu
{
public class ExportCAVp8AsWebMCommand(
IStandardDialogs standardDialogs,
IFileSystemAccess fileSystemAccess,
IPackFileService packFileService,
IAudioRepository audioRepository,
IMovieAudioResolver movieAudioResolver) : IContextMenuCommand
{
private readonly IStandardDialogs _standardDialogs = standardDialogs;
private readonly IFileSystemAccess _fileSystemAccess = fileSystemAccess;
private readonly IPackFileService _packFileService = packFileService;
private readonly IAudioRepository _audioRepository = audioRepository;
private readonly IMovieAudioResolver _movieAudioResolver = movieAudioResolver;

public string GetDisplayName(TreeNode node) => "Export as WebM";
public bool ShouldAdd(TreeNode node) => node.NodeType == NodeType.File && node.Item != null;
public bool IsEnabled(TreeNode node) => node.Item != null && node.Item.Name.EndsWith(".ca_vp8", StringComparison.OrdinalIgnoreCase);

public void Execute(TreeNode selectedNode)
{
var caVp8PackFile = selectedNode.Item;
if (caVp8PackFile == null)
return;

var dialogResult = _standardDialogs.ShowSystemFolderBrowserDialog();
if (!dialogResult.Result || string.IsNullOrWhiteSpace(dialogResult.FolderPath))
return;

DirectoryHelper.EnsureCreated(dialogResult.FolderPath);

_audioRepository.Load(Wh3LanguageInformation.GetAllLanguages());

var caVp8PackFilePath = _packFileService.GetFullPath(caVp8PackFile);
var wemPackFile = _movieAudioResolver.ResolveMovieWem(caVp8PackFilePath);
var webMPath = Path.Combine(dialogResult.FolderPath, Path.ChangeExtension(caVp8PackFile.Name, ".webm"));
var webMBytes = CAVp8Exporter.ExportToWebM(caVp8PackFile, wemPackFile);
_fileSystemAccess.FileWriteAllBytes(webMPath, webMBytes);
}
}
}
18 changes: 18 additions & 0 deletions Editors/Audio/DependencyInjectionContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@
using Editors.Audio.AudioExplorer;
using Editors.Audio.AudioProjectConverter;
using Editors.Audio.AudioProjectMerger;
using Editors.Audio.ContextMenu;
using Editors.Audio.DialogueEventMerger;
using Editors.Audio.Shared.AudioProject;
using Editors.Audio.Shared.AudioProject.Compiler;
using Editors.Audio.Shared.AudioProject.Factories;
using Editors.Audio.Shared.Dat;
using Editors.Audio.Shared.Storage;
using Editors.Audio.Shared.Utilities;
using Editors.Audio.Shared.Wwise;
using Editors.Audio.Shared.Wwise.Generators;
using Editors.Audio.WaveformVisualiser.Presentation;
using Microsoft.Extensions.DependencyInjection;
using Shared.Core.DependencyInjection;
using Shared.Core.DevConfig;
using Shared.Core.ToolCreation;
using Shared.Ui.BaseDialogs.PackFileTree.ContextMenu;

namespace Editors.Audio
{
Expand Down Expand Up @@ -129,9 +132,15 @@ public override void Register(IServiceCollection serviceCollection)

// Shared audio stuff
serviceCollection.AddScoped<IAudioRepository, AudioRepository>();
serviceCollection.AddScoped<IMovieAudioResolver, MovieAudioResolver>();
serviceCollection.AddSingleton<BnkLoader>();
serviceCollection.AddSingleton<DatLoader>();

// Context menu
serviceCollection.AddScoped<ExportCAVp8AsWebMCommand>();
serviceCollection.AddScoped<ExportCAVp8AsIvfCommand>();
serviceCollection.AddSingleton<IPackFileContextMenuRegistration, AudioPackFileContextMenuRegistration>();

RegisterAllAsInterface<IDeveloperConfiguration>(serviceCollection, ServiceLifetime.Transient);
}

Expand All @@ -148,4 +157,13 @@ public override void RegisterTools(IEditorDatabase factory)
.Build(factory);
}
}

public class AudioPackFileContextMenuRegistration : IPackFileContextMenuRegistration
{
public void Register(PackFileContextMenuRegistry registry)
{
registry.RegisterPackFileContextMenuItem<ExportCAVp8AsWebMCommand>(ContextMenuType.MainApplication, path: "Export", priority: 20, ContextMenuCluster.Export);
registry.RegisterPackFileContextMenuItem<ExportCAVp8AsIvfCommand>(ContextMenuType.MainApplication, path: "Export", priority: 30, ContextMenuCluster.Export);
}
}
}
4 changes: 4 additions & 0 deletions Editors/Audio/Editors.Audio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
</PropertyGroup>


<ItemGroup>
<None Remove="C:\Users\george\.nuget\packages\naudio.vorbis\1.5.0\contentFiles\any\netstandard2.0\README.md" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NAudio" Version="2.2.1" />
<PackageReference Include="NAudio.Vorbis" Version="1.5.0" />
Expand Down
36 changes: 36 additions & 0 deletions Editors/Audio/Shared/Utilities/CAVp8Exporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Shared.Core.PackFiles;
using Shared.Core.PackFiles.Models;
using Shared.GameFormats.Audio.Codecs.Vorbis;
using Shared.GameFormats.Video;

namespace Editors.Audio.Shared.Utilities
{
public static class CAVp8Exporter
{
public static byte[] ExportToWebM(PackFile caVp8PackFile, PackFile wemPackFile)
{
var vorbisAudio = VorbisAudio.CreateFromWemBytes(wemPackFile.DataSource.ReadData());
var caVp8File = new CAVp8File(caVp8PackFile.DataSource.ReadData());
var webMFile = new WebMFile
{
Width = caVp8File.Width,
Height = caVp8File.Height,
Framerate = caVp8File.Framerate,
FrameTable = caVp8File.FrameTable,
FrameData = caVp8File.FrameData,
VorbisCodecPrivate = vorbisAudio.VorbisCodecPrivateData,
VorbisAudioPackets = vorbisAudio.Packets,
VorbisSampleRate = checked((int)vorbisAudio.SampleRate),
VorbisChannels = vorbisAudio.Channels,
};
return webMFile.WriteData();
}

public static byte[] ExportToIvf(PackFile caVp8PackFile)
{
var caVp8File = new CAVp8File(caVp8PackFile.DataSource.ReadData());
var ivfFile = new IvfFile(caVp8File);
return ivfFile.WriteData();
}
}
}
45 changes: 45 additions & 0 deletions Editors/Audio/Shared/Utilities/MovieAudioResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Linq;
using Editors.Audio.Shared.GameInformation.Warhammer3;
using Editors.Audio.Shared.Storage;
using Editors.Audio.Shared.Wwise.HircExploration;
using Shared.Core.PackFiles.Models;
using Shared.GameFormats.Wwise;
using Shared.GameFormats.Wwise.Hirc;

namespace Editors.Audio.Shared.Utilities
{
public interface IMovieAudioResolver
{
PackFile ResolveMovieWem(string caVp8PackFilePath);
}

public class MovieAudioResolver(IAudioRepository audioRepository) : IMovieAudioResolver
{
private readonly IAudioRepository _audioRepository = audioRepository;

public PackFile ResolveMovieWem(string caVp8PackFilePath)
{
var actionEventName = Wh3ActionEventInformation.GetMovieActionEventName(caVp8PackFilePath);
var actionEventId = WwiseHash.Compute(actionEventName);

var actionEventHircs = _audioRepository.GetHircs(actionEventId);
if (actionEventHircs.Count == 0)
throw new Exception($"Cannot find Action Event: {actionEventName}.");

var hircTreeChildrenParser = new HircTreeChildrenParser(_audioRepository);
var nodes = hircTreeChildrenParser.BuildHierarchyAsFlatList(actionEventHircs.First());

var sound = nodes.FirstOrDefault(node => node.Hirc is ICAkSound)?.Hirc as ICAkSound;
if (sound == null)
throw new Exception($"Cannot find a Sound for Action Event: {actionEventName}.");

var sourceId = sound.GetSourceId();
var wemPackFile = _audioRepository.FindWem(sourceId.ToString());
if (wemPackFile == null)
throw new Exception($"Cannot find {sourceId}.wem");

return wemPackFile;
}
}
}
18 changes: 3 additions & 15 deletions Editors/Audio/Shared/Wwise/SoundEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
using NAudio.Vorbis;
using NAudio.Wave;
using Shared.Core.PackFiles;
using Shared.GameFormats.Wwise.Wem.V132;
using Shared.GameFormats.Wwise.Wem.V132.Decoding;
using Shared.GameFormats.Wwise.Wem.V132.Encoding;
using Shared.GameFormats.Audio.Containers.Ogg;

namespace Editors.Audio.Shared.Wwise
{
Expand Down Expand Up @@ -39,15 +37,7 @@ public class SoundEngine(IPackFileService packFileService) : ISoundEngine
public TimeSpan ReaderTimeAtLastPlayOrResume { get; private set; } = TimeSpan.Zero;
public long DeviceBytesAtLastPlayOrResume { get; private set; } = 0;

public PlaybackState PlaybackState
{
get
{
if (_wavePlayer == null)
return PlaybackState.Stopped;
return _wavePlayer.PlaybackState;
}
}
public PlaybackState PlaybackState => _wavePlayer?.PlaybackState ?? PlaybackState.Stopped;

public event EventHandler<StoppedEventArgs> PlaybackStopped;

Expand Down Expand Up @@ -80,9 +70,7 @@ public void LoadFromWemBytes(byte[] wemBytes)
{
DisposeReaderOnly();

var codebookLibrary = new WwiseCodebookLibrary();
var decoder = new WemVorbisDecoder(codebookLibrary);
var oggBytes = decoder.Decode(WemFile.CreateFromBytes(wemBytes)).ToOgg();
var oggBytes = OggFile.CreateFromWemBytes(wemBytes).WriteData();
_memoryStream = new MemoryStream(oggBytes, writable: false);
_waveFileReader = new VorbisWaveReader(_memoryStream);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
using NAudio.Wave;
using NAudio.WaveFormRenderer;
using Shared.Core.PackFiles;
using Shared.GameFormats.Wwise.Wem.V132;
using Shared.GameFormats.Wwise.Wem.V132.Decoding;
using Shared.GameFormats.Wwise.Wem.V132.Encoding;
using Shared.GameFormats.Audio.Containers.Ogg;
using Color = System.Drawing.Color;
using DrawingImage = System.Drawing.Image;

Expand Down Expand Up @@ -55,9 +53,7 @@ public async Task<WaveformRenderResult> RenderFromWemBytesAsync(byte[] wemBytes,

return await Task.Run(() =>
{
var codebookLibrary = new WwiseCodebookLibrary();
var decoder = new WemVorbisDecoder(codebookLibrary);
var oggBytes = decoder.Decode(WemFile.CreateFromBytes(wemBytes)).ToOgg();
var oggBytes = OggFile.CreateFromWemBytes(wemBytes).WriteData();
return RenderWaveformFromOggBytes(oggBytes, baseSettings, overlaySettings);
}, cancellationToken).ConfigureAwait(false);
}
Expand Down
14 changes: 0 additions & 14 deletions Shared/GameFiles/Audio/Codecs/PcmAudio.cs

This file was deleted.

26 changes: 26 additions & 0 deletions Shared/GameFiles/Audio/Codecs/Vorbis/VorbisAudio.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Shared.GameFormats.Wwise.Wem.V132;
using Shared.GameFormats.Wwise.Wem.V132.Decoding;
using Shared.GameFormats.Wwise.Wem.V132.Encoding;

namespace Shared.GameFormats.Audio.Codecs.Vorbis
{
public class VorbisAudio
{
public byte Channels { get; set; }
public byte[] VorbisCodecPrivateData { get; set; } = [];
public VorbisIdentificationPacket IdentificationHeader { get; set; } = new();
public VorbisCommentPacket CommentHeader { get; set; } = new();
public VorbisSetupPacket SetupHeader { get; set; } = new();
public List<VorbisAudioPacket> Packets { get; set; } = [];
public int SampleCount { get; set; }
public uint SampleRate { get; set; }

public static VorbisAudio CreateFromWemBytes(byte[] wemBytes)
{
var wemFile = WemFile.CreateFromWemBytes(wemBytes);
var codebookLibrary = new WwiseCodebookLibrary();
var decoder = new WemVorbisDecoder(codebookLibrary);
return decoder.Decode(wemFile);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Shared.GameFormats.Audio.Codecs
namespace Shared.GameFormats.Audio.Codecs.Vorbis
{
public class VorbisAudioPacket
{
Expand Down
Loading
Loading