/***********************************************************************
 * VirtualDub Modification for OGM
 *
 * Copyright (C) 2002 Cyrius
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *   
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *   
 * You should have received a copy of the GNU General Public License along
 * with this program (see the file COPYING); if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * or visit http://www.gnu.org/copyleft/gpl.html
 *
 ***********************************************************************
 *
 *
 *
 */

#include "OGMAudioSource.h"

#include "../Error.h"
#include "../ProgressDialog.h"
#include <fstream>
using namespace std;
#include <math.h>

#define INPUT_BUFFERSIZE (2*1024*1024)

OGMAudioSource::OGMAudioSource(char *fn, LONG buffer_size) {
	input = NULL;
	file = NULL;
	desc = NULL;
	isOGM = FALSE;
	isText = FALSE;
	if(buffer_size <= 0)
		buffer_size = INPUT_BUFFERSIZE;
	this->buffer_size = buffer_size;
	if(fn) {
		file = strdup(fn);
		desc = strdup(fn);
	} else {
		throw MyError("No file name specified when opening a new Audio source");
	}
	firstFrame = currentFrame = lastFrame = NULL;
#ifdef __OGM_CAN_CUT_FIRST_PACKET__
	can_cut_first_packet = true;
#endif
}

OGMAudioSource::~OGMAudioSource(void) {
	if(input) {
		input->Close();
		delete input;
	}
	if(file)
		delete file;
	if(desc)
		delete desc;
	while(firstFrame = frames.RemoveTail())
		delete firstFrame;
}

void OGMAudioSource::add_frame(u16 size, u16 samples) {
	s64 file_pos = 0;
	s64 pos = 0;

	bool first = true;
	if(lastFrame) {
		first = false;
		file_pos = lastFrame->file_pos + lastFrame->size;
		pos = lastFrame->pos + lastFrame->samples;
	}

	lastFrame = new frameNode();
	frames.AddTail(lastFrame);

	if(first)
		firstFrame = currentFrame = lastFrame;

	lastFrame->file_pos = file_pos;
	lastFrame->pos = pos;
	lastFrame->size = size;
	lastFrame->samples = samples;
}

__int64 OGMAudioSource::getFilePos(long lStart) {
	if((lStart<0) || !lastFrame)
		return 0;
	if(lStart > lastFrame->pos)
		return lastFrame->file_pos;

	// Find our nearest pointer
	if(lStart < currentFrame->pos / 2)
		currentFrame = firstFrame;
	else if(lStart > (currentFrame->pos + lastFrame->pos)/2)
		currentFrame = lastFrame;

	if(currentFrame->pos<lStart) {
		// Go forward
		while((currentFrame!=lastFrame) && (currentFrame->NextFromHead()->pos<=lStart))
			currentFrame = currentFrame->NextFromHead();
	} else if(currentFrame->pos>lStart) {
		while((currentFrame!=firstFrame) && (currentFrame->pos>lStart))
			currentFrame = currentFrame->NextFromTail();
	}

	return currentFrame->file_pos;
}

