From 51899d4a69c67ac7e0f2ed3d71324e06dfbe3a45 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 26 Oct 2023 22:54:20 +0200 Subject: [PATCH] Dump T6 sound PCM data as wav --- .../ObjContainer/SoundBank/SoundBankTypes.h | 8 +-- .../AssetDumpers/AssetDumperLoadedSound.cpp | 42 +++------------ .../T6/AssetDumpers/AssetDumperSndBank.cpp | 50 +++++++++++++++-- src/ObjWriting/Sound/WavWriter.cpp | 53 +++++++++++++++++++ src/ObjWriting/Sound/WavWriter.h | 21 ++++++++ 5 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 src/ObjWriting/Sound/WavWriter.cpp create mode 100644 src/ObjWriting/Sound/WavWriter.h diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h b/src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h index 46927c27..4254ec8e 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h @@ -38,8 +38,8 @@ struct SoundAssetBankEntry unsigned int size; unsigned int offset; unsigned int frameCount; - char frameRateIndex; - char channelCount; - char looping; - char format; + unsigned char frameRateIndex; + unsigned char channelCount; + unsigned char looping; + unsigned char format; }; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp index f7c97209..66ab28e3 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperLoadedSound.cpp @@ -1,6 +1,7 @@ #include "AssetDumperLoadedSound.h" #include "Sound/WavTypes.h" +#include "Sound/WavWriter.h" using namespace IW4; @@ -11,43 +12,16 @@ bool AssetDumperLoadedSound::ShouldDump(XAssetInfo* asset) void AssetDumperLoadedSound::DumpWavPcm(AssetDumpingContext& context, const LoadedSound* asset, std::ostream& stream) { - const auto riffMasterChunkSize = sizeof(WAV_CHUNK_ID_RIFF) - + sizeof(uint32_t) - + sizeof(WAV_WAVE_ID) - + sizeof(WavChunkHeader) - + sizeof(WavFormatChunkPcm) - + sizeof(WavChunkHeader) - + sizeof(asset->sound.info.data_len); + const WavWriter writer(stream); - stream.write(reinterpret_cast(&WAV_CHUNK_ID_RIFF), sizeof(WAV_CHUNK_ID_RIFF)); - stream.write(reinterpret_cast(&riffMasterChunkSize), sizeof(riffMasterChunkSize)); - stream.write(reinterpret_cast(&WAV_WAVE_ID), sizeof(WAV_WAVE_ID)); - - const WavChunkHeader formatChunkHeader - { - WAV_CHUNK_ID_FMT, - sizeof(WavFormatChunkPcm) + const WavMetaData metaData{ + static_cast(asset->sound.info.channels), + static_cast(asset->sound.info.rate), + static_cast(asset->sound.info.bits) }; - stream.write(reinterpret_cast(&formatChunkHeader), sizeof(formatChunkHeader)); - WavFormatChunkPcm formatChunk - { - WavFormat::PCM, - static_cast(asset->sound.info.channels), - asset->sound.info.rate, - asset->sound.info.rate * asset->sound.info.channels * asset->sound.info.bits / 8, - static_cast(asset->sound.info.block_size), - static_cast(asset->sound.info.bits) - }; - stream.write(reinterpret_cast(&formatChunk), sizeof(formatChunk)); - - const WavChunkHeader dataChunkHeader - { - WAV_CHUNK_ID_DATA, - asset->sound.info.data_len - }; - stream.write(reinterpret_cast(&dataChunkHeader), sizeof(dataChunkHeader)); - stream.write(asset->sound.data, asset->sound.info.data_len); + writer.WritePcmHeader(metaData, asset->sound.info.data_len); + writer.WritePcmData(asset->sound.data, asset->sound.info.data_len); } void AssetDumperLoadedSound::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index 18b8f79b..351c51cb 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -7,6 +7,7 @@ #include "Utils/ClassUtils.h" #include "Csv/CsvStream.h" #include "ObjContainer/SoundBank/SoundBank.h" +#include "Sound/WavWriter.h" using namespace T6; namespace fs = std::filesystem; @@ -85,6 +86,19 @@ namespace "raw/", "devraw/", }; + + constexpr size_t FRAME_RATE_FOR_INDEX[] + { + 8000, + 12000, + 16000, + 24000, + 32000, + 44100, + 48000, + 96000, + 192000 + }; } class AssetDumperSndBank::Internal @@ -258,6 +272,37 @@ class AssetDumperSndBank::Internal return {}; } + void DumpSoundFilePcm(const char* assetFileName, const SoundBankEntryInputStream& soundFile, const unsigned bitsPerSample) const + { + const auto outFile = OpenAssetOutputFile(assetFileName, ".wav"); + if (!outFile) + { + std::cerr << "Failed to open sound output file: \"" << assetFileName << "\"\n"; + return; + } + + const WavWriter writer(*outFile); + + if (soundFile.m_entry.frameRateIndex >= std::extent_v) + return; + + const WavMetaData metaData{ + soundFile.m_entry.channelCount, + FRAME_RATE_FOR_INDEX[soundFile.m_entry.frameRateIndex], + bitsPerSample + }; + + writer.WritePcmHeader(metaData, soundFile.m_entry.size); + + while (!soundFile.m_stream->eof()) + { + char buffer[2048]; + soundFile.m_stream->read(buffer, sizeof(buffer)); + const auto readSize = soundFile.m_stream->gcount(); + outFile->write(buffer, readSize); + } + } + void DumpSoundFilePassthrough(const char* assetFileName, const SoundBankEntryInputStream& soundFile, const std::string& extension) const { const auto outFile = OpenAssetOutputFile(assetFileName, extension); @@ -284,15 +329,14 @@ class AssetDumperSndBank::Internal const auto format = static_cast(soundFile.m_entry.format); switch (format) { - case SND_ASSET_FORMAT_MP3: - DumpSoundFilePassthrough(alias.assetFileName, soundFile, ".mp3"); + case SND_ASSET_FORMAT_PCMS16: + DumpSoundFilePcm(alias.assetFileName, soundFile, 16u); break; case SND_ASSET_FORMAT_FLAC: DumpSoundFilePassthrough(alias.assetFileName, soundFile, ".flac"); break; - case SND_ASSET_FORMAT_PCMS16: case SND_ASSET_FORMAT_PCMS24: case SND_ASSET_FORMAT_PCMS32: case SND_ASSET_FORMAT_IEEE: diff --git a/src/ObjWriting/Sound/WavWriter.cpp b/src/ObjWriting/Sound/WavWriter.cpp new file mode 100644 index 00000000..160aa0f9 --- /dev/null +++ b/src/ObjWriting/Sound/WavWriter.cpp @@ -0,0 +1,53 @@ +#include "WavWriter.h" + +#include "Sound/WavTypes.h" + +WavWriter::WavWriter(std::ostream& stream) + : m_stream(stream) +{ +} + +void WavWriter::WritePcmHeader(const WavMetaData& metaData, const size_t dataLen) const +{ + constexpr auto riffMasterChunkSize = sizeof(WAV_CHUNK_ID_RIFF) + + sizeof(uint32_t) + + sizeof(WAV_WAVE_ID) + + sizeof(WavChunkHeader) + + sizeof(WavFormatChunkPcm) + + sizeof(WavChunkHeader) + + sizeof(dataLen); + + m_stream.write(reinterpret_cast(&WAV_CHUNK_ID_RIFF), sizeof(WAV_CHUNK_ID_RIFF)); + m_stream.write(reinterpret_cast(&riffMasterChunkSize), sizeof(riffMasterChunkSize)); + m_stream.write(reinterpret_cast(&WAV_WAVE_ID), sizeof(WAV_WAVE_ID)); + + constexpr WavChunkHeader formatChunkHeader + { + WAV_CHUNK_ID_FMT, + sizeof(WavFormatChunkPcm) + }; + m_stream.write(reinterpret_cast(&formatChunkHeader), sizeof(formatChunkHeader)); + + const WavFormatChunkPcm formatChunk + { + WavFormat::PCM, + static_cast(metaData.channelCount), + metaData.samplesPerSec, + metaData.samplesPerSec * metaData.channelCount * metaData.bitsPerSample / 8, + static_cast(metaData.channelCount * (metaData.bitsPerSample / 8)), + static_cast(metaData.bitsPerSample) + }; + m_stream.write(reinterpret_cast(&formatChunk), sizeof(formatChunk)); + + const WavChunkHeader dataChunkHeader + { + WAV_CHUNK_ID_DATA, + dataLen + }; + m_stream.write(reinterpret_cast(&dataChunkHeader), sizeof(dataChunkHeader)); +} + +void WavWriter::WritePcmData(const void* data, const size_t dataLen) const +{ + m_stream.write(static_cast(data), dataLen); +} diff --git a/src/ObjWriting/Sound/WavWriter.h b/src/ObjWriting/Sound/WavWriter.h new file mode 100644 index 00000000..4e7c2784 --- /dev/null +++ b/src/ObjWriting/Sound/WavWriter.h @@ -0,0 +1,21 @@ +#pragma once +#include + +struct WavMetaData +{ + unsigned channelCount; + unsigned samplesPerSec; + unsigned bitsPerSample; +}; + +class WavWriter +{ +public: + explicit WavWriter(std::ostream& stream); + + void WritePcmHeader(const WavMetaData& metaData, size_t dataLen) const; + void WritePcmData(const void* data, size_t dataLen) const; + +private: + std::ostream& m_stream; +};