Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / Win32

Play Audio Files with DirectSound and Display its Spectrum in Real Time - Part 3

Rate me:
Please Sign up or sign in to vote.
4.93/5 (21 votes)
24 Dec 2008CPOL3 min read 274.5K   17.7K   86  
An article to show how to play audio file with DirectSound and display its spectrum in real time accurately
#include "IO.h"

/************************************************************************/
/* CInput                                                               */
/************************************************************************/
CInput::CInput(void)
{
	memset(&m_Position, 0, sizeof(LARGE_INTEGER));
	memset(&m_Size, 0, sizeof(LARGE_INTEGER));

	m_Position.QuadPart = 0;
	m_Size.QuadPart = 0;
	m_SampleSize = 0;
	m_Busy = FALSE;
	m_BufferStart = 0;
	m_BufferEnd = 0;
	m_EndOfStream = FALSE;
	m_DataCS = new CCriticalSection;
}

CInput::~CInput(void)
{
	if(m_DataCS != NULL)
		delete m_DataCS;
}

DWORD CInput::GetTotalTime()
{
	return 0;
}

LARGE_INTEGER CInput::GetTotalSamples()
{
	LARGE_INTEGER ret;
	memset(&ret, 0, sizeof(LARGE_INTEGER));

	ret.QuadPart = 0;
	return ret;
}

LARGE_INTEGER CInput::GetSize()
{
	return m_Size;
}

LARGE_INTEGER CInput::GetPosition()
{
	return m_Position;
}

void CInput::Init()
{
	m_DataCS->Enter();
	try
	{
		m_EndOfStream = FALSE;
		InitInternal();
	}
	catch(...)
	{
		m_EndOfStream = TRUE;
	}

	m_DataCS->Leave();
}

void CInput::GetData(void** buffer, DWORD* size)
{
	m_DataCS->Enter();
	try
	{
		GetDataInternal(buffer, size);
	}
	catch (...) {}

	m_DataCS->Leave();
}

DWORD CInput::CopyData(void* buffer, DWORD size)
{
	void* p = NULL;
	GetData(&p, &size);

	if(p != NULL)
		memcpy(buffer, p, size);

	return size;
}

DWORD CInput::FillBuffer(void* buffer, DWORD size, BOOL *eof)
{
	BYTE* pBuffer = (BYTE*)buffer;
	DWORD r = size, ret = 0;

	while( (size - ret) > 0 && r != 0)
	{
		r = CopyData(pBuffer + ret, size - ret);
		ret += r;
	}

	*eof = (r == 0);
	return ret;
}

void CInput::Flush()
{
	m_DataCS->Enter();
	try
	{
		FlushInternal();
	}
	catch(...) {}

	m_DataCS->Leave();
}

void CInput::Reset()
{
	try
	{
		Flush();
	}
	catch(...) {}

	m_Busy = FALSE;
}

void CInput::Lock()
{
	m_DataCS->Enter();
}

void CInput::Unlock()
{
	m_DataCS->Leave();
}

void CInput::Pause()
{
}

void CInput::Resume()
{
}

/************************************************************************/
/* CStreamedInput                                                       */
/************************************************************************/
CStreamedInput::CStreamedInput(void) : CInput()
{
	m_pStream = NULL;
	m_StreamAssigned = FALSE;
	m_Seekable = TRUE;

	memset(&m_StartSample, 0, sizeof(LARGE_INTEGER));
	m_StartSample.LowPart = 0;

	memset(&m_EndSample, 0, sizeof(LARGE_INTEGER));
	m_EndSample.LowPart = -1;

	memset(&m_TotalSamples, 0, sizeof(LARGE_INTEGER));
	m_TotalSamples.LowPart = 0;
}

CStreamedInput::~CStreamedInput(void)
{
}

void CStreamedInput::SetStream(CStream* pStream)
{
	m_pStream = pStream;
	if(m_pStream != NULL) m_StreamAssigned = TRUE;
	else m_StreamAssigned = FALSE;
}

void CStreamedInput::GetData(void** buf, DWORD* Bytes)
{
	DWORD tmpBytes = *Bytes;

	m_DataCS->Enter();
	try
	{
		if(m_EndOfStream)
		{
			*buf = NULL;
			*Bytes = 0;
		}
		else
		{
			GetDataInternal(buf, Bytes);
			if(Bytes == 0)
				m_EndOfStream = TRUE;
			else
			{
				m_Position.LowPart += *Bytes;
				if(m_Size.LowPart > 0 && m_Position.LowPart >= m_Size.LowPart)
				{
					m_EndOfStream = TRUE;
					if(m_Position.LowPart > m_Size.LowPart)
					{
						*Bytes -= (DWORD)(m_Position.LowPart - m_Size.LowPart);
						m_Position.LowPart = m_Size.LowPart;
					}
				}
			}

			if(m_EndOfStream && m_Loop)
			{
				m_EndOfStream = FALSE;
				if(m_Seekable)
					SeekInternal(&m_StartSample);
				else
				{
					Flush();
					Init();
				}

				m_Position.LowPart = 0;
				*Bytes = tmpBytes;
				GetDataInternal(buf, Bytes);
				if(*Bytes == 0)
					m_EndOfStream = TRUE;
				else
				{
					m_Position.LowPart += *Bytes;
					if(m_Size.LowPart > 0 && m_Position.LowPart >= m_Size.LowPart)
						m_EndOfStream = TRUE;
				}
			}
		}

	}
	catch(...) {}

	m_DataCS->Leave();
}

BOOL CStreamedInput::Seek(LARGE_INTEGER* SampleNum)
{
	BOOL ret = FALSE;
	if(!m_Seekable)
		return FALSE;

	if(m_TotalSamples.LowPart != 0 && SampleNum->LowPart > m_TotalSamples.LowPart)
		return FALSE;

	m_DataCS->Enter();
	if(!m_Busy)
	{
		m_StartSample.LowPart = SampleNum->LowPart;
		m_Position.LowPart = SampleNum->LowPart * m_SampleSize;
		m_EndSample.LowPart = -1;
		return TRUE;
	}
	else
	{
		try
		{
			SampleNum->LowPart += m_StartSample.LowPart;
			ret = SeekInternal(SampleNum);
			m_Position.LowPart = (SampleNum->LowPart - m_StartSample.LowPart) * m_SampleSize;
		}
		catch(...) {}
	}

	m_DataCS->Leave();
	return ret;
}

/************************************************************************/
/* CFileIn                                                              */
/************************************************************************/
CFileIn::CFileIn(void) : CStreamedInput()
{
	m_OpenCS = new CCriticalSection;
	StringCchCopy(m_FileName, 1, _TEXT(""));
	m_FileNameLen = 0;
	m_Opened = 0;
	m_Valid = FALSE;
	m_BitsPerSample = 0;
	m_SampleRate = 0;
	m_Channels = 0;
	m_Time = 0;
}

CFileIn::~CFileIn(void)
{
	delete m_OpenCS;
}

void CFileIn::SetFileName(TCHAR *pszFileName)
{
	CloseFile();
	StringCchLength(pszFileName, STRSAFE_MAX_CCH, &m_FileNameLen);
	StringCchCopy(m_FileName, m_FileNameLen+1, pszFileName);
}

size_t CFileIn::GetFileNameLen()
{
	return m_FileNameLen;
}

WORD CFileIn::GetBitsPerSample()
{
	OpenFile();
	return m_BitsPerSample;
}

DWORD CFileIn::GetSampleRate()
{
	OpenFile();
	return m_SampleRate;
}

WORD CFileIn::GetChannels()
{
	OpenFile();
	return m_Channels;
}

INT CFileIn::GetOpened()
{
	return m_Opened;
}

BOOL CFileIn::GetValid()
{
	if(!m_StreamAssigned && m_FileNameLen == 0)
	{
		; // do nothing
	}
	else
	{
		try
		{
			OpenFile();
		}
		catch(...) { m_Valid = FALSE; }

		if(!m_Valid)
		{
			if(m_Opened == 0)
			{
				if(!m_StreamAssigned && m_pStream != NULL)
					delete m_pStream;
			}
			else
			{
				try
				{
					CloseFile();
				}
				catch(...) {}
			}
		}
	}

	return m_Valid;
}

LARGE_INTEGER CFileIn::GetTotalSamples()
{
	OpenFile();
	if(m_Size.QuadPart > 0)
		m_TotalSamples.LowPart = m_Size.LowPart / (m_Channels * m_BitsPerSample / 8);
	else m_TotalSamples.LowPart = -1;

	return m_TotalSamples;
}