int OGMAudioSource::_read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lBytesRead, LONG *lSamplesRead) {
	if( (lStart < 0) || !lastFrame || (lStart > lastFrame->pos) ) {
		if(lSamplesRead)
			*lSamplesRead = 0;
		if(lBytesRead)
			*lBytesRead = 0;
		return AVIERR_OK;
	}

	// Find our nearest pointer
	if(lStart < currentFrame->pos / 2)
		currentFrame = firstFrame;
	else if(lStart > (currentFrame->pos + lastFrame->pos)/2)
		currentFrame = lastFrame;

	if(currentFrame->pos<lStart) {
		// Go forward
		if(can_cut_first_packet) {
			while((currentFrame!=lastFrame) && (currentFrame->NextFromHead()->pos<=lStart))
				currentFrame = currentFrame->NextFromHead();
		} else {
			while((currentFrame!=lastFrame) && (currentFrame->pos<lStart))
				currentFrame = currentFrame->NextFromHead();
		}
	} else if(currentFrame->pos>lStart) {
		// Go backward
		if(can_cut_first_packet) {
			while((currentFrame!=firstFrame) && (currentFrame->pos>lStart))
				currentFrame = currentFrame->NextFromTail();
		} else {
			while((currentFrame!=firstFrame) && (currentFrame->NextFromTail()->pos>=lStart))
				currentFrame = currentFrame->NextFromTail();
		}
	}

	s32 s_lag = 0;
	if(can_cut_first_packet)
		s_lag = lStart - currentFrame->pos;
	// The "normal" way would have been to read enough samples
	// But there is a problem with OggDS :
	// When initializing we defined a buffersize (the maximum bytes
	// found in a frame) which is used when creating the OGM file
	// But if here we read more than one frame at a time the data
	// put in the resulting Packet will be larger than the buffersize
	// Or it seems this cause problems (sync lost, ...) when playing the
	// resulting OGM file.
	// As using a higher buffersize solved the problem I think that
	// OggDS doesn't realloc its buffer when reading data, which means
	// data beyond buffersize in the Packet are lost :(
	// So the solution here is to read only one frame at a time
	// And it's up to the function who called us to redo a read
	// if there are not enough samples
	/*
	long bytes_read = 0;
	long samples_read = 0;
	u16 size = 0;
	while(samples_read < lCount) {
		size = currentFrame->size;
		if(lpBuffer) {
			if(input->Seek(currentFrame->file_pos, SEEK_SET) == -1)
				return AVIERR_FILEREAD;
			if(input->Read(lpBuffer, size) != size)
				return AVIERR_FILEREAD;
			lpBuffer = (char *)lpBuffer + size;
		}
		bytes_read += size;
		samples_read += currentFrame->samples;
		if(!currentFrame->next)
			break;
		currentFrame = currentFrame->next;
	}

	if(lBytesRead)
		*lBytesRead = bytes_read;
	if(lSamplesRead)
		*lSamplesRead = samples_read;
	*/

	u16 size = currentFrame->size;
	if(lpBuffer) {
		if(input->Seek(currentFrame->file_pos, SEEK_SET) == -1)
			return AVIERR_FILEREAD;
		if(input->Read(lpBuffer, size) != size)
			return AVIERR_FILEREAD;
		lpBuffer = (char *)lpBuffer + size;
	}

	if(lBytesRead)
		*lBytesRead = size;
	if(lSamplesRead)
		*lSamplesRead = currentFrame->samples - s_lag;

	return AVIERR_OK;
}

long OGMAudioSource::samplesLag(__int64 lStart) {
	if( (lStart < 0) || !lastFrame || (lStart >= lastFrame->pos))
		return 0;
	if(can_cut_first_packet) {
		// For txt stream, if lStart!=0, return 1, so that
		// first Packet start @ granulepos 1
		// This make the text allways shown by OggDS (not allways
		// the case if granulepos = 0)
		if(lStart && isText)
			return 1;
		else
			return 0;
	}

	// Find our nearest pointer
	if(lStart < currentFrame->pos / 2)
		currentFrame = firstFrame;
	else if(lStart > (currentFrame->pos + lastFrame->pos)/2)
		currentFrame = lastFrame;

	if(currentFrame->pos<lStart) {
		// Go forward
		while((currentFrame!=lastFrame) && (currentFrame->pos<lStart))
			currentFrame = currentFrame->NextFromHead();
	} else if(currentFrame->pos>lStart) {
		// Go backward
		while((currentFrame!=firstFrame) && (currentFrame->NextFromTail()->pos>=lStart))
			currentFrame = currentFrame->NextFromTail();
	}

	return currentFrame->pos - lStart;
}

/////////////////////////////////////////////

OGMTextSourceSRT::OGMTextSourceSRT(char *fn)
: OGMAudioSource(fn, 0)
{
	firstSub = currentSub = lastSub = NULL;
	last_e = 0;
	isText = TRUE;
}

OGMTextSourceSRT::~OGMTextSourceSRT(void) {
	while(firstSub = subs.RemoveTail())
		delete firstSub;
}

