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

/*
File: Tape.h
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.h: interface for the CTape class.
//


#pragma once

#include "Defines.h"
#include "LockVarType.h"

#include <mmsystem.h>

//"RIFF"
#define RIFF_TAG 0x46464952
// "fmt "
#define FMT_TAG 0x20746d66
// "data"
#define DATA_TAG 0x61746164
// "WAVE"
#define WAVE_TAG 0x45564157

#pragma pack(push)
#pragma pack(1) // envelope period (envT, R13-R14) has invalid word alignment
struct TAPE_FILE_INFO
{
	DWORD               start_tuning;
	DWORD               synchro_start;
	DWORD               marker1;
	DWORD               synchro_header;
	DWORD               header;
	WORD                address;
	WORD                length;
	BYTE                name[16];
	DWORD               marker2;
	DWORD               synchro_data;
	DWORD               data;
	WORD                crc;
	DWORD               synchro6;
	DWORD               end_tuning;
	DWORD               end;
};
#pragma pack(pop)

struct WaveHeader
{
	DWORD               riffTag;
	DWORD               size;
	DWORD               waveTag;
	DWORD               fmtTag;
	DWORD               fmtSize;
};

struct DataHeader
{
	DWORD               dataTag;
	DWORD               dataSize;
};

#define MAX_TABLE_SIZE 65000
#define RECORD_BUFFER (4 * 1024 * 1024)
#define RECORD_GROW (2 * 1024 * 1024)
#define BAD_CODE 65535
#define RESET_CODE 256
#define PREFIX 256
#define CODE 257
#define BAD_LENGTH -1

class CTape
{
		int             m_nWorkingSSR;      //рабочая частота дискретизации
		int             m_nWorkingChannels; //рабочее количество каналов, битность всегда 16
		int             m_nSampleSize;      //размер сэмпла. для текущих значений
		double          m_dWorkingResCoeff; //коэффициент для ресемплинга из 44100 в текущую частоту

		LockVarType     m_lockPlay;
		SAMPLE_TYPE    *m_pWave;            //собственно массив wave.
		int             m_nWaveLength;      //размер массива m_pWave в сэмплах
		int             m_nPlayPos;         //текущая позиция воспроизведения в сэмплах

		LockVarType     m_lockRecord;       //флаг записи
		SAMPLE_TYPE    *m_pRecord;          //массив, куда записывается звук
		int             m_nRecordPos;       //текущая позиция в массиве в сэмплах
		int             m_nRecordLength;    //длина массива в сэмплах

		bool            m_bAutoBeginRecord;
		bool            m_bAutoEndRecord;

		BYTE           *m_pBin;             //массив бинарных данных, в которые преобразуются wav и tap
		int             m_nPos;
		SAMPLE_TYPE     m_nAverage;
		int             m_nLastLen;
		int             m_nAvgLength;

		WORD           *m_pScanTable[MAX_TABLE_SIZE]; //массив для упаковки/распаковки
		int             m_nScanTableSize;
		//функции упаковки/распаковки wav в tap
		void            InitTables();
		void            ClearTables();
		int             TableLookup_Fast(BYTE *pPackBits, WORD prefix, int end_pos);
		void            AddWord_Fast(BYTE *pPackBits, WORD prefix, int end_pos);
		bool            UnpackWord(BYTE *pPackBits, int &nPackBitsLength, WORD code);
		//функции преобразования модулированного wav в bin
		bool            FindTuning(int nLength, DWORD &wave_pos, int &wave_length);
		bool            FindSyncro();
		bool            FindSyncro6();
		int             CalcImpLength(SAMPLE_TYPE *pWave, int &pos, int length);
		inline SAMPLE_TYPE GetCurrentSampleMono(register SAMPLE_TYPE *pWave, register int pos);
		void            SetCurrentSampleMono(SAMPLE_TYPE *pWave, int pos, SAMPLE_TYPE sample);
		int             DefineLength(int length);
		bool            ReadBit(bool &bBit);
		bool            ReadByte(BYTE &byte);
		//функции преобразования bin в модулированный wav
		bool            SaveTuning(int length);
		bool            SaveSyncro();
		bool            SaveSyncro6();
		bool            SaveImp(int size);
		bool            SaveBit(bool bBit);
		bool            SaveByte(BYTE byte);

		int             ConvertSamples(WAVEFORMATEX wfx_in, BYTE *inBuf, int nBufSize, int nDataLen);
		void            ResampleBuffer(int nSrcSSR, int nDstSSR);
		bool            FindRecordBegin(int buffLength);
		bool            FindRecordEnd();
		void            CalculateAverage();

	public:
		CTape();
		virtual ~CTape();
		void            SetWaveParam(int nWorkingSSR = DEFAULT_SOUND_SAMPLE_RATE, int nWorkingChn = BUFFER_CHANNELS);
		int             GetWorkingSSR()
		{
			return m_nWorkingSSR;
		}
		int             GetWorkingChannels()
		{
			return m_nWorkingChannels;
		}
		// Wave pack/unpack methods
		void            PackBits(BYTE *pPackBits, int nPackBitsLength);
		void            UnpackBits(BYTE *pPackBits, int nPackBitsLength);

		int             PackLZW_Fast(BYTE *pPackBits, int nPackBitsLength, WORD *pPackLZW);
		void            UnpackLZW_Fast(BYTE *pPackBits, int nPackBitsLength, WORD *pPackLZW, int nPackLZWLength);

		// Wave read/write methods
		bool            SetWaveFile(TAPE_FILE_INFO *pTfi);
		bool            GetWaveFile(TAPE_FILE_INFO *pTfi, bool bHeaderOnly = false);
		bool            CalcCRC(TAPE_FILE_INFO *pTfi, bool bCheck);

		bool            LoadWaveFile(const CString &strPath);
		bool            SaveWaveFile(const CString &strPath);

		bool            LoadBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi);
		bool            SaveBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi);

		bool            LoadMSFFile(const CString &strPath);
		bool            SaveMSFFile(const CString &strPath);

		bool            LoadTmpFile(const CString &strPath);

		// Wave buffer methods
		bool            AllocWaveBuffer(int nSize);
		bool            LoadBuffer(BYTE *pBuff, int nSize);
		inline int      GetWaveLength()
		{
			return m_nWaveLength;
		}
		inline BYTE    *GetWaveBuffer()
		{
			return reinterpret_cast<BYTE *>(m_pWave);
		}

		// Playing methods
		int             GetPlayWavePos();
		void            SetPlayWavePos(int play_pos);
		void            PlayWaveGetBuffer(BYTE *pBuff, int buffLength);

		void            StartPlay();
		void            StopPlay();
		bool            IsPlaying();
		void            StartRecord(bool bAutoBeginRecord, bool bAutoEndRecord);
		void            RecordWaveGetBuffer(BYTE *pBuff, int buffLength);
		void            StopRecord();
		bool            IsRecording();
};
