// Tape.cpp: implementation of the CTape class.
//
//!!!Остался один непонятный косяк. Wav файлы, которые записаны эмулятором
// на частотах 44100 и 96000 не воспринимаются эмулятором на частоте 48000
//даже преобразованный в tap такой wav не воспринимается. Где-то затесалась
//незамеченная ошибка.
//Wav файл, записанный на 48000 не воспринимается на частоте 44100, а на
//96000 - воспринимается. Видимо, погрешность ресемплирования.

#include "stdafx.h"
#include "resource.h"
#include "Config.h"
#include "MSFManager.h"
#include "Tape.h"
#include "BKErrMsgSys.h"


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction


CTape::CTape()
	: m_pWave(nullptr)
	, m_nWaveLength(0)
	, m_nPlayPos(0)
	, m_pRecord(nullptr)
	, m_nRecordPos(0)
	, m_nRecordLength(0)
	, m_bAutoBeginRecord(false)
	, m_bAutoEndRecord(false)
	, m_pBin(nullptr)
	, m_nPos(0)
	, m_nAverage(0)
	, m_nLastLen(0)
	, m_nAvgLength(0)
	, m_nScanTableSize(0)
{
	SetWaveParam(DEFAULT_SOUND_SAMPLE_RATE, BUFFER_CHANNELS);

	// Initialize table pointers
	for (auto &y : m_pScanTable)
	{
		y = nullptr;
	}
}

CTape::~CTape()
{
	SAFE_DELETE_MEMORY(m_pWave);
	SAFE_DELETE_MEMORY(m_pRecord);
	SAFE_DELETE_ARRAY(m_pBin);
	//очищаем таблицы
	ClearTables();
}


void CTape::SetWaveParam(int nWorkingSSR /*= DEFAULT_SOUND_SAMPLE_RATE*/, int nWorkingChn /*= BUFFER_CHANNELS*/)
{
	m_nWorkingSSR = nWorkingSSR;
	m_nWorkingChannels = nWorkingChn;
	m_nSampleSize = m_nWorkingChannels * sizeof(SAMPLE_TYPE);
	m_dWorkingResCoeff = double(m_nWorkingSSR) / DEFAULT_SOUND_SAMPLE_RATE;
}

void CTape::ClearTables()
{
	for (auto &y : m_pScanTable)
	{
		SAFE_DELETE_ARRAY(y);
	}
}


bool CTape::LoadWaveFile(const CString &strPath)
{
	CFile waveFile;

	if (!waveFile.Open(strPath, CFile::modeRead))
	{
		return false;
	}

	WaveHeader waveHeader;
	WAVEFORMATEX wfx;
	DataHeader dataHeader;

	if (waveFile.Read(&waveHeader, sizeof(WaveHeader)) != sizeof(WaveHeader))
	{
		return false;
	}

	if (waveHeader.riffTag != RIFF_TAG || waveHeader.fmtTag != FMT_TAG)
	{
		return false;
	}

	if (waveFile.Read(&wfx, waveHeader.fmtSize) != waveHeader.fmtSize)
	{
		return false;
	}

	wfx.cbSize = 0;

	if (wfx.wFormatTag != WAVE_FORMAT_PCM)
	{
		return false;
	}

	for (;;)
	{
		if (waveFile.Read(&dataHeader, sizeof(DataHeader)) != sizeof(DataHeader))
		{
			return false;
		}

		if (dataHeader.dataTag == DATA_TAG)
		{
			break;
		}

		waveFile.Seek(dataHeader.dataSize, CFile::current);
	}

	/*
	тут нужна конвертация из любого формата wav WAVE_FORMAT_PCM, в m_nWorkingSSR, 16 бит, 2 канала

	для WAVE_FORMAT_PCM данные могут быть только 8 или 16 бит,
	каналов 1 или 2 по стандарту, но никто не мешает сделать и больше
	частота дискретизации 8000, 11025, 22050, 44100, но никто не мешает сделать и больше
	*/
	//если звук изначально в нужном нам формате, то просто читаем его как обычно
	if (
	    (wfx.nSamplesPerSec == m_nWorkingSSR) &&
	    (wfx.nChannels == m_nWorkingChannels) &&
	    (wfx.wBitsPerSample == BUFFER_BPS)
	)
	{
		if (AllocWaveBuffer(dataHeader.dataSize))
		{
			waveFile.Read(m_pWave, dataHeader.dataSize);
		}
		else
		{
			return false;
		}
	}
	else //иначе будем конвертировать
	{
		//определим количество сэмплов в исходном сигнале
		int nSrcSamples = dataHeader.dataSize / wfx.nBlockAlign;
		//определим количество сэмплов в результирующем сигнале
		auto nDstSamples = int(double(nSrcSamples) * double(m_nWorkingSSR) / wfx.nSamplesPerSec);

		//выделяем буфер под звук
		if (!AllocWaveBuffer(nDstSamples * m_nSampleSize))
		{
			return false;
		}

		m_nWaveLength = 0; //длину надо обнулить
		int readed;
		auto inBuf = new BYTE[wfx.nAvgBytesPerSec]; //входной буфер в байтах

		if (inBuf)
		{
			int converted;

			while ((readed = waveFile.Read(inBuf, wfx.nAvgBytesPerSec)) > 0)
			{
				int nDataLen = readed / wfx.nBlockAlign; //сколько сэмплов в буфере
				converted = ConvertSamples(wfx, inBuf, wfx.nAvgBytesPerSec, nDataLen);
			}

			delete [] inBuf;
		}
		else
		{
			CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
		}
	}

	waveFile.Close();
	CalculateAverage();
	return true;
}