BOOL OGMTextSourceSRT::init(void) {
	if(!file)
		return FALSE;

	if(!allocFormat(sizeof(WAVEFORMATEX)))
		return FALSE;

	char buffer[16*1024+1];
	ifstream i_stream;
	i_stream.open(file, ios::in);
	while(i_stream.getline(buffer, sizeof(buffer))) {
		if(strlen(buffer) <= 0) {
			if(i_stream.eof())
				break;
			else
				continue;
		}
		// Current line should be the sub number, skip to get the start and end times
		if(!i_stream.getline(buffer, sizeof(buffer)))
			break;
		// Add the times
		if(!add_times(buffer))
			break;
		// Add the subs
		while(i_stream.getline(buffer, sizeof(buffer)) && (strlen(buffer)>0))
			add_sub(buffer);
	}

	//How to use a text stream within audio routines of VirtualDub :p
	getWaveFormat()->nChannels = 1;
	getWaveFormat()->wFormatTag = 0x0000;
	getWaveFormat()->nSamplesPerSec = 1000;
	getWaveFormat()->cbSize = 0;
	getWaveFormat()->nAvgBytesPerSec = 1;
	getWaveFormat()->nBlockAlign = 1;
	getWaveFormat()->wBitsPerSample = 1;

	lSampleFirst	= 0;
	lSampleLast		= lastSub->pos + lastSub->samples;

	streamInfo.fccType					= streamtypeTEXT;
	streamInfo.fccHandler				= 0;
	streamInfo.dwFlags					= 0;
	streamInfo.wPriority				= 0;
	streamInfo.wLanguage				= 0;
	streamInfo.dwInitialFrames	= 0;
	streamInfo.dwScale					= 1;
	streamInfo.dwRate						= getWaveFormat()->nSamplesPerSec;
	streamInfo.dwStart					= 0;
	streamInfo.dwLength					= lSampleLast;
	streamInfo.dwSuggestedBufferSize	= 16*1024;
	streamInfo.dwQuality				= 0xffffffff;
	streamInfo.dwSampleSize			= getWaveFormat()->nBlockAlign;

	if(!firstSub)
		return FALSE;
	else
		return TRUE;
}

BOOL OGMTextSourceSRT::add_times(char *line) {
	if(!line)
		return FALSE;
	if(strlen(line)<=0)
		return FALSE;

	int h1, m1, s1, ms1;
	int h2, m2, s2, ms2;
	if(sscanf(line, "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d"
		,&h1, &m1, &s1, &ms1
		,&h2, &m2, &s2, &ms2) != 8)
		return FALSE;
	s64 sub_s = ms1
		+ (1000 * s1)
		+ (1000 * 60 * m1)
		+ (1000 * 60 * 60 * h1);
	s64 sub_e = ms2
		+ (1000 * s2)
		+ (1000 * 60 * m2)
		+ (1000 * 60 * 60 * h2);
	if( (sub_e <= sub_s) || (sub_s < last_e) )
		return FALSE;

	// This subtitle doesn't begin just after the last one
	// We must fill the "space" by a void subtitle
	if(sub_s - last_e)
		add_sub(sub_s - last_e);

	add_sub(sub_e - sub_s);

	last_e = sub_e;

	return TRUE;
}

void OGMTextSourceSRT::add_sub(s32 samples) {
	s64 pos = 0;

	bool first = true;
	if(lastSub) {
		first = false;
		pos = lastSub->pos + lastSub->samples;
	}

	lastSub = new subNode();
	subs.AddTail(lastSub);

	if(first)
		firstSub= currentSub = lastSub;

	lastSub->samples = samples;
	lastSub->pos = pos;
}

BOOL OGMTextSourceSRT::add_sub(char *line) {
	if(!lastSub)
		return FALSE;
	if(!line)
		return TRUE;

	if(!lastSub->sub) {
		lastSub->sub = strdup(line);
	} else {
		int sub_l = strlen(lastSub->sub);
		int line_l = strlen(line);
		char *old_sub = lastSub->sub;
		lastSub->sub = new char[sub_l+1+line_l+1+16];
		if(!lastSub->sub) {
			lastSub->sub = old_sub;
			throw MyMemoryError();
		}
		sprintf(lastSub->sub, "%s\r\n%s", old_sub, line);
		delete old_sub;
	}

	return TRUE;
}

