mirror of
				https://github.com/RetroDECK/Duckstation.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	Common: Add WAVWriter class
This commit is contained in:
		
							parent
							
								
									6a6aa72f3a
								
							
						
					
					
						commit
						c7a74cabaa
					
				|  | @ -54,6 +54,8 @@ add_library(common | |||
|   timestamp.cpp | ||||
|   timestamp.h | ||||
|   types.h | ||||
|   wav_writer.cpp | ||||
|   wav_Writer.h | ||||
| ) | ||||
| 
 | ||||
| target_include_directories(common PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") | ||||
|  |  | |||
|  | @ -70,6 +70,7 @@ | |||
|     <ClInclude Include="timestamp.h" /> | ||||
|     <ClInclude Include="types.h" /> | ||||
|     <ClInclude Include="cd_xa.h" /> | ||||
|     <ClInclude Include="wav_writer.h" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="assert.cpp" /> | ||||
|  | @ -102,6 +103,7 @@ | |||
|     <ClCompile Include="string_util.cpp" /> | ||||
|     <ClCompile Include="timer.cpp" /> | ||||
|     <ClCompile Include="timestamp.cpp" /> | ||||
|     <ClCompile Include="wav_writer.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Natvis Include="bitfield.natvis" /> | ||||
|  |  | |||
|  | @ -52,6 +52,7 @@ | |||
|     </ClInclude> | ||||
|     <ClInclude Include="hash_combine.h" /> | ||||
|     <ClInclude Include="progress_callback.h" /> | ||||
|     <ClInclude Include="wav_writer.h" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="jit_code_buffer.cpp" /> | ||||
|  | @ -100,6 +101,7 @@ | |||
|     </ClCompile> | ||||
|     <ClCompile Include="cd_image_chd.cpp" /> | ||||
|     <ClCompile Include="progress_callback.cpp" /> | ||||
|     <ClCompile Include="wav_writer.cpp" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Natvis Include="bitfield.natvis" /> | ||||
|  |  | |||
							
								
								
									
										115
									
								
								src/common/wav_writer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/common/wav_writer.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,115 @@ | |||
| #include "wav_writer.h" | ||||
| #include "file_system.h" | ||||
| #include "log.h" | ||||
| Log_SetChannel(WAVWriter); | ||||
| 
 | ||||
| #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) | ||||
| 
 | ||||
| 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()) | ||||
|   { | ||||
|     Log_ErrorPrintf("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()) | ||||
|     Log_ErrorPrintf("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<u32>(std::fwrite(samples, sizeof(s16) * m_num_channels, num_frames, m_file)); | ||||
|   if (num_frames_written != num_frames) | ||||
|     Log_ErrorPrintf("Only wrote %u of %u frames to output file", num_frames_written); | ||||
| 
 | ||||
|   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<u16>(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<u16>(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); | ||||
| } | ||||
| 
 | ||||
| } // namespace Common
 | ||||
							
								
								
									
										31
									
								
								src/common/wav_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/common/wav_writer.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | |||
| #pragma once | ||||
| #include "types.h" | ||||
| #include <cstdio> | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| class WAVWriter | ||||
| { | ||||
| public: | ||||
|   WAVWriter(); | ||||
|   ~WAVWriter(); | ||||
| 
 | ||||
|   ALWAYS_INLINE 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
 | ||||
		Loading…
	
		Reference in a new issue
	
	 Connor McLaughlin
						Connor McLaughlin