/*
преобразование массива m_pWave из частоты nSrcSSR в nDstSSR
Цифровое. Не подходит для музыки.
Правильный ресемплинг с помощью БПФ превращает цифровой звук в
полное непотребство.
*/
void CTape::ResampleBuffer(int nSrcSSR, int nDstSSR)
{
	if (nSrcSSR == nDstSSR)
	{
		return;
	}

	SAMPLE_TYPE *pOldWave = m_pWave;
	int nOldWaveLen = m_nWaveLength;
	//новая длина в сэмплах
	int nNewLen = int(double(nOldWaveLen) * double(nDstSSR) / double(nSrcSSR)) + 1;
	m_pWave = nullptr;

	if (!AllocWaveBuffer(nNewLen * m_nSampleSize)) //выделяем новый буфер
	{
		return;
	}

	m_nWaveLength = 0; //длину надо обнулить
	//формируем параметры входного wave
	WAVEFORMATEX wfx;
	ZeroMemory(&wfx, sizeof(WAVEFORMATEX));
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nSamplesPerSec = nSrcSSR;
	wfx.wBitsPerSample = BUFFER_BPS;
	wfx.nChannels = m_nWorkingChannels;
	wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
	auto inBuf = new BYTE[wfx.nAvgBytesPerSec]; //входной буфер в байтах

	if (!inBuf)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	SAMPLE_TYPE *pOldWavePtr = pOldWave; //указатель в буфере
	int nOldWaveLenByte = nOldWaveLen * wfx.nBlockAlign; //размер ваве в байтах

	while (nOldWaveLenByte > 0)
	{
		int readedBytes = nOldWaveLenByte > int(wfx.nAvgBytesPerSec) ? wfx.nAvgBytesPerSec : nOldWaveLenByte; //сколько читать в байтах
		memcpy(inBuf, pOldWavePtr, readedBytes);
		nOldWaveLenByte -= wfx.nAvgBytesPerSec;
		pOldWavePtr += (readedBytes / sizeof(SAMPLE_TYPE));
		int nDataLen = readedBytes / wfx.nBlockAlign; //сколько сэмплов в буфере
		int converted = ConvertSamples(wfx, inBuf, wfx.nAvgBytesPerSec, nDataLen);
	}

	SAFE_DELETE_MEMORY(pOldWave);
}

/*
Конвертирование сэмплов из одной частоты в другую.
Вход: wfx_in - параметры входного wave.
inBuf - буфер кусочка входных данных
nBufSize - размер буфера в байтах
nDataLen - количество сэмплов в буфере
результирующий массив формируется в m_pWave
выход - количество обработанных сэмплов, должно быть равно nDataLen
*/
int CTape::ConvertSamples(WAVEFORMATEX wfx_in, BYTE *inBuf, int nBufSize, int nDataLen)
{
	auto *inBuf16 = reinterpret_cast<SAMPLE_TYPE *>(inBuf); //буфер для 16 битного звука
	int nDestBufferBasePos = m_nWaveLength; //начнём отсюда в буфере
	SAMPLE_TYPE sample;
	int pds = -1;
	int nDestinationSamplePos = 0;

	//для каждого прочитавшегося сэмпла
	for (int nSourceSamplePos = 0; nSourceSamplePos < nDataLen; ++nSourceSamplePos)
	{
		//при необходимости делаем ресэмплинг
		nDestinationSamplePos = int(double(nSourceSamplePos) * double(m_nWorkingSSR) / double(wfx_in.nSamplesPerSec));

		//для каждого канала в сэмпле
		for (int channel = 0; channel < wfx_in.nChannels; ++channel)
		{
			//получаем сэмпл, и делаем его 16 битным
			switch (wfx_in.wBitsPerSample)
			{
				case 8:
					//sample = (SAMPLE_TYPE(*inBuf++) << 8) - 0x8000; //unsigned 8 to signed 16
					//я везде использую только положительный полупериод, половину амплитуды
					//поэтому и преобразование надо делать такое-же
					sample = (SAMPLE_TYPE(*inBuf++) << 7); //unsigned 8 to signed 16
					break;

				case 16:
					sample = *inBuf16++;
					break;

				//в WAVE_FORMAT_PCM может быть только 8 и 16 бит и 1 или 2 канала
				//в WAVE_FORMAT_EXTENSIBLE может быть ещё и 24 и 32-х битный звук и много каналов
				//вот как-то нафиг, неохота возиться с этим.
				default:
					sample = 0;
			}

			//если канал меньше максимально возможного
			if (channel < m_nWorkingChannels)
			{
				int nCurrentDestinationSamplePos = pds;

				if (nCurrentDestinationSamplePos < 0) //если самое первое действие, просто копируем сэмпл
				{
					m_pWave[(nDestBufferBasePos + nDestinationSamplePos) * m_nWorkingChannels + channel] = sample;
				}
				else
				{
					//при апсэмплинге размножаем сэмпл
					//при даунсэмплинге - не выполняем цикл, пока не придёт время,
					//т.е. пропускаем заданное количество сэмплов
					while (nCurrentDestinationSamplePos <= nDestinationSamplePos)
					{
						m_pWave[(nDestBufferBasePos + nCurrentDestinationSamplePos) * m_nWorkingChannels + channel] = sample;
						nCurrentDestinationSamplePos++;
					}
				}
			}
		}

		//если канал всего один, дублируем последний сэмпл на остальные возможные каналы (т.е. на второй).
		if (wfx_in.nChannels == 1)
		{
			int channel = 1;

			while (channel < m_nWorkingChannels)
			{
				int nCurrentDestinationSamplePos = pds;

				if (nCurrentDestinationSamplePos < 0)
				{
					m_pWave[(nDestBufferBasePos + nDestinationSamplePos) * m_nWorkingChannels + channel] = sample;
				}
				else
				{
					while (nCurrentDestinationSamplePos <= nDestinationSamplePos)
					{
						m_pWave[(nDestBufferBasePos + nCurrentDestinationSamplePos) * m_nWorkingChannels + channel] = sample;
						nCurrentDestinationSamplePos++;
					}
				}

				channel++;
			}
		}

		pds = nDestinationSamplePos + 1; //откуда начинать для следующего сэмпла
	}

	m_nWaveLength = nDestBufferBasePos + pds;
	return pds;
}



/*
Расчёт среднего уровня массива. Это будет среднее арифметическое.
Так считать дольше, но корректнее.
*/
void CTape::CalculateAverage()
{
	double avg = 0.0;

	for (register int i = 0; i < m_nWaveLength; ++i)
	{
		avg += double(GetCurrentSampleMono(m_pWave, i));
	}

	m_nAverage = SAMPLE_TYPE(avg / m_nWaveLength);
}