int OGMTextSourceSRT::_read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lBytesRead, LONG *lSamplesRead) {
	if( (lStart < 0) || !lastSub || (lStart > lastSub->pos) ) {
		if(lSamplesRead)
			*lSamplesRead = 0;
		if(lBytesRead)
			*lBytesRead = 0;
		return AVIERR_OK;
	}

	// Find our nearest pointer
	if(lStart < currentSub->pos / 2)
		currentSub = firstSub;
	else if(lStart > (currentSub->pos + lastSub->pos)/2)
		currentSub = lastSub;

	if(currentSub->pos<lStart) {
		// Go forward
		while((currentSub!=lastSub) && (currentSub->NextFromHead()->pos<=lStart))
			currentSub = currentSub->NextFromHead();
	} else if(currentSub->pos>lStart) {
		// Go backward
		while((currentSub!=firstSub) && (currentSub->pos>lStart))
			currentSub = currentSub->NextFromTail();
	}

	int len = 0;
	if(currentSub->sub) {
		len = strlen(currentSub->sub) + 1;
		if(lpBuffer)
			memcpy(lpBuffer, currentSub->sub, len);
	} else {
		len = 1;
		if(lpBuffer)
			*(char *)lpBuffer = 0;
	}

	if(lBytesRead)
		*lBytesRead = len;
	if(lSamplesRead)
		*lSamplesRead = currentSub->samples - (lStart - currentSub->pos);

	return AVIERR_OK;
}

bool OGMTextSourceSRT::isEOS(void) {
	// Assume EOS reached when current sub = last one
	return (currentSub==lastSub);
}

/////////////////////////////////////////////

OGMAudioSourceOGM::OGMAudioSourceOGM(char *fn, OGMReadHandler *handler, int num_stream, bool is_inputfile)
: OGMAudioSource(fn, 0)
{
	isOGM = TRUE;
	pAVIFile	= handler;
	pAVIStream	= NULL;
	this->num_stream = num_stream;
	if(desc)
		delete desc;
	desc = NULL;
	int length = 0;
	if(fn)
		length += strlen(fn);
	desc = (char *)malloc(length+50);
	if(!desc)
		throw MyMemoryError();
	if(is_inputfile)
		sprintf(desc, "Input file (%s), audio stream %d", fn, num_stream+1);
	else
		sprintf(desc, "%s, audio stream %d", fn, num_stream+1);
}

OGMAudioSourceOGM::~OGMAudioSourceOGM() {
	if(pAVIStream)
		delete pAVIStream;
}

BOOL OGMAudioSourceOGM::init() {
	if(!pAVIFile)
		return FALSE;

	pAVIStream = pAVIFile->GetStream(streamtypeAUDIO, num_stream);
	if(!pAVIStream)
		return FALSE;

	LONG format_len;

	if(pAVIStream->Info(&streamInfo, sizeof streamInfo))
		return FALSE;

	pAVIStream->FormatSize(0, &format_len);

	if(!allocFormat(format_len)) return FALSE;

	if(pAVIStream->ReadFormat(0, getFormat(), &format_len))
		return FALSE;

	lSampleFirst = pAVIStream->Start();
	lSampleLast = pAVIStream->End();

	return TRUE;
}

__int64 OGMAudioSourceOGM::FramePos(long lStart) {
	return ((OGMReadStream *)pAVIStream)->FramePos(lStart);
}

int OGMAudioSourceOGM::_read(LONG lStart, LONG lCount, LPVOID lpBuffer, LONG cbBuffer, LONG *lpBytesRead, LONG *lpSamplesRead) {
	int err;
	long lBytes, lSamples;

	// There are some video clips roaming around with truncated audio streams
	// (audio streams that state their length as being longer than they
	// really are).  We use a kludge here to get around the problem.

	err = pAVIStream->Read(lStart, lCount, lpBuffer, cbBuffer, lpBytesRead, lpSamplesRead);

	if (err != AVIERR_FILEREAD)
		return err;

	// Suspect a truncated stream.
	//
	// AVISTREAMREAD_CONVENIENT will tell us if we're actually encountering a
	// true read error or not.  At least for the AVI handler, it returns
	// AVIERR_ERROR if we've broached the end.  

	*lpBytesRead = *lpSamplesRead = 0;

	while(lCount > 0) {
		err = pAVIStream->Read(lStart, AVISTREAMREAD_CONVENIENT, NULL, 0, &lBytes, &lSamples);

		if (err)
			return 0;

		if (!lSamples) return AVIERR_OK;

		if (lSamples > lCount) lSamples = lCount;

		err = pAVIStream->Read(lStart, lSamples, lpBuffer, cbBuffer, &lBytes, &lSamples);

		if (err)
			return err;

		lpBuffer = (LPVOID)((char *)lpBuffer + lBytes);
		cbBuffer -= lBytes;
		lCount -= lSamples;

		*lpBytesRead += lBytes;
		*lpSamplesRead += lSamples;
	}

	return AVIERR_OK;
}

