#ifndef WAVE_H
#define WAVE_H

/*
 * @(#)Wave.h
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 2002-2003 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDWserver.cpp for details.
 */

#include "File.h"
#include "FileInputStream.h"

typedef unsigned long uint32;
typedef unsigned short uint16;

/**
 * WAV file header
 *
 * see
 * http://ccrma-www.stanford.edu/CCRMA/Courses/422/projects/WaveFormat/
 *
 * @version 20030221
 * @author Jörg P. M. Haeger
 */
struct WaveHeader {
	char chunkID[4]; // "RIFF"
	uint32 chunkSize; // length of file
	char format[4]; // "WAVE"

	char subchunk1ID[4]; // "fmt "
	uint32 subchunk1Size; // always 16 bytes
	uint16 audioFormat; // always 1 = PCM-code
	uint16 numChannels; // 1 = mono, 2 = stereo
	uint32 sampleRate;
	uint32 byteRate;
	uint16 blockAlign;
	uint16 bitsPerSample;

	char subchunk2ID[4]; // "data"
	uint32 subchunk2Size; // length of data

	WaveHeader() {
		strncpy(chunkID, "RIFF", 4);
		strncpy(format, "WAVE", 4);
		strncpy(subchunk1ID, "fmt ", 4);
		subchunk1Size = 16;
		audioFormat = 1;
		numChannels = 2;
		sampleRate = 44100;
		byteRate = 44100 * 4;
		blockAlign = 4;
		bitsPerSample = 16;
		strncpy(subchunk2ID, "data", 4);
		setLength(0);
	}

	int getLength() {
		return subchunk2Size;
	}

	void setLength(int n) {
		subchunk2Size = n;
		chunkSize = subchunk2Size + 36;
	}
};


/**
 * A class to handle WAV files.
 *
 * @version 20030220
 * @author Jörg P. M. Haeger
 */
class Wave {
private:
	int format;
	int channels;
	long frequency;
	long bytesPerSecond;
	int bytesPerSample;
	long dataLength;

	public: Wave(File &file) {
		FileInputStream inStream = new FileInputStream(file);
		char bs[44];
		if (inStream.read(bs, 0, 44) < 44
			|| bs[0] != 'R' || bs[1] != 'I'
			|| bs[2] != 'F' || bs[3] != 'F'
			|| bs[8] != 'W' || bs[9] != 'A'
			|| bs[10] != 'V' || bs[11] != 'E')
			throw new Exception("is not a WAV");

//		inStream.close();
		format = uint16(bs, 20);
		if (format != 1)
			throw new Exception("has an invalid format");

		channels = uint16(bs, 22);
		frequency = uint32(bs, 24);
		bytesPerSecond = uint32(bs, 28);
		bytesPerSample = uint16(bs, 32);
		dataLength = uint32(bs, 40);
	}

	public: int getAudioChannels() {
		return channels;
	}

	public: int getAudioSampleBits() {
		return 8 * bytesPerSample / channels;
	}

	public: int getAudioSampleRate() {
		return frequency;
	}

	public: long getSeconds() {
		return dataLength / bytesPerSecond;
	}

	private: static int uint16(char *bs, int offset) {
		return
			((bs[offset + 1] & 0xff) << 8)
			| (bs[offset] & 0xff);
	}

	private: static long uint32(char *bs, int offset) {
		return
			((bs[offset + 3] & 0xff) << 24)
			| ((bs[offset + 2] & 0xff) << 16)
			| ((bs[offset + 1] & 0xff) << 8)
			| (bs[offset] & 0xff);
	}
};

#endif