bool CTape::LoadTmpFile(const CString &strPath)
{
	CFile tmpFile;

	if (!tmpFile.Open(strPath, CFile::modeRead))
	{
		return false;
	}

	auto ullLen = UINT(tmpFile.GetLength());

	if (!AllocWaveBuffer(int(ullLen)))
	{
		return false;
	}

	tmpFile.Read(m_pWave, ullLen);
	tmpFile.Close();
	// Calculate average
	CalculateAverage();
	return true;
}

//такой недостаток - сохраанение всегда с текущей частотой дискретизации.
//а нужно добавить опцию, с какой частотой сохранять.
//ибо не всегда нужно, цифровому ваве вполне достаточно 44100Гц.
bool CTape::SaveWaveFile(const CString &strPath)
{
	CFile waveFile;

	if (!waveFile.Open(strPath, CFile::modeCreate | CFile::modeReadWrite))
	{
		return false;
	}

	WAVEFORMATEX wfx;
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = m_nWorkingChannels;
	wfx.nSamplesPerSec = m_nWorkingSSR;
	wfx.wBitsPerSample = BUFFER_BPS;
	wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; //количество байтов на сэмпл
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; //сколько байтов в секунду проиграется
	wfx.cbSize = 0;
	DataHeader dataHeader;
	dataHeader.dataTag = DATA_TAG;
	dataHeader.dataSize = m_nWaveLength * m_nSampleSize;
	WaveHeader waveHeader;
	waveHeader.riffTag = RIFF_TAG;
	waveHeader.size = sizeof(WaveHeader) + sizeof(WAVEFORMATEX) + sizeof(DataHeader) + dataHeader.dataSize;
	waveHeader.waveTag = WAVE_TAG;
	waveHeader.fmtTag = FMT_TAG;
	waveHeader.fmtSize = sizeof(WAVEFORMATEX);
	waveFile.Write(&waveHeader, sizeof(WaveHeader));
	waveFile.Write(&wfx, waveHeader.fmtSize);
	waveFile.Write(&dataHeader, sizeof(DataHeader));
	waveFile.Write(m_pWave, UINT(dataHeader.dataSize));
	return true;
}

//упакоываем каждый сэмпл в битовый массив. 1 сэмпл - 1 бит
void CTape::PackBits(BYTE *pPackBits, int nPackBitsLength)
{
	int pos = 0; // Текущая позиция упакованного байта
	BYTE mask = 1; // Циклическая битовая маска
	ZeroMemory(pPackBits, nPackBitsLength);  //обнуляем массив, чтобы не выставлять вместе с 1 ещё и 0
	//преобразуем частоту дискретизации из текущей в 44100 (по умолчанию)
	ResampleBuffer(m_nWorkingSSR, DEFAULT_SOUND_SAMPLE_RATE);

	for (int i = 0; i < m_nWaveLength; ++i)
	{
		// Берём текущий сэмпл
		if (GetCurrentSampleMono(m_pWave, i) >= m_nAverage) // Если больше нуля, то выставляем 1, иначе пропускаем
		{
			pPackBits[pos] |= mask;
		}

		mask <<= 1; //сдвигаем маску на 1 влево

		if (!mask) //если маска опустела,
		{
			// то байт заполнился, надо дальше заново установить маску и переходить к следующему байту
			mask = 1;
			pos++;
		}
	}
}


void CTape::UnpackBits(BYTE *pPackBits, int nPackBitsLength)
{
	// Выделяем память для распакованных данных, тут уже должно быть 2 канала
	AllocWaveBuffer((nPackBitsLength * 8) * m_nSampleSize);
	m_nWaveLength = 0;

	for (int i = 0; i < nPackBitsLength; ++i)
	{
		BYTE mask = 1;

		// Разжимаем каждый байт в 8 сэмплов
		for (int b = 0; b < 8; ++b)
		{
			//из бита 1 делаем байт MAX_SAMPLE, из бита 0 делаем байт MIN_SAMPLE
			SAMPLE_TYPE sample = (pPackBits[i] & mask) ? MAX_SAMPLE : MIN_SAMPLE;
			SetCurrentSampleMono(m_pWave, m_nWaveLength, sample);
			mask <<= 1; //сдвигаем маску
			m_nWaveLength++;
		}
	}

	//преобразуем частоту дискретизации из 44100 (по умолчанию) в текущую
	ResampleBuffer(DEFAULT_SOUND_SAMPLE_RATE, m_nWorkingSSR);
}


int CTape::PackLZW_Fast(BYTE *pPackBits, int nPackBitsLength, WORD *pPackLZW)
{
	/*
	pPackBits[0] = 'A';
	pPackBits[1] = 'B';
	pPackBits[2] = 'C';
	pPackBits[3] = 'A';
	pPackBits[4] = 'B';
	pPackBits[5] = 'C';
	pPackBits[6] = 'A';
	pPackBits[7] = 'B';
	pPackBits[8] = 'C';
	pPackBits[9] = 'A';
	pPackBits[10] = 'B';
	pPackBits[11] = 'C';
	pPackBits[12] = 'A';
	pPackBits[13] = 'B';
	pPackBits[14] = 'C';
	pPackBits[15] = 'A';
	pPackBits[16] = 'B';
	pPackBits[17] = 'C';
	pPackBits[18] = 'A';
	pPackBits[19] = 'B';
	pPackBits[20] = 'C';
	pPackBits[21] = 'A';
	pPackBits[22] = 'B';
	pPackBits[23] = 'C';
	pPackBits[24] = 'A';
	pPackBits[25] = 'B';
	pPackBits[26] = 'C';
	pPackBits[27] = 'A';
	pPackBits[28] = 'B';
	pPackBits[29] = 'C';
	*/
	// Инициализируем таблицы
	InitTables();
	int nPackLZWLength = 0;
	WORD last_code = BAD_CODE;
	int read_pos;

	for (read_pos = 0; read_pos < nPackBitsLength; ++read_pos)
	{
		int code = TableLookup_Fast(pPackBits, last_code, read_pos);  // Берём текущую последовательность

		if (code == BAD_CODE || read_pos == (nPackBitsLength - 1))
		{
			// Если нет в таблице, или уже конец
			pPackLZW[nPackLZWLength++] = last_code;  // Упаковка: последовательность - 1 байт
			AddWord_Fast(pPackBits, last_code, read_pos);  // Добавление: последовательность в таблицу
			last_code = pPackBits[read_pos];
		}
		else
		{
			last_code = code;
		}
	}

	pPackLZW[nPackLZWLength++] = pPackBits[read_pos - 1]; // Пакуем последний байт как есть
	return nPackLZWLength; // Возвращаем длину сжатого массива
}


