#include #include #include #include #include "CreamTrackerEngine.h" #ifdef CreamTrackerCode #ifndef CreamTrackerTRICode #include "trackdata.h" #endif #endif #define SampleRate 44100 #define SynthBufferSize 8192 typedef cte_float_t TSynthEndBuffer[SynthBufferSize][2]; typedef TSynthEndBuffer *PSynthEndBuffer; TSynthEndBuffer Buffers[4]; HWAVEOUT WaveOutHandle; DWORD BufferCounter = 0; WAVEFORMATEX WaveFormat = { .wFormatTag = 1, .nChannels = 2, .nSamplesPerSec = SampleRate, .nAvgBytesPerSec = SampleRate * 2 * 2, .nBlockAlign = 2 * 2, .wBitsPerSample = 2 * 8, .cbSize = 0 }; WAVEHDR WaveHandler[4] = { { .lpData = (cte_pointer)(&(Buffers[0])), .dwBufferLength = sizeof(TSynthEndBuffer) >> 1, .dwBytesRecorded = 0, .dwUser = 0, .dwFlags = WHDR_DONE, .dwLoops = 0 }, { .lpData = (cte_pointer)(&(Buffers[1])), .dwBufferLength = sizeof(TSynthEndBuffer) >> 1, .dwBytesRecorded = 0, .dwUser = 0, .dwFlags = WHDR_DONE, .dwLoops = 0 }, { .lpData = (cte_pointer)(&(Buffers[2])), .dwBufferLength = sizeof(TSynthEndBuffer) >> 1, .dwBytesRecorded = 0, .dwUser = 0, .dwFlags = WHDR_DONE, .dwLoops = 0 }, { .lpData = (cte_pointer)(&(Buffers[3])), .dwBufferLength = sizeof(TSynthEndBuffer) >> 1, .dwBytesRecorded = 0, .dwUser = 0, .dwFlags = WHDR_DONE, .dwLoops = 0 }, }; cte_pointer Data; cte_int32_t DataSize; TCreamTrackerInstance Instance; cte_uchar_t *CommandLine; cte_uchar_t* SkipParamStrEx(cte_uchar_t *p, cte_uchar_t **PStart, cte_uchar_t **PEnd) { while (1) { while (*p && (*p <= ' ')) { p++; } if ((*p == '"') && (p[1] == '"')) { p++; } else { break; } } while (*p > ' ') { if (*p == '"') { p++; while (*p && (*p != '"')) { if (!(*PStart)) { *PStart = p; } *PEnd = p; p++; } if (*p) { p++; } } else { if (!(*PStart)) { *PStart = p; } *PEnd = p; p++; } } return p; } cte_uchar_t* SkipParamStr(cte_uchar_t *p) { cte_uchar_t *PStart, *PEnd; PStart = NULL; PEnd = NULL; p = SkipParamStrEx(p, &PStart, &PEnd); while (*p && (*p <= ' ')) { p++; } return p; } cte_int32_t ParamCount() { cte_uchar_t* p; cte_int32_t c = -1; p = CommandLine; while (p && *p) { p = SkipParamStr(p), c++; } return c; } cte_uchar_t* ParamStr(cte_int32_t ParamNr) { cte_uchar_t *p, *PStart, *PEnd, *result; cte_int32_t c, l, i; result = NULL; c = -1; p = CommandLine; while (p && *p) { if ((c + 1) == ParamNr) { PStart = NULL; PEnd = NULL; SkipParamStrEx(p, &PStart, &PEnd); if (PStart && PEnd) { l = (PEnd - PStart) + 1; if (l > 0) { result = CreamTrackerGetMem(l + 1); for (i = 0; i < l; i++) { result[i] = PStart[i]; } result[l] = 0; } } break; } p = SkipParamStr(p), c++; } return result; } #pragma pack(push,1) typedef struct { cte_uchar_t RIFF[4]; cte_uint32_t Size; cte_uchar_t WAVE[4]; cte_uchar_t fmt[4]; cte_uint32_t Dummy; cte_uint16_t Format; cte_uint16_t Channels; cte_uint32_t Sample_Rate; cte_uint32_t SamplesPerSecond; cte_uint16_t BytesPerSample; cte_uint16_t Bits; cte_uchar_t Data[4]; cte_uint32_t DataSize; } TWAVHeader; #pragma pack(pop) const TWAVHeader WAVHeader = { 'R', 'I', 'F', 'F' , 36, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' ', 16, 1, 2, SampleRate, SampleRate * 4, 4, 16, 'd', 'a', 't', 'a', 0 }; HANDLE ConsoleOutHandle; void Print(cte_uchar_t *s) { cte_uchar_t *p = s; cte_uint32_t l = 0; if (p) { while (*p++) { l++; } } WriteFile(ConsoleOutHandle, s, l, NULL, NULL); } void roundDown(cte_pointer sp){ cte_float_p sfs = sp; cte_int16_p sis = sp; for(cte_int32_t i = SynthBufferSize * 2; i > 0 ; i--) { cte_int32_t j = (cte_int32_t)CreamTrackerRound(*sfs++ * 32767.5); if(j < -32768) { j = -32768; } else if(j > 32767) { j = 32767; } *sis++ = j; } } #ifdef _MSC_VER LONG _fltused = 0; int mainCRTStartup() #else void CTE_CDECL main(int argc, char *argv[]) #endif { CommandLine = GetCommandLineA(); ConsoleOutHandle = GetStdHandle(STD_OUTPUT_HANDLE); Print("\r\nCreamTracker softsynth executable music player\r\nCopyright (C) 2012-2017, Benjamin 'BeRo' Rosseaux\r\n\r\nUsage: "); Print(ParamStr(0)); Print(" ([output.wav])\r\n\r\n"); #if defined(CreamTrackerCode) && !defined(CreamTrackerTRICode) Data = (void*)&TrackDataBytes; DataSize = TrackDataSize; #else Data = NULL; DataSize = 0; #ifndef _MSC_VER if (argc > 1) #endif { #ifdef _MSC_VER char* fn = "test.s3m"; #else char* fn = argv[1]; #endif HANDLE FileHandle = CreateFileA(fn, GENERIC_READ,FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if(FileHandle){ DataSize = SetFilePointer(FileHandle, 0, NULL, FILE_END); if(SetFilePointer(FileHandle, 0, NULL, FILE_BEGIN) == 0){ DWORD DataRead = 0; Data = CreamTrackerGetMem(DataSize); if(!ReadFile(FileHandle, Data, DataSize, &DataRead, NULL)){ CreamTrackerFreeMem(Data); Data = NULL; } } CloseHandle(FileHandle); } } #endif if(Data) { Print("Precalculating SINC tables . . . "); CreamTrackerCreate(&Instance, SampleRate); Print("done!\r\nLoading . . . "); if (CreamTrackerLoad(&Instance, Data, DataSize)) { CreamTrackerReset(&Instance); Instance.Playing = CTE_TRUE; Instance.RepeatCounter = 0; Print("done!\r\n"); #ifdef _MSC_VER if (ParamCount() > 0) { cte_uchar_t *fileName = ParamStr(1); if (fileName && *fileName) { Print("Exporting wave file \""); Print(fileName); Print("\" . . . "); cte_uint32_t BytesWritten = 0; cte_uint32_t WAVLength = 0; HANDLE WAVFile = CreateFileA(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); if (WAVFile == INVALID_HANDLE_VALUE) { goto exit; } if (!WriteFile(WAVFile, &WAVHeader, sizeof(TWAVHeader), &BytesWritten, NULL)) { goto wavfail; } while (!Instance.TrackEnd) { CreamTrackerFillBuffer(&Instance, (cte_pointer)&Buffers[0], SynthBufferSize); roundDown((cte_pointer)&Buffers[0]); if (!WriteFile(WAVFile, (cte_pointer)&Buffers[0], SynthBufferSize * 4, &BytesWritten, NULL)) { goto wavfail; } WAVLength += SynthBufferSize * 4; } if (((cte_uint32_t)SetFilePointer(WAVFile, 40, NULL, FILE_BEGIN)) == 0xfffffffful) { goto wavfail; } if (!WriteFile(WAVFile, (cte_pointer)&WAVLength, sizeof(cte_uint32_t), &BytesWritten, NULL)) { goto wavfail; } if (((cte_uint32_t)SetFilePointer(WAVFile, 4, NULL, FILE_BEGIN)) == 0xfffffffful) { goto wavfail; } WAVLength += 36; if (!WriteFile(WAVFile, (cte_pointer)&WAVLength, sizeof(cte_uint32_t), &BytesWritten, NULL)) { goto wavfail; } if (((cte_uint32_t)SetFilePointer(WAVFile, 0, NULL, FILE_END)) == 0xfffffffful) { goto wavfail; } Print("done!\r\n"); goto wavok; wavfail: Print("Wave file export error!\r\n"); wavok: CloseHandle(WAVFile); goto exit; } } #endif Print("Playing . . . "); waveOutOpen(&WaveOutHandle, WAVE_MAPPER, &WaveFormat, 0, 0, 0); while (!Instance.TrackEnd) { if (WaveHandler[BufferCounter].dwFlags & WHDR_DONE) { if(waveOutUnprepareHeader(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)) != WAVERR_STILLPLAYING) { WaveHandler[BufferCounter].dwFlags &= ~WHDR_DONE; CreamTrackerFillBuffer(&Instance, (cte_pointer)WaveHandler[BufferCounter].lpData, SynthBufferSize); roundDown((cte_pointer)WaveHandler[BufferCounter].lpData); waveOutPrepareHeader(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)); waveOutWrite(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)); BufferCounter = (BufferCounter + 1) & 3; } } else { Sleep(1); } } Print("done!\r\n"); #ifndef CreamTrackerMinimalPlayer waveOutReset(WaveOutHandle); waveOutClose(WaveOutHandle); #endif } #ifndef CreamTrackerMinimalPlayer CreamTrackerDestroy(&Instance); CreamTrackerFreeMem(Data); #endif } exit: //Print("\r\nHave a nice day!\r\n"); //TerminateProcess(GetCurrentProcess(), 0); ExitProcess(0); //return 0; }