diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 0221d8f3a4..a74cfd2633 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -34,6 +34,7 @@ target_sources(common PRIVATE StringUtil.cpp Timer.cpp ThreadPool.cpp + WAVWriter.cpp WindowInfo.cpp emitter/avx.cpp emitter/bmi.cpp @@ -102,6 +103,7 @@ target_sources(common PRIVATE Threading.h ThreadPool.h TraceLog.h + WAVWriter.h WindowInfo.h emitter/cpudetect_internal.h emitter/implement/dwshift.h diff --git a/common/WAVWriter.cpp b/common/WAVWriter.cpp new file mode 100644 index 0000000000..884d7f779c --- /dev/null +++ b/common/WAVWriter.cpp @@ -0,0 +1,129 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "common/PrecompiledHeader.h" + +#include "common/WAVWriter.h" +#include "common/FileSystem.h" +#include "common/Console.h" + +#pragma pack(push, 1) +struct WAV_HEADER +{ + u32 chunk_id; // RIFF + u32 chunk_size; + u32 format; // WAVE + + struct FormatChunk + { + u32 chunk_id; // "fmt " + u32 chunk_size; + u16 audio_format; // pcm = 1 + u16 num_channels; + u32 sample_rate; + u32 byte_rate; + u16 block_align; + u16 bits_per_sample; + } fmt_chunk; + + struct DataChunkHeader + { + u32 chunk_id; // "data " + u32 chunk_size; + } data_chunk_header; +}; +#pragma pack(pop) + +using namespace Common; + +WAVWriter::WAVWriter() = default; + +WAVWriter::~WAVWriter() +{ + if (IsOpen()) + Close(); +} + +bool WAVWriter::Open(const char* filename, u32 sample_rate, u32 num_channels) +{ + if (IsOpen()) + Close(); + + m_file = FileSystem::OpenCFile(filename, "wb"); + if (!m_file) + return false; + + m_sample_rate = sample_rate; + m_num_channels = num_channels; + + if (!WriteHeader()) + { + Console.Error("Failed to write header to file"); + m_sample_rate = 0; + m_num_channels = 0; + std::fclose(m_file); + m_file = nullptr; + return false; + } + + return true; +} + +void WAVWriter::Close() +{ + if (!IsOpen()) + return; + + if (std::fseek(m_file, 0, SEEK_SET) != 0 || !WriteHeader()) + Console.Error("Failed to re-write header on file, file may be unplayable"); + + std::fclose(m_file); + m_file = nullptr; + m_sample_rate = 0; + m_num_channels = 0; + m_num_frames = 0; +} + +void WAVWriter::WriteFrames(const s16* samples, u32 num_frames) +{ + const u32 num_frames_written = + static_cast(std::fwrite(samples, sizeof(s16) * m_num_channels, num_frames, m_file)); + if (num_frames_written != num_frames) + Console.Error("Only wrote %u of %u frames to output file", num_frames_written, num_frames); + + m_num_frames += num_frames_written; +} + +bool WAVWriter::WriteHeader() +{ + const u32 data_size = sizeof(SampleType) * m_num_channels * m_num_frames; + + WAV_HEADER header = {}; + header.chunk_id = 0x46464952; // 0x52494646 + header.chunk_size = sizeof(WAV_HEADER) - 8 + data_size; + header.format = 0x45564157; // 0x57415645 + header.fmt_chunk.chunk_id = 0x20746d66; // 0x666d7420 + header.fmt_chunk.chunk_size = sizeof(header.fmt_chunk) - 8; + header.fmt_chunk.audio_format = 1; + header.fmt_chunk.num_channels = static_cast(m_num_channels); + header.fmt_chunk.sample_rate = m_sample_rate; + header.fmt_chunk.byte_rate = m_sample_rate * m_num_channels * sizeof(SampleType); + header.fmt_chunk.block_align = static_cast(m_num_channels * sizeof(SampleType)); + header.fmt_chunk.bits_per_sample = 16; + header.data_chunk_header.chunk_id = 0x61746164; // 0x64617461 + header.data_chunk_header.chunk_size = data_size; + + return (std::fwrite(&header, sizeof(header), 1, m_file) == 1); +} diff --git a/common/WAVWriter.h b/common/WAVWriter.h new file mode 100644 index 0000000000..e9b014e153 --- /dev/null +++ b/common/WAVWriter.h @@ -0,0 +1,48 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#pragma once +#include "common/Pcsx2Defs.h" +#include + +namespace Common +{ + class WAVWriter + { + public: + WAVWriter(); + ~WAVWriter(); + + __fi u32 GetSampleRate() const { return m_sample_rate; } + __fi u32 GetNumChannels() const { return m_num_channels; } + __fi u32 GetNumFrames() const { return m_num_frames; } + __fi bool IsOpen() const { return (m_file != nullptr); } + + bool Open(const char* filename, u32 sample_rate, u32 num_channels); + void Close(); + + void WriteFrames(const s16* samples, u32 num_frames); + + private: + using SampleType = s16; + + bool WriteHeader(); + + std::FILE* m_file = nullptr; + u32 m_sample_rate = 0; + u32 m_num_channels = 0; + u32 m_num_frames = 0; + }; +} // namespace Common \ No newline at end of file diff --git a/common/common.vcxproj b/common/common.vcxproj index a2f90bcea3..ffd9c0f0a3 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -98,6 +98,7 @@ + @@ -196,6 +197,7 @@ + diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index d71e5af636..d34353b4fd 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -208,6 +208,9 @@ Source Files + + Source Files + @@ -492,6 +495,9 @@ Header Files + + Header Files +