void CTape::UnpackLZW_Fast(BYTE *pPackBits, int nPackBitsLength, WORD *pPackLZW, int nPackLZWLength)
{
	// Инициализируем таблицы
	InitTables();
	//Очищаем будущий распакованный массив. я так понимаю, что nPackBitsLength - это размер будущих распакованных данных
	ZeroMemory(pPackBits, nPackBitsLength);
	nPackBitsLength = 0;
	int unpack_pos = 0;
	/*
	m_pPackLZW[0] = 'A';
	m_pPackLZW[1] = 'B';
	m_pPackLZW[2] = 'C';
	m_pPackLZW[3] = 257;
	m_pPackLZW[4] = 259;
	m_pPackLZW[5] = 258;
	m_pPackLZW[6] = 260;
	m_pPackLZW[7] = 263;
	m_pPackLZW[8] = 262;
	m_pPackLZW[9] = 265;
	m_pPackLZW[10] = 261;
	m_pPackLZW[11] = 267;
	*/
	WORD last_code = BAD_CODE;

	for (int read_pos = 0; read_pos < nPackLZWLength; ++read_pos)
	{
		if (pPackLZW[read_pos] < 256) // Если текущий код < 256, распакуем его как есть
		{
			pPackBits[nPackBitsLength++] = BYTE(pPackLZW[read_pos]);
		}
		else
		{
			// Иначе создаём таблику от текущей позиции до позиции этого кода
			while (unpack_pos < nPackBitsLength)
			{
				int code = TableLookup_Fast(pPackBits, last_code, unpack_pos);

				if (code == BAD_CODE)
				{
					// Если нет текущей последовательности в таблице, добавим её
					AddWord_Fast(pPackBits, last_code, unpack_pos);
					last_code = pPackBits[unpack_pos];
				}
				else
				{
					last_code = code;
				}

				unpack_pos++;
			}

			if (!UnpackWord(pPackBits, nPackBitsLength, pPackLZW[read_pos]))  // Пробуем распаковать текущий код
			{
				// Если нет в таблице (случай XиX)
				int first_pos = nPackBitsLength;
				UnpackWord(pPackBits, nPackBitsLength, last_code);  // Распаковываем последний известный код
				pPackBits[nPackBitsLength++] = TableLookup_Fast(pPackBits, BAD_CODE, first_pos);  // Распаковываем последний известный код первого байта
				AddWord_Fast(pPackBits, last_code, unpack_pos);  // Добавление: Xпоследовательность + X в таблицу
				last_code = BAD_CODE;
			}
		}
	}
}


void CTape::InitTables()
{
	for (auto &y : m_pScanTable)
	{
		//т.к. эту функцию вызываем много раз, то выделять память надо только в первый раз
		if (!y)
		{
			y = new WORD[258];

			if (!y)
			{
				CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
			}
		}

		//все остальные разы - только заполнять таблицы начальными данными
		for (int x = 0; x < 258; ++x)
		{
			y[x] = BAD_CODE;
		}
	}

	for (int y = 0; y < 256; ++y)
	{
		m_pScanTable[y][CODE] = y;
	}

	m_nScanTableSize = CODE;
}

int CTape::TableLookup_Fast(BYTE *pPackBits, WORD prefix, int end_pos)
{
	if (prefix == BAD_CODE)
	{
		return pPackBits[end_pos];
	}

	return m_pScanTable[prefix][pPackBits[end_pos]];
}


void CTape::AddWord_Fast(BYTE *pPackBits, WORD prefix, int end_pos)
{
	//если размер таблицы превысил все допустимые рамки
	if (m_nScanTableSize >= MAX_TABLE_SIZE)
	{
		//обнулим его, и начнем заполнять сначала
		InitTables();
		return;
	}

	m_pScanTable[prefix][pPackBits[end_pos]] = m_nScanTableSize;
	m_pScanTable[m_nScanTableSize][PREFIX] = prefix;
	m_pScanTable[m_nScanTableSize][CODE] = pPackBits[end_pos];
	m_nScanTableSize++;
}


bool CTape::UnpackWord(BYTE *pPackBits, int &nPackBitsLength, WORD code)
{
	if (code == BAD_CODE)
	{
		return false;
	}

	if (code < 256)
	{
		pPackBits[nPackBitsLength++] = LOBYTE(code);
		return true;
	}

	if (!UnpackWord(pPackBits, nPackBitsLength, m_pScanTable[code][PREFIX]))
	{
		return false;
	}

	pPackBits[nPackBitsLength++] = (BYTE)m_pScanTable[code][CODE];
	return true;
}