BOOL CFileIn::SetStartTime(DWORD Minutes, DWORD Seconds)
{
	if(!m_Seekable)
		return FALSE;

	OpenFile();
	DWORD Sample = (Minutes * 60 + Seconds) * m_SampleRate;
	if(Sample > m_TotalSamples.LowPart) return FALSE;

	m_StartSample.LowPart = Sample;
	return TRUE;
}

BOOL CFileIn::SetEndTime(DWORD Minutes, DWORD Seconds)
{
	if(!m_Seekable)
		return FALSE;

	OpenFile();
	DWORD Sample = (Minutes * 60 + Seconds) * m_SampleRate;
	if(Sample > m_TotalSamples.LowPart) return FALSE;

	m_StartSample.LowPart = Sample;
	return TRUE;
}

void CFileIn::Reset()
{
	CStreamedInput::Reset();
	m_Opened = 0;
}

void CFileIn::Jump(INT offset)
{
	DOUBLE Curpos = 0;
	LARGE_INTEGER Cursample;
	memset(&Cursample, 0, sizeof(LARGE_INTEGER));

	if(!m_Seekable || m_Size.QuadPart == 0)
		return;

	Curpos = m_Position.LowPart / m_Size.LowPart + offset / 100;
	if (Curpos < 0) Curpos = 0;
	if (Curpos > 1) Curpos = 1;

	Cursample.LowPart = (DWORD)floor(Curpos * m_TotalSamples.LowPart);
	Seek(&Cursample);
}

void CFileIn::SetStream(CStream *pStream)
{
	CloseFile();
	CStreamedInput::SetStream(pStream);
}

void CFileIn::FlushInternal()
{
	CloseFile();
	m_StartSample.LowPart = 0;
	m_EndSample.LowPart = -1;
	m_Busy = FALSE;
}

DWORD CFileIn::GetTotalTime()
{
	OpenFile();
	if(m_SampleRate == 0 || m_Channels == 0 || m_BitsPerSample == 0)
		return 0;

	DOUBLE x = m_Size.LowPart / (m_SampleRate * m_Channels * (m_BitsPerSample >> 3));
	return (DWORD)floor(x);
}

void CFileIn::InitInternal()
{
	if(m_Busy)
	{
		MessageBox(GetActiveWindow(), _TEXT("The component is Busy"), _TEXT("Error"), MB_OK);
		return;
	}

	if(!m_StreamAssigned && m_FileNameLen == 0)
	{
		MessageBox(GetActiveWindow(), _TEXT("The file name is not assigned"), _TEXT("Error"), MB_OK);
		return;
	}

	m_Busy = TRUE;
	m_Position.LowPart = 0;
	OpenFile();

	m_SampleSize = m_Channels * m_BitsPerSample >> 3;
	m_TotalSamples.LowPart = m_Size.LowPart / m_SampleSize;
	m_Time = m_TotalSamples.LowPart / m_SampleRate;

	if(m_StartSample.LowPart > 0)
	{
		LARGE_INTEGER seek;
		memset(&seek, 0, sizeof(LARGE_INTEGER));

		Seek(&seek);
		m_Position.LowPart = 0;
	}

	if(m_StartSample.LowPart > 0 && m_EndSample.LowPart != -1)
	{
		if(m_EndSample.LowPart > m_TotalSamples.LowPart)
			m_EndSample.LowPart = -1;

		if(m_EndSample.LowPart == -1)
			m_TotalSamples.LowPart -= m_StartSample.LowPart + 1;
		else
			m_TotalSamples.LowPart = m_EndSample.LowPart - m_StartSample.LowPart + 1;

		m_Size.LowPart = m_TotalSamples.LowPart * m_SampleSize;
	}

	m_BufferStart = 1;
	m_BufferEnd = 0;
}

/************************************************************************/
/* CWMIn                                                                */
/************************************************************************/
CWMIn::CWMIn(void) : CFileIn()
{
	m_Reader = (WMA_SYNC_READER*)malloc(sizeof(WMA_SYNC_READER));
	memset(m_Reader, 0, sizeof(WMA_SYNC_READER));

	m_Duration = 0;
	m_HighPrecision = FALSE;
	m_OutputChannels = cnMonoOrStereo;
	m_Format = 0;
}

CWMIn::~CWMIn(void)
{
	CloseFile();

	if(m_Reader != NULL)
		free(m_Reader);

	m_Reader = NULL;
}

SHORT CWMIn::CNToShortInt()
{
	SHORT ret = 0;
	switch(m_OutputChannels)
	{
	case cnMaxAvailable:
		ret = -1;
		break;
	case cnMonoOrStereo:
		ret = 0;
		break;
	case cn5dot1:
		ret = 6;
		break;
	case cn7dot1:
		ret = 8;
		break;
	default:
		ret = 0;
	}

	return ret;
}

DWORD CWMIn::GetBitrate()
{
	OpenFile();
	return WMA_Reader_GetBitrate(m_Reader);
}

DWORD CWMIn::GetFormatsCount()
{
	if(m_Busy)
		return 0;

	if(m_FileNameLen > 0 && m_pStream != NULL)
	{
		OpenFile();
		return WMA_Reader_GetFormatCount(m_Reader, m_HighPrecision);
	}

	return 0;
}

void CWMIn::GetFormatSpec(INT index, WMAFormatSpec* pFormatSpec)
{
	if(pFormatSpec != NULL)
	{
		pFormatSpec->BitsPerSample = 0;
		pFormatSpec->Channels = 0;
		pFormatSpec->SampleRate = 0;
		if(m_Busy)
			return;

		if(wcslen(m_FileName) > 0 && m_pStream != NULL)
		{
			OpenFile();
			if(index >= 0)
				WMA_Reader_GetFormat(m_Reader, m_HighPrecision, index, pFormatSpec);
		}
	}
}

void CWMIn::SetFormat(INT iFormat)
{
	if(m_Busy)
		return;

	OpenFile();

	if(iFormat == wmfDefault)
	{
		WMA_Reader_SetFormat(m_Reader, TRUE, 0);
		m_Format = 0;
	}

	if(iFormat >= 0)
	{
		WMA_Reader_SetFormat(m_Reader, TRUE, (DWORD)iFormat);
		m_Format = iFormat;
	}

	m_Duration = WMA_Reader_GetDuration(m_Reader);
	m_Channels = (WORD)m_Reader->dwChannels;
	m_BitsPerSample = (WORD)m_Reader->dwBitsPerSample;
	m_SampleRate = m_Reader->dwSampleRate;
	m_TotalSamples.LowPart = m_Duration / 100 * m_SampleRate;
	m_Size.LowPart = m_TotalSamples.LowPart * (m_BitsPerSample >> 3);
}

INT CWMIn::GetFormat()
{
	return m_Format;
}

BOOL CWMIn::GetHasAudio()
{
	OpenFile();
	return m_Reader->bHasAudio;
}

BOOL CWMIn::GetProtected()
{
	OpenFile();
	return m_Reader->bProtected;
}

BOOL CWMIn::GetIsVBR()
{
	OpenFile();
	return WMA_Reader_GetIsVBR(m_Reader);
}

void CWMIn::SetHighPrecision(BOOL HighPrecision)
{
	if(m_Busy)
		return;

	if(m_OutputChannels == cnMonoOrStereo)
	{
		m_HighPrecision = HighPrecision;
		if(m_Opened > 0)
		{
			CloseFile();
			OpenFile();
		}
	}
}

void CWMIn::SetOutputChannels(ChannelsNumber OutputChannels)
{
	if(m_Busy)
		return;

	if(m_OutputChannels == cnMonoOrStereo)
		m_HighPrecision = TRUE;

	m_OutputChannels = OutputChannels;
	if(m_Opened > 0)
	{
		CloseFile();
		OpenFile();
	}
}

void CWMIn::CloseFile()
{
	m_OpenCS->Enter();

	try
	{
		if(m_Opened > 0)
		{
			WMA_Reader_Free(m_Reader);
			m_Opened = 0;
			if(!m_StreamAssigned)
				delete m_pStream;
			else
				m_pStream->Seek(0, FILE_BEGIN);
		}
	}
	catch(...) {}

	m_OpenCS->Leave();
}

void CWMIn::GetDataInternal(void** buffer, DWORD* bytes)
{
	if(m_Size.LowPart > 0 && (m_Size.LowPart - m_Position.LowPart) < *bytes)
		*bytes = m_Size.LowPart - m_Position.LowPart;

	WMA_Reader_GetData(m_Reader, buffer, bytes);
}