/////////////////////////////////////////////////////////////////////

OGMTextSourceOGM::OGMTextSourceOGM(char *fn, OGMReadHandler *handler, int num_stream, bool is_inputfile)
: OGMAudioSourceOGM(fn, handler, num_stream, is_inputfile)
{
	isText = TRUE;
	if(desc)
		delete desc;
	desc = NULL;
	int length = 0;
	if(fn)
		length += strlen(fn);
	desc = (char *)malloc(length+50);
	if(!desc)
		throw MyMemoryError();
	if(is_inputfile)
		sprintf(desc, "Input file (%s), text stream %d", fn, num_stream+1);
	else
		sprintf(desc, "%s, text stream %d", fn, num_stream+1);
}

BOOL OGMTextSourceOGM::init() {
	if(!pAVIFile)
		return FALSE;

	pAVIStream = pAVIFile->GetStream(streamtypeTEXT, num_stream);
	if(!pAVIStream)
		return FALSE;

	LONG format_len;

	if(pAVIStream->Info(&streamInfo, sizeof streamInfo))
		return FALSE;

	pAVIStream->FormatSize(0, &format_len);

	if(!allocFormat(format_len)) return FALSE;

	if(pAVIStream->ReadFormat(0, getFormat(), &format_len))
		return FALSE;

	lSampleFirst = pAVIStream->Start();
	lSampleLast = pAVIStream->End();

	return TRUE;
}

bool OGMTextSourceOGM::isEOS() {
	return ((OGMTextReadStream *)pAVIStream)->isEOS();
}

/////////////////////////////////////////////////////////////////////

OGMAudioSourceMP3::OGMAudioSourceMP3(char *fn, LONG inputBufferSize)
: OGMAudioSource(fn, inputBufferSize) {
}