bool CTape::LoadMSFFile(const CString &strPath)
{
	CMSFManager msf;

	if (!msf.OpenFile(strPath, true))
	{
		return false;
	}

	DWORD nPackBitsLength, nPackLZWLength;

	if (!msf.GetBlockTape(nullptr, &nPackBitsLength, &nPackLZWLength))
	{
		return false;
	}

	bool bRet = false;
	auto pPackBits = new BYTE[nPackBitsLength];
	auto pPackLZW = new WORD[nPackLZWLength];

	if (pPackBits && pPackLZW)
	{
		bRet = msf.GetBlockTape(reinterpret_cast<BYTE *>(pPackLZW), &nPackBitsLength, &nPackLZWLength);

		if (bRet)
		{
			UnpackLZW_Fast(pPackBits, nPackBitsLength, pPackLZW, nPackLZWLength / 2);
			UnpackBits(pPackBits, nPackBitsLength);
			// Calculate average
			CalculateAverage();
		}

		//чистим память за собой
		SAFE_DELETE_ARRAY(pPackBits);
		SAFE_DELETE_ARRAY(pPackLZW);
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	return bRet;
}


bool CTape::SaveMSFFile(const CString &strPath)
{
	CMSFManager msf;

	if (!msf.OpenFile(strPath, false))
	{
		return false;
	}

	bool bRet = false;
	//получим длину в битах из длины в сэмплах.
	int nPackBitsLength = (m_nWaveLength + 7) >> 3;
	auto pPackBits = new BYTE[nPackBitsLength];
	auto pPackLZW = new WORD[nPackBitsLength];

	if (pPackBits && pPackLZW)
	{
		PackBits(pPackBits, nPackBitsLength); //пакуем биты один сэмпл умещается в один бит
		int nPackLZW = PackLZW_Fast(pPackBits, nPackBitsLength, pPackLZW) * 2;
		bRet = msf.SetBlockTape(reinterpret_cast<BYTE *>(pPackLZW), nPackBitsLength, nPackLZW);
		//чистим память за собой
		SAFE_DELETE_ARRAY(pPackBits);
		SAFE_DELETE_ARRAY(pPackLZW);
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	return bRet;
}

bool CTape::GetWaveFile(TAPE_FILE_INFO *pTfi, bool bHeaderOnly)
{
	if (!m_pWave)
	{
		return false;
	}

	m_nPos = 0;
	m_nLastLen = 0;
	memset(pTfi, 255, sizeof(TAPE_FILE_INFO));
	int nLength = 0;

	if (!FindTuning(1024, pTfi->start_tuning, nLength))
	{
		return false;
	}

	pTfi->synchro_start = m_nPos;

	if (!FindSyncro())
	{
		return false;
	}

	if (!FindTuning(7, pTfi->marker1, nLength))
	{
		return false;
	}

	pTfi->synchro_header = m_nPos;

	if (!FindSyncro())
	{
		return false;
	}

	pTfi->header = m_nPos;

	for (int i = 0; i < 20; ++i)
	{
		if (!ReadByte(((BYTE *)&pTfi->address)[i]))
		{
			return false;
		}
	}

	if (!FindTuning(7, pTfi->marker2, nLength))
	{
		return false;
	}

	pTfi->synchro_data = m_nPos;

	if (!FindSyncro())
	{
		return false;
	}

	if (bHeaderOnly)
	{
		return true;
	}

	SAFE_DELETE_ARRAY(m_pBin);
	m_pBin = new BYTE[pTfi->length];
	pTfi->data = m_nPos;

	for (int i = 0; i < pTfi->length; ++i)
	{
		if (!ReadByte(m_pBin[i]))
		{
			return false;
		}
	}

	if (!ReadByte(((BYTE *)&pTfi->crc)[0]))
	{
		return false;
	}

	if (!ReadByte(((BYTE *)&pTfi->crc)[1]))
	{
		return false;
	}

	pTfi->synchro6 = m_nPos;

	if (!FindSyncro6())
	{
		return false;
	}

	FindTuning(128, pTfi->end_tuning, nLength);
	pTfi->end = pTfi->end_tuning + nLength;
	return true;
}


bool CTape::FindTuning(int nLength, DWORD &wave_pos, int &wave_length)
{
	int syncro_pos = BAD_LENGTH;
	int last_pos = m_nPos;
	int imp_num = 0;
	int begin_pos = m_nPos;
	bool bFound = false;
	m_nAvgLength = 0;

	for (;;)
	{
		last_pos = m_nPos;
		int len = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

		if (len == BAD_LENGTH)
		{
			m_nPos = last_pos;
			return false;
		}

		register int a = (m_nLastLen - len);
		register int b = 3;

		if (a * a <= b * b)
		{
			m_nAvgLength += len;
			imp_num++;

			if (imp_num >= nLength && !bFound)
			{
				syncro_pos = begin_pos;
				bFound = true;
			}
		}
		else if (!bFound)
		{
			m_nAvgLength = 0;
			imp_num = 0;
			begin_pos = m_nPos;
		}
		else
		{
			break;
		}

		m_nLastLen = len;
		//TRACE ("\nWave pos %06i, Start = %i, Len = %i, Imp_Num = %i", m_nPos, begin_pos, len, imp_num);
		//Sleep (1);
	}

	m_nPos = last_pos;
	wave_pos = syncro_pos;
	wave_length = m_nPos - syncro_pos;
	m_nAvgLength = (m_nAvgLength + imp_num / 2) / imp_num;
	/*
	for (int i = 0; i < 100; ++i)
	{
	    int len = CalcImpLength (m_pWave, m_nPos, m_nWaveLength);
	    TRACE ("\nAfter Wave pos %06i, Start = %i, Len = %i, Imp_Num = %i", m_nPos, begin_pos, len, imp_num);
	    Sleep (1);
	}
	/*/
	return true;
}


bool CTape::FindSyncro()
{
	int size = 0;
	int nLength = 0;

	for (;;)
	{
		nLength = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

		if (nLength == BAD_LENGTH)
		{
			return false;
		}

		size = DefineLength(nLength);

		if (size >= 3)
		{
			break;
		}
	}

	int nSyncLen = 2;

	while (nSyncLen > 0)
	{
		nLength = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

		if (nLength == BAD_LENGTH)
		{
			return false;
		}

		size = DefineLength(nLength);

		if (size == nSyncLen)
		{
			nSyncLen--;
		}
	}

	return true;
}


bool CTape::FindSyncro6()
{
	int nLength = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

	if (nLength == BAD_LENGTH)
	{
		return false;
	}

	int size = DefineLength(nLength);

	if (size < 6)
	{
		return false;
	}

	return true;
}


bool CTape::ReadBit(bool &bBit)
{
	int length0 = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

	if (length0 == BAD_LENGTH)
	{
		return false;
	}

	int size0 = DefineLength(length0);
	int length1 = CalcImpLength(m_pWave, m_nPos, m_nWaveLength);

	if (length1 == BAD_LENGTH)
	{
		return false;
	}

	int size1 = DefineLength(length1);

	//if (size1 != 1)
	//  return false;
	if (size0 == 1)
	{
		bBit = false;
	}
	else if (size0 == 2)
	{
		bBit = true;
	}
	else
	{
		return false;
	}

	return true;
}


bool CTape::ReadByte(BYTE &byte)
{
	BYTE mask = 1;
	byte = 0;
	bool bBit;

	for (int i = 0; i < 8; ++i)
	{
		if (!ReadBit(bBit))
		{
			return false;
		}

		if (bBit)
		{
			byte |= mask;
		}

		mask <<= 1;
	}

	return true;
}

//на входе: pWave - указатель на массив, pos - позиция в сэмплах
SAMPLE_TYPE CTape::GetCurrentSampleMono(register SAMPLE_TYPE *pWave, register int pos)
{
	register int chan = 0; //счётчик каналов
	register int n = pos * m_nWorkingChannels;
	register double s = 0.0;

	do
	{
		s += pWave[n + chan];
	}
	while (++chan < m_nWorkingChannels);

	return SAMPLE_TYPE(s / m_nWorkingChannels); //выдаём среднее арифметическое
}

//на входе: pWave - указатель на массив, pos - позиция в сэмплах
void CTape::SetCurrentSampleMono(SAMPLE_TYPE *pWave, int pos, SAMPLE_TYPE sample)
{
	register int chan = 0; //счётчик каналов
	register int n = pos * m_nWorkingChannels;

	do
	{
		pWave[n + chan] = sample; //размножаем сэмпл по каналам
	}
	while (++chan < m_nWorkingChannels);
}

//////////////////////////////////////////////////////////////////////
//pos - позиция в сэмплах
//nLength - длина в сэмплах
int CTape::CalcImpLength(SAMPLE_TYPE *pWave, int &pos, int nLength)
{
	if (pos >= nLength)
	{
		return BAD_LENGTH;
	}

	// Пропускаем инверсные импульсы
	while (GetCurrentSampleMono(pWave, pos) < m_nAverage)
	{
		if (pos >= nLength)
		{
			return BAD_LENGTH;
		}

		pos++;
	}

	int len_1 = 0;

	// Считаем количество единиц
	while (GetCurrentSampleMono(pWave, pos) >= m_nAverage)
	{
		if (pos >= nLength)
		{
			return BAD_LENGTH;
		}

		len_1++;
		pos++;
	}

	int len_0 = 0;

	// Считаем количество нулей
	while (GetCurrentSampleMono(pWave, pos) < m_nAverage)
	{
		if (pos >= nLength)
		{
			return BAD_LENGTH;
		}

		len_0++;
		pos++;
	}

	return len_1 + len_0;
}


int CTape::DefineLength(int nLength)
{
	return (nLength + m_nAvgLength / 2) / m_nAvgLength;
}


bool CTape::CalcCRC(TAPE_FILE_INFO *pTfi, bool bCheck)
{
	if (!m_pBin)
	{
		return false;
	}

	int crc = 0;

	for (int i = 0; i < pTfi->length; ++i)
	{
		crc += m_pBin[i];

		if (crc & 0xFFFF0000)
		{
			//если случился перенос в 17 разряд (т.е. бит С для word)
			crc &= 0x0000FFFF; //его обнулим
			crc++; //но прибавим к сумме
		}
	}

	if (bCheck)
	{
		if (crc != pTfi->crc)
		{
			return false;
		}
	}
	else
	{
		pTfi->crc = LOWORD(crc);
	}

	return true;
}


bool CTape::LoadBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi)
{
	CFile binFile;

	if (!binFile.Open(strPath, CFile::modeRead))
	{
		return false;
	}

	//читаем первое слово - адрес загрузки
	if (binFile.Read(&pTfi->address, sizeof(WORD)) != sizeof(WORD))
	{
		return false;
	}

	//читаем второе слово, длину.
	if (binFile.Read(&pTfi->length, sizeof(WORD)) != sizeof(WORD))
	{
		return false;
	}

	//проверим настоящий бин файл или просто расширение совпало.
	if (pTfi->length != binFile.GetLength() - 4) //если не простой бин
	{
		if (pTfi->length != binFile.GetLength() - 22)   //и не сложный
		{
			return false;
		}
		else
		{
			//а если сложный, надо пропустить немного ненужных данных
			binFile.Seek(20, CFile::begin);
		}
	}

	SAFE_DELETE_ARRAY(m_pBin);
	m_pBin = new BYTE[pTfi->length];

	if (!m_pBin)
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}

	if (binFile.Read(m_pBin, pTfi->length) != pTfi->length)
	{
		return false;
	}

	CString strName = g_Config.GetFileName(strPath);
	CString strExt = g_Config.GetFileExt(strPath);

	if (!strExt.CompareNoCase(CString(MAKEINTRESOURCE(IDS_FILEEXT_BINARY))))
	{
		strName = g_Config.GetFileTitle(strPath);
	}

	//кроме всего этого, тут надо отлавливать бейсиковские файлы. у них свой формат имени.
	//6 символов - имя (пустое место забивается пробелами), потом .COD, .BIN, .ASC
	//а потом конец забивается нулями, а не пробелами.
	// .ASC обрабатывать не будем. .ASC нормальные люди не используют
	strExt = g_Config.GetFileExt(strName).MakeUpper();

	if (strExt == _T(".COD")) //если это бейсиковский .COD
	{
		memset(pTfi->name, 0, 16);
		strName = g_Config.GetFileTitle(strName);
		//преобразуем юникодную строку в бкашную
		UNICODEtoBK(strName, pTfi->name, 6, true);
		pTfi->name[6] = '.';
		pTfi->name[7] = 'C';
		pTfi->name[8] = 'O';
		pTfi->name[9] = 'D';
	}
	else if (strExt == _T(".BIN")) //если это бейсиковский .BIN
	{
		memset(pTfi->name, 0, 16);
		strName = g_Config.GetFileTitle(strName);
		//преобразуем юникодную строку в бкашную
		UNICODEtoBK(strName, pTfi->name, 6, true);
		pTfi->name[6] = '.';
		pTfi->name[7] = 'B';
		pTfi->name[8] = 'I';
		pTfi->name[9] = 'N';
	}
	else
	{
		//преобразуем юникодную строку в бкашную
		UNICODEtoBK(strName, pTfi->name, 16, true);
	}

	SAFE_DELETE_MEMORY(m_pWave); //m_pWave = 0
	m_nPos = 0; //тут будет формироваться длина

	if (!SetWaveFile(pTfi)) //рассчитываем размер массива
	{
		return false;
	}

	AllocWaveBuffer(m_nPos * m_nSampleSize); //выделяем память под массив
	m_nPos = 0;

	if (!SetWaveFile(pTfi)) //и теперь генерируем файл по настоящему.
	{
		return false;
	}

	//посчитаем средний уровень проще. массив-то полностью цифровой
	m_nAverage = AVG_SAMPLE;
	return true;
}