void CWMIn::OpenFile()
{
	WORD channels = 0, bitspersamples = 0;
	DWORD samplerate = 0;

	m_OpenCS->Enter();

	try
	{
		if(m_Opened == 0)
		{
			if(!m_StreamAssigned && m_FileNameLen == 0)
			{
				MessageBox(GetActiveWindow(), _TEXT("File name is not assigned"), _TEXT("Error"), MB_OK);
				return;
			}

			if(!m_StreamAssigned)
				m_pStream = new CFileStream(m_FileName, FILE_OPEN, 0);

			WMA_Reader_Init(m_Reader, m_pStream, m_HighPrecision, CNToShortInt());
			m_Valid = m_Reader->bHasAudio;

			if(m_Reader->pWMSyncReader == NULL)
				return;

			if(m_Reader->bProtected)
			{
				MessageBox(GetActiveWindow(), _TEXT("File is protected"), _TEXT("Error"), MB_OK);
				return;
			}

			m_Duration = WMA_Reader_GetDuration(m_Reader);
			WMA_Reader_GetAudioPropties(m_Reader, &channels, &bitspersamples, &samplerate);

			m_Channels = channels;
			m_BitsPerSample = bitspersamples;
			m_SampleRate = samplerate;

			m_TotalSamples.QuadPart = (m_Duration / 100) * samplerate;
			m_Size.QuadPart = m_TotalSamples.QuadPart * channels * (bitspersamples >> 3);
			m_Seekable = TRUE;
			m_Opened++;
		}
	}
	catch(...)
	{
		MessageBox(GetActiveWindow(), _TEXT("[OpenFile] Error occurred."), _TEXT("Error"), MB_OK);
		m_OpenCS->Leave();
		return;
	}

	m_OpenCS->Leave();
}

BOOL CWMIn::SeekInternal(LARGE_INTEGER *SampleNum)
{
	DWORD Offset;
	DWORD ret = FALSE;
	if(m_Busy)
	{
		Offset = SampleNum->QuadPart / m_TotalSamples.QuadPart * m_Duration;
		WMA_Reader_Seek(m_Reader, Offset);
		ret = TRUE;
	}

	return ret;
}

/************************************************************************/
/* CWaveIn                                                              */
/************************************************************************/

const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00,
0xaa, 0x00, 0x38, 0x9b, 0x71} };
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00,
0xaa, 0x00, 0x38, 0x9b, 0x71} };

// DVI IMA ADPCM stuff
INT StepTab[89] = {7,     8,     9,    10,    11,    12,    13,    14,
16,    17,    19,    21,    23,    25,    28,    31,
34,    37,    41,    45,    50,    55,    60,    66,
73,    80,    88,    97,   107,   118,   130,   143,
157,   173,   190,   209,   230,   253,   279,   307,
337,   371,   408,   449,   494,   544,   598,   658,
724,   796,   876,   963,  1060,  1166,  1282,  1411,
1552,  1707,  1878,  2066,  2272,  2499,  2749,  3024,
3327,  3660,  4026,  4428,  4871,  5358,  5894,  6484,
7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
32767 };

INT IndexTab[16] = {-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8 };

// MS ADPCM Stuff
SHORT adaptive[16]= {230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230 };

CWaveIn::CWaveIn(void) : CFileIn()
{
	memset(m_Buf, 0, BUF_SIZE);
	m_WavType = wtUnsupported;
	memset(&m_DVI_ADPCM_INFO, 0, sizeof(DVI_ADPCM_INFO));
	memset(&m_DVI_ADPCM_STATE, 0, sizeof(DVI_ADPCM_STATE_STEREO));
	memset(&m_MS_ADPCM_INFO, 0, sizeof(MS_ADPCM_INFO));
	memset(&m_MS_ADPCM_STATE, 0, sizeof(MSADPCMBlockHeaderStereo));
	m_HeaderSize = 0;
	m_MS = NULL;
	m_OldStream = NULL;
	m_OldStreamAssigned = FALSE;
	m_ShortIEEEFloat = FALSE;
}

CWaveIn::~CWaveIn(void)
{
}

void CWaveIn::DecodeDVIADPCMMono(BYTE* pInData, SHORT* pOutData, DWORD* len)
{
	int i(0), j(0), SP(0), Diff(0), PSample(0), Index(0);
	BYTE Code(0);

	pOutData[0] = m_DVI_ADPCM_STATE.valprev_l;
	PSample = m_DVI_ADPCM_STATE.valprev_l;
	Index = m_DVI_ADPCM_STATE.index_l;

	for(i = 0; i < (*len << 1); ++i)
	{
		j = i >> 1;
		Code = pInData[j];

		if((i & 1) == 0)
			Code = Code & 15;
		else
			Code = Code >> 4;

		Diff = (StepTab[Index] >> 3 );

		if((Code & 4) != 0)
			Diff += StepTab[Index];

		if((Code & 2) != 0)
			Diff += StepTab[Index] >> 1;

		if((Code & 1) != 0)
			Diff += StepTab[Index] >> 2;

		if((Code & 8) != 0)
			Diff = -Diff;

		PSample += Diff;
		if(PSample > 32767) PSample = 32767;
		if(PSample < -32767) PSample = -32767;

		SP++;
		pOutData[SP] = PSample;
		Index += IndexTab[Code];
		if(Index > 88) Index = 88;
		if(Index < 0) Index = 0;
	}

	*len = SP + 1;
}

void CWaveIn::DecodeDVIADPCMStereo(BYTE* pInData, SHORT* pOutData, DWORD* len)
{
	int i(0), j(0), SP(0), Diff(0), PSample(0), Index(0);
	BYTE Code(0);

	pOutData[0] = m_DVI_ADPCM_STATE.valprev_l;
	PSample = m_DVI_ADPCM_STATE.valprev_l;
	Index = m_DVI_ADPCM_STATE.index_l;

	for(i = 0; i<*len; ++i)
	{
		j = i >> 1;
		Code = pInData[(j / 4) * 8 + (j % 4)];

		if((i & 1) == 0)
			Code = Code & 15;
		else
			Code = Code >> 4;

		Diff = (StepTab[Index] >> 3 );
		if((Code & 4) != 0)
			Diff += StepTab[Index];

		if((Code & 2) != 0)
			Diff += (StepTab[Index] >> 1);

		if((Code & 1) != 0)
			Diff += (StepTab[Index] >> 2);

		if((Code & 8) != 0)
			Diff = -Diff;

		PSample += Diff;
		if(PSample > 32767) PSample = 32767;
		if(PSample < -32767) PSample = -32767;

		SP +=2;
		pOutData[SP] = PSample;
		Index += IndexTab[Code];
		if(Index > 88) Index = 88;
		if(Index < 0) Index = 0;
	}

	i = 1;
	pOutData[i] = m_DVI_ADPCM_STATE.valprev_r;
	SP = 1;
	PSample = m_DVI_ADPCM_STATE.valprev_r;
	Index = m_DVI_ADPCM_STATE.index_r;

	for(i = 0; i<*len; ++i)
	{
		j = i >> 1;
		Code = pInData[(j / 4) * 8 + (j % 4)];

		if((i & 1) == 0)
			Code = Code & 15;
		else
			Code = Code >> 4;

		Diff = (StepTab[Index] >> 3 );
		if((Code & 4) != 0)
			Diff += StepTab[Index];

		if((Code & 2) != 0)
			Diff += (StepTab[Index] >> 1);

		if((Code & 1) != 0)
			Diff += (StepTab[Index] >> 2);

		if((Code & 8) != 0)
			Diff = -Diff;

		PSample += Diff;
		if(PSample > 32767) PSample = 32767;
		if(PSample < -32767) PSample = -32767;

		SP +=2;
		pOutData[SP] = PSample;
		Index += IndexTab[Code];
		if(Index > 88) Index = 88;
		if(Index < 0) Index = 0;
	}

	*len = (SP / 2) + 1;
}