BOOL OGMAudioSourceMP3::init(void) {
	input = new OGMInputFile(file, buffer_size);
	if(!input)
		throw MyMemoryError();

	if(!allocFormat(sizeof(WAVEFORMATEX)))
		return FALSE;

	if(!input->SynchOn(MP3_SYNCWORD, MP3_SYNCWORD, 2*MP3_MAXFRAMESIZE))
		return FALSE;

	unsigned char buffer[MP3_MAXFRAMESIZE];

	s64 total_bytes = 0;
	s64 total_samples = 0;
	s32 max_frm_size = 0;

	ProgressDialog pd(NULL, "MP3 File Import Filter", "Parsing File", (long)(input->get_size()/1024), true);
	pd.setValueFormat("%ldK of %ldK");

	while(!input->_EOF()) {
		if(input->Read(buffer, MP3_SYNCBLOCK_LEN)!=MP3_SYNCBLOCK_LEN)
			break;

		unsigned char *p = buffer;
		u16 syncword = 256*(*p) + *(p+1);

		// Verify the syncword is correct (so this is likely to be a real MP3 frame)
		if((syncword&MP3_SYNCWORD) != MP3_SYNCWORD)
			break;

		p++;
		MpegVersion mpeg_version = MPEG_RESERVED;
		LayerVersion layer_version = LAYER_RESERVED;

		// Get the MPEG version
		u8 n_ver = ((*p)&0x18)>>3;
		switch(n_ver) {
			case 0:
				mpeg_version = MPEG_V25;
				break;

			case 2:
				mpeg_version = MPEG_V2;
				break;

			case 3:
				mpeg_version = MPEG_V1;
				break;

			case 1:
			default:
				mpeg_version = MPEG_RESERVED;
				break;
		}
		// Verify the value is not RESERVED
		if(mpeg_version == MPEG_RESERVED)
			break;

		// Get the Layer Version
		u8 n_layer = ((*p)&0x06)>>1;
		switch(n_layer) {
			case 1:
				layer_version = LAYER_3;
				break;

			case 2:
				layer_version = LAYER_2;
				break;

			case 3:
				layer_version = LAYER_1;
				break;

			case 0:
			default:
				layer_version = LAYER_RESERVED;
				break;
		}
		// Verify the value is not RESERVED
		if(layer_version == MPEG_RESERVED)
			break;

		// Is there a CRC ? Could be usefull if one want to check data integrity :)
		//int is_crc = 1 - ((*p)&0x01);

		p++;

		// Get the bitrate
		s32 bitrate = -1;
		if(mpeg_version == MPEG_V1) {
			bitrate = mp3_bitrate_V1[layer_version][((*p)&0xF0)>>4];
		} else {
			bitrate = mp3_bitrate_V2[layer_version][((*p)&0xF0)>>4];
		}
		s32 sample_rate = mp3_sample_rate[((*p)&0x0C)>>2][mpeg_version];

		// Is there a Padding at the end ?
		int is_padded = (((*p)&0x02) ? 1 : 0);

		p++;
		// Get the number of channels
		s16 nChannels = mp3_channels[((*p)&0xC0)>>6];
		// Then the size of the Frame
		s32 frmsize = 0;
		if(layer_version == LAYER_1)
			frmsize = ((12 * 1000*bitrate * nChannels) / (2 * sample_rate) + is_padded) * 4;
		else
			frmsize = (144 * 1000*bitrate * nChannels) / (2 * sample_rate) + is_padded;

		p++;

		// Verify the sample rate value is not RESERVED (in this case we cannot do anything)
		if(sample_rate == -1)
			break;

		long nread = input->Read(buffer, frmsize-MP3_SYNCBLOCK_LEN);
		if(nread == -1)
			break;

		u16 n_samples = (layer_version==LAYER_1 ? MP3_L1_SAMPLESPERFRAME : MP3_L23_SAMPLESPERFRAME);

		total_bytes += nread+MP3_SYNCBLOCK_LEN;
		total_samples += n_samples;

		if(frmsize > max_frm_size)
			max_frm_size = frmsize;

		if(!firstFrame) {
			getWaveFormat()->nChannels = nChannels;
			getWaveFormat()->wFormatTag = (layer_version==LAYER_3 ? 0x0055 : 0x0050);
			getWaveFormat()->nSamplesPerSec = sample_rate;
		}

		add_frame(nread+MP3_SYNCBLOCK_LEN, n_samples);

		if(nread != frmsize-MP3_SYNCBLOCK_LEN)
			break;

		pd.advance((long)(total_bytes/1024));
		pd.check();
	}

	firstFrame = frames.AtHead();
	lastFrame = frames.AtTail();

	if(firstFrame) {
		getWaveFormat()->cbSize = 0;
		getWaveFormat()->nAvgBytesPerSec = (total_bytes * getWaveFormat()->nSamplesPerSec) / total_samples;
		getWaveFormat()->nBlockAlign = ceil((double)total_bytes / (double)total_samples);
		getWaveFormat()->wBitsPerSample = (8 * total_bytes) / (getWaveFormat()->nChannels * total_samples);

		lSampleFirst	= 0;
		lSampleLast		= lastFrame->pos + lastFrame->samples;

		streamInfo.fccType					= streamtypeAUDIO;
		streamInfo.fccHandler				= 0;
		streamInfo.dwFlags					= 0;
		streamInfo.wPriority				= 0;
		streamInfo.wLanguage				= 0;
		streamInfo.dwInitialFrames			= 0;
		streamInfo.dwScale					= 1;
		streamInfo.dwRate					= getWaveFormat()->nSamplesPerSec;
		streamInfo.dwStart					= 0;
		streamInfo.dwLength					= lSampleLast;
		streamInfo.dwSuggestedBufferSize	= max_frm_size;
		streamInfo.dwQuality				= 0xffffffff;
		streamInfo.dwSampleSize				= getWaveFormat()->nBlockAlign;

		return TRUE;
	} else
		return FALSE;
}

/////////////////////////////////////////////////////////////////////

OGMAudioSourceAC3::OGMAudioSourceAC3(char *fn, LONG inputBufferSize)
: OGMAudioSource(fn, inputBufferSize) {
}