bool CTape::SaveBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi)
{
	if (!m_pBin)
	{
		return false;
	}

	CFile binFile;

	if (!binFile.Open(strPath, CFile::modeCreate | CFile::modeWrite))
	{
		return false;
	}

	binFile.Write(&pTfi->address, sizeof(WORD));
	binFile.Write(&pTfi->length, sizeof(WORD));
	binFile.Write(m_pBin, pTfi->length);
	return true;
}


bool CTape::SetWaveFile(TAPE_FILE_INFO *pTfi)
{
	m_nPos = 0;
	m_nAvgLength = int(17 * m_dWorkingResCoeff) | 1;
	ASSERT(m_nAvgLength & 1);

	if (!SaveTuning(4096))
	{
		return false;
	}

	if (!SaveSyncro())
	{
		return false;
	}

	if (!SaveTuning(8))
	{
		return false;
	}

	if (!SaveSyncro())
	{
		return false;
	}

	for (int i = 0; i < 20; ++i)
	{
		if (!SaveByte(((BYTE *)&pTfi->address)[i]))
		{
			return false;
		}
	}

	if (!SaveTuning(8))
	{
		return false;
	}

	if (!SaveSyncro())
	{
		return false;
	}

	for (int i = 0; i < pTfi->length; ++i)
	{
		if (!SaveByte(m_pBin[i]))
		{
			return false;
		}
	}

	CalcCRC(pTfi, false);

	if (!SaveByte(((BYTE *)&pTfi->crc)[0]))
	{
		return false;
	}

	if (!SaveByte(((BYTE *)&pTfi->crc)[1]))
	{
		return false;
	}

	if (!SaveSyncro6())
	{
		return false;
	}

	if (!SaveTuning(256))
	{
		return false;
	}

	return true;
}