void CWaveIn::DecodeMSADPCMMono(BYTE* pInData, SHORT* pOutData, DWORD* len)
{
	INT pos(0), i(0), PredSamp(0), ErrorDelta(0);
	pOutData[pos] = m_MS_ADPCM_STATE.Samp2[0];
	pos++;
	pOutData[pos] = m_MS_ADPCM_STATE.Samp1[0];
	pos++;

	for(i = 0; i< (*len  >> 1); ++i)
	{
		PredSamp = (m_MS_ADPCM_STATE.Samp1[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef1 +
			m_MS_ADPCM_STATE.Samp2[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef2) / 256;

		ErrorDelta = pInData[i] >> 4;
		if((ErrorDelta & 8) != 0)
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*(ErrorDelta - 16);
		else
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*ErrorDelta;

		if(PredSamp > 32767) PredSamp = 32767;
		if(PredSamp < -32768) PredSamp = -32768;
		pOutData[pos] = PredSamp;
		pos++;

		m_MS_ADPCM_STATE.Delta[0] = (m_MS_ADPCM_STATE.Delta[0]*adaptive[ErrorDelta]) / 256;
		if (m_MS_ADPCM_STATE.Delta[0] < 16) m_MS_ADPCM_STATE.Delta[0] = 16;

		m_MS_ADPCM_STATE.Samp2[0] = m_MS_ADPCM_STATE.Samp1[0];
		m_MS_ADPCM_STATE.Samp1[0] = PredSamp;

		PredSamp = (m_MS_ADPCM_STATE.Samp1[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef1 +
			m_MS_ADPCM_STATE.Samp2[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef2) / 256;
		ErrorDelta = pInData[i] & 15;

		if((ErrorDelta & 8) != 0)
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*(ErrorDelta - 16);
		else
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*ErrorDelta;

		if(PredSamp > 32767) PredSamp = 32767;
		if(PredSamp < -32768) PredSamp = -32768;
		pOutData[pos] = PredSamp;
		pos++;

		m_MS_ADPCM_STATE.Delta[0] = (m_MS_ADPCM_STATE.Delta[0]*adaptive[ErrorDelta]) / 256;
		if (m_MS_ADPCM_STATE.Delta[0] < 16) m_MS_ADPCM_STATE.Delta[0] = 16;
		m_MS_ADPCM_STATE.Samp2[0] = m_MS_ADPCM_STATE.Samp1[0];
		m_MS_ADPCM_STATE.Samp1[0] = PredSamp;
	}

	*len = pos*2;
}

void CWaveIn::DecodeMSADPCMStereo(BYTE* pInData, SHORT* pOutData, DWORD* len)
{
	INT pos(0), i(0), PredSamp(0), ErrorDelta(0);

	pOutData[pos] = m_MS_ADPCM_STATE.Samp2[0];
	pos++;
	pOutData[pos] = m_MS_ADPCM_STATE.Samp2[1];
	pos++;
	pOutData[pos] = m_MS_ADPCM_STATE.Samp1[0];
	pos++;
	pOutData[pos] = m_MS_ADPCM_STATE.Samp1[1];
	pos++;

	for(i = 0; i< *len; ++i)
	{
		PredSamp = (m_MS_ADPCM_STATE.Samp1[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef1 +
			m_MS_ADPCM_STATE.Samp2[0]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[0]].Coef2) / 256;
		ErrorDelta = pInData[i] >> 4;

		if((ErrorDelta & 8) != 0)
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*(ErrorDelta - 16);
		else
			PredSamp += m_MS_ADPCM_STATE.Delta[0]*(ErrorDelta);

		if(PredSamp > 32767) PredSamp = 32767;
		if(PredSamp < -32768) PredSamp = -32768;
		pOutData[pos] = PredSamp;
		pos++;

		m_MS_ADPCM_STATE.Delta[0] = (m_MS_ADPCM_STATE.Delta[0]*adaptive[ErrorDelta]) / 256;
		if (m_MS_ADPCM_STATE.Delta[0] < 16) m_MS_ADPCM_STATE.Delta[0] = 16;
		m_MS_ADPCM_STATE.Samp2[0] = m_MS_ADPCM_STATE.Samp1[0];
		m_MS_ADPCM_STATE.Samp1[0] = PredSamp;

		PredSamp = (m_MS_ADPCM_STATE.Samp1[1]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[1]].Coef1 +
			m_MS_ADPCM_STATE.Samp2[1]*m_MS_ADPCM_INFO.CoefSets[m_MS_ADPCM_STATE.predictor[1]].Coef2) / 256;
		ErrorDelta = pInData[i] & 15;

		if((ErrorDelta & 8) != 0)
			PredSamp += m_MS_ADPCM_STATE.Delta[1]*(ErrorDelta - 16);
		else
			PredSamp += m_MS_ADPCM_STATE.Delta[1]*(ErrorDelta);

		if(PredSamp > 32767) PredSamp = 32767;
		if(PredSamp < -32768) PredSamp = -32768;
		pOutData[pos] = PredSamp;
		pos++;

		m_MS_ADPCM_STATE.Delta[1] = (m_MS_ADPCM_STATE.Delta[1]*adaptive[ErrorDelta]) / 256;
		if (m_MS_ADPCM_STATE.Delta[1] < 16) m_MS_ADPCM_STATE.Delta[1] = 16;
		m_MS_ADPCM_STATE.Samp2[1] = m_MS_ADPCM_STATE.Samp1[1];
		m_MS_ADPCM_STATE.Samp1[1] = PredSamp;
	}

	*len = pos*2;
}

void CWaveIn::OpenFile()
{
	CWaveConverter* pWaveConverter = NULL;
	DWORD ValidItems = 0;
	UINT Res = 0;

	m_OpenCS->Enter();
	m_Valid = TRUE;
	if(m_Opened == 0)
	{
		m_ShortIEEEFloat = FALSE;
		m_WavType = wtUnsupported;

		if(!m_StreamAssigned)
			m_pStream = new CFileStream(m_FileName, FILE_OPEN);

		ReadRIFFHeader();
		switch(m_WavType)
		{
		case wtUnsupported:
			m_Valid = FALSE;
			break;
		case wtDVIADPCM:
			{
				if(m_BitsPerSample != 4)
					m_Valid = FALSE;

				m_BitsPerSample = 16;
				m_Size.QuadPart = (DWORD)m_DVI_ADPCM_INFO.DataSize * 2 * m_Channels;
			}
			break;
		case wtMSADPCM:
			{
				m_BitsPerSample = 16;
				m_Size.QuadPart = (DWORD)m_MS_ADPCM_INFO.DataSize * 2 * m_Channels;
			}
			break;
		case wtIEEEFloat:
		case wtExtIEEEFloat:
			{
				if(m_BitsPerSample == 32)
					m_ShortIEEEFloat = TRUE;
				else
					m_Size.QuadPart = m_Size.QuadPart / 2;

				m_BitsPerSample = 32;
			}
			break;
		case wtACM:
			{
				pWaveConverter = new CWaveConverter;
				m_pStream->SetPosition(0);
				pWaveConverter->LoadStream(m_pStream);

				pWaveConverter->NewFormat.format.wFormatTag = WAVE_FORMAT_PCM;
				ValidItems = ACM_FORMATSUGGESTF_WFORMATTAG;
				Res = acmFormatSuggest(NULL, &pWaveConverter->CurrentFormat.format,
					&pWaveConverter->NewFormat.format, sizeof(ACMWaveFormat), ValidItems);

				if(Res != 0)
				{
					m_Valid = FALSE;
					delete pWaveConverter;
					pWaveConverter = NULL;
					return;
				}

				if(pWaveConverter->Convert() != 0)
				{
					m_Valid = FALSE;
					delete pWaveConverter;
					pWaveConverter = NULL;
					return;
				}

				m_MS = new CMemoryStream;
				m_OldStream = m_pStream;
				m_OldStreamAssigned = m_StreamAssigned;
				m_pStream = m_MS;
				m_MS->SetPosition(0);
				pWaveConverter->SaveWavToStream(m_MS);
				m_Size.QuadPart = m_MS->GetSize();
				m_MS->Seek(0, FILE_BEGIN);
				ReadRIFFHeader();
				m_WavType = wtACM;

				delete pWaveConverter;
				pWaveConverter = NULL;
			}
			break;
		default:
			break;
		}

		m_Opened++;
	}

	m_OpenCS->Leave();
}

void CWaveIn::CloseFile()
{
	m_OpenCS->Enter();

	if(m_Opened > 0)
	{
		if(!m_StreamAssigned)
		{
			delete m_pStream;
			m_pStream = NULL;
		}
		else if (m_Seekable)
			m_pStream->Seek(0, FILE_BEGIN);

		if(m_WavType == wtACM)
		{
			if(m_MS != NULL)
			{
				if(m_OldStreamAssigned)
					m_pStream = m_OldStream;
				else
				{
					m_pStream = NULL;
					delete m_OldStream;
					m_OldStream = NULL;
				}

				m_MS = NULL;
			}
		}

		m_Opened = 0;
	}

	m_OpenCS->Leave();
}

void CWaveIn::GetDataInternal(void **buffer, DWORD *bytes)
{
	INT l(0), Aligned(0);
	DWORD csize(0);
	void* Data = NULL;

	if(!m_Busy)
	{
		MessageBox(GetActiveWindow(), _TEXT("The Stream is not opened!"), _TEXT("Error"), MB_OK);
		return;
	}

	if(m_BufferStart > m_BufferEnd)
	{
		switch(m_WavType)
		{
		case wtDVIADPCM:
			{
				m_BufferStart = 1;
				csize = m_DVI_ADPCM_INFO.BlockLength - m_Channels * 4;
				Data = malloc(csize);

				if(ReadDVIADPCMBlock(Data))
				{
					if(m_Channels == 2)
					{
						DecodeDVIADPCMStereo((BYTE*)Data, (SHORT*)m_Buf, &csize);
						m_BufferEnd = csize * 4;
					}
					else
					{
						DecodeDVIADPCMMono((BYTE*)Data, (SHORT*)m_Buf, &csize);
						m_BufferEnd = csize * 4;
						free(Data);
						Data = NULL;
					}
				}
				else
				{
					free(Data);
					Data = NULL;

					if(!m_Seekable)
					{
						*bytes = 0;
						*buffer = NULL;
					}
					else m_BufferEnd = 0;
				}
			}

			break;

		case wtMSADPCM:
			{
				m_BufferStart = 1;
				if(m_Channels == 2)
				{
					csize = m_MS_ADPCM_INFO.BlockLength - sizeof(MSADPCMBlockHeaderStereo);
					Data = malloc(csize);

					if(ReadMSADPCMBlock(Data))
					{
						csize = m_MS_ADPCM_INFO.SamplesPerBlock - 2;
						DecodeMSADPCMStereo((BYTE*)Data, (SHORT*)m_Buf, &csize);
						m_BufferEnd = csize;
						free(Data);
						Data = NULL;
					}
					else m_BufferEnd = 0;
				}
				else
				{
					csize = m_MS_ADPCM_INFO.BlockLength - sizeof(MSADPCMBlockHeaderMono);
					Data = malloc(csize);

					if(ReadMSADPCMBlock(Data))
					{
						csize = m_MS_ADPCM_INFO.SamplesPerBlock - 2;
						DecodeMSADPCMMono((BYTE*)Data, (SHORT*)m_Buf, &csize);
						m_BufferEnd = csize;
						free(Data);
						Data = NULL;
					}
					else m_BufferEnd = 0;
				}
			}

			break;
		case wtPCM:
		case wtExtPCM:
			{
				m_BufferStart = 1;
				Aligned = BUF_SIZE - (BUF_SIZE % (m_Channels * m_BitsPerSample >> 3));
				l = m_pStream->Read(m_Buf, Aligned);
				m_BufferEnd = l;
			}
			break;
		case wtExtIEEEFloat:
		case wtIEEEFloat:
			{
				m_BufferStart = 1;
				Aligned = BUF_SIZE - (BUF_SIZE % (m_Channels * 8));
				l = m_pStream->Read(m_Buf, Aligned);
				if(m_ShortIEEEFloat)
				{
					ConvertShortIEEEFloatTo32((INT*)m_Buf, l);
					m_BufferEnd = l;
				}
				else
				{
					ConvertIEEEFloatTo32((INT*)m_Buf, l);
					m_BufferEnd = l / 2;
				}
			}

			break;

		case wtACM:
			{
				m_BufferStart = 1;
				m_pStream->Read(m_Buf, BUF_SIZE);
			}
			break;
		}
	}

	*bytes -= *bytes % (m_Channels * m_BitsPerSample >> 3);
	if(*bytes > (m_BufferEnd - m_BufferStart + 1))
		*bytes = (m_BufferEnd - m_BufferStart + 1);

	*buffer = (void*)(m_Buf + m_BufferStart - 1);
	m_BufferStart += *bytes;
}

BOOL CWaveIn::SeekInternal(LARGE_INTEGER* SampleNum)
{
	INT64 OffsSize(0);

	m_BufferStart = 1;
	m_BufferEnd = 0;
	BOOL ret = TRUE;

	switch(m_WavType)
	{
	case wtPCM:
		OffsSize = SampleNum->QuadPart * (m_BitsPerSample >> 3) * m_Channels;
		m_pStream->Seek((DWORD)(OffsSize + m_HeaderSize), FILE_BEGIN);
		break;
	case wtDVIADPCM:
		OffsSize = (SampleNum->QuadPart / m_DVI_ADPCM_INFO.SamplesPerBlock) * m_DVI_ADPCM_INFO.BlockLength;
		m_pStream->Seek((DWORD)(OffsSize + m_HeaderSize), FILE_BEGIN);
		break;
	case wtMSADPCM:
		OffsSize = (SampleNum->QuadPart / m_MS_ADPCM_INFO.SamplesPerBlock) * m_MS_ADPCM_INFO.BlockLength;
		m_pStream->Seek((DWORD)(OffsSize + m_HeaderSize), FILE_BEGIN);
		break;
	default:
		ret = FALSE;
		break;
	}

	return ret;
}

BOOL CWaveIn::ReadDVIADPCMBlock(void* pData)
{
	BYTE* block = NULL;
	DVIADPCMBlockHeader BH = {0};
	BOOL ret = FALSE;

	if(m_Seekable)
		if(m_pStream->GetPosition() >= m_pStream->GetSize())
			return ret;

	block = (BYTE*)malloc(m_DVI_ADPCM_INFO.BlockLength);
	if(m_pStream->Read(block, m_DVI_ADPCM_INFO.BlockLength) == 0)
	{
		free(block);
		block = NULL;
		return ret;
	}

	ret = TRUE;
	memcpy(&BH, block, sizeof(DVIADPCMBlockHeader));
	m_DVI_ADPCM_STATE.valprev_l = BH.Samp0;
	m_DVI_ADPCM_STATE.index_l = BH.StepTableIndex;

	if(m_Channels == 2)
	{
		memcpy(&BH, block + 4, sizeof(DVIADPCMBlockHeader));
		m_DVI_ADPCM_STATE.valprev_l = BH.Samp0;
		m_DVI_ADPCM_STATE.index_l = BH.StepTableIndex;
		memcpy(pData, block + 8, m_DVI_ADPCM_INFO.BlockLength - 8);
	}
	else memcpy(pData, block + 4, m_DVI_ADPCM_INFO.BlockLength - 4);

	free(block);
	block = NULL;

	return ret;
}

BOOL CWaveIn::ReadMSADPCMBlock(void* pData)
{
	BYTE* block = NULL;
	MSADPCMBlockHeaderMono BHM = {0};
	MSADPCMBlockHeaderStereo BHS = {0};
	BOOL ret = FALSE;

	if(m_pStream->GetPosition() >= m_pStream->GetSize())
		return ret;

	ret = TRUE;
	block = (BYTE*)malloc(m_MS_ADPCM_INFO.BlockLength);
	m_pStream->Read(block, m_MS_ADPCM_INFO.BlockLength);

	if(m_Channels == 1)
	{
		memcpy(&BHM, block, sizeof(MSADPCMBlockHeaderMono));
		m_MS_ADPCM_STATE.predictor[0] = BHM.predictor;
		m_MS_ADPCM_STATE.Delta[0] = BHM.Delta;
		m_MS_ADPCM_STATE.Samp1[0] = BHM.Samp1;
		m_MS_ADPCM_STATE.Samp2[0] = BHM.Samp2;
		memcpy(pData, block + sizeof(MSADPCMBlockHeaderMono),
			m_MS_ADPCM_INFO.BlockLength-sizeof(MSADPCMBlockHeaderMono));
	}
	else
	{
		memcpy(&BHS, block, sizeof(MSADPCMBlockHeaderStereo));
		m_MS_ADPCM_STATE = BHS;
		memcpy(pData, block + sizeof(MSADPCMBlockHeaderStereo),
			m_MS_ADPCM_INFO.BlockLength - sizeof(MSADPCMBlockHeaderStereo));
	}

	return ret;
}

void CWaveIn::ReadRIFFHeader()
{
	const int buffer_size = 4096;
	INT i(0);
	WORD WordVal(0);
	INT IntVal(0);
	char Buff[buffer_size];
	INT State(0);
	INT ChunkSize(0);
	GUID SubType;

	memset(Buff, 0, sizeof(char) * buffer_size);
	m_Size.QuadPart = 0;
	m_BitsPerSample = 0;
	m_Channels = 0;
	m_SampleRate = 0;
	m_WavType = wtUnsupported;
	State = LookingForRIFF;
	i = 4;

	m_pStream->Read(Buff, 4);
	while(i < 8192)
	{
		switch(State)
		{
		case LookingForRIFF:
			{
				if(!Compare4(Buff + i - 4, "RIFF"))
				{
					m_pStream->Read(Buff + i, 1);
					i++;
				}
				else
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					State = LookingForWave;
				}
			}
			break;
		case LookingForWave:
			{
				if(!Compare4(Buff + i - 4, "WAVE"))
				{
					m_pStream->Read(Buff + i, 1);
					i++;
				}
				else
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					State = LookingForFMT;
				}
			}
			break;

		case LookingForFMT:
			{
				if(!Compare4(Buff + i - 4, "fmt "))
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					memcpy(&ChunkSize, Buff + i - 4, 4);

					m_pStream->Read(Buff + i, ChunkSize);
					i += ChunkSize;

					m_pStream->Read(Buff + i, 4);
					i += 4;
				}
				else
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					memcpy(&ChunkSize, Buff + i - 4, 4);

					m_pStream->Read(Buff + i, ChunkSize);
					i += ChunkSize;
					memcpy(&WordVal, Buff + i - ChunkSize, 2);

					switch(WordVal)
					{
					case WAVE_FORMAT_PCM:
						m_WavType = wtPCM;
						break;
					case WAVE_FORMAT_IMA_ADPCM:
						m_WavType = wtDVIADPCM;
						break;
					case WAVE_FORMAT_ADPCM:
						m_WavType = wtMSADPCM;
						break;
					case WAVE_FORMAT_MP3:
						m_WavType = wtACM;
						break;
					case WAVE_FORMAT_IEEE_FLOAT:
						m_WavType = wtIEEEFloat;
						break;
					case WAVE_FORMAT_EXTENSIBLE:
						m_WavType = wtExtPCM;
						break;
					default:
						return;
					}

					memcpy(&WordVal, Buff + i + 2 - ChunkSize, 2);
					m_Channels = WordVal;

					memcpy(&IntVal, Buff + i + 4 - ChunkSize, 4);
					m_SampleRate = IntVal;

					memcpy(&WordVal, Buff + i + 12 - ChunkSize, 2);
					if(m_WavType == wtDVIADPCM)
						m_DVI_ADPCM_INFO.BlockLength = WordVal;
					else
						m_MS_ADPCM_INFO.BlockLength = WordVal;

					memcpy(&WordVal, Buff + i + 14 - ChunkSize, 2);
					m_BitsPerSample = WordVal;

					if(m_WavType == wtExtPCM)
					{
						memcpy(&SubType, Buff + i - 16, 16);
						if(IsEqualGUID(SubType, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
							m_WavType = wtExtIEEEFloat;
						else if(!IsEqualGUID(SubType, KSDATAFORMAT_SUBTYPE_PCM))
							m_WavType = wtUnsupported;
					}

					if(m_WavType == wtDVIADPCM || m_WavType == wtMSADPCM || m_WavType == wtACM)
					{
						memcpy(&WordVal, Buff + i + 18 - ChunkSize, 2);
						if(m_WavType == wtDVIADPCM)
							m_DVI_ADPCM_INFO.SamplesPerBlock = WordVal;
						else
							m_MS_ADPCM_INFO.SamplesPerBlock = WordVal;

						if(m_WavType == wtMSADPCM)
						{
							memcpy(&WordVal, Buff + i + 20 - ChunkSize, 2);
							m_MS_ADPCM_INFO.NumCoeff = WordVal;
							memcpy(&m_MS_ADPCM_INFO.CoefSets[0], Buff + i + 22 - ChunkSize,
								m_MS_ADPCM_INFO.NumCoeff * sizeof(MS_ADPCM_COEF_SET));
						}

						State = LookingForFACT;
					}
					else State = LookingForDATA;

					m_pStream->Read(Buff + i, 4);
					i += 4;
				}
			}

			break;

		case LookingForFACT:
			{
				if(!Compare4(Buff + i - 4, "fact"))
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					memcpy(&ChunkSize, Buff + i - 4, 4);

					m_pStream->Read(Buff + i, ChunkSize);
					i += ChunkSize;

					m_pStream->Read(Buff + i, 4);
					i += 4;
				}
				else
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					memcpy(&ChunkSize, Buff + i - 4, 4);

					m_pStream->Read(Buff + i, ChunkSize);
					i += ChunkSize;

					memcpy(&IntVal, Buff + i - ChunkSize, 4);
					if(m_WavType == wtDVIADPCM)
						m_DVI_ADPCM_INFO.DataSize = IntVal;
					else
						m_MS_ADPCM_INFO.DataSize = IntVal;

					m_pStream->Read(Buff + i, 4);
					i += 4;
					State = LookingForDATA;
				}
			}

			break;

		case LookingForDATA:
			{
				if(!Compare4(Buff + i - 4, "data"))
				{
					m_pStream->Read(Buff + i, 4);
					i += 4;
					memcpy(&ChunkSize, Buff + i - 4, 4);

					m_pStream->Read(Buff + i, ChunkSize);
					i += ChunkSize;

					m_pStream->Read(Buff + i, 4);
					i += 4;
				}
				else
				{
					m_pStream->Read(Buff + i, 4);

					if(m_WavType == wtPCM || m_WavType == wtExtPCM || m_WavType == wtIEEEFloat
						|| m_WavType == wtExtIEEEFloat)
						memcpy(&m_Size.QuadPart, Buff + i, 4);

					i += 4;
					m_HeaderSize = i;
					return;
				}
			}

			break;
		default:
			break;
		}

		if(m_Seekable)
			if(m_pStream->GetPosition() >= m_pStream->GetSize())
				break;
	}

	m_WavType = wtUnsupported;
}

