jamie@233: #include "WaveFile.h" jamie@233: jamie@233: #include jamie@233: #include jamie@233: #include jamie@233: #include jamie@233: #include jamie@233: jamie@233: namespace jamie@233: { jamie@233: struct RIFFChunk jamie@233: { jamie@233: std::uint32_t chunkID; jamie@233: std::uint32_t chunkSize; jamie@233: std::uint32_t format; jamie@233: }; jamie@233: struct fmtChunk jamie@233: { jamie@233: std::uint32_t chunkID; jamie@233: std::uint32_t chunkSize; jamie@233: std::uint16_t audioFormat; jamie@233: std::uint16_t numChannels; jamie@233: std::uint32_t sampleRate; jamie@233: std::uint32_t byteRate; jamie@233: std::uint16_t blockAlign; jamie@233: std::uint16_t bitsPerSample; jamie@233: }; jamie@233: struct WaveHeader jamie@233: { jamie@233: RIFFChunk riff; jamie@233: fmtChunk fmt; jamie@233: }; jamie@233: } jamie@233: jamie@233: WaveFile::WaveFile() : data(nullptr), size(0) jamie@233: { jamie@233: } jamie@233: WaveFile::WaveFile(const std::string &filename) : data(nullptr), size(0) jamie@233: { jamie@233: Load(filename); jamie@233: } jamie@233: WaveFile::~WaveFile() jamie@233: { jamie@233: Unload(); jamie@233: } jamie@233: jamie@233: bool WaveFile::Load(const std::string &filename) jamie@233: { jamie@233: if (IsLoaded()) jamie@233: { jamie@233: Unload(); jamie@233: } jamie@233: jamie@233: std::fstream file(filename, std::ios::in | std::ios::binary); jamie@233: jamie@233: if (!file.is_open()) jamie@233: { jamie@233: std::cerr << "Error: Could not open file." << std::endl; jamie@233: return false; jamie@233: } jamie@233: jamie@233: WaveHeader header; jamie@233: std::memset(&header, 0, sizeof(WaveHeader)); jamie@233: jamie@233: while (file.peek() != std::char_traits::eof()) jamie@233: { jamie@233: std::uint32_t chunkID; jamie@233: std::uint32_t chunkSize; jamie@233: jamie@233: file.read(reinterpret_cast(&chunkID), sizeof(std::uint32_t)); jamie@233: file.read(reinterpret_cast(&chunkSize), sizeof(std::uint32_t)); jamie@233: jamie@233: switch (chunkID) jamie@233: { jamie@233: case 'FFIR': jamie@233: { jamie@233: header.riff.chunkID = chunkID; jamie@233: header.riff.chunkSize = chunkSize; jamie@233: file.read(reinterpret_cast(&header.riff.format), sizeof(std::uint32_t)); jamie@233: jamie@233: if (header.riff.format != 'EVAW') jamie@233: { jamie@233: std::cerr << "Error: Not a valid WAVE file." << std::endl; jamie@233: return false; jamie@233: } jamie@233: jamie@233: break; jamie@233: } jamie@233: case ' tmf': jamie@233: { jamie@233: header.fmt.chunkID = chunkID; jamie@233: header.fmt.chunkSize = chunkSize; jamie@233: file.read(reinterpret_cast(&header.fmt.audioFormat), sizeof(std::uint16_t)); jamie@233: file.read(reinterpret_cast(&header.fmt.numChannels), sizeof(std::uint16_t)); jamie@233: file.read(reinterpret_cast(&header.fmt.sampleRate), sizeof(std::uint32_t)); jamie@233: file.read(reinterpret_cast(&header.fmt.byteRate), sizeof(std::uint32_t)); jamie@233: file.read(reinterpret_cast(&header.fmt.blockAlign), sizeof(std::uint16_t)); jamie@233: file.read(reinterpret_cast(&header.fmt.bitsPerSample), sizeof(std::uint16_t)); jamie@233: jamie@233: if (header.fmt.audioFormat != PCM && jamie@233: header.fmt.audioFormat != WAVE_FORMAT_IEEE_FLOAT) jamie@233: { jamie@233: std::cerr << "Error: Not in valid format" << std::endl; jamie@233: return false; jamie@233: } jamie@233: if (header.fmt.bitsPerSample % 2 != 0) jamie@233: { jamie@233: std::cerr << "Error: Invalid number of bits per sample" << std::endl; jamie@233: return false; jamie@233: } jamie@233: if (header.fmt.byteRate != (header.fmt.sampleRate * header.fmt.numChannels * header.fmt.bitsPerSample / 8)) jamie@233: { jamie@233: std::cerr << "Error: Invalid byte rate" << std::endl; jamie@233: return false; jamie@233: } jamie@233: if (header.fmt.blockAlign != (header.fmt.numChannels * header.fmt.bitsPerSample / 8)) jamie@233: { jamie@233: std::cerr << "Error: Invalid block align" << std::endl; jamie@233: return false; jamie@233: } jamie@233: jamie@233: break; jamie@233: } jamie@233: case 'atad': jamie@233: { jamie@233: assert(data == nullptr); jamie@233: size = chunkSize; jamie@233: data = new char[size]; jamie@233: file.read(data, chunkSize); jamie@233: jamie@233: break; jamie@233: } jamie@233: default: jamie@233: { jamie@233: file.ignore(chunkSize); jamie@233: jamie@233: break; jamie@233: } jamie@233: } jamie@233: } jamie@233: jamie@233: // Check that we got all chunks jamie@233: if (header.riff.chunkID != 'FFIR') jamie@233: { jamie@233: std::cerr << "Error: Missing RIFF chunk." << std::endl; jamie@233: return false; jamie@233: } jamie@233: if (header.fmt.chunkID != ' tmf') jamie@233: { jamie@233: std::cerr << "Error: Missing fmt chunk." << std::endl; jamie@233: return false; jamie@233: } jamie@233: if (data == nullptr || size == 0) jamie@233: { jamie@233: std::cerr << "Error: Missing data chunk." << std::endl; jamie@233: return false; jamie@233: } jamie@233: jamie@233: // Fill meta struct jamie@233: meta.audioFormat = static_cast(header.fmt.audioFormat); jamie@233: meta.numChannels = header.fmt.numChannels; jamie@233: meta.sampleRate = header.fmt.sampleRate; jamie@233: meta.bitsPerSample = header.fmt.bitsPerSample; jamie@233: jamie@233: return true; jamie@233: } jamie@233: void WaveFile::Unload() jamie@233: { jamie@233: delete[] data; jamie@233: data = nullptr; jamie@233: size = 0; jamie@233: }