bool CTape::SaveTuning(int nLength)
{
	for (int i = 0; i < nLength; ++i)
	{
		if (!SaveImp(1))
		{
			return false;
		}
	}

	return true;
}


bool CTape::SaveSyncro()
{
	if (!SaveImp(3))
	{
		return false;
	}

	if (!SaveImp(2))
	{
		return false;
	}

	if (!SaveImp(1))
	{
		return false;
	}

	return true;
}


bool CTape::SaveSyncro6()
{
	if (!SaveImp(12))
	{
		return false;
	}

	return true;
}


bool CTape::SaveImp(int size)
{
	if (m_pWave)
	{
		for (int i = 0; i < size * (m_nAvgLength / 2 + 1); ++i)
		{
			if (m_nPos >= m_nWaveLength)
			{
				return false;
			}

			SetCurrentSampleMono(m_pWave, m_nPos++, MAX_SAMPLE);
		}

		for (int i = 0; i < size * (m_nAvgLength / 2); ++i)
		{
			if (m_nPos >= m_nWaveLength)
			{
				return false;
			}

			SetCurrentSampleMono(m_pWave, m_nPos++, MIN_SAMPLE);
		}
	}
	else
	{
		m_nPos += size * m_nAvgLength;
	}

	return true;
}


bool CTape::SaveBit(bool bBit)
{
	if (bBit)
	{
		if (!SaveImp(2))
		{
			return false;
		}
	}
	else
	{
		if (!SaveImp(1))
		{
			return false;
		}
	}

	if (!SaveImp(1))
	{
		return false;
	}

	return true;
}


bool CTape::SaveByte(BYTE byte)
{
	BYTE mask = 1;

	for (int i = 0; i < 8; ++i)
	{
		if (!SaveBit((byte & mask) != 0))
		{
			return false;
		}

		mask <<= 1;
	}

	return true;
}


//////////////////////////////////////////////////////////////////////
//на входе, nSize - размер в байтах
bool CTape::AllocWaveBuffer(int nSize)
{
	if (m_pWave == nullptr || m_nWaveLength * m_nSampleSize < nSize)
	{
		SAFE_DELETE_MEMORY(m_pWave);
		m_pWave = (SAMPLE_TYPE *)malloc(nSize);

		if (!m_pWave)
		{
			m_nWaveLength = 0;
			return false;
		}
	}

	m_nWaveLength = nSize / m_nSampleSize;
	m_nAverage = MIN_SAMPLE;
	memset(m_pWave, 0, nSize);
	return true;
}


bool CTape::LoadBuffer(BYTE *pBuff, int nSize)
{
	if (!AllocWaveBuffer(nSize))
	{
		return false;
	}

	memcpy(m_pWave, pBuff, nSize);
	CalculateAverage();
	return true;
}


int CTape::GetPlayWavePos()
{
	return m_nPlayPos;
}

void CTape::SetPlayWavePos(int play_pos)
{
	m_nPlayPos = play_pos;
}


/*
вход: pBuff - буфер, куда передаётся tape, формат данных в буфере - 16бит 2 канала
buffLength - размер буфера в байтах.
*/
void CTape::PlayWaveGetBuffer(BYTE *pBuff, int buffLength)
{
	register int nSmplBuffLen = buffLength / m_nSampleSize; //количество сэмплов, которое поместится в выходном буфере.

	//m_nWaveLength и m_nPlayPos - измеряются в сэмплах
	if (m_lockPlay.IsLocked())
	{
		register int nLength = m_nWaveLength - m_nPlayPos;

		if (nLength > nSmplBuffLen) //если длина больше чем буфер,
		{
			nLength = nSmplBuffLen;    // уменьшим до длины буфера
		}

		register int nByteLength = nLength * m_nSampleSize;
		memcpy(pBuff, reinterpret_cast<BYTE *>(m_pWave) + (m_nPlayPos * m_nSampleSize), nByteLength);
		m_nPlayPos += nLength;

		// но если nLength < nSmplBuffLen, остаток буфера надо добить нулями
		if (nByteLength < buffLength)
		{
			memset(pBuff + nByteLength, 0, (buffLength - nByteLength));
		}

		if (m_nPlayPos >= m_nWaveLength)
		{
			StopPlay();
		}
	}
}