BOOL Compare4(CHAR* S1, CHAR* S2)
{
	BYTE i(0), Diff(0);

	for(i = 0; i <= 3; ++i)
	{
		Diff = (BYTE)S1[i] - (BYTE)S2[i];
		if( ! (Diff == 0 || Diff == 32 || Diff == 224))
			return FALSE;
	}

	return TRUE;
}

void ConvertIEEEFloatTo32(INT* InOutBuf, INT InSize)
{
	DOUBLE* p = (DOUBLE*)InOutBuf;
	for(int i = 0; i < InSize / 8; ++i)
	{
		if(p[i*2] >= 1) InOutBuf[i] = 2147483647;
		else if(p[i*2] <= -1) InOutBuf[i] = -2147483647;
		else if(p[i*2] == 0) InOutBuf[i] = 0;
		else InOutBuf[i] = (INT)floor(p[i*2] * 2147483647);
	}
}

void ConvertShortIEEEFloatTo32(INT* InOutBuf, INT InSize)
{
	FLOAT* p = (FLOAT*)InOutBuf;
	for(int i = 0; i < InSize / 4; ++i)
	{
		if(p[i] >= 1) InOutBuf[i] = 2147483647;
		else if(p[i] <= -1) InOutBuf[i] = -2147483647;
		else if(p[i] == 0) InOutBuf[i] = 0;
		else InOutBuf[i] = (INT)floor(p[i] * 2147483647);
	}
}

