changeset 233:6c04845f83dd

Add WaveFile class for loading audio files in simpletest
author Jamie Bullock <jamie@jamiebullock.com>
date Wed, 04 Jun 2014 18:09:43 +0100
parents f7a7a54e9de4
children 5ee4d256b937
files examples/simpletest/WaveFile.cpp examples/simpletest/WaveFile.h
diffstat 2 files changed, 238 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/simpletest/WaveFile.cpp	Wed Jun 04 18:09:43 2014 +0100
@@ -0,0 +1,172 @@
+#include "WaveFile.h"
+
+#include <fstream>
+#include <iostream>
+#include <cstdint>
+#include <cassert>
+#include <cstring>
+
+namespace
+{
+	struct RIFFChunk
+	{
+		std::uint32_t chunkID;
+		std::uint32_t chunkSize;
+		std::uint32_t format;
+	};
+	struct fmtChunk
+	{
+		std::uint32_t chunkID;
+		std::uint32_t chunkSize;
+		std::uint16_t audioFormat;
+		std::uint16_t numChannels;
+		std::uint32_t sampleRate;
+		std::uint32_t byteRate;
+		std::uint16_t blockAlign;
+		std::uint16_t bitsPerSample;
+	};
+	struct WaveHeader
+	{
+		RIFFChunk riff;
+		fmtChunk fmt;
+	};
+}
+
+WaveFile::WaveFile() : data(nullptr), size(0)
+{
+}
+WaveFile::WaveFile(const std::string &filename) : data(nullptr), size(0)
+{
+	Load(filename);
+}
+WaveFile::~WaveFile()
+{
+	Unload();
+}
+
+bool WaveFile::Load(const std::string &filename)
+{
+	if (IsLoaded())
+	{
+		Unload();
+	}
+
+	std::fstream file(filename, std::ios::in | std::ios::binary);
+
+	if (!file.is_open())
+	{
+		std::cerr << "Error: Could not open file." << std::endl;
+		return false;
+	}
+
+	WaveHeader header;
+	std::memset(&header, 0, sizeof(WaveHeader));
+
+	while (file.peek() != std::char_traits<char>::eof())
+	{
+		std::uint32_t chunkID;
+		std::uint32_t chunkSize;
+
+		file.read(reinterpret_cast<char*>(&chunkID), sizeof(std::uint32_t));
+		file.read(reinterpret_cast<char*>(&chunkSize), sizeof(std::uint32_t));
+
+		switch (chunkID)
+		{
+		case 'FFIR':
+			{
+				header.riff.chunkID = chunkID;
+				header.riff.chunkSize = chunkSize;
+				file.read(reinterpret_cast<char*>(&header.riff.format), sizeof(std::uint32_t));
+
+				if (header.riff.format != 'EVAW')
+				{
+					std::cerr << "Error: Not a valid WAVE file." << std::endl;
+					return false;
+				}
+
+				break;
+			}
+		case ' tmf':
+			{
+				header.fmt.chunkID = chunkID;
+				header.fmt.chunkSize = chunkSize;
+				file.read(reinterpret_cast<char*>(&header.fmt.audioFormat), sizeof(std::uint16_t));
+				file.read(reinterpret_cast<char*>(&header.fmt.numChannels), sizeof(std::uint16_t));
+				file.read(reinterpret_cast<char*>(&header.fmt.sampleRate), sizeof(std::uint32_t));
+				file.read(reinterpret_cast<char*>(&header.fmt.byteRate), sizeof(std::uint32_t));
+				file.read(reinterpret_cast<char*>(&header.fmt.blockAlign), sizeof(std::uint16_t));
+				file.read(reinterpret_cast<char*>(&header.fmt.bitsPerSample), sizeof(std::uint16_t));
+
+				if (header.fmt.audioFormat != PCM &&
+                    header.fmt.audioFormat != WAVE_FORMAT_IEEE_FLOAT)
+				{
+					std::cerr << "Error: Not in valid format" << std::endl;
+					return false;
+				}
+				if (header.fmt.bitsPerSample % 2 != 0)
+				{
+					std::cerr << "Error: Invalid number of bits per sample" << std::endl;
+					return false;
+				}
+				if (header.fmt.byteRate != (header.fmt.sampleRate * header.fmt.numChannels * header.fmt.bitsPerSample / 8))
+				{
+					std::cerr << "Error: Invalid byte rate" << std::endl;
+					return false;
+				}
+				if (header.fmt.blockAlign != (header.fmt.numChannels * header.fmt.bitsPerSample / 8))
+				{
+					std::cerr << "Error: Invalid block align" << std::endl;
+					return false;
+				}
+
+				break;
+			}
+		case 'atad':
+			{
+				assert(data == nullptr);
+				size = chunkSize;
+				data = new char[size];
+				file.read(data, chunkSize);
+
+				break;
+			}
+		default:
+			{
+				file.ignore(chunkSize);
+
+				break;
+			}
+		}
+	}
+
+	// Check that we got all chunks
+	if (header.riff.chunkID != 'FFIR')
+	{
+		std::cerr << "Error: Missing RIFF chunk." << std::endl;
+		return false;
+	}
+	if (header.fmt.chunkID != ' tmf')
+	{
+		std::cerr << "Error: Missing fmt chunk." << std::endl;
+		return false;
+	}
+	if (data == nullptr || size == 0)
+	{
+		std::cerr << "Error: Missing data chunk." << std::endl;
+		return false;
+	}
+
+	// Fill meta struct
+	meta.audioFormat   = static_cast<AudioFormat>(header.fmt.audioFormat);
+	meta.numChannels   = header.fmt.numChannels;
+	meta.sampleRate    = header.fmt.sampleRate;
+	meta.bitsPerSample = header.fmt.bitsPerSample;
+
+	return true;
+}
+void WaveFile::Unload()
+{
+	delete[] data;
+	data = nullptr;
+	size = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/simpletest/WaveFile.h	Wed Jun 04 18:09:43 2014 +0100
@@ -0,0 +1,66 @@
+#ifndef WaveFile_h__
+#define WaveFile_h__
+
+#include <string>
+
+///\note All meta data is undefined if IsLoaded() == false
+class WaveFile
+{
+public:
+	enum AudioFormat
+	{
+		PCM = 1,
+        WAVE_FORMAT_IEEE_FLOAT = 3
+	};
+
+	WaveFile();
+	WaveFile(const std::string &filename);
+	~WaveFile();
+
+	bool Load(const std::string &filename);
+	void Unload();
+
+	inline bool IsLoaded() const
+	{
+		return (data != nullptr && size != 0);
+	}
+
+	inline AudioFormat GetAudioFormat() const
+	{
+		return meta.audioFormat;
+	}
+	inline unsigned int GetNumChannels() const
+	{
+		return meta.numChannels;
+	}
+	inline unsigned int GetSampleRate() const
+	{
+		return meta.sampleRate;
+	}
+	inline unsigned int GetBitsPerSample() const
+	{
+		return meta.bitsPerSample;
+	}
+
+	inline const char *GetData() const
+	{
+		return data;
+	}
+	inline std::size_t GetDataSize() const
+	{
+		return size;
+	}
+
+private:
+	struct Meta
+	{
+		AudioFormat audioFormat;
+		unsigned int numChannels;
+		unsigned int sampleRate;
+		unsigned int bitsPerSample;
+	} meta;
+	char *data;
+	std::size_t size;
+};
+
+#endif // WaveFile_h__