/*
вход: pBuff - буфер, откуда берётся tape, формат данных в буфере - 16 бит 2 канала
nBufLen - размер буфера в байтах.
*/
void CTape::RecordWaveGetBuffer(BYTE *pBuff, int nBufLen)
{
	if (!m_lockRecord.IsLocked())
	{
		return;
	}

	//посчитаем размер буфера в сэмплах
	register int nSmplBufLen = nBufLen / m_nSampleSize;

	//если место в буфере кончается, надо сделать новый буфер, побольше, и данные из старого скопировать в новый
	while (m_nRecordPos + nSmplBufLen >= m_nRecordLength)
	{
		m_nRecordLength += RECORD_GROW;
		m_pRecord = (SAMPLE_TYPE *)realloc(m_pRecord, m_nRecordLength * m_nSampleSize);
		ASSERT(m_pRecord);
	}

	memcpy(m_pRecord + m_nRecordPos * m_nWorkingChannels, pBuff, nBufLen);
	/*
	Ещё тут надо высчитывать m_fAverage, потому что функция FindRecordBegin использует это значение
	причём, среднее значение в пределах заданного буфера вполне подойдёт.
	*/
	auto pSrcPos = reinterpret_cast<SAMPLE_TYPE *>(pBuff);
	double avg = 0.0;

	for (register int i = 0; i < nSmplBufLen; ++i)
	{
		avg += double(GetCurrentSampleMono(pSrcPos, i));
	}

	m_nAverage = SAMPLE_TYPE(avg / nSmplBufLen);

	if (FindRecordEnd())
	{
		StopRecord();
	}

	if (FindRecordBegin(nSmplBufLen))  // Если нашли точку начала записи
	{
		m_nRecordPos += nSmplBufLen;
	}
}

void CTape::StartPlay()
{
	m_nPos = 0;
	m_lockPlay.Lock();
}

void CTape::StopPlay()
{
	m_lockPlay.UnLock();
}

bool CTape::IsPlaying()
{
	return m_lockPlay.IsLocked() != 0;
}


void CTape::StartRecord(bool bAutoBeginRecord, bool bAutoEndRecord)
{
	m_bAutoBeginRecord = bAutoBeginRecord;
	m_bAutoEndRecord = bAutoEndRecord;
	m_nAverage = MIN_SAMPLE;
	m_nRecordPos = 0;
	m_nLastLen = BAD_LENGTH;
	SAFE_DELETE_MEMORY(m_pRecord);
	m_pRecord = (SAMPLE_TYPE *)malloc(RECORD_BUFFER * m_nSampleSize); //создаём новый массив длиной RECORD_BUFFER сэмплов, каждый сэмпл - m_nWorkingChannels * sizeof(SAMPLE_TYPE)
	ZeroMemory(m_pRecord, RECORD_BUFFER * m_nSampleSize);
	m_nRecordLength = RECORD_BUFFER; //длина массива в сэмплах
	m_lockRecord.Lock();
}


void CTape::StopRecord()
{
	if (m_lockRecord.IsLocked())
	{
		m_lockRecord.UnLock();
		SAFE_DELETE_MEMORY(m_pWave);
		m_pWave = m_pRecord;
		m_pRecord = nullptr;
		m_nWaveLength = m_nRecordPos;
		m_nRecordPos = 0;
		CalculateAverage();
	}
}

bool CTape::IsRecording()
{
	return m_lockRecord.IsLocked() != 0;
}


//////////////////////////////////////////////////////////////////////
//nSmplBufLen - длина буфера в сэмплах
bool CTape::FindRecordBegin(int nSmplBufLen)
{
	if (m_bAutoBeginRecord)
	{
		m_nRecordPos = 0;
		register int imp_num = 0;

		for (;;)
		{
			register int len = CalcImpLength(m_pRecord, m_nRecordPos, nSmplBufLen);

			//тут len - в сэмплах
			if (len == BAD_LENGTH)
			{
				m_nRecordPos = 0;
				return false;
			}

			//без возведения в квадрат логически правильнее, т.к. отрицательное значение тоже надо учитывать
			register int a = m_nLastLen - len;
			register int b = int(3 * m_dWorkingResCoeff);

			//if (a <= b)   //вот не помню, откуда у меня брались отрицательные значения
			if (a * a <= b * b)
			{
				if (++imp_num >= int(50 * m_dWorkingResCoeff))
				{
					break;
				}
			}
			else
			{
				imp_num = 0;
			}

			m_nLastLen = len;
			//TRACE ("\nWave pos %06i, Start = %i, Len = %i, Imp_Num = %i", m_nPos, begin_pos, len, imp_num);
			//Sleep (1);
		}

		m_nRecordPos = 0;
		m_bAutoBeginRecord = false;
	}

	return true;
}


bool CTape::FindRecordEnd()
{
	if (m_bAutoBeginRecord)
	{
		return false;
	}

	if (m_bAutoEndRecord)
	{
		register SAMPLE_TYPE last_sample = -1;
		register int last_length = 0;
		register int nEnd = m_nRecordPos + m_nWorkingSSR / CPU_FRAMERATE;

		for (register int i = m_nRecordPos; i < nEnd; ++i)
		{
			register SAMPLE_TYPE sample = GetCurrentSampleMono(m_pRecord, i);

			if (sample == last_sample)
			{
				last_length++;
			}
			else
			{
				last_sample = sample;
				last_length = 0;
			}

			if (last_length == 500)
			{
				m_bAutoEndRecord = false;
				return true;
			}
		}
	}

	return false;
}