/************************************************************************/
/* CMP3In                                                               */
/************************************************************************/
CMP3In::CMP3In(void) : CFileIn()
{
	memset(m_buffer, 0, sizeof(m_buffer));
	memset(m_pcmbuffer, 0, sizeof(m_pcmbuffer));

	m_xing.flags = 0;
	m_samplecount = 0;
	m_timer = mad_timer_zero;
	m_length = mad_timer_zero;
	m_rate = 0;
	m_frames = 0;
	m_buflen = 0;
}

CMP3In::~CMP3In(void)
{
}

void CMP3In::GetDataInternal(void** buffer, DWORD* bytes)
{
	unsigned char* samples = m_pcmbuffer;
	unsigned int nsamples;

	nsamples = (BUF_SIZE / (SAMPLE_DEPTH / 8)) >> (m_Channels == 2 ? 1 : 0);
	*bytes = 0;

	while (nsamples)
	{
		unsigned int count, bitrate;
		count = m_synth.pcm.length - m_samplecount;

		if (count > nsamples)
			count = nsamples;

		if (count)
		{
			mad_fixed_t const *ch1, *ch2;
			ch1 = m_synth.pcm.samples[0] + m_samplecount;
			ch2 = m_synth.pcm.samples[1] + m_samplecount;

			if (m_Channels != 2)
				ch2 = 0;
			else if (m_synth.pcm.channels == 1)
				ch2 = ch1;

			pack_pcm(&samples, count, ch1, ch2);

			m_samplecount += count;
			nsamples -= count;

			if (nsamples == 0)
				break;
		}

		while (mad_frame_decode(&m_frame, &m_stream) == -1)
		{
			// DWORD bytes;
			unsigned int bytes;
			if (MAD_RECOVERABLE(m_stream.error))
				continue;

			if (m_stream.next_frame)
			{
				memmove(m_buffer, m_stream.next_frame, m_buflen = m_buffer + m_buflen - m_stream.next_frame);
			}

			if ((bytes = m_pStream->Read(m_buffer + m_buflen, sizeof(m_buffer) - m_buflen)) == 0)
				return;

			mad_stream_buffer(&m_stream, m_buffer, m_buflen += bytes);
		}

		bitrate = m_frame.header.bitrate / 1000;
		m_rate += bitrate;
		m_frames++;

		mad_synth_frame(&m_synth, &m_frame);
		m_samplecount = 0;
		mad_timer_add(&m_timer, m_frame.header.duration);
	}

	*bytes = samples - m_pcmbuffer;
	*buffer = m_pcmbuffer;
}

