/*
 * SoundFormat.cpp
 *
 *  Created on: 24.01.2012
 *      Author: stefan.detter
 */

#include "Sound.h"
#include "SoundFormat.h"

#if USE_SOUND

#include <QFile>
#include <qendian.h>

class WavFile
{
public:
	WavFile(QIODevice* device);

	// Reads WAV header and seeks to start of data
	bool readHeader();

	const QAudioFormat& 	format() const;
	const int 				headerLength() const;

private:
	QIODevice* 		m_device;
	QAudioFormat 	m_format;
};

struct chunk
{
	char id[4];
	quint32 size;
};

struct RIFFHeader
{
	chunk descriptor; // "RIFF"
	char type[4]; // "WAVE"
};

struct WAVEHeader
{
	chunk descriptor;
	quint16 audioFormat;
	quint16 numChannels;
	quint32 sampleRate;
	quint32 byteRate;
	quint16 blockAlign;
	quint16 bitsPerSample;
};

struct DATAHeader
{
	chunk descriptor;
};

struct CombinedHeader
{
	RIFFHeader riff;
	WAVEHeader wave;
	DATAHeader data;
};

static const int WavHeaderLength = sizeof(CombinedHeader);

WavFile::WavFile(QIODevice* device)
	: m_device(device)
{

}

bool WavFile::readHeader()
{
	bool result = true;

	if (!m_device->isSequential())
		result = m_device->seek(0);
	// else, assume that current position is the start of the header

	if (result)
	{
		CombinedHeader header;
		result = (m_device->read(reinterpret_cast<char *>(&header), WavHeaderLength)
				== WavHeaderLength);
		if (result)
		{
			if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
					|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
					&& memcmp(&header.riff.type, "WAVE", 4) == 0
					&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
					&& header.wave.audioFormat == 1 // PCM
					)
			{
				if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
					m_format.setByteOrder(QAudioFormat::LittleEndian);
				else
					m_format.setByteOrder(QAudioFormat::BigEndian);

#if QT_VERSION > 0x050000
				m_format.setChannelCount(qFromLittleEndian<quint16>(header.wave.numChannels));
#else
				m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
#endif
				m_format.setCodec("audio/pcm");
#if QT_VERSION > 0x050000
				m_format.setSampleRate(qFromLittleEndian<quint32>(header.wave.sampleRate));
#else
				m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
#endif
				m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));

				switch (header.wave.bitsPerSample)
				{
				case 8:
					m_format.setSampleType(QAudioFormat::UnSignedInt);
					break;
				case 16:
					m_format.setSampleType(QAudioFormat::SignedInt);
					break;
				default:
					result = false;
					break;
				}
			}
			else
			{
				result = false;
			}
		}
	}

	return result;
}

const QAudioFormat& WavFile::format() const
{
	return m_format;
}

const int WavFile::headerLength() const
{
	return WavHeaderLength;
}




SoundFormat::SoundFormat(QFile* device)
{
	// Set up the format, eg.
#if QT_VERSION > 0x050000
	m_format.setSampleRate(22050);
	m_format.setChannelCount(1);
#else
	m_format.setFrequency(22050);
	m_format.setChannels(1);
#endif
	m_format.setSampleSize(8);
	m_format.setCodec("audio/pcm");
	m_format.setByteOrder(QAudioFormat::LittleEndian);
	m_format.setSampleType(QAudioFormat::SignedInt);
	m_offset = 0;

	if(device->fileName().endsWith("wav", Qt::CaseInsensitive))
	{
		WavFile f(device);
		if(f.readHeader())
		{
			m_format = f.format();
			m_offset = f.headerLength();
		}
	}
}

SoundFormat::~SoundFormat()
{
}

const QAudioFormat& SoundFormat::format()
{
	return m_format;
}

const int SoundFormat::offset()
{
	return m_offset;
}

#endif