BOOL OGMAudioSourceAC3::init(void) {
	input = new OGMInputFile(file, buffer_size);
	if(!input)
		throw MyMemoryError();

	if(!allocFormat(sizeof(WAVEFORMATEX)))
		return FALSE;


	if(!input->SynchOn(AC3_SYNCWORD, AC3_SYNCWORD_BITS, 2*AC3_MAXFRAMESIZE))
		return FALSE;

	s64 total_bytes = 0;
	s64 total_samples = 0;
	s32 max_frm_size = 0;

	unsigned char buffer[AC3_MAXFRAMESIZE];
	unsigned char *p = buffer;

	ProgressDialog pd(NULL, "AC3 File Import Filter", "Parsing File", (long)(input->get_size()/1024), true);
	pd.setValueFormat("%ldK of %ldK");

	while(!input->_EOF()) {
		u16 syncword;
		//u16 crc;
		u8  fscod;
		u8  frmsizecod;
		s32 sample_rate;
		s32 bitrate;
		s32 frmsize;
		u8  version;
		u8  acmod;
		// Read the synchronisation block of the frame
		if(input->Read(buffer, AC3_SYNCBLOCK2_LEN)!=AC3_SYNCBLOCK2_LEN)
			break;

		p = buffer;

		syncword = 256*(*p) + *(p+1);
		// Verify the syncword is correct (so this is likely to be a real AC3 frame)
		if(syncword != AC3_SYNCWORD)
			break;

		p += 4;
		// Get fscod (determining the sample rate)
		fscod = ((*p)&0xC0)>>6;
		sample_rate = ac3_sample_rate[fscod];
		// Get frmsizecod (determining the bitrate)
		frmsizecod = (*p)&0x3F;
		bitrate = ac3_bitrate[frmsizecod];
		p++;
		version = ((*p)&0xF8)>>3;
		p++;
		acmod = ((*p)&0xE0)>>5;
		s16 nChannels = ac3_channels[acmod];
		// Verify the sample rate value is not RESERVED (in this case we cannot do anything)
		if(sample_rate == -1)
			break;

		// Verify struture version of this frame, should be less or equal to 8 at the time being
		if(version > 8)
			break;
		// Everything seems OK, we can get the size (in bytes) of the frame
		frmsize = ac3_framesize[frmsizecod][fscod] * 2;

		long nread = input->Read(buffer, frmsize-AC3_SYNCBLOCK2_LEN);
		if(nread == -1)
			break;

		if(!firstFrame) {
			getWaveFormat()->nChannels = nChannels;
			getWaveFormat()->wFormatTag = 0x2000;
			getWaveFormat()->nSamplesPerSec = sample_rate;
		}

		if(frmsize > max_frm_size)
			max_frm_size = frmsize;

		total_bytes += nread+AC3_SYNCBLOCK2_LEN;
		total_samples += AC3_SAMPLESPERFRAME;

		add_frame(nread+AC3_SYNCBLOCK2_LEN , AC3_SAMPLESPERFRAME);

		if(nread != frmsize-AC3_SYNCBLOCK2_LEN)
			break;

		pd.advance((long)(total_bytes/1024));
		pd.check();
	}

	if(firstFrame) {
		getWaveFormat()->cbSize = 0;
		getWaveFormat()->nAvgBytesPerSec = (total_bytes * getWaveFormat()->nSamplesPerSec) / total_samples;
		getWaveFormat()->nBlockAlign = ceil((double)total_bytes / (double)total_samples);
		getWaveFormat()->wBitsPerSample = (8 * total_bytes) / (getWaveFormat()->nChannels * total_samples);

		lSampleFirst	= 0;
		lSampleLast		= lastFrame->pos + lastFrame->samples;

		streamInfo.fccType					= streamtypeAUDIO;
		streamInfo.fccHandler				= 0;
		streamInfo.dwFlags					= 0;
		streamInfo.wPriority				= 0;
		streamInfo.wLanguage				= 0;
		streamInfo.dwInitialFrames			= 0;
		streamInfo.dwScale					= 1;
		streamInfo.dwRate					= getWaveFormat()->nSamplesPerSec;
		streamInfo.dwStart					= 0;
		streamInfo.dwLength					= lSampleLast;
		streamInfo.dwSuggestedBufferSize	= max_frm_size;
		streamInfo.dwQuality				= 0xffffffff;
		streamInfo.dwSampleSize				= getWaveFormat()->nBlockAlign;

		return TRUE;
	} else
		return FALSE;
}