BOOL CMP3In::SeekInternal(LARGE_INTEGER* SampleNum)
{
	if(SampleNum->QuadPart > m_size)
		return FALSE;

	double fraction = SampleNum->QuadPart / m_size;
	unsigned long position = (unsigned long)
		(mad_timer_count(m_length, MAD_UNITS_MILLISECONDS) * fraction);
	mad_timer_set(&m_timer, position / 1000, position % 1000, 1000);

	if (m_xing.flags & XING_TOC)
	{
		int percent(0), p1(0), p2(0);
		percent = (int)(fraction * 100);
		p1 = (percent < 100) ? m_xing.toc[percent] : 0x100;
		p2 = (percent < 99) ? m_xing.toc[percent+1] : 0x100;

		fraction = (p1 + (p2 - p1) * (fraction * 100 - percent)) / 0x100;
	}

	m_pStream->Seek((LONG)(m_size * fraction), FILE_BEGIN);

	if ((m_buflen = m_pStream->Read(m_buffer, sizeof(m_buffer))) == 0)
		m_buflen = 0;

	mad_stream_buffer(&m_stream, m_buffer, m_buflen);
	mad_frame_mute(&m_frame);
	mad_synth_mute(&m_synth);

	if (SampleNum->QuadPart)
	{
		int skip = 2;
		do
		{
			if (mad_frame_decode(&m_frame, &m_stream) == 0)
			{
				mad_timer_add(&m_timer, m_frame.header.duration);

				if (--skip == 0)
					mad_synth_frame(&m_synth, &m_frame);
			}
			else if (!MAD_RECOVERABLE(m_stream.error))
				break;

		} while (skip);
	}

	m_synth.pcm.length = 0;
	m_samplecount = 0;
	return TRUE;
}

void CMP3In::OpenFile()
{
	int iStreamStart = 0;

	m_OpenCS->Enter();
	m_Valid = TRUE;

	if(m_Opened == 0)
	{
		if(!m_StreamAssigned)
			m_pStream = new CFileStream(m_FileName, FILE_OPEN);

		m_size = m_pStream->GetSize();
		m_streamsize = m_size;

		// Skip over ID3v2 tag (if there is one)
		// only works on seekable streams
		ID3v2Tag tag;
		memset(&tag, 0, sizeof(ID3v2Tag));

		m_pStream->Read(&tag, sizeof(ID3v2Tag));
		if (memcmp(tag.m_cTAG, "ID3", 3) == 0)
		{
			iStreamStart = sizeof(ID3v2Tag);
			iStreamStart += (tag.m_cSize_Encoded[0] << 21) | (tag.m_cSize_Encoded[1] << 14)
				| (tag.m_cSize_Encoded[2] << 7) | tag.m_cSize_Encoded[3];

			m_streamsize -= iStreamStart;
			m_pStream->Seek(iStreamStart, FILE_BEGIN);
		}

		// If ID3v1 tag exists here
		DWORD dwPos = m_pStream->Seek(0, FILE_CURRENT);
		m_pStream->Seek(0, FILE_END);
		m_pStream->Seek(-128, FILE_CURRENT);

		ID3Tag tag1;
		memset(&tag1, 0, sizeof(ID3Tag));

		m_pStream->Read(&tag1, sizeof(ID3Tag));
		if(memcmp(tag1.m_cTAG, "TAG", 3) == 0)
			m_streamsize -= 128;

		mad_stream_init(&m_stream);
		mad_frame_init(&m_frame);
		mad_synth_init(&m_synth);

		m_pStream->Seek(iStreamStart, FILE_BEGIN);
		scan_header(m_pStream, &m_frame.header, &m_xing);
		m_vbr = m_xing.flags ? TRUE : FALSE;
		m_pStream->Seek(iStreamStart, FILE_BEGIN);

		m_synth.pcm.length = 0;
		m_samplecount = 0;
		m_timer = mad_timer_zero;

		if (m_xing.flags & XING_FRAMES)
		{
			m_length = m_frame.header.duration;
			mad_timer_multiply(&m_length, m_xing.frames);
		}
		else
		{
			/* estimate playing time from file size */
			mad_timer_set(&m_length, 0, 1, m_frame.header.bitrate / 8);
			mad_timer_multiply(&m_length, m_size);
		}

		m_rate = 0;
		m_frames = 0;
		m_buflen = 0;

		m_SampleRate = m_frame.header.samplerate;
		m_Channels = (m_frame.header.mode == MAD_MODE_SINGLE_CHANNEL) ? 1: 2;
		m_BitsPerSample = SAMPLE_DEPTH;

		m_Opened++;
	}

	m_OpenCS->Leave();
}

void CMP3In::CloseFile()
{
	m_OpenCS->Enter();

	if(m_Opened > 0)
	{
		if(!m_StreamAssigned)
		{
			delete m_pStream;
			m_pStream = NULL;
		}
		else if (m_Seekable)
			m_pStream->Seek(0, FILE_BEGIN);

		mad_synth_finish(&m_synth);
		mad_frame_finish(&m_frame);
		mad_stream_finish(&m_stream);		

		m_Opened = 0;
	}

	m_OpenCS->Leave();
}

int parse_xing(struct xing *xing, struct mad_bitptr ptr, unsigned int bitlen)
{
	if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
		goto fail;

	xing->flags = mad_bit_read(&ptr, 32);
	bitlen -= 64;

	if (xing->flags & XING_FRAMES)
	{
		if (bitlen < 32)
			goto fail;

		xing->frames = mad_bit_read(&ptr, 32);
		bitlen -= 32;
	}

	if (xing->flags & XING_BYTES)
	{
		if (bitlen < 32)
			goto fail;

		xing->bytes = mad_bit_read(&ptr, 32);
		bitlen -= 32;
	}

	if (xing->flags & XING_TOC)
	{
		int i;

		if (bitlen < 800)
			goto fail;

		for (i = 0; i < 100; ++i)
			xing->toc[i] = (unsigned char) mad_bit_read(&ptr, 8);

		bitlen -= 800;
	}

	if (xing->flags & XING_SCALE)
	{
		if (bitlen < 32)
			goto fail;

		xing->scale = mad_bit_read(&ptr, 32);
		bitlen -= 32;
	}

	return 0;

fail:
	xing->flags = 0;
	return -1;
}

/*
* NAME:  scan_header()
* DESCRIPTION: read the initial frame(s) to get stream statistics
*/
int scan_header(CStream* pInStream, struct mad_header *header, struct xing *xing)
{
	struct mad_stream stream;
	struct mad_frame frame;
	unsigned char buffer[8192];
	unsigned int buflen = 0;
	int count = 0, result = 0;

	mad_stream_init(&stream);
	mad_frame_init(&frame);

	if (xing)
		xing->flags = 0;

	while (1)
	{
		if (buflen < sizeof(buffer))
		{
			unsigned int bytes;
			if ((bytes = pInStream->Read(buffer + buflen, sizeof(buffer) - buflen)) == 0)
			{
				result = -1;
				break;
			}

			buflen += bytes;
		}

		mad_stream_buffer(&stream, buffer, buflen);

		while (1)
		{
			if (mad_frame_decode(&frame, &stream) == -1)
			{
				if (!MAD_RECOVERABLE(stream.error))
					break;

				continue;
			}

			if (count++ || (xing && parse_xing(xing, stream.anc_ptr, stream.anc_bitlen) == 0))
				break;
		}

		if (count || stream.error != MAD_ERROR_BUFLEN)
			break;

		memmove(buffer, stream.next_frame,
			buflen = &buffer[buflen] - stream.next_frame);
	}

	if (count)
	{
		if (header)
			*header = frame.header;
	}

	else
		result = -1;

	mad_frame_finish(&frame);
	mad_stream_finish(&stream);

	return result;
}


