/*************************************************
*           BK Emulator v3.9 for Windows         *
*************************************************/

/*
File: Tape.cpp
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


// 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"


#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;
		//определим количество сэмплов в результирующем сигнале
		int 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]; //входной буфер в байтах
		int converted;

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

		delete [] inBuf;
	}

	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]; //входной буфер в байтах
	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;
	}

	UINT 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];
		}

		//все остальные разы - только заполнять таблицы начальными данными
		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);
	}

	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);
	}

	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 (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;
}
