
// Project: webCDwriter - Network CD Writing
// File: MP3toWAV.cpp
// Author: Jörg Haeger, 14.05.2000

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include "Config.h"
#include "MP3toWAV.h"
#include "Process.h"
#include "Support.h"

typedef unsigned long uint32;
typedef unsigned short uint16;

// header for WAV-files
struct WaveHeader {
	char main_chunk[4]; // RIFF
	uint32 length; // length of file
	char chunk_type[4]; // WAVE
	char sub_chunk[4]; // fmt
	uint32 length_chunk; // length sub_chunk, always 16 bytes
	uint16 format; // always 1 = PCM-code
	uint16 modus; // 1 = mono, 2 = stereo
	uint32 sample_fq; // sample frequence
	uint32 byte_p_sec; // data per sec
	uint16 byte_p_spl; // bytes per sample
	uint16 bit_p_spl; // bits per sample
	char data_chunk[4]; // data
	uint32 data_length; // length of data

	WaveHeader()
	{
		strncpy(main_chunk, "RIFF", 4);
		strncpy(chunk_type, "WAVE", 4);
		strncpy(sub_chunk, "fmt ", 4);
		length_chunk = 16;
		format = 1;
		modus = 2;
		sample_fq = 44100;
		byte_p_sec = 44100 * 4;
		byte_p_spl = 4;
		bit_p_spl = 16;
		strncpy(data_chunk, "data", 4);
		setLength(0);
	}

	int getLength()
	{
		return data_length;
	}

	void setLength(int n)
	{
		data_length = n;
		length = data_length + 36;
	}
};

MP3toWAV::MP3toWAV(FILE *inStream)
{
	this->inStream = inStream;
	mpg123 = new Process(config.getMpg123Path());
	mpg123->addArg("-q");
	mpg123->addArg("-r 44100");
	mpg123->addArg("-s");
	mpg123->addArg("-");
	mpg123->start(NULL, 1);
	leftInStream = 0;
	writeBuf0 = writeBuf1 = 0;
	readBufN = 44;
	waveLength = 0;
	mode = NORMAL;
}

MP3toWAV::~MP3toWAV()
{
	mpg123->terminate();
	delete mpg123;
}

void MP3toWAV::end()
{
	mode = CLOSEOUT;
}

void MP3toWAV::fillReadBuf()
{
	int free = sizeof readBuf - readBufN;
	if (free <= 0)
		return;

	int n = mpg123->read(&readBuf[readBufN], free);
	if (n == 0)
		mode = INCLOSED;
	else
	if (n > 0)
	{
		readBufN += n;
		waveLength += n;
	}
}

void MP3toWAV::fillWriteBuf()
{
	int n = fread(writeBuf, 1,
		min(leftInStream, sizeof writeBuf), inStream);
	if (n > 0)
	{
		leftInStream -= n;
		writeBuf0 = 0;
		writeBuf1 = n;
	}
	else
		if (leftInStream > 0)
			throw "unexpected end of inStream";
}

int MP3toWAV::get(const char **buf)
{
	while (1)
	{
		mpg123->select();
		fillReadBuf();
		if (mode == INCLOSED)
		{
			if (leftInStream > 0
				|| writeBuf0 < writeBuf1)
			{
				// unexpected end of mpg123
				skipBytes(inStream, leftInStream);
				leftInStream = 0;
				throw "error_converting_MP3_to_WAV";
			}

			int n = readBufN;
			readBufN = 0;
			*buf = readBuf;
			return n;
		}

		if (readBufN == sizeof readBuf)
		{
			readBufN = 0;
			*buf = readBuf;
			return sizeof readBuf;
		}

		if (leftInStream == 0)
			if (mode == NORMAL)
				return 0;
			else
			if (mode == CLOSEOUT && writeBuf0 >= writeBuf1)
			{
				mpg123->closePipeToChild();
				mode = OUTCLOSED;
			}

		if (writeBuf0 >= writeBuf1)
			fillWriteBuf();

		int n = mpg123->write(&writeBuf[writeBuf0],
			writeBuf1 - writeBuf0);
		if (n > 0)
			writeBuf0 += n;
	}
}

void MP3toWAV::put(int n)
{
	leftInStream += n;
}

void MP3toWAV::setInStream(FILE *inStream)
{
	this->inStream = inStream;
}

void MP3toWAV::updateHeader(FILE *outStream)
{
	fseek(outStream, 0, SEEK_SET);
	WaveHeader header;
	header.setLength(waveLength);
	fwrite(&header, 1, sizeof header, outStream);
	fflush(outStream);
}