/*
* NAME:  prng()
* DESCRIPTION: 32-bit pseudo-random number generator
*/
__inline unsigned long prng(unsigned long state)
{
	return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}

/*
* NAME:  dither()
* DESCRIPTION: dither and scale sample
*/
__inline signed int dither(mad_fixed_t sample, struct dither *dither)
{
	unsigned int scalebits;
	mad_fixed_t output, mask, random;

	enum
	{
		MIN = -MAD_F_ONE,
		MAX =  MAD_F_ONE - 1
	};

	/* noise shape */
	sample += dither->error[0] - dither->error[1] + dither->error[2];
	dither->error[2] = dither->error[1];
	dither->error[1] = dither->error[0] / 2;

	/* bias */
	output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1));
	scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH;
	mask = (1L << scalebits) - 1;

	/* dither */
	random  = prng(dither->random);
	output += (random & mask) - (dither->random & mask);
	dither->random = random;

	/* clip */

	if (output > MAX)
	{
		output = MAX;
		if (sample > MAX)
			sample = MAX;
	}

	else if (output < MIN)
	{
		output = MIN;
		if (sample < MIN)
			sample = MIN;
	}

	/* quantize */
	output &= ~mask;

	/* error feedback */
	dither->error[0] = sample - output;

	/* scale */
	return output >> scalebits;
}

/*
* NAME:  pack_pcm()
* DESCRIPTION: scale and dither MAD output
*/
void pack_pcm(unsigned char **pcm, unsigned int nsamples,
			  mad_fixed_t const *ch1, mad_fixed_t const *ch2)
{
	register signed int s0, s1;
	static struct dither d0, d1;

	if (ch2)    /* stereo */
	{
		while (nsamples--)
		{
			s0 = scale(*ch1++, &d0);
			s1 = scale(*ch2++, &d1);
# if SAMPLE_DEPTH == 16
			(*pcm)[0 + 0] = s0 >> 0;
			(*pcm)[0 + 1] = s0 >> 8;
			(*pcm)[2 + 0] = s1 >> 0;
			(*pcm)[2 + 1] = s1 >> 8;
			*pcm += 2 * 2;
# elif SAMPLE_DEPTH == 8
			(*pcm)[0] = s0 ^ 0x80;
			(*pcm)[1] = s1 ^ 0x80;

			*pcm += 2;
# else
#  error "bad SAMPLE_DEPTH"
# endif
		}
	}

	else    /* mono */
	{
		while (nsamples--)
		{
			s0 = scale(*ch1++, &d0);

# if SAMPLE_DEPTH == 16
			(*pcm)[0] = s0 >> 0;
			(*pcm)[1] = s0 >> 8;
			*pcm += 2;
# elif SAMPLE_DEPTH == 8
			*(*pcm)++ = s0 ^ 0x80;
# endif
		}
	}
}

/************************************************************************/
/* CVorbisIn                                                            */
/************************************************************************/
CVorbisIn* gVorbisIn;
CVorbisIn::CVorbisIn(void) : CFileIn()
{
	m_CurrentSection = 0;
	memset(m_pcmbuffer, 0, BUF_SIZE);
}

CVorbisIn::~CVorbisIn(void)
{

}

void CVorbisIn::GetDataInternal(void** buffer, DWORD* bytes)
{
	int ret;
	BOOL CONTINUE = TRUE;
	DWORD bytes_read = 0;

	while (bytes_read < BUF_SIZE)
	{
		ret = ov_read(&m_OggVorbisFile, (char *)m_pcmbuffer + bytes_read,
			BUF_SIZE - bytes_read, 0, 2, 1, &m_CurrentSection);

		if (ret == 0)
		{
			*bytes = bytes_read;
			CONTINUE = FALSE;
			break;
		}
		else if (ret < 0)
		{
			OutputDebugString(_TEXT("Hole in OGG/VORBIS datastream, ignoring..."));
		}

		else
		{
			bytes_read += ret;
		}
	}

	*buffer = m_pcmbuffer;
	*bytes = bytes_read;
}

BOOL CVorbisIn::SeekInternal(LARGE_INTEGER* SampleNum)
{
	ov_pcm_seek(&m_OggVorbisFile, (ogg_int64_t)(((float)SampleNum->QuadPart /
		(float)m_Size.QuadPart) * (float)ov_pcm_total(&m_OggVorbisFile, -1)));
	return TRUE;
}

void CVorbisIn::OpenFile()
{
	m_OpenCS->Enter();
	m_Valid = TRUE;

	if(m_Opened == 0)
	{
		if(!m_StreamAssigned)
			m_pStream = new CFileStream(m_FileName, FILE_OPEN);

		gVorbisIn = this;
		m_Size.QuadPart = m_pStream->GetSize();
		memset(&m_OggVorbisFile, 0, sizeof(OggVorbis_File));

		ov_callbacks callbacks = {
			(size_t (*)(void *, size_t, size_t, void *)) vorbisRead,
			(int(*)(void *, ogg_int64_t, int)) vorbisSeek,
			(int(*)(void *)) vorbisClose,
			(long(*)(void *)) vorbisTell
		};

		if (ov_open_callbacks((void *)(this), &m_OggVorbisFile, NULL, 0, callbacks) < 0)
		{
			MessageBox(GetActiveWindow(), _TEXT("Input does not appear to be an Ogg bitstream."),
				_TEXT("Error"), MB_OK);

			CloseFile();
			return;
		}

		if (ov_streams(&m_OggVorbisFile) != 1)
		{
			TCHAR BUFFER[512];
			StringCbPrintf(BUFFER, 512, _TEXT("Can\'t deal with multiple streams yet. Streams:%d"),
				ov_streams(&m_OggVorbisFile));
			MessageBox(GetActiveWindow(), BUFFER, _TEXT("Error"), MB_OK);
	
			ov_clear(&m_OggVorbisFile);
			CloseFile();
			return;
		}

		vorbis_info *pInfo = NULL;
		pInfo = ov_info(&m_OggVorbisFile, -1);

		if (pInfo == NULL)
		{
			MessageBox(GetActiveWindow(), _TEXT("Unable to get ogg info."), _TEXT("Error"), MB_OK);

			ov_clear(&m_OggVorbisFile);
			CloseFile();
			return;
		}

		if (pInfo->channels > 2)
		{
			TCHAR BUFFER[512];
			StringCbPrintf(BUFFER, 512, _TEXT("Can\'t deal with more than 2 channels yet. Channels:%d"),
				pInfo->channels);
			MessageBox(GetActiveWindow(), BUFFER, _TEXT("Error"), MB_OK);

			ov_clear(&m_OggVorbisFile);
			CloseFile();
			return;
		}

		m_SampleRate = pInfo->rate;
		m_Channels = pInfo->channels;
		m_BitsPerSample = 16;

		m_Opened++;
	}

	m_OpenCS->Leave();
}

void CVorbisIn::CloseFile()
{
	m_OpenCS->Enter();

	if(m_Opened > 0)
	{
		if(!m_StreamAssigned)
		{
			delete m_pStream;
			m_pStream = NULL;
		}
		else if (m_Seekable)
			m_pStream->Seek(0, FILE_BEGIN);

		ov_clear(&m_OggVorbisFile);
		m_Opened = 0;
	}

	m_OpenCS->Leave();
}

size_t vorbisRead(void *buffer, size_t size, size_t amount, void *file)
{
	size_t result = 0;
	result = gVorbisIn->GetStream()->Read(buffer, size*amount);
	return result;
}

int vorbisSeek(void *file, ogg_int64_t newpos, int set)
{
	if (set == SEEK_END)
		newpos = gVorbisIn->GetStream()->GetSize();

	gVorbisIn->GetStream()->Seek((UINT)newpos, FILE_BEGIN);
	return (int)newpos;
}

int vorbisClose(void *file)
{
	return 1;
}

long vorbisTell(void *file)
{
	return gVorbisIn->GetStream()->Seek(0, FILE_CURRENT);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer none
China China
To be, or not to be, this is question. That's are all depend on your decision. What do you think?

Comments and Discussions