#include "CreamTrackerEngine.h" #ifdef __cplusplus extern "C" { #endif #ifdef _MSC_VER #pragma warning(disable: 4276) #pragma warning(disable: 4731) #endif #ifdef CreamTrackerCode #ifndef CreamTrackerTRICode #include "trackcode.h" #define CreamTrackerNoChecks #endif #endif #ifndef CTE_GCC_COMPATIBLE #include #endif #ifndef CreamTrackerPortable HANDLE HeapHandle; #endif void CreamTrackerInitMemoryManager() { static cte_longbool_t initialized = CTE_FALSE; if(!initialized) { initialized = CTE_TRUE; #ifndef CreamTrackerPortable HeapHandle = GetProcessHeap(); #endif } } void CreamTrackerDoneMemoryManager() { } cte_pointer CreamTrackerGetMem(size_t Size) { CreamTrackerInitMemoryManager(); #ifdef CreamTrackerPortable return malloc(Size); #else return HeapAlloc(HeapHandle, HEAP_GENERATE_EXCEPTIONS, Size); #endif } cte_pointer CreamTrackerReallocMem(cte_pointer ThePointer, size_t Size) { if(ThePointer) { #ifdef CreamTrackerPortable return realloc(ThePointer, Size); #else return HeapReAlloc(HeapHandle, HEAP_GENERATE_EXCEPTIONS, ThePointer, Size); #endif } else { return CreamTrackerGetMem(Size); } } void CreamTrackerFreeMem(cte_pointer ThePointer) { #ifdef CreamTrackerPortable free(ThePointer); #else HeapFree(HeapHandle, 0, ThePointer); #endif } const cte_uint16_t CreamTrackerCW = 0x027f; // rounding to nearest, round to double precision, no interrupts for exceptions const cte_uint16_t CreamTrackerCWRound = 0x037f; // rounding to nearest, round to extended precision, no interrupts for exceptions const cte_uint16_t CreamTrackerCWTrunc = 0x0f7f; // rounding to nearest, round to extended precision, no interrupts for exceptions cte_int64_t CreamTrackerRound(cte_double_t Value) { #ifdef CreamTrackerPortable return (cte_int64_t)round(Value); #else cte_int64_t result; cte_int16_t OldCW; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %1\n\t" "fldcw %2\n\t" "fldl %3\n\t" "fistpll %0\n\t" "fldcw %1\n\t" : "=m"(result) : "m"(OldCW), "m"(CreamTrackerCWRound), "m"(Value)); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCWRound fld qword ptr Value fistp qword ptr result fldcw word ptr OldCW } #endif return result; #endif } cte_int64_t CreamTrackerTrunc(cte_double_t Value) { #ifdef CreamTrackerPortable return (cte_int64_t)floor(Value); #else cte_int64_t result; cte_int16_t OldCW; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %1\n\t" "fldcw %2\n\t" "fldl %3\n\t" "fistpll %0\n\t" "fldcw %1\n\t" : "=m"(result) : "m"(OldCW), "m"(CreamTrackerCWTrunc), "m"(Value)); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCWTrunc fld qword ptr Value fistp qword ptr result fldcw word ptr OldCW } #endif return result; #endif } cte_double_t CreamTrackerFrac(cte_double_t Value) { #ifdef CreamTrackerPortable return Value - floor(Value); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fld1\n\t" "fxch %%st(1)\n\t" "fprem\n\t" "fxch %%st(1)\n\t" "fstp %%st(0)\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fld1 fxch st(1) fprem fxch st(1) fstp st(0) fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerExp(cte_double_t Value) { #ifdef CreamTrackerPortable return exp(Value); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fldl2e\n\t" "fmulp %%st(0),%%st(1)\n\t" "fld1\n\t" "fld %%st(1)\n\t" "fprem\n\t" "f2xm1\n\t" "faddp %%st(0),%%st(1)\n\t" "fscale\n\t" "fstp %%st(1)\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fldl2e fmulp st(1),st(0) fld1 fld st(1) fprem f2xm1 faddp st(1),st(0) fscale fstp st(1) fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerPower(cte_double_t Number, cte_double_t Exponent) { #ifdef CreamTrackerPortable return pow(Number, Exponent); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fldl %2\n\t" "fyl2x\n\t" "fld1\n\t" "fld %%st(1)\n\t" "fprem\n\t" "f2xm1\n\t" "faddp %%st(0),%%st(1)\n\t" "fscale\n\t" "fstp %%st(1)\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Exponent), "m"(Number)); #else __asm { fld qword ptr Exponent fld qword ptr Number fyl2x fld1 fld st(1) fprem f2xm1 faddp st(1),st fscale fstp st(1) fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerSinus(cte_double_t Value) { #ifdef CreamTrackerPortable return sin(Value); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fsin\n\t" "fnstsw %%ax\n\t" "sahf\n\t" "jnp CreamTrackerSinusNotOutOfRange\n\t" "fstp %%st(0)\n\t" "fldz\n\t" "CreamTrackerSinusNotOutOfRange:\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fsin fnstsw ax sahf jnp CreamTrackerSinusNotOutOfRange fstp st(0) fldz CreamTrackerSinusNotOutOfRange: fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerCosinus(cte_double_t Value) { #ifdef CreamTrackerPortable return cos(Value); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fcos\n\t" "fnstsw %%ax\n\t" "sahf\n\t" "jnp CreamTrackerCosinusNotOutOfRange\n\t" "fstp %%st(0)\n\t" "fldz\n\t" "CreamTrackerCosinusNotOutOfRange:\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fcos fnstsw ax sahf jnp CreamTrackerCosinusNotOutOfRange fstp st(0) fldz CreamTrackerCosinusNotOutOfRange: fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerSQRT(cte_double_t Value) { #ifdef CreamTrackerPortable return sqrt(Value); #else cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fsqrt\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fsqrt fstp qword ptr result } #endif return result; #endif } cte_double_t CreamTrackerLog10(cte_double_t Value) { #ifdef CreamTrackerPortable return log10(Value); #else const cte_double_t DivLOG10 = 0.4342944819; cte_double_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldl %1\n\t" "fldln2\n\t" "fxch %%st(1)\n\t" "fyl2x\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value)); #else __asm { fld qword ptr Value fldln2 fxch st(1) fyl2x fstp qword ptr result } #endif return result * DivLOG10; #endif } #ifdef CreamTrackerMultithreading cte_int32_t CreamTrackerInterlockedDecrement(cte_int32_p Target) { cte_int32_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "movl $0xffffffff,%%eax\n\t" "movl %1,%%edx\n\t" "lock\n\t" "xaddl %%eax,(%%edx)\n\t" "decl %%eax\n\t" "movl %%eax,%0\n\t" : "=m"(result) : "m"(Target) : "%eax", "%edx"); #else __asm { push eax push edx mov eax,0xffffffff mov edx,dword ptr Target lock xadd dword ptr [edx],eax dec eax mov dword ptr result,eax pop edx pop eax } #endif return result; } cte_int32_t CreamTrackerInterlockedIncrement(cte_int32_p Target) { cte_int32_t result; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "movl $1,%%eax\n\t" "movl %1,%%edx\n\t" "lock\n\t" "xaddl %%eax,(%%edx)\n\t" "incl %%eax\n\t" "movl %%eax,%0\n\t" : "=m"(result) : "m"(Target) : "%eax", "%edx"); #else __asm { push eax push edx mov eax,1 mov edx,dword ptr Target lock xadd dword ptr [edx],eax inc eax mov dword ptr result,eax pop edx pop eax } #endif return result; } #endif #define GetMem(Size) CreamTrackerGetMem(Size) #define FreeMem(p) CreamTrackerFreeMem(p) #define AlignedGetMem CreamTrackerAlignedGetMem cte_pointer AlignedGetMem(size_t Size, cte_int32_t Align) { cte_uint8_p p = GetMem(Size + sizeof(cte_pointer) + Align); cte_uint8_p op = p; cte_uint8_p rp = p + sizeof(cte_pointer); if (((size_t)(rp)) % Align) { rp += Align - (((size_t)(rp)) % Align); } p = rp - sizeof(cte_pointer); *((cte_pointer*)p) = op; return rp; } #define AlignedFreeMem CreamTrackerAlignedFreeMem void AlignedFreeMem(cte_pointer p) { if(p) { cte_uint8_p np = p; np -= sizeof(cte_pointer); FreeMem(*((cte_pointer*)np)); } } #define FillChar CreamTrackerFillChar #ifdef _MSC_VER #pragma optimize( "s", off ) #pragma optimize( "t", on ) #endif void FillChar(cte_pointer Data, size_t Len, cte_uint8_t Value) { cte_uint8_p ByteData = Data; for(; Len > 0; Len--) { *ByteData++ = Value; } } #ifdef _MSC_VER #pragma optimize( "", on ) #endif #define Move CreamTrackerMove void Move(cte_pointer Src, cte_pointer Dest, size_t Len) { cte_uint8_p ByteSrc = Src; cte_uint8_p ByteDest = Dest; for(; Len > 0; Len--) { *ByteDest++ = *ByteSrc++; } } #ifdef _MSC_VER #ifdef CreamTrackerPortable #define CreamTrackerInt64Div(Dividend, Divisor) (((cte_uint64_t)(Dividend)) / ((cte_uint64_t)(Divisor))) #define CreamTrackerInt64Mul(Multiplier, Multiplicand) (((cte_uint64_t)(Multiplier)) * ((cte_uint64_t)(Multiplicand))) #else cte_uint64_t CreamTrackerUInt64Div(cte_uint64_t Dividend, cte_uint64_t Divisor) { if (Dividend < Divisor) { // If the dividend is smaller than the divisor, then we can return zero. return 0; } else if ((Dividend < 0x100000000ull) && (Divisor < 0x100000000ull)) { // Now do the divide. First look to see if the dividend and the divisor are less than 0x100000000. // If so, then we can use a 32-bit divide, otherwise things get a little more complex. return ((cte_uint32_t)Dividend) / ((cte_uint32_t)Divisor); } else if (Divisor < 0x100000000ull) { // Now do the divide. First look to see if the divisor is less than 0x100000000. // If so, then we can use a simple algorithm with word divides, otherwise things get a little more complex. cte_uint32_t l = ((cte_uint32_t)Dividend) & 0xfffful; cte_uint32_t ml = ((cte_uint32_t)(Dividend >>= 16)) & 0xfffful; cte_uint32_t mh = ((cte_uint32_t)(Dividend >>= 16)) & 0xfffful; cte_uint32_t h = (cte_uint32_t)(Dividend >> 16); cte_uint32_t Divisor32 = (cte_uint32_t)Divisor; Dividend = h / Divisor32; mh += (h % Divisor32) << 16; Dividend = (Dividend << 16) + (mh / Divisor32); ml += (mh % Divisor32) << 16; Dividend = (Dividend << 16) + (ml / Divisor32); l += (ml % Divisor32) << 16; return (Dividend << 16) + (l / Divisor32); } else { // Here we do it the hard way. #if 1 uint64_t ShiftedDivisor = Divisor; { uint64_t Temp = Divisor; while ((ShiftedDivisor < Dividend) && ((Temp <<= 1) > ShiftedDivisor)) { ShiftedDivisor = Temp; } } { uint64_t Quotient; #define Remainder Dividend for (Quotient = 0; ShiftedDivisor >= Divisor; ShiftedDivisor >>= 1) { Quotient <<= 1; if (Remainder >= ShiftedDivisor) { Remainder -= ShiftedDivisor; Quotient |= 1; } } return Quotient; #undef Remainder } #else cte_uint64_t Quotient, Remainder; cte_uint32_t Bit; Quotient = Dividend; Remainder = 0; for (Bit = 64; Bit > 0; Bit--) { Remainder = (Remainder << 1) || (Quotient >> 63); Quotient <<= 1; if(Remainder >= Divisor) { Remainder -= Divisor; Quotient |= 1; } } return Quotient; #endif } } cte_int64_t CreamTrackerInt64Div(cte_int64_t Dividend, cte_int64_t Divisor) { cte_int64_t result; cte_int32_t Sign = 1; if (Dividend < 0) { Dividend = -Dividend; Sign = -Sign; } if (Divisor < 0) { Divisor = -Divisor; Sign = -Sign; } result = CreamTrackerUInt64Div(Dividend, Divisor); return (Sign < 0) ? -result : result; } cte_uint64_t CreamTrackerUInt64Mul(cte_uint64_t Multiplier, cte_uint64_t Multiplicand) { cte_uint32_t MultiplierHigh = (cte_uint32_t)(Multiplier >> 32); cte_uint32_t MultiplierLow = (cte_uint32_t)(Multiplier & 0xfffffffful); cte_uint32_t MultiplicandHigh = (cte_uint32_t)(Multiplicand >> 32); cte_uint32_t MultiplicandLow = (cte_uint32_t)(Multiplicand & 0xfffffffful); cte_uint32_t ResultHigh; cte_uint32_t ResultLow; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "movl %2, %%eax\n\t" "movl %2, %%ecx\n\t" "mull %%ecx\n\t" "movl %%edx,%0\n\t" "movl %%eax,%1\n\t" : "=m"(ResultHigh), "=m"(ResultLow) : "m"(MultiplierLow), "m"(MultiplicandLow) : "%eax", "%edx" ); #else __asm { mov eax, dword ptr MultiplierLow mov ecx, dword ptr MultiplicandLow mul ecx mov dword ptr ResultHigh, edx mov dword ptr ResultLow, eax } #endif return (((cte_uint64_t)((MultiplierLow * MultiplicandHigh) + (MultiplierHigh * MultiplicandLow) + ResultHigh)) << 32) | ResultLow; } cte_int64_t CreamTrackerInt64Mul(cte_int64_t Multiplier, cte_int64_t Multiplicand) { cte_int64_t result; cte_int32_t Sign = 1; if (Multiplier < 0) { Multiplier = -Multiplier; Sign = -Sign; } if (Multiplicand < 0) { Multiplicand = -Multiplicand; Sign = -Sign; } result = CreamTrackerUInt64Mul(Multiplier, Multiplicand); return (Sign < 0) ? -result : result; } #endif #else #define CreamTrackerInt64Div(Dividend, Divisor) (((cte_uint64_t)(Dividend)) / ((cte_uint64_t)(Divisor))) #define CreamTrackerInt64Mul(Multiplier, Multiplicand) (((cte_uint64_t)(Multiplier)) * ((cte_uint64_t)(Multiplicand))) #endif #ifdef CreamTrackerCode #ifdef CreamTrackerTRICode typedef struct { cte_pointer ImageData; cte_int32_t ImageSize; cte_int32_t CodeSize; } TTRIInstance; typedef TTRIInstance* PTRIInstance; typedef cte_pointer(*TGetExternalPointer)(cte_pointer Context, cte_uchar_p Name); typedef void(*TSetPublicPointer)(cte_pointer Context, cte_uchar_p Name, cte_pointer ThePointer); PTRIInstance TRILink(cte_pointer RawCode, cte_int32_t RawCodeSize, cte_pointer Context, TGetExternalPointer GetExternalPointer, TSetPublicPointer SetPublicPointer) { cte_int32_t ImageCodeSize, ImageSize, ImageRelocations, ImagePublics, i, Position, Len; cte_uint8_t t, Bits; cte_uint8_p r, p; cte_longbool_t Relative; PTRIInstance result; cte_uchar_p RawCodeBytes, ExternalName, LibName, LibImportName, PublicName; cte_pointer ExternalPointer, LibImportPointer, ImageData; result = NULL; if(RawCode && (RawCodeSize > 0)) { RawCodeBytes = RawCode; if( (RawCodeBytes[0] != (cte_uchar_t)'T') || (RawCodeBytes[1] != (cte_uchar_t)'R') || (RawCodeBytes[2] != (cte_uchar_t)'I') || (RawCodeBytes[3] != (cte_uchar_t)'\0') ) { return NULL; } ImageCodeSize = *((cte_int32_p)&RawCodeBytes[4]); ImageSize = *((cte_int32_p)&RawCodeBytes[8]); ImageRelocations = *((cte_int32_p)&RawCodeBytes[12]); ImagePublics = *((cte_int32_p)&RawCodeBytes[16]); r = (cte_uint8_p)RawCode + *((cte_int32_p)&RawCodeBytes[20]); p = (cte_uint8_p)RawCode + *((cte_int32_p)&RawCodeBytes[24]); result = GetMem(sizeof(TTRIInstance)); ImageData = VirtualAlloc(NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); result->ImageData = ImageData; result->ImageSize = ImageSize; result->CodeSize = ImageCodeSize; FillChar(ImageData, ImageSize, 0); Move((cte_pointer)&RawCodeBytes[28], ImageData, ImageCodeSize); for(i = 0; iImageData, Instance->ImageSize, MEM_DECOMMIT); FreeMem(Instance); return CTE_TRUE; } return CTE_FALSE; } #endif #endif #define SINC_FRACBITS 12 #define SINC_LUTLEN (1 << SINC_FRACBITS) #define SINC_LOG2WIDTH 7 #define SINC_WIDTH (1 << SINC_LOG2WIDTH) #define SINC_HALFWIDTH (SINC_WIDTH >> 1) #define SINC_FRACSHIFT (32 - SINC_FRACBITS) #define SINC_FRACMASK ((1 << SINC_FRACBITS) - 1) #define SINC_FRACSHIFTLENGTH (1 << SINC_FRACSHIFT) #define SINC_FRACSHIFTMASK (SINC_FRACSHIFTLENGTH - 1) #define SINC_FRACSHIFTFACTOR (1.0 / (cte_double_t)(SINC_FRACSHIFTLENGTH)) #define SINCCUTOFF_LEN 128 typedef cte_float_t TResamplerSINCSubSubArray[SINC_WIDTH]; typedef TResamplerSINCSubSubArray* PResamplerSINCSubSubArray; typedef TResamplerSINCSubSubArray TResamplerSINCSubArray[SINC_LUTLEN]; typedef TResamplerSINCSubArray *PResamplerSINCSubArray; typedef TResamplerSINCSubArray TResamplerSINCArray[SINCCUTOFF_LEN]; typedef TResamplerSINCArray* PResamplerSINCArray; typedef cte_int64_t TResamplerSINCCutOffIncrementTable[SINCCUTOFF_LEN]; typedef TResamplerSINCCutOffIncrementTable* PResamplerSINCCutOffIncrementTable; #define ResamplerSINCArray CreamTrackerResamplerSINCArray PResamplerSINCArray ResamplerSINCArray; #define ResamplerSINCWindowArray CreamTrackerResamplerSINCWindowArray TResamplerSINCSubArray ResamplerSINCWindowArray; #define ResamplerSINCCutOffIncrementTable CreamTrackerResamplerSINCCutOffIncrementTable TResamplerSINCCutOffIncrementTable ResamplerSINCCutOffIncrementTable; #define PI CreamTrackerPI const cte_double_t PI = 3.14159265358979323846; #define PI2 CreamTrackerPI2 const cte_double_t PI2 = 3.14159265358979323846 * 2.0; #define CreamTrackerEffectOffset 64 #define WaveTables CreamTrackerWaveTables const cte_int16_t WaveTables[4][64]= { { 0x00,0x18,0x31,0x4a,0x61,0x78,0x8d,0xa1, 0xb4,0xc5,0xd4,0xe0,0xeb,0xf4,0xfa,0xfd, 0xff,0xfd,0xfa,0xf4,0xeb,0xe0,0xd4,0xc5, 0xb4,0xa1,0x8d,0x78,0x61,0x4a,0x31,0x18, 0x00,-0x018,-0x031,-0x04a,-0x061,-0x078,-0x08d,-0x0a1, -0xb4,-0x0c5,-0x0d4,-0x0e0,-0x0eb,-0x0f4,-0x0fa,-0x0fd, -0xff,-0x0fd,-0x0fa,-0x0f4,-0x0eb,-0x0e0,-0x0d4,-0x0c5, -0xb4,-0x0a1,-0x08d,-0x078,-0x061,-0x04a,-0x031,-0x018 }, { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0,-248,-240,-232,-224,-216,-208,-200,-192, -184,-176,-168,-160,-152,-144,-136,-128,-120, -112,-104,-96,-88,-80,-72,-64,-56,-48,-40, -32,-24,-16,-8,0,8,16,24,32,40,48,56,64,72, 80,88,96,104,112,120,128,136,144,152,160, 168,176,184,192,200,208,216,224,232,240,248 }, { 0x00,0x18,0x31,0x4a,0x61,0x78,0x8d,0xa1, 0xb4,0xc5,0xd4,0xe0,0xeb,0xf4,0xfa,0xfd, 0xff,0xfd,0xfa,0xf4,0xeb,0xe0,0xd4,0xc5, 0xb4,0xa1,0x8d,0x78,0x61,0x4a,0x31,0x18, 0x00,-0x018,-0x031,-0x04a,-0x061,-0x078,-0x08d,-0x0a1, -0xb4,-0x0c5,-0x0d4,-0x0e0,-0x0eb,-0x0f4,-0x0fa,-0x0fd, -0xff,-0x0fd,-0x0fa,-0x0f4,-0x0eb,-0x0e0,-0x0d4,-0x0c5, -0xb4,-0x0a1,-0x08d,-0x078,-0x061,-0x04a,-0x031,-0x018 } }; #define CreamTrackerInstanceStubDataSize sizeof(TCreamTrackerInstanceStubData) //const cte_int32_t CreamTrackerInstanceStubDataSize = sizeof(TCreamTrackerInstanceStubData); const cte_uint16_t CreamTrackerFineTuneTable[16] = { 7895, 7941, 7985, 8046, 8107, 8169, 8232, 8280, 8363, 8413, 8463, 8529, 8581, 8651, 8723, 8757 }; const cte_int32_t CreamTrackerADPCMIMAStepTable[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 }; const cte_int32_t CreamTrackerADPCMIMAIndexTable[16] = { -1,-1,-1,-1,2,4,6,8, -1,-1,-1,-1,2,4,6,8 }; const cte_int32_t CreamTrackerADPCMIMADifferenceLookUpTable[16] = { 1,3,5,7,9,11,13,15, -1,-3,-5,-7,-9,-11,-13,-15 }; cte_int32_t CreamTrackerADPCMIMADecompressSample(TCreamTrackerADPCMIMAState* ADPCMIMAState, cte_uint8_t Nibble) { ADPCMIMAState->PrevSample += (CreamTrackerADPCMIMAStepTable[ADPCMIMAState->StepIndex] * CreamTrackerADPCMIMADifferenceLookUpTable[Nibble & 0xf]) / 8; if (ADPCMIMAState->PrevSample < -32768) { ADPCMIMAState->PrevSample = -32768; } else if(ADPCMIMAState->PrevSample > 32767) { ADPCMIMAState->PrevSample = 32767; } ADPCMIMAState->StepIndex += CreamTrackerADPCMIMAIndexTable[Nibble]; if(ADPCMIMAState->StepIndex < 0) { ADPCMIMAState->StepIndex = 0; } else if(ADPCMIMAState->StepIndex>88) { ADPCMIMAState->StepIndex = 88; } return ADPCMIMAState->PrevSample; } #ifdef CreamTrackerSaveRoutines cte_uint8_t CreamTrackerADPCMIMACompressSample(TCreamTrackerADPCMIMAState* ADPCMIMAState, cte_int16_t Sample) { cte_int32_t Delta = Sample - ADPCMIMAState->PrevSample; cte_int32_t Nibble = ((Delta < 0 ? -Delta : Delta) * 4) / CreamTrackerADPCMIMAStepTable[ADPCMIMAState->StepIndex]; if(Nibble > 7) { Nibble = 7; } if(Delta < 0) { Nibble |= 8; } ADPCMIMAState->PrevSample += (CreamTrackerADPCMIMAStepTable[ADPCMIMAState->StepIndex] * CreamTrackerADPCMIMADifferenceLookUpTable[Nibble & 0xf]) / 8; if (ADPCMIMAState->PrevSample < -32768) { ADPCMIMAState->PrevSample = -32768; } else if(ADPCMIMAState->PrevSample > 32767) { ADPCMIMAState->PrevSample = 32767; } ADPCMIMAState.StepIndex += CreamTrackerADPCMIMAIndexTable[Nibble]; if(ADPCMIMAState->StepIndex < 0) { ADPCMIMAState->StepIndex = 0; } else if(ADPCMIMAState->StepIndex>88) { ADPCMIMAState->StepIndex = 88; } return Nibble; } #endif #define ComparePAnsiChar CreamTrackerComparePAnsiChar cte_longbool_t ComparePAnsiChar(cte_uchar_p a,cte_uchar_p b) { if(a && b) { while(*a && *b) { cte_uchar_t ac = *a; cte_uchar_t bc = *b; if((ac >= 'A') && (ac <= 'Z')) { ac += 'a' - 'A'; } if((bc >= 'A') && (bc <= 'Z')) { bc += 'a' - 'A'; } if(ac != bc) { return CTE_FALSE; } a++; b++; } if(*a != *b) { return CTE_FALSE; } } return CTE_TRUE; } #define KillDenormal CreamTrackerKillDenormal cte_float_t KillDenormal(cte_float_t Value) { cte_uint32_p casted = (cte_pointer)(&Value); *casted = *casted & (0xfffffffful + (((((*casted & 0x7f800000ul) + 0x800000ul) & 0x7f800000ul) - 0x1000000ul) >> 31)); return Value; } #define RoundToNextOfPowerTwo CreamTrackerRoundToNextOfPowerTwo cte_uint32_t RoundToNextOfPowerTwo(cte_uint32_t x) { x--; x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return x + 1; } #define Clip CreamTrackerClip cte_float_t Clip(cte_float_t Value, cte_float_t Min, cte_float_t Max) { return (cte_float_t)(((fabs(Value - Min) + (Min + Max)) - fabs(Value - Max)) * 0.5f); } #ifdef CreamTrackerSINCResampling #ifndef CTE_GCC_COMPATIBLE #define Convolve CreamTrackerConvolveSSE #define ConvolveSSE CreamTrackerConvolveSSE float ConvolveSSE(cte_float_p Data, cte_float_p Kernel, cte_int32_t Count) { __m128 t0 = _mm_setzero_ps(); __m128 t1 = _mm_setzero_ps(); __m128 t2 = _mm_setzero_ps(); __m128 t3 = _mm_setzero_ps(); while (Count >= 16) { t0 = _mm_add_ps(t0, _mm_mul_ps(_mm_loadu_ps(Data), _mm_load_ps(Kernel))); t1 = _mm_add_ps(t1, _mm_mul_ps(_mm_loadu_ps(Data + 4), _mm_load_ps(Kernel + 4))); t2 = _mm_add_ps(t2, _mm_mul_ps(_mm_loadu_ps(Data + 8), _mm_load_ps(Kernel + 8))); t3 = _mm_add_ps(t3, _mm_mul_ps(_mm_loadu_ps(Data + 12), _mm_load_ps(Kernel + 12))); Data += 16; Kernel += 16; Count -= 16; } while (Count >= 8) { t0 = _mm_add_ps(t0, _mm_mul_ps(_mm_loadu_ps(Data), _mm_load_ps(Kernel))); t1 = _mm_add_ps(t1, _mm_mul_ps(_mm_loadu_ps(Data + 4), _mm_load_ps(Kernel + 4))); Data += 8; Kernel += 8; Count -= 8; } while (Count >= 4) { t0 = _mm_add_ps(t0, _mm_mul_ps(_mm_loadu_ps(Data), _mm_load_ps(Kernel))); Data += 4; Kernel += 4; Count -= 4; } while (Count > 0) { t0 = _mm_add_ss(t0, _mm_mul_ss(_mm_load_ss(Data), _mm_load_ss(Kernel))); Data++; Kernel++; Count--; } t0 = _mm_add_ps(t0, t1); t2 = _mm_add_ps(t2, t3); t0 = _mm_add_ps(t0, t2); float result; __m128 result128 = _mm_setzero_ps(); result128 = _mm_add_ps(_mm_movehl_ps(t0, t0), t0); _mm_store_ss(&result, _mm_add_ss(result128, _mm_shuffle_ps(result128, result128, 1))); return result; } #else #define Convolve4 CreamTrackerConvolve4 cte_float_t Convolve4(cte_float_p Data, cte_float_p Kernel) { return (Data[0] * Kernel[0]) + (Data[1] * Kernel[1]) + (Data[2] * Kernel[2]) + (Data[3] * Kernel[3]); } #define Convolve CreamTrackerConvolve cte_float_t Convolve(cte_float_p Data, cte_float_p Kernel, cte_int32_t Count) { cte_float_t result = 0.0; while(Count > 7) { result += (Data[0] * Kernel[0]) + (Data[1] * Kernel[1]) + (Data[2] * Kernel[2]) + (Data[3] * Kernel[3]) + (Data[4] * Kernel[4]) + (Data[5] * Kernel[5]) + (Data[6] * Kernel[6]) + (Data[7] * Kernel[7]); Data += 8; Kernel += 8; Count -= 8; } while(Count > 3) { result += (Data[0] * Kernel[0]) + (Data[1] * Kernel[1]) + (Data[2] * Kernel[2]) + (Data[3] * Kernel[3]); Data += 4; Kernel += 4; Count -= 4; } while(Count--) { result += (*Data++) * (*Kernel++); } /* cte_int32_t CountBlocks = (Count + 7) >> 3; switch (Count & 7) { case 0: do { result += ((*Data++) * (*Kernel++)); case 7: result += ((*Data++) * (*Kernel++)); case 6: result += ((*Data++) * (*Kernel++)); case 5: result += ((*Data++) * (*Kernel++)); case 4: result += ((*Data++) * (*Kernel++)); case 3: result += ((*Data++) * (*Kernel++)); case 2: result += ((*Data++) * (*Kernel++)); case 1: result += ((*Data++) * (*Kernel++)); } while (--CountBlocks > 0); }*/ return result; } #endif #endif #define SIMDSetFlags CreamTrackerSIMDSetFlags void SIMDSetFlags() { cte_uint32_t SIMDCtrl; #ifdef CTE_GCC_COMPATIBLE /*__asm__ __volatile__( "fldl %1\n\t" "fsin\n\t" "fnstsw %%ax\n\t" "sahf\n\t" "jnp CreamTrackerSinusNotOutOfRange\n\t" "fstp %%st(0)\n\t" "fldz\n\t" "CreamTrackerSinusNotOutOfRange:\n\t" "fstpl %0\n\t" : "=m"(result) : "m"(Value));*/ #error Ups! #else __asm { push eax stmxcsr dword ptr SIMDCtrl mov eax,dword ptr SIMDCtrl or eax, (1u << 6) | (1u << 7) | (1u << 8) | (1u << 9) | (1u << 10) | (1u << 11) | (1u << 12) | (1u << 15) and eax,0xffffffffu & ~((1u << 13) | (1u << 14)) mov dword ptr SIMDCtrl,eax ldmxcsr dword ptr SIMDCtrl pop eax } #endif } #define CheckCPU CreamTrackerCheckCPU void CheckCPU() { } #ifdef CreamTrackerSINCResampling #define i0 CreamTrackeri0 cte_double_t i0(cte_double_t x) { const cte_double_t epsilon = 1e-6; cte_double_t result = 1.0; cte_double_t u = 1.0; cte_int32_t n = 1; cte_double_t halfx = x * 0.5; do { cte_double_t t = halfx / n; n++; u *= t * t; result += u; } while (u >= (epsilon * result)); return result; } #define ResamplerSINCArrayInitialized CreamTrackerResamplerSINCArrayInitialized cte_longbool_t ResamplerSINCArrayInitialized; #define InitResamplerSINC CreamTrackerInitResamplerSINC void InitResamplerSINC() { static cte_longbool_t ResamplerSINCArrayInitializedEx = CTE_FALSE; const cte_double_t EPSILON = 1e-10; if((!ResamplerSINCArrayInitialized) || (!ResamplerSINCArrayInitializedEx)) { ResamplerSINCArrayInitialized = CTE_TRUE; ResamplerSINCArrayInitializedEx = CTE_TRUE; ResamplerSINCArray = AlignedGetMem(sizeof(TResamplerSINCArray), 16); const cte_int32_t Points = SINC_WIDTH; const cte_int32_t Len = SINC_LUTLEN; const cte_double_t HalfPoints = Points * 0.5; const cte_double_t a = (-20.0) * CreamTrackerLog10(1.0 / ((cte_double_t)(1ul << 24))); const cte_double_t beta = (a > 50.0) ? (0.1102 * (a - 8.7)) : ((a > 21.0) ? ((0.5842 * CreamTrackerPower(a - 21.0, 0.4)) + (0.07886 * (a - 21.0))) : 0.0); cte_double_t i0beta = i0(beta); cte_int32_t Counter, CutOffCounter; for(Counter = 0; Counter < Len; Counter++) { cte_int32_t SubCounter; cte_double_t FracValue = (((cte_double_t)Counter) / ((cte_double_t)Len)) - 0.5; for(SubCounter = 0; SubCounter < Points; SubCounter++) { cte_double_t OtherPosition = SubCounter - FracValue; cte_double_t Position = OtherPosition - HalfPoints; cte_double_t WindowParameter = Position / HalfPoints; ResamplerSINCWindowArray[Counter][SubCounter] = (cte_float_t)((fabs(WindowParameter) < 1.0) ? (i0(beta * CreamTrackerSQRT(1.0 - (WindowParameter * WindowParameter))) / i0beta) : 0.0); } } for(CutOffCounter = 0; CutOffCounter < SINCCUTOFF_LEN; CutOffCounter++) { cte_double_t Factor = (!CutOffCounter) ? 1.0 : (1.0 * CreamTrackerPower(2.0, CutOffCounter / 12.0)); cte_double_t CutOff = 1.0 / Factor; ResamplerSINCCutOffIncrementTable[CutOffCounter] = CreamTrackerRound((0x100000000ull) * Factor); for(Counter = 0; Counter < Len; Counter++) { cte_int32_t SubCounter; cte_double_t FracValue = (((cte_double_t)Counter) / ((cte_double_t)Len)) - 0.5; for(SubCounter = 0; SubCounter < Points; SubCounter++) { cte_double_t OtherPosition = SubCounter - FracValue; cte_double_t Position = OtherPosition - HalfPoints; (*ResamplerSINCArray)[CutOffCounter][Counter][SubCounter] = (cte_float_t)(((fabs(Position) < EPSILON) ? CutOff : (CreamTrackerSinus(CutOff * Position * PI) / (Position * PI))) * ResamplerSINCWindowArray[Counter][SubCounter]); } } } } } #ifndef CreamTrackerMinimalPlayer #define DoneResamplerSINC CreamTrackerDoneResamplerSINC void DoneResamplerSINC() { if(ResamplerSINCArrayInitialized) { AlignedFreeMem(ResamplerSINCArray); ResamplerSINCArrayInitialized = CTE_FALSE; } } #endif #endif #ifdef CreamTrackerWRS void CreamTrackerRandomInit(PCreamTrackerRandomGenerator RandomGenerator, cte_uint32_t Seed) { RandomGenerator->XorShift128x = 0xc8a2d216ul ^ Seed; RandomGenerator->XorShift128y = (0xb46f23c7ul * Seed) ^ Seed; RandomGenerator->XorShift128z = (0xa54c2364ul ^ Seed) * Seed; RandomGenerator->XorShift128w = 0x8c1ca70cul + Seed; RandomGenerator->LCG = 0x72c1a602ul ^ Seed; RandomGenerator->MWCx = 0xa182f231ul ^ Seed; RandomGenerator->MWCy = 0x80d3c173ul - Seed; RandomGenerator->MWCc = 0; } cte_uint32_t CreamTrackerRandomGet(PCreamTrackerRandomGenerator RandomGenerator) { cte_uint32_t t = RandomGenerator->XorShift128x ^ (RandomGenerator->XorShift128x << 11); RandomGenerator->XorShift128x = RandomGenerator->XorShift128y; RandomGenerator->XorShift128y = RandomGenerator->XorShift128z; RandomGenerator->XorShift128z = RandomGenerator->XorShift128w; RandomGenerator->XorShift128w = (RandomGenerator->XorShift128w ^ (RandomGenerator->XorShift128w >> 19)) ^ (t ^ (t >> 8)); RandomGenerator->LCG = (RandomGenerator->LCG * 1664525ul) + 1013904223ul; t = RandomGenerator->MWCx + RandomGenerator->MWCy + RandomGenerator->MWCc; RandomGenerator->MWCx = RandomGenerator->MWCy; RandomGenerator->MWCc = t >> 31; RandomGenerator->MWCy = t & 0x7ffffffful; return RandomGenerator->LCG + RandomGenerator->XorShift128w + (RandomGenerator->MWCx << 1); } // WRSSynthese BEGIN typedef struct { cte_float_t Amplitude; cte_float_t Frequency; cte_float_t Phase; cte_longbool_t Active; } TSinusoidFrame; typedef TSinusoidFrame* PSinusoidFrame; #define SinusoidFrames CreamTrackerWRSSinusoidFrames #define TrackLengths CreamTrackerWRSTrackLengths #define SampleRate CreamTrackerWRSSampleRate #define NewLength CreamTrackerWRSNewLength #define OriginalLength CreamTrackerWRSOriginalLength #define FrameSize CreamTrackerWRSFrameSize #define Parts CreamTrackerWRSParts #define NumTracks CreamTrackerWRSNumTracks #define PitchScale CreamTrackerWRSPitchScale #define TimeScale CreamTrackerWRSTimeScale #define HasPhases CreamTrackerWRSHasPhases #define InputData CreamTrackerWRSInputData #define InputDataSize CreamTrackerWRSInputDataSize #define InputDataPosition CreamTrackerWRSInputDataPosition #define OutputData CreamTrackerWRSOutputData #define OutputDataStepSize CreamTrackerWRSOutputDataStepSize #define OutputLength CreamTrackerWRSOutputLength #define Read CreamTrackerWRSRead #define ReadByte CreamTrackerWRSReadByte #define ReadInteger CreamTrackerWRSReadInteger #define ReadFloat CreamTrackerWRSReadFloat #define Load CreamTrackerWRSLoad #define MagTodB CreamTrackerWRSMagTodB #define Synthese CreamTrackerWRSSubSynthese #define WRSSynthese CreamTrackerWRSSynthese TSinusoidFrame **SinusoidFrames; cte_uint32_p TrackLengths; cte_int32_t SampleRate; cte_int32_t NewLength; cte_int32_t OriginalLength; cte_int32_t FrameSize; cte_int32_t Parts; cte_int32_t NumTracks; cte_float_t PitchScale; cte_float_t TimeScale; cte_longbool_t HasPhases; cte_uint8_p InputData; cte_int32_t InputDataSize; cte_int32_t InputDataPosition; cte_float_p OutputData; cte_int32_t OutputDataStepSize; cte_int32_t OutputLength; cte_int32_t Read(cte_uint8_p Data, cte_int32_t Bytes) { cte_int32_t Counter; cte_int32_t Len = InputDataSize - InputDataPosition; if(Len < 0) { Len = 0; } else if (Len > Bytes) { Len = Bytes; } for(Counter = Len; Counter > 0; Counter--) { *Data++ = InputData[InputDataPosition++]; } return Len; } cte_uint8_t ReadByte() { cte_uint8_t value = 0; Read((cte_pointer)&value, sizeof(cte_uint8_t)); return value; } cte_int32_t ReadInteger() { cte_int32_t value = 0; Read((cte_pointer)&value, sizeof(cte_int32_t)); return value; } cte_float_t ReadFloat() { cte_float_t value = 0.0; Read((cte_pointer)&value, sizeof(cte_float_t)); return value; } cte_longbool_t Load() { cte_int32_t ByteIndex, Frame, Track, NumFrames, Count, Index; cte_uint8_t Value, Flags; cte_float_t MaxAmplitude, Float32; cte_uint32_p Temp; TCreamTrackerRandomGenerator RandomGenerator; cte_longbool_t HighQualityAmplitudes; InputDataPosition = 0; CreamTrackerRandomInit(&RandomGenerator, 0); SampleRate = ReadInteger(); OriginalLength = ReadInteger(); FrameSize = ReadInteger(); Parts = ReadInteger(); PitchScale = ReadFloat(); TimeScale = ReadFloat(); NumTracks = ReadInteger(); SinusoidFrames = GetMem(NumTracks * sizeof(cte_pointer)); FillChar(SinusoidFrames, NumTracks * sizeof(cte_pointer), 0); MaxAmplitude = ReadFloat(); Flags = ReadByte(); HasPhases = Flags & 1; HighQualityAmplitudes = Flags & 2; TrackLengths = GetMem(NumTracks * sizeof(cte_uint32_t)); FillChar(TrackLengths, NumTracks * sizeof(cte_uint32_t), 0); Count = 0; for(ByteIndex = 0; ByteIndex < 4; ByteIndex++) { Value = 0; for(Track = 0; Track < NumTracks; Track++) { Value += ReadByte(); TrackLengths[Track] |= Value << (ByteIndex << 3); } } for(Track = 0; Track < NumTracks; Track++) { SinusoidFrames[Track] = GetMem(TrackLengths[Track] * sizeof(TSinusoidFrame)); FillChar(SinusoidFrames[Track], TrackLengths[Track] * sizeof(TSinusoidFrame), 0); Count += TrackLengths[Track]; } Temp = GetMem(Count * sizeof(cte_uint32_t)); FillChar(Temp, Count * sizeof(cte_uint32_t), 0); for(ByteIndex = 0; ByteIndex < 2; ByteIndex++) { Index = 0; Value = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { Value += ReadByte(); Temp[Index++] |= Value << (ByteIndex << 3); } } } Index = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { (SinusoidFrames[Track][Frame]).Frequency = (cte_float_t)((Temp[Index++] / 65535.0) * SampleRate); } } if(HighQualityAmplitudes) { FillChar(Temp, Count * sizeof(cte_uint32_t), 0); for(ByteIndex = 0; ByteIndex < 2; ByteIndex++) { Index = 0; Value = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { Value += ReadByte(); Temp[Index++] |= Value << (ByteIndex << 3); } } } Index = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { (SinusoidFrames[Track][Frame]).Amplitude = (cte_float_t)((Temp[Index++] / 65535.0) * MaxAmplitude); } } } else { Value = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { Value += ReadByte(); (SinusoidFrames[Track][Frame]).Amplitude = (cte_float_t)((Value / 255.0) * MaxAmplitude); } } } if(HasPhases) { Value = 0; for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { Value += ReadByte(); (SinusoidFrames[Track][Frame]).Phase = (cte_float_t)((Value / 255.0) * PI2); } } } else { for(Track = 0; Track < NumTracks; Track++) { NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { cte_uint32_p p = (cte_pointer)&Float32; *p = ((CreamTrackerRandomGet(&RandomGenerator) >> 9) & 0x7ffffful) | 0x3f800000ul; (SinusoidFrames[Track][Frame]).Phase = (cte_float_t)((Float32 - 1.0) * PI2); } } } if(Temp) { FreeMem(Temp); } return CTE_TRUE; } cte_double_t MagTodB(cte_double_t x) { return (x < 0.00001) ? 0.0 : (20.0 * CreamTrackerLog10(x * 100000.0)); } void Synthese(cte_float_t Pitch) { cte_int32_t NumFrames,Track,Frame,Sample,PartSize,PartPos,SampleIndex,IntPartSize,CurrentPartSize; PSinusoidFrame SinusoidFrame; cte_double_t CurrentPhase,PrevAmp,CurrentFreq,PrevFreq,PrevPhase,InstPhase,InstAmp,AmpInc,CurrentAmplitude,InstFreq,FreqInc,VolumeScale,FloatPartSize,FloatPartSizeFrac; cte_float_p Samples; cte_uint32_t XorShift128x,XorShift128y,XorShift128z,XorShift128w,LCG,MWCx,MWCy,MWCc,Temp; cte_float_t Float32; NewLength = (cte_int32_t)CreamTrackerRound(OriginalLength * TimeScale); Samples = GetMem(NewLength * sizeof(cte_float_t)); XorShift128x = 0xc8a2d216ul; XorShift128y = 0xb46f23c7ul; XorShift128z = 0xa54c2364ul; XorShift128w = 0x8c1ca70cul; LCG = 0x72c1a602ul; MWCx = 0xa182f231ul; MWCy = 0x80d3c173ul; MWCc =0; Pitch *= PitchScale; for(Sample = 0; Sample < OriginalLength; Sample++) { Samples[Sample] = 0.0; } PartSize = FrameSize / Parts; FloatPartSize = PartSize * TimeScale; IntPartSize = (cte_int32_t)CreamTrackerTrunc(FloatPartSize); FloatPartSizeFrac = CreamTrackerFrac(FloatPartSize); VolumeScale = 1.0 / (FrameSize * 0.25); for(Track = 0; Track < NumTracks; Track++) { PrevAmp = 0; PrevFreq = 0; PrevPhase = 0; Sample = 0; FloatPartSize = 0; NumFrames = TrackLengths[Track]; for(Frame = 0; Frame < NumFrames; Frame++) { CurrentPartSize = IntPartSize + ((cte_int32_t)CreamTrackerTrunc(FloatPartSize)); FloatPartSize = CreamTrackerFrac(FloatPartSize + FloatPartSizeFrac); while(FloatPartSize >= 1.0) { FloatPartSize -= 1.0; } while(FloatPartSize < 0.0) { FloatPartSize += 1.0; } if((Frame - Parts) < 0) { Sample = (Frame-Parts) * PartSize; } else if((Frame - Parts) == 0) { Sample = 0; } SinusoidFrame = &(SinusoidFrames[Track][Frame]); CurrentFreq = (SinusoidFrame->Frequency / ((cte_double_t)SampleRate)) * Pitch; if((CurrentFreq >= 0.0) && (CurrentFreq<0.5)) { CurrentFreq = CurrentFreq * PI2; CurrentAmplitude = SinusoidFrame->Amplitude; } else { CurrentFreq = 0.0; CurrentAmplitude = 0.0; } if((CurrentAmplitude > 0.0) || (PrevAmp > 0.0)) { if(HasPhases) { CurrentPhase = SinusoidFrame->Phase; } else { Temp = XorShift128x ^ (XorShift128x << 11); XorShift128x = XorShift128y; XorShift128y = XorShift128z; XorShift128z = XorShift128w; XorShift128w = (XorShift128w ^ (XorShift128w >> 19)) ^ (Temp ^ (Temp >> 8)); LCG = (LCG * 1664525ul) + 1013904223ul; Temp = MWCx + MWCy + MWCc; MWCx = MWCy; MWCc = Temp >> 31; MWCy = Temp & 0x7fffffff; Temp = (LCG + XorShift128w + (MWCx << 1)) >> 24; cte_uint32_p p = (cte_pointer)&Float32; *p = ((Temp >> 9) & 0x7fffff) | 0x40000000ul; CurrentPhase = (Float32 - 3.0) * PI; } if(PrevAmp <= 0.0) { PrevFreq = CurrentFreq; PrevPhase= CurrentPhase - (CurrentFreq*CurrentPartSize); while(PrevPhase >= PI) { PrevPhase -= PI2; } while(PrevPhase < -PI) { PrevPhase += PI2; } } else if((Frame > 0) && (CurrentAmplitude <= 0.0)) { CurrentFreq = (((SinusoidFrames[Track][Frame-1]).Frequency / ((cte_double_t)SampleRate)) * Pitch) * PI2; CurrentPhase = PrevPhase + (PrevFreq * CurrentPartSize); while(CurrentPhase >= PI) { CurrentPhase -= PI2; } while(CurrentPhase < -PI) { CurrentPhase += PI2; } } InstAmp = MagTodB(PrevAmp); AmpInc = (MagTodB(CurrentAmplitude) - InstAmp) / ((cte_double_t)CurrentPartSize); InstFreq = PrevFreq; FreqInc = (CurrentFreq - PrevFreq) / ((cte_double_t) CurrentPartSize); for(PartPos = 0; PartPos < CurrentPartSize; PartPos++) { InstAmp += AmpInc; InstFreq += FreqInc; PrevPhase += InstFreq; while(PrevPhase >= PI) { PrevPhase -= PI2; } while(PrevPhase < -PI) { PrevPhase += PI2; } InstPhase = PrevPhase; SampleIndex = Sample + PartPos; if(((SampleIndex >= 0) && (SampleIndex < NewLength)) && (InstAmp >= 0.00001)) { Samples[SampleIndex] = Samples[SampleIndex] + (cte_float_t)((CreamTrackerSinus(InstPhase) * (0.00001 * CreamTrackerPower(10.0, InstAmp * 0.05))) * VolumeScale); } } } PrevFreq = CurrentFreq; PrevAmp = CurrentAmplitude; Sample += CurrentPartSize; } } for(Sample = 0; Sample < NewLength; Sample++) { if(Sample < OutputLength) { cte_uint8_p d = ((cte_uint8_p)((cte_pointer)OutputData)) + (Sample * OutputDataStepSize); *((cte_float_p)((cte_pointer)d)) = Samples[Sample]; } else { break; } } FreeMem(Samples); } cte_longbool_t WRSSynthese(cte_pointer pInputData, cte_int32_t pInputDataSize, cte_pointer pOutputData, cte_int32_t pOutputDataStepSize, cte_int32_t pOutputLength) { InputData = pInputData; InputDataPosition = 0; InputDataSize = pInputDataSize; OutputData = pOutputData; OutputDataStepSize = pOutputDataStepSize; OutputLength = pOutputLength; SinusoidFrames = NULL; TrackLengths = NULL; NumTracks = 0; if(Load()) { Synthese(1.0); } if(TrackLengths) { FreeMem(TrackLengths); } if(SinusoidFrames) { cte_int32_t Track; for(Track = 0; Track < NumTracks; Track++) { if(SinusoidFrames[Track]) { FreeMem(SinusoidFrames[Track]); } } FreeMem(SinusoidFrames); } return CTE_TRUE; } #undef SinusoidFrames #undef TrackLengths #undef SampleRate #undef NewLength #undef OriginalLength #undef FrameSize #undef Parts #undef NumTracks #undef PitchScale #undef TimeScale #undef HasPhases #undef InputData #undef InputDataSize #undef InputDataPosition #undef OutputData #undef OutputDataStepSize #undef OutputLength #undef Read #undef ReadByte #undef ReadInteger #undef ReadFloat #undef Load #undef MagTodB #undef Synthese // WRSSynthese END #endif #define Log2Div12 (0.0577622650466621) #define MinusLog2Div12 (-Log2Div12) void CreamTrackerCreate(PCreamTrackerInstance Instance, cte_int32_t SampleRate) { cte_int32_t i, j, Row; PCreamTrackerPattern Pattern; PCreamTrackerPatternNote PatternNote; #ifdef CreamTrackerSINCResampling InitResamplerSINC(); #endif FillChar(Instance, sizeof(TCreamTrackerInstance), 0); FillChar(&Instance->Orders, sizeof(TCreamTrackerOrders), 0xff); #ifdef CreamTrackerCode Instance->CodeData = GetMem(sizeof(TCreamTrackerCodeData)); FillChar(Instance->CodeData, sizeof(TCreamTrackerCodeData), 0); #endif #ifdef CreamTrackerGUI Instance->CodeData->CodeTextAllocated = NULL; Instance->CodeData->CodeTextSize = 0; #endif #ifndef CreamTrackerCompact Instance->RowHilightMinor = 4; Instance->RowHilightMajor = 16; Instance->Header.InitialSpeed = 6; Instance->Header.InitialTempo = 125; Instance->Header.GlobalVolume = 128; Instance->Header.MasterVolume = 0x80 | 48; Instance->Header.Panning = 0xfc; #endif Instance->SampleRate = SampleRate; Instance->InvSampleRate = 1.0 / ((cte_double_t)SampleRate); Instance->ClickRemovalFadeOutFactor = (cte_float_t)CreamTrackerExp((-1.0) / (((cte_double_t)SampleRate) * 0.002825)); // power(0.992 , 44100.0 / SampleRate); Instance->SmoothFactor = (cte_float_t)(1.0 - CreamTrackerExp((-1.0) / (((cte_double_t)SampleRate) * 0.002255555))); // power(0.99, 44100.0 / SampleRate); Instance->DCFilterCoef = (cte_float_t)(1.0 - (PI2 * (94.0 / ((cte_double_t)SampleRate)))); // -3dB @ 10 Hz (250*10Hz/40Hz) for(i = 0; i < 16; i++) { // power(2.0, i / 12.0) Instance->NoteFactorTable[i] = CreamTrackerExp(i * Log2Div12); } for(i = 0; i < 195; i++) { // 1.0 / power(2.0, (i / 12.0)) Instance->NoteToPeriodTable[i] = CreamTrackerExp(i * MinusLog2Div12); } for(i = 0; i < 256; i++) { Pattern = &(Instance->Patterns[i]); for(Row = 0; Row < 64; Row++) { for(j = 0; j < 32; j++) { PatternNote = &((*Pattern)[Row][j]); PatternNote->Note = 255; PatternNote->Instrument = 0; PatternNote->Volume = 255; PatternNote->Effect = 0; PatternNote->EffectParameter = 0; } } } Instance->Header.OrdNum = 1; Instance->Orders[0] = 0; for(i = 0; i < 32; i++) { Instance->Header.ChannelSettings[i] = ((((i & 15) >> 1) | ((i & 1) << 3)) && (-((!(i >> 4)) & 1))) | (i & (-((i >> 4) & 1))); Instance->ChannelPannings[i] = 0x27; } for(i = 0; i < 64; i++) { Instance->ChannelOrder[i] = i; } for(i = 0; i < 32; i++) { Instance->ChannelBuffers[i] = &Instance->Channels[i].Buffer[0]; } CreamTrackerReset(Instance); #ifdef CreamTrackerMultithreading CreamTrackerJobCreateThreads(Instance); #endif } #ifndef CreamTrackerMinimalPlayer void CreamTrackerDestroy(PCreamTrackerInstance Instance) { cte_int32_t i; #ifdef CreamTrackerMultithreading if(Instance->UseMultithreading) { Instance->ThreadsTerminated = CTE_TRUE; CreamTrackerJobWakeThreads(Instance); if(Instance->UseMultithreading) { i = 0; while((Instance->Threads > 0) && (i<1000)) { Sleep(10); i += 10; } } } CreamTrackerJobFreeThreads(Instance); #endif for(i = 1; i < 100; i++) { CreamTrackerDestroyInstrument(Instance, &Instance->Instruments[i]); } #ifdef CreamTrackerCode if(Instance->CodeData) { CreamTrackerCodeDestroy(Instance, Instance->CodeData); Instance->CodeData = NULL; } #endif FillChar(Instance, sizeof(TCreamTrackerInstance), 0); } #endif #ifdef CreamTrackerCode float CTE_STDCALL CreamTrackerRandomFloat() { static cte_uint32_t Seed = 0x321277daul; Seed = (Seed * 1664525ul) + 1013904223ul; cte_uint32_t RandomValue = ((Seed >> 9) & 0x7ffffful) | 0x40000000ul; cte_float_p p = (cte_pointer)&RandomValue; return ((*p - 3.0f) + 1.0f) * 0.5f; } void CTE_STDCALL CreamTrackerHostRandom() { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "call %P0\n\t" "pushl %%eax\n\t" "fldl (%%esp)\n\t" "popl %%eax\n\t" : : "i"(CreamTrackerRandomFloat)); #else __asm { call CreamTrackerRandomFloat push eax fld dword ptr [esp] pop eax } #endif } void CTE_STDCALL CreamTrackerHostKillDenormal(cte_uint32_p Value) { *Value &= (0xfffffffful + (((((*Value & 0x7f800000ul) + 0x800000ul) & 0x7f800000ul) - 0x1000000) >> 31)); } void CTE_STDCALL CreamTrackerHostDenormalKill(cte_float_t Value) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "movl %0, %%eax\n\t" "movl %%edx,%%eax\n\t" "andl $0x7f800000, %%eax\n\t" "addl $0x00800000, %%eax\n\t" "andl $0x7f800000, %%eax\n\t" "subl $0x01000000, %%eax\n\t" "shrl $31, %%eax\n\t" "decl %%eax\n\t" "andl %%edx, %%eax\n\t" "pushl %%eax\n\t" "fldl (%%esp)\n\t" "popl %%eax\n\t" : : "m"(Value)); #else __asm { mov eax, dword ptr Value mov edx, eax and eax, 0x7f800000 add eax, 0x00800000 and eax, 0x7f800000 sub eax, 0x01000000 shr eax, 31 dec eax and eax, edx push eax fld dword ptr [esp] pop eax } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostGetSampleRateEx(PCreamTrackerInstanceStubData InstanceStubData) { return InstanceStubData ? ((PCreamTrackerInstance)InstanceStubData->Instance)->SampleRate : 0; } void CTE_STDCALL CreamTrackerHostGetSampleRate() { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "leal (%%eax,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P1\n\t" : : "r"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetSampleRateEx)); #else __asm { lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetSampleRateEx } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostGetValueEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t Slot) { if(InstanceStubData) { if((InstanceStubData->Instance)&&((Slot >= 0) && (Slot <= 63))) { return ((PCreamTrackerInstance)InstanceStubData->Instance)->ValueMemory[Slot]; } else if((InstanceStubData->Channel)&&((Slot >= 64) && (Slot <= 127))) { return (*((PCreamTrackerChannel)InstanceStubData->Channel)->ValueMemory)[Slot-64]; } } return 0; } void CTE_STDCALL CreamTrackerHostGetValue(cte_int32_t Slot) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P2\n\t" : : "a"(Slot), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetValueEx)); #else __asm { push dword ptr Slot lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetValueEx } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostSetValueEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t Slot, cte_int32_t Value) { if(InstanceStubData) { if((InstanceStubData->Instance)&&((Slot >= 0) && (Slot <= 63))) { ((PCreamTrackerInstance)InstanceStubData->Instance)->ValueMemory[Slot] = Value; } else if((InstanceStubData->Channel)&&((Slot >= 64) && (Slot <= 127))) { (*((PCreamTrackerChannel)InstanceStubData->Channel)->ValueMemory)[Slot-64] = Value; } } return 0; } void CTE_STDCALL CreamTrackerHostSetValue(cte_int32_t Slot, cte_int32_t Value) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%ecx\n\t" "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P3\n\t" : : "a"(Slot), "c"(Value), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostSetValueEx)); #else __asm { push dword ptr Value push dword ptr Slot lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostSetValueEx } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostGetChannelValueEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t Channel, cte_int32_t Slot) { if(InstanceStubData) { if((InstanceStubData->Instance)&&((Slot >= 0) && (Slot <= 63))) { return ((PCreamTrackerInstance)InstanceStubData->Instance)->ValueMemory[Slot]; } else if((InstanceStubData->Channel)&&((Slot >= 64) && (Slot <= 127))) { return (*(((PCreamTrackerInstance)InstanceStubData->Instance)->Channels[Channel].ValueMemory))[Slot-64]; } } return 0; } void CTE_STDCALL CreamTrackerHostGetChannelValue(cte_int32_t Channel, cte_int32_t Slot) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "pushl %%ecx\n\t" "call %P3\n\t" : : "c"(Channel), "a"(Slot), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetChannelValueEx)); #else __asm { push dword ptr Slot push dword ptr Channel lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetChannelValueEx } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostSetChannelValueEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t Channel, cte_int32_t Slot, cte_int32_t Value) { if(InstanceStubData) { if((InstanceStubData->Instance)&&((Slot >= 0) && (Slot <= 63))) { ((PCreamTrackerInstance)InstanceStubData->Instance)->ValueMemory[Slot] = Value; } else if((InstanceStubData->Channel)&&((Slot >= 64) && (Slot <= 127))) { (*(((PCreamTrackerInstance)InstanceStubData->Instance)->Channels[Channel].ValueMemory))[Slot-64] = Value; } } return 0; } void CTE_STDCALL CreamTrackerHostSetChannelValue(cte_int32_t Channel, cte_int32_t Slot, cte_int32_t Value) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__("push %ebx"); __asm__ __volatile__( "push %%ecx\n\t" "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "pushl %%ebx\n\t" "call %P4\n\t" : : "b"(Channel), "a"(Slot), "c"(Value), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostSetChannelValue)); __asm__ __volatile__("pop %ebx"); #else __asm { push dword ptr Value push dword ptr Slot push dword ptr Channel lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostSetChannelValue } #endif } cte_pointer CTE_STDCALL CreamTrackerHostGetMemory(cte_int32_t Size) { return GetMem(Size); } void CTE_STDCALL CreamTrackerHostFreeMemory(cte_pointer ThePointer) { FreeMem(ThePointer); } void CTE_STDCALL CreamTrackerHostZeroMemory(cte_pointer ThePointer, cte_int32_t Size) { FillChar(ThePointer, Size, 0); } void CTE_STDCALL CreamTrackerHostCopyMemory(cte_pointer FromPointer, cte_pointer ToPointer, cte_int32_t Size) { Move(FromPointer, ToPointer, Size); } cte_pointer CTE_STDCALL CreamTrackerHostGetSampleDataEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t InstrumentIndex) { if (InstanceStubData) { if ((InstanceStubData->Instance) && ((InstrumentIndex >= 1) && (InstrumentIndex <= 99))) { return ((PCreamTrackerInstance)InstanceStubData->Instance)->Instruments[InstrumentIndex].Data; } } return NULL; } void CTE_STDCALL CreamTrackerHostGetSampleData(cte_int32_t InstrumentIndex) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P2\n\t" : : "a"(InstrumentIndex), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetSampleDataEx)); #else __asm { push dword ptr InstrumentIndex lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetSampleDataEx } #endif } cte_int32_t CTE_STDCALL CreamTrackerHostGetSampleSizeEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t InstrumentIndex) { if (InstanceStubData) { if ((InstanceStubData->Instance) && ((InstrumentIndex >= 1) && (InstrumentIndex <= 99))) { return ((PCreamTrackerInstance)InstanceStubData->Instance)->Instruments[InstrumentIndex].Header.Length; } } return 0; } void CTE_STDCALL CreamTrackerHostGetSampleSize(cte_int32_t InstrumentIndex) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P2\n\t" : : "a"(InstrumentIndex), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetSampleSizeEx)); #else __asm { push dword ptr InstrumentIndex lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetSampleSizeEx } #endif } void CTE_STDCALL CreamTrackerHostDebugInt(cte_int32_t Value) { } void CTE_STDCALL CreamTrackerHostDebugFloat(cte_float_t Value) { } cte_pointer CTE_STDCALL CreamTrackerHostGetChannelDataEx(PCreamTrackerInstanceStubData InstanceStubData, cte_int32_t ChannelIndex) { if(InstanceStubData && InstanceStubData->Instance && ((ChannelIndex >= 0) && (ChannelIndex < 32))) { return (void*)&(((PCreamTrackerInstance)InstanceStubData->Instance)->ChannelDataArray[ChannelIndex]); }else{ return NULL; } } void CTE_STDCALL CreamTrackerHostGetChannelData(cte_int32_t ChannelIndex) { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "push %%eax\n\t" "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P2\n\t" : : "a"(ChannelIndex), "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetChannelDataEx)); #else __asm { push dword ptr ChannelIndex lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetChannelDataEx } #endif } cte_pointer CTE_STDCALL CreamTrackerHostGetCurrentChannelDataEx(PCreamTrackerInstanceStubData InstanceStubData) { if(InstanceStubData && InstanceStubData->Instance && InstanceStubData->Channel && !((PCreamTrackerChannel)InstanceStubData->Channel)->Master) { cte_int32_t ChannelIndex = ((PCreamTrackerChannel)InstanceStubData->Channel)->Index; return (void*)&(((PCreamTrackerInstance)InstanceStubData->Instance)->ChannelDataArray[ChannelIndex & 31]); }else{ return NULL; } } void CTE_STDCALL CreamTrackerHostGetCurrentChannelData() { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P1\n\t" : : "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetCurrentChannelDataEx)); #else __asm { lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetCurrentChannelDataEx } #endif } cte_pointer CTE_STDCALL CreamTrackerHostGetCurrentMasterChannelDataEx(PCreamTrackerInstanceStubData InstanceStubData) { if(InstanceStubData && InstanceStubData->Instance && InstanceStubData->Channel) { cte_int32_t ChannelIndex; if(((PCreamTrackerChannel)InstanceStubData->Channel)->Master){ ChannelIndex = ((PCreamTrackerChannel)(((PCreamTrackerChannel)InstanceStubData->Channel)->Master))->Index; }else{ ChannelIndex = ((PCreamTrackerChannel)InstanceStubData->Channel)->Index; } return (void*)&(((PCreamTrackerInstance)InstanceStubData->Instance)->ChannelDataArray[ChannelIndex & 31]); }else{ return NULL; } } void CTE_STDCALL CreamTrackerHostGetCurrentMasterChannelData() { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "leal (%%edx,%%esi), %%eax\n\t" "pushl %%eax\n\t" "call %P1\n\t" : : "d"(-CreamTrackerInstanceStubDataSize), "i"(CreamTrackerHostGetCurrentMasterChannelDataEx)); #else __asm { lea eax, [esi - size TCreamTrackerInstanceStubData] push eax call CreamTrackerHostGetCurrentMasterChannelDataEx } #endif } #ifdef CreamTrackerFFT void CTE_STDCALL CreamTrackerHostFFT(PCreamTrackerFFTComplex InBuffer, PCreamTrackerFFTComplex OutBuffer, cte_int32_t NumSamples, cte_longbool_t Inverse) { cte_int32_t NumBits; cte_float_t Angle = 6.28318530718f * (Inverse ? -1.0f : 1.0f); NumBits = 0; for (cte_uint32_t i = 0; i < 31; i++) { if (NumSamples & (1 << i)) { NumBits = i; break; } } for (cte_int32_t i = 0; i < NumSamples; i++) { cte_uint32_t n = (cte_uint32_t)i, j = 0u; for (cte_int32_t k = 0; k < NumBits; k++) { j = (j << 1u) | (n & 1u); n >>= 1u; } OutBuffer[j].Re = InBuffer[i].Re; OutBuffer[j].Im = InBuffer[i].Im; } cte_int32_t BlockEnd = 1; for (cte_int32_t BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1) { cte_float_t delta_angle = Angle / BlockSize, alpha = (cte_float_t)CreamTrackerSinus(0.5f * delta_angle); alpha = 2.0f * (alpha * alpha); cte_float_t beta = (cte_float_t)CreamTrackerSinus(delta_angle); for (cte_int32_t i = 0; i < NumSamples; i += BlockSize) { cte_float_t ar = 1.0f, ai = 0.0f; cte_int32_t j = i; for (cte_int32_t n = 0; n < BlockEnd; n++) { cte_int32_t k = j + BlockEnd; cte_float_t tr = (ar * OutBuffer[k].Re) - (ai * OutBuffer[k].Im), ti = (ar * OutBuffer[k].Im) + (ai * OutBuffer[k].Re); OutBuffer[k].Re = OutBuffer[j].Re - tr; OutBuffer[k].Im = OutBuffer[j].Im - ti; OutBuffer[j].Re = OutBuffer[j].Re + tr; OutBuffer[j].Im = OutBuffer[j].Im + ti; cte_float_t delta_ar = (alpha * ar) + (beta * ai); ai -= (alpha * ai) - (beta * ar); ar -= delta_ar; j++; } } BlockEnd = BlockSize; } } #endif #ifdef CreamTrackerTRICode cte_pointer CreamTrackerGetExternalPointer(cte_pointer Context, cte_uchar_p Name) { PCreamTrackerCodeData CodeData = Context; if(CodeData) { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETSAMPLERATE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC0") ) { return &CreamTrackerHostGetSampleRate; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETVALUE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC1") ) { return &CreamTrackerHostGetValue; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTSETVALUE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC2") ) { return &CreamTrackerHostSetValue; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETCHANNELVALUE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC3") ) { return &CreamTrackerHostGetChannelValue; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTSETCHANNELVALUE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC4") ) { return &CreamTrackerHostSetChannelValue; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTKILLDENORMAL") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC5") ) { return &CreamTrackerHostKillDenormal; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTDENORMALKILL") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC6") ) { return &CreamTrackerHostDenormalKill; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTRANDOM") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC7") ) { return &CreamTrackerHostRandom; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETMEMORY") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC8") ) { return &CreamTrackerHostGetMemory; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTFREEMEMORY") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xC9") ) { return &CreamTrackerHostFreeMemory; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTZEROMEMORY") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCA") ) { return &CreamTrackerHostZeroMemory; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTCOPYMEMORY") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCB") ) { return &CreamTrackerHostCopyMemory; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETSAMPLEDATA") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCC") ) { return &CreamTrackerHostGetSampleData; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETSAMPLESIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCD") ) { return &CreamTrackerHostGetSampleSize; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTDEBUGINT") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCE") ) { return &CreamTrackerHostDebugInt; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTDEBUGFLOAT") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xCF") ) { return &CreamTrackerHostDebugFloat; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETCHANNELDATA") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xD0") ) { return &CreamTrackerHostGetChannelData; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETCURRENTCHANNELDATA") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xD1") ) { return &CreamTrackerHostGetCurrentChannelData; } else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTGETCURRENTMASTERCHANNELDATA") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xD2") ) { return &CreamTrackerHostGetCurrentMasterChannelData; } #ifdef CreamTrackerFFT else if ( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$HOSTFFT") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\xD3") ) { return &CreamTrackerHostFFT; } #endif } return NULL; } void CreamTrackerSetPublicPointer(cte_pointer Context, cte_uchar_p Name, cte_pointer ThePointer) { PCreamTrackerCodeData CodeData = Context; if(CodeData) { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$INSTANCEDATASIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x80") ) { if(*((cte_int32_p)ThePointer) > 0) { CodeData->InstanceDataSize = *((cte_int32_p)ThePointer); } } else { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INITIALIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x81") ) { CodeData->ProcInitialize = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$DEINITIALIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x82") ) { CodeData->ProcDeinitialize = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$RESET") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x83") ) { CodeData->ProcReset = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$SAMPLEPROCESS") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x84") ) { CodeData->SampleProcProcess = ThePointer; } else { if(CodeData->Synth) { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCEINITIALIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x85") ) { CodeData->SynthProcInstanceInitialize = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCEDEINITIALIZE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x86") ) { CodeData->SynthProcInstanceDeinitialize = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCERESET") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x87") ) { CodeData->SynthProcInstanceReset = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCENOTEON") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x88") ) { CodeData->SynthProcInstanceNoteOn = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCENOTEOFF") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x89") ) { CodeData->SynthProcInstanceNoteOff = ThePointer; } else if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$INSTANCEPROCESS") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x8A") ) { CodeData->SynthProcInstanceProcess = ThePointer; } } else { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$PROCESS") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x8B") ) { CodeData->GlobalProcProcess = ThePointer; } } else { if( #ifndef CreamTrackerCompact ComparePAnsiChar(Name, (cte_uchar_p)"CODE$FUNCTION$PROCESSPATTERNNOTE") || #endif ComparePAnsiChar(Name, (cte_uchar_p)"\x8C") ) { CodeData->GlobalProcProcessPatternNote = ThePointer; } } } } } } #else cte_int32_t CreamTrackerCodeIndex; #endif void CreamTrackerCodeInit(PCreamTrackerInstance Instance, PCreamTrackerCodeData CodeData) { cte_int32_t i, SampleRate; cte_pointer pp, pd; cte_int16_t OldCW; if(CodeData) { CodeData->Instances = CodeData->Synth ? 65 : 1; CodeData->Instance = NULL; #ifdef CreamTrackerTRICode if(CodeData->TRIInstance) { TRIFree(CodeData->TRIInstance); CodeData->TRIInstance = NULL; } #endif if(CodeData->InstanceData) { FreeMem(CodeData->InstanceData); CodeData->InstanceData = NULL; } #ifdef CreamTrackerTRICode if(CodeData->TRIData) #endif { CodeData->Instance = Instance; CodeData->InstanceDataSize = 0; CodeData->InstanceWorkDataSize = 0; CodeData->ProcInitialize = NULL; CodeData->ProcDeinitialize = NULL; CodeData->ProcReset = NULL; CodeData->SynthProcInstanceInitialize = NULL; CodeData->SynthProcInstanceDeinitialize = NULL; CodeData->SynthProcInstanceReset = NULL; CodeData->SynthProcInstanceNoteOn = NULL; CodeData->SynthProcInstanceNoteOff = NULL; CodeData->SynthProcInstanceProcess = NULL; CodeData->SampleProcProcess = NULL; CodeData->GlobalProcProcess = NULL; CodeData->GlobalProcProcessPatternNote = NULL; #ifdef CreamTrackerTRICode CodeData->TRIInstance = TRILink(CodeData->TRIData, CodeData->TRIDataSize, CodeData, CreamTrackerGetExternalPointer, CreamTrackerSetPublicPointer); if(CodeData->TRIInstance) #else CreamTrackerSetCodePointers(Instance, CreamTrackerCodeIndex); #endif { CodeData->InstanceWorkDataSize = sizeof(TCreamTrackerInstanceStubData) + CodeData->InstanceDataSize; if(CodeData->InstanceWorkDataSize > 0) { CodeData->InstanceData = GetMem(CodeData->InstanceWorkDataSize * CodeData->Instances); FillChar(CodeData->InstanceData,CodeData->InstanceWorkDataSize * CodeData->Instances, 0); } else { CodeData->InstanceData = NULL; } if(CodeData->ProcInitialize) { pp = CodeData->ProcInitialize; pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[(CodeData->Instances - 1) * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = NULL; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); SampleRate = Instance->SampleRate; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "pushl %3\n\t" "pushl %3\n\t" "movl %4,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(SampleRate), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov edi,esp mov esi,dword ptr pd push dword ptr SampleRate push dword ptr SampleRate mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } if(CodeData->Synth && CodeData->SynthProcInstanceInitialize && CodeData->InstanceData) { pp = CodeData->SynthProcInstanceInitialize; for(i = 0; i < 64; i++) { pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[i * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = &Instance->Channels[i]; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "pushl %3\n\t" "movl %4,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(i), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov edi,esp mov esi,dword ptr pd push dword ptr i mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } } #ifdef CreamTrackerTRICode } else { CodeData->Instance = NULL; CodeData->InstanceDataSize = 0; CodeData->InstanceWorkDataSize = 0; #endif } } } } #ifndef CreamTrackerMinimalPlayer void CreamTrackerCodeDestroy(PCreamTrackerInstance Instance, PCreamTrackerCodeData CodeData) { cte_int32_t i; cte_pointer pp, pd; cte_int16_t OldCW; if(CodeData) { if(CodeData->Synth) { if(CodeData->SynthProcInstanceDeinitialize && CodeData->InstanceData) { pp = CodeData->SynthProcInstanceDeinitialize; for(i = 0; i < 64; i++) { pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[i * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = &Instance->Channels[i]; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "movl %3,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } } } if(CodeData->ProcDeinitialize) { pp = CodeData->ProcDeinitialize; pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[(CodeData->Instances - 1) * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = NULL; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "movl %3,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } if(CodeData->CodeText) { FreeMem(CodeData->CodeText); CodeData->CodeText = NULL; } #ifdef CreamTrackerTRICode if(CodeData->TRIData) { FreeMem(CodeData->TRIData); CodeData->TRIData = NULL; } if(CodeData->TRIInstance) { TRIFree(CodeData->TRIInstance); CodeData->TRIInstance = NULL; } #endif if(CodeData->InstanceData) { FreeMem(CodeData->InstanceData); CodeData->InstanceData = NULL; } FreeMem(CodeData); } } #endif void CreamTrackerCodeReset(PCreamTrackerInstance Instance, PCreamTrackerCodeData CodeData) { cte_int32_t i; cte_pointer pp, pd; cte_int16_t OldCW; if(CodeData && CodeData->Instance #ifdef CreamTrackerTRICode && CodeData->TRIData && CodeData->TRIInstance #endif ) { if(CodeData->InstanceData && (CodeData->InstanceWorkDataSize > 0)) { FillChar(CodeData->InstanceData, CodeData->InstanceWorkDataSize * CodeData->Instances, 0); } if(CodeData->ProcReset) { pp = CodeData->ProcReset; pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[(CodeData->Instances - 1) * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = NULL; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "movl %3,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } if(CodeData->Synth) { if(CodeData->SynthProcInstanceReset && CodeData->InstanceData && (CodeData->InstanceWorkDataSize > 0)) { pp = CodeData->SynthProcInstanceReset; for(i = 0; i < 64; i++) { pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[i * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = &Instance->Channels[i]; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "movl %3,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(pp) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } } } } } #endif #ifndef CreamTrackerMinimalPlayer void CreamTrackerDestroyInstrument(PCreamTrackerInstance Instance, PCreamTrackerInstrument Instrument) { if(Instrument->Data) { FreeMem(Instrument->Data); Instrument->Data = NULL; } #ifdef CreamTrackerSINCResampling if(Instrument->SINCLeftData) { FreeMem(Instrument->SINCLeftData); Instrument->SINCLeftData = NULL; } Instrument->SINCRightData = NULL; #else if(Instrument->MixData) { FreeMem(Instrument->MixData); Instrument->MixData = NULL; } #endif if(Instrument->RawData) { FreeMem(Instrument->RawData); Instrument->RawData = NULL; } #ifdef CreamTrackerCode if(Instrument->CodeData) { CreamTrackerCodeDestroy(Instance, Instrument->CodeData); Instrument->CodeData = NULL; } #endif } void CreamTrackerDestroySampleData(PCreamTrackerInstance Instance, PCreamTrackerInstrument Instrument) { if(Instrument->Data) { FreeMem(Instrument->Data); Instrument->Data = NULL; } #ifdef CreamTrackerSINCResampling if(Instrument->SINCLeftData) { FreeMem(Instrument->SINCLeftData); Instrument->SINCLeftData = NULL; } Instrument->SINCRightData = NULL; #else if(Instrument->MixData) { FreeMem(Instrument->MixData); Instrument->MixData = NULL; } #endif } #endif void CreamTrackerReset(PCreamTrackerInstance Instance) { cte_int16_t i, j; PCreamTrackerChannel Channel; PCreamTrackerPatternNote PatternNote; #ifdef CreamTrackerCode FillChar(&Instance->ValueMemory, sizeof(TCreamTrackerValueMemory), 0); #endif for(i = 0; i < 32; i++) { Channel = &Instance->Channels[i]; FillChar(Channel, sizeof(TCreamTrackerChannel), 0); Channel->Instance = Instance; #ifdef CreamTrackerCode Channel->ValueMemory = &Instance->ChannelValueMemory[i]; #endif Channel->Index = i; Channel->Active = CTE_FALSE; Channel->BaseNote = 255; Channel->NoteCutCounter = 0; Channel->ChannelVolume = 64; Channel->Enabled = !(Instance->Header.ChannelSettings[i] & 0x80); Channel->Muted = (Instance->Header.Panning == 0xfc) && (Instance->ChannelPannings[i] & 0x40); if((Instance->Header.Panning == 0xfc) && (Instance->ChannelPannings[i] & 0x80)) { Channel->Panning = 128; } else if((Instance->Header.Panning == 0xfc) && (Instance->ChannelPannings[i] & 0x20)) { Channel->Panning = (((Instance->ChannelPannings[i] & 0xf) << 8) + 8) / 15; } else { switch(Instance->Header.ChannelSettings[i] & 0x7f) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: Channel->Panning = 64; break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: Channel->Panning = 192; break; default: Channel->Panning = 128; break; } } Channel->Glissando = CTE_FALSE; #ifdef CreamTrackerEffectQxx Channel->RetrigCounter = 0; #endif PatternNote = &Channel->PatternNote; PatternNote->Note = 255; PatternNote->Instrument = 0; PatternNote->Volume = 255; PatternNote->Effect = 0; PatternNote->EffectParameter = 0; Instance->ChannelOrder[i] = i; } i = 0; while((i + 1) < 32) { if((Instance->Header.ChannelSettings[Instance->ChannelOrder[i]] & 0x1f) > (Instance->Header.ChannelSettings[Instance->ChannelOrder[i + 1]] & 0x1f)) { j = Instance->ChannelOrder[i]; Instance->ChannelOrder[i] = Instance->ChannelOrder[i + 1]; Instance->ChannelOrder[i + 1] = j; i += (i > 0) ? -1 : 1; } else { i++; } } for(i = 32; i < 64; i++) { Channel = &Instance->Channels[i]; Move(&Instance->Channels[i - 32], Channel, sizeof(TCreamTrackerChannel)); Channel->Instance = Instance; Channel->Master = &Instance->Channels[i-32]; #ifdef CreamTrackerCode Channel->ValueMemory = &Instance->ChannelValueMemory[i]; #endif Channel->Index = i; Instance->ChannelOrder[i] = i; } Instance->Speed = (!Instance->Header.InitialSpeed) ? 6 : Instance->Header.InitialSpeed; Instance->Tempo = (Instance->Header.InitialTempo < 33) ? 125 : Instance->Header.InitialTempo; Instance->GlobalVolume = Instance->Header.GlobalVolume; Instance->HertzRatio = (8363 * 1712) / Instance->SampleRate; CreamTrackerUpdateTempo(Instance); Instance->NextRow = 0; Instance->NextOrder = 0; #ifndef CreamTrackerCompact Instance->ForceOrder = -1; Instance->ForceRow = -1; #endif Instance->Row = 0; Instance->Order = 0xff; Instance->Pattern = 0xff; Instance->Tick = 0x3ffffffful; Instance->AbsoluteTick = 0x3ffffffful; Instance->FrameDelay = 0; Instance->PatternDelay = 0; Instance->PatternDelayRowCounter = 0; Instance->FirstTick = CTE_FALSE; Instance->FirstRowTick = CTE_FALSE; Instance->VeryFirstTick = CTE_TRUE; Instance->TrackEnd = CTE_FALSE; Instance->PatternLoop = CTE_FALSE; Instance->RepeatCounter = -1; FillChar(&Instance->RepeatCounters, sizeof(Instance->RepeatCounters), 0); FillChar(&Instance->RepeatRowCounters, sizeof(Instance->RepeatRowCounters), 0); Instance->LoopRow = 0; Instance->LoopRowCounter = 0; #ifdef CreamTrackerCode FillChar(&Instance->ChannelDataArray, sizeof(TCreamTrackerChannelDataArray), 0); CreamTrackerCodeReset(Instance, Instance->CodeData); for(i = 1; i < 100; i++) { CreamTrackerCodeReset(Instance, Instance->Instruments[i].CodeData); } #endif } void CreamTrackerUpdateTempo(PCreamTrackerInstance Instance) { if(Instance->Tempo < 32) { Instance->Tempo = 32; } Instance->TickSamples = (Instance->SampleRate * (5 * 128)) / (Instance->Tempo << 8); // (2.5 * SampleRate) / Tempo } void CreamTrackerFixUpSample(PCreamTrackerInstance Instance, PCreamTrackerInstrument Instrument) { cte_float_p MixData, Src, Dst; cte_int32_t Index, Index2, First, Last; if (Instrument->Data && (Instrument->Header.Length > 0)) { if(Instrument->Header.LoopStart < 0) { Instrument->Header.LoopStart = 0; } else if(Instrument->Header.LoopStart >= Instrument->Header.Length) { Instrument->Header.LoopStart = Instrument->Header.Length - 1; } if(Instrument->Header.LoopEnd < 0) { Instrument->Header.LoopEnd = 0; } else if(Instrument->Header.LoopEnd >= Instrument->Header.Length) { Instrument->Header.LoopEnd = Instrument->Header.Length; } if(Instrument->Header.CrossfadeStart < Instrument->Header.LoopStart) { Instrument->Header.CrossfadeStart = Instrument->Header.LoopStart; } if(Instrument->Header.CrossfadeStart >= Instrument->Header.LoopEnd) { Instrument->Header.CrossfadeStart = Instrument->Header.LoopEnd - 1; } #ifdef CreamTrackerSINCResampling MixData = GetMem((Instrument->Header.Length + (TotalFixUpSafeAdditionalSampleLength * 4)) * (2 * sizeof(cte_float_t))); FillChar(MixData, (Instrument->Header.Length + (TotalFixUpSafeAdditionalSampleLength * 4)) * (2 * sizeof(cte_float_t)), 0); #else if(Instrument->MixData) { FreeMem(Instrument->MixData); } Instrument->MixData = GetMem((Instrument->Header.Length + (TotalFixUpSafeAdditionalSampleLength * 4)) * (2 * sizeof(cte_float_t))); FillChar(Instrument->MixData, (Instrument->Header.Length + (TotalFixUpSafeAdditionalSampleLength * 4)) * (2 * sizeof(cte_float_t)), 0); MixData = Instrument->MixData; #endif Src = Instrument->Data; Dst = (cte_pointer)&((cte_float_p)MixData)[TotalFixUpSafeAdditionalSampleLength * 4]; Move(Src, Dst, Instrument->Header.Length * (2 * sizeof(cte_float_t))); Last = Instrument->Header.Length - 1; for(Index = 1; Index < FixUpSampleLength; Index++) { Dst[((-Index) << 1) + 0] = Src[0]; Dst[((-Index) << 1) + 1] = Src[1]; Dst[((Last + Index) << 1) + 0] = Src[(Last << 1) + 0]; Dst[((Last + Index) << 1) + 1] = Src[(Last << 1) + 1]; } if(Instrument->Header.Flags & 1) { if(Instrument->Header.LoopStart >= Instrument->Header.LoopEnd) { Instrument->Header.Flags &= ~1; } else { #ifdef CreamTrackerSampleLoopCrossFade if(((Instrument->Header.CrossfadeType & 3) >= 1) && ((Instrument->Header.CrossfadeType & 3) <= 2)) { cte_int32_t Index3, Len, Channel, Tries; cte_float_t s0, s1, x; Len = Instrument->Header.LoopEnd - Instrument->Header.CrossfadeStart; Index2 = Instrument->Header.LoopStart - Len; for(Channel = 0; Channel < 2; Channel++) { for(Index = 0; Index < Len; Index++) { s0 = Src[((Instrument->Header.CrossfadeStart + Index) << 1) + Channel]; Index3 = Index2 + Index; for(Tries = 10; Tries > 0; Tries--) { if(Index3 < 0) { Index3 = -Index3; } else if(Index3 > Instrument->Header.LoopEnd) { Index3 = Instrument->Header.LoopEnd - (Index3 - Instrument->Header.LoopEnd); } else { break; } } if(Index3<0) { Index3 = 0; } else if(Index3 > Instrument->Header.LoopEnd) { Index3 = Instrument->Header.LoopEnd; } s1 = Src[(Index3 << 1) + Channel]; x = (cte_float_t)((cte_double_t)Index / (cte_double_t)Len); switch(Instrument->Header.CrossfadeType & 3) { case 1: Dst[((Instrument->Header.CrossfadeStart + Index) << 1) + Channel] = (cte_float_t)((s0 * (1.0 - x)) + (s1 * x)); break; case 2: x = (cte_float_t)((x * 0.5f) * PI); Dst[((Instrument->Header.CrossfadeStart + Index) << 1) + Channel] = (cte_float_t)((s0 * CreamTrackerCosinus(x)) + (s1 * CreamTrackerSinus(x))); break; } } } } #endif First = Instrument->Header.LoopStart; Last = Instrument->Header.LoopEnd; if((First < Last) && ((First >= 0) && (Last<=Instrument->Header.Length))) { for(Index = 0; Index < FixUpSampleLength; Index++) { Index2 = First + Index; if(Index2 >= Last) { Index2 = Index2 + (First - Last); if(Index2Header.Length) { Dst[((Last + Index) << 1) + 0] = Src[(Index2 << 1) + 0]; Dst[((Last + Index) << 1) + 1] = Src[(Index2 << 1) + 1]; } else { Dst[((Last+Index) << 1) + 0] = Src[((Instrument->Header.Length - 1) << 1) + 0]; Dst[((Last+Index) << 1) + 1] = Src[((Instrument->Header.Length - 1) << 1) + 1]; } } } } } #ifdef CreamTrackerSINCResampling { cte_int32_t Len; if(Instrument->SINCLeftData) { FreeMem(Instrument->SINCLeftData); Instrument->SINCLeftData = NULL; } Instrument->SINCRightData = NULL; Len = Instrument->Header.Length + (TotalFixUpSafeAdditionalSampleLength * 4); Instrument->SINCLeftData = GetMem(Len * (2 * sizeof(cte_float_t))); FillChar(Instrument->SINCLeftData, Len * (2 * sizeof(cte_float_t)),0); Instrument->SINCRightData = (cte_pointer)(&(((cte_float_p)Instrument->SINCLeftData)[Len])); Dst = (cte_pointer)MixData; Index2 = 0; for(Index = 0; Index < Len; Index++) { Instrument->SINCLeftData[Index] = Dst[Index2++]; Instrument->SINCRightData[Index] = Dst[Index2++]; } } #endif #ifdef CreamTrackerSINCResampling FreeMem(MixData); #endif } } #ifdef CreamTrackerStrippedPatterns cte_longbool_t CreamTrackerLoadPatternsAsSingleChain(PCreamTrackerInstance Instance, cte_pointer InputData, cte_int32_t InputDataSize) { cte_int32_t RowIndex, ChannelIndex, PatternIndex, RowDeltaStreamSize, FlagStreamSize, NoteStreamSize, InstrumentStreamSize, VolumeStreamSize, EffectStreamSize, EffectParameterStreamSize, RowDeltaStreamIndex, FlagStreamIndex, NoteStreamIndex, InstrumentStreamIndex, VolumeStreamIndex, EffectStreamIndex, EffectParameterStreamIndex; cte_uint8_p RowDeltaStream, FlagStream, NoteStream, InstrumentStream, VolumeStream, EffectStream, EffectParameterStream, p; cte_uint8_t LastFlags, LastNote, LastInstrument, LastVolume, LastEffect, LastEffectParameter; PCreamTrackerPatternNote PatternNote; PCreamTrackerPattern Pattern; p = (cte_pointer)InputData; RowDeltaStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); FlagStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); NoteStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); InstrumentStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); VolumeStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t);; EffectStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); EffectParameterStreamSize = *((cte_int32_p)p); p += sizeof(cte_int32_t); RowDeltaStream = (cte_pointer)p; p += RowDeltaStreamSize; FlagStream = (cte_pointer)p; p += FlagStreamSize; NoteStream = (cte_pointer)p; p += NoteStreamSize; InstrumentStream = (cte_pointer)p; p += InstrumentStreamSize; VolumeStream = (cte_pointer)p; p += VolumeStreamSize; EffectStream = (cte_pointer)p; p += EffectStreamSize; EffectParameterStream = (EffectParameterStreamSize > 0) ? ((cte_pointer)p) : NULL; if(RowDeltaStreamSize > 0) { FlagStreamIndex = 0; NoteStreamIndex = 0; InstrumentStreamIndex = 0; VolumeStreamIndex = 0; EffectStreamIndex = 0; EffectParameterStreamIndex = 0; LastFlags = 0; LastNote = 0; LastInstrument = 0; LastVolume = 0; LastEffect = 0; LastEffectParameter = 0; RowIndex = 0; ChannelIndex = 0; for(RowDeltaStreamIndex = 0; RowDeltaStreamIndex < RowDeltaStreamSize; RowDeltaStreamIndex++) { RowIndex += RowDeltaStream[RowDeltaStreamIndex]; LastFlags += FlagStream[FlagStreamIndex]; FlagStreamIndex++; switch(LastFlags) { case 64: // Skip pattern Instance->Orders[RowIndex >> 6] = 0xfe; break; case 64 | 32: // Track end Instance->Orders[RowIndex >> 6] = 0xff; break; case 128: // Channel end RowIndex = 0; ChannelIndex++; if(ChannelIndex>=32) { goto next; } break; default: PatternIndex = RowIndex >> 6; Instance->Orders[RowIndex >> 6] = PatternIndex; Pattern = &Instance->Patterns[PatternIndex]; PatternNote = &((*Pattern)[RowIndex & 0x3f][ChannelIndex]); if(LastFlags & 1) { LastNote += NoteStream[NoteStreamIndex]; NoteStreamIndex++; PatternNote->Note = LastNote; } if(LastFlags & 2) { LastInstrument += InstrumentStream[InstrumentStreamIndex]; InstrumentStreamIndex++; PatternNote->Instrument = LastInstrument; } if(LastFlags & 4) { LastVolume += VolumeStream[VolumeStreamIndex]; VolumeStreamIndex++; PatternNote->Volume = LastVolume; } if(LastFlags & 8) { LastEffect += EffectStream[EffectStreamIndex]; EffectStreamIndex++; PatternNote->Effect = LastEffect; } if(LastFlags & 16) { LastEffectParameter += EffectParameterStream[EffectParameterStreamIndex]; EffectParameterStreamIndex++; PatternNote->EffectParameter = LastEffectParameter; } } } next: if(1) { } } return CTE_TRUE; } #endif #define InputData CreamTrackerLoadInputData #define InputDataPosition CreamTrackerLoadInputDataPosition #define InputDataSize CreamTrackerLoadInputDataSize cte_uint8_p InputData; cte_int32_t InputDataPosition, InputDataSize; cte_int32_t Read(cte_pointer Data, cte_int32_t Bytes) { cte_int32_t Len = InputDataSize - InputDataPosition; if(Len < 0) { Len = 0; } else if (Len > Bytes) { Len = Bytes; } if(Len > 0) { Move(&InputData[InputDataPosition], Data, Len); InputDataPosition += Len; } return Len; } #undef InputData #undef InputDataPosition #undef InputDataSize #define LoadCode CreamTrackerLoadCode #define Instance CreamTrackerLoadInstance PCreamTrackerInstance Instance; #ifdef CreamTrackerCode cte_longbool_t LoadCode(PCreamTrackerCodeData* CodeData, cte_longbool_t Synth) { #ifndef CreamTrackerTRICode cte_int32_t TRIDataSize; #endif #ifndef CreamTrackerMinimalPlayer if(*CodeData) { CreamTrackerCodeDestroy(Instance, *CodeData); } #endif *CodeData = GetMem(sizeof(TCreamTrackerCodeData)); FillChar(*CodeData, sizeof(TCreamTrackerCodeData), 0); (*CodeData)->Synth = Synth; #ifdef CreamTrackerTRICode if (Read(&(*CodeData)->TRIDataSize, sizeof(cte_int32_t)) != sizeof(cte_int32_t)) { return CTE_FALSE; } #else if (Read(&TRIDataSize, sizeof(cte_int32_t)) != sizeof(cte_int32_t)) { return CTE_FALSE; } #endif if(Read(&(*CodeData)->CodeTextSize, sizeof(cte_int32_t)) != sizeof(cte_int32_t)) { return CTE_FALSE; } #ifdef CreamTrackerTRICode if ((*CodeData)->TRIDataSize > 0) { (*CodeData)->TRIData = GetMem((*CodeData)->TRIDataSize); if(Read((*CodeData)->TRIData, (*CodeData)->TRIDataSize) != (*CodeData)->TRIDataSize) { return CTE_FALSE; } } #else CreamTrackerLoadInputDataPosition += (TRIDataSize > 0) ? TRIDataSize : 0; #endif (*CodeData)->CodeTextAllocated = (*CodeData)->CodeTextSize; if((*CodeData)->CodeTextSize > 0) { (*CodeData)->CodeText = GetMem((*CodeData)->CodeTextSize); if(Read((*CodeData)->CodeText, (*CodeData)->CodeTextSize) != (*CodeData)->CodeTextSize) { return CTE_FALSE; } } CreamTrackerCodeInit(Instance, *CodeData); return CTE_TRUE; } #endif #undef Instance cte_longbool_t CreamTrackerLoad(PCreamTrackerInstance Instance, cte_pointer InputData, cte_int32_t InputDataSize) { #ifdef CreamTrackerDPCM4 typedef cte_uint8_t TDPCM4Table[16]; typedef TDPCM4Table *PDPCM4Table; #endif cte_int32_t NextInputDataPosition, i, j, k, Row, SignedOffset; #ifdef CreamTrackerADPCMIMA cte_int32_t Sample; #endif cte_uint16_t InstrumentPointers[100], PatternPointers[256]; PCreamTrackerInstrument Instrument; PCreamTrackerPattern Pattern; PCreamTrackerPatternNote PatternNote; #ifdef CreamTrackerNormalPatterns cte_uint16_t PatternLength; #endif cte_uint8_p Buffer; #ifdef CreamTrackerNormalPatterns cte_uint8_t ChannelNr, p; #endif #if defined(CreamTrackerADPCMIMA) || defined(CreamTrackerNormalPatterns) cte_uint8_t b; #endif cte_float_p s; cte_uint8_p sb; cte_uint16_p sw; #ifdef CreamTrackerDPCM4 PDPCM4Table DPCM4Table; #endif #ifdef CreamTrackerADPCMIMA TCreamTrackerADPCMIMAState ADPCMIMAState; #endif TCreamTrackerChunk Chunk; cte_longbool_t BufferNeedToFree, IsStripped; #ifdef CreamTrackerDeltaPCM cte_uint8_t lb; cte_uint16_t lw; #endif #ifdef CreamTrackerCode cte_uint16_t OldCW; cte_pointer p1, p2, pd; #endif CreamTrackerLoadInstance = Instance; CreamTrackerLoadInputData = InputData; CreamTrackerLoadInputDataPosition = 0; CreamTrackerLoadInputDataSize = InputDataSize; #define InputData CreamTrackerLoadInputData #define InputDataPosition CreamTrackerLoadInputDataPosition #define InputDataSize CreamTrackerLoadInputDataSize if (Read(&Instance->Header, sizeof(TCreamTrackerHeader)) != sizeof(TCreamTrackerHeader)) { return CTE_FALSE; } #ifndef CreamTrackerNoChecks if((((Instance->Header.Signature[0] != (cte_uchar_t)'S') || (Instance->Header.Signature[1] != (cte_uchar_t)'C') || (Instance->Header.Signature[2] != (cte_uchar_t)'R') || (Instance->Header.Signature[3] != (cte_uchar_t)'M')) && ((Instance->Header.Signature[0] != (cte_uchar_t)'C') || (Instance->Header.Signature[1] != (cte_uchar_t)'R') || ((Instance->Header.Signature[2] != (cte_uchar_t)'E') && (Instance->Header.Signature[2] != (cte_uchar_t)'S')) || (Instance->Header.Signature[3] != (cte_uchar_t)'M'))) || (Instance->Header.EOFChar != 0x1a) || (Instance->Header.Type_ != 0x10) || (Instance->Header.OrdNum > 256) || (Instance->Header.InsNum > 99) || (Instance->Header.PatNum > 256)) { return CTE_FALSE; } #endif Instance->CreamTrackerModule = (Instance->Header.Signature[0] == (cte_uchar_t)'C') && (Instance->Header.Signature[1] == (cte_uchar_t)'R') && ((Instance->Header.Signature[2] == (cte_uchar_t)'E') || (Instance->Header.Signature[2] == (cte_uchar_t)'S')) && (Instance->Header.Signature[3] == (cte_uchar_t)'M'); IsStripped = Instance->CreamTrackerModule && (Instance->Header.Signature[2] == 'S'); #ifndef CreamTrackerCompact if (Instance->CreamTrackerModule) { Instance->RowHilightMinor = Instance->Header.RowHilightMinor; Instance->RowHilightMajor = Instance->Header.RowHilightMajor; } #endif i = Instance->Header.OrdNum; if(!i) { i = 1; } FillChar(&Instance->Orders, sizeof(TCreamTrackerOrders), 0xff); if(IsStripped) { Instance->Orders[0] = 0; } else { if(Read(&Instance->Orders,i) != i) { return CTE_FALSE; } if(Instance->Header.OrdNum & 1) { if((InputDataPosition < InputDataSize) && (((cte_uint8_p)InputData)[InputDataPosition] == 255)) { InputDataPosition++; } } } Instance->Header.OrdNum = i; if((Instance->Header.MasterVolume & 0x7f) < 0x10) { Instance->Header.MasterVolume = (Instance->Header.MasterVolume & 0x80) | 0x10; } if(Read((cte_pointer)&InstrumentPointers[1], Instance->Header.InsNum * sizeof(cte_uint16_t)) != (Instance->Header.InsNum * sizeof(cte_uint16_t))) { return CTE_FALSE; } if(IsStripped) { if(Read((cte_pointer)&PatternPointers[0], sizeof(cte_uint16_t)) != sizeof(cte_uint16_t)) { return CTE_FALSE; } } else { if(Read((cte_pointer)&PatternPointers[0], (Instance->Header.PatNum * sizeof(cte_uint16_t))) != (Instance->Header.PatNum * sizeof(cte_uint16_t))) { return CTE_FALSE; } } FillChar(&Instance->ChannelPannings, sizeof(TCreamTrackerChannelPannings), 0); if(Instance->Header.Panning == 0xfc) { if(Read((cte_pointer)&Instance->ChannelPannings, sizeof(TCreamTrackerChannelPannings)) != sizeof(TCreamTrackerChannelPannings)) { return CTE_FALSE; } if(!Instance->CreamTrackerModule) { for(i = 0; i < 32; i++) { Instance->ChannelPannings[i] = Instance->ChannelPannings[i] & ~0x40; } } } for(i = 1; i <= Instance->Header.InsNum; i++) { if(InstrumentPointers[i]) { Instrument = &Instance->Instruments[i]; InputDataPosition = InstrumentPointers[i] << 4; if(Read(&Instrument->Header, sizeof(TCreamTrackerInstrumentHeader)) != sizeof(TCreamTrackerInstrumentHeader)) { return CTE_FALSE; } #ifdef CreamTrackerCode if(((Instrument->Header.Signature[0] == (cte_uchar_t)'C') && (Instrument->Header.Signature[1] == (cte_uchar_t)'R') && (Instrument->Header.Signature[2] == (cte_uchar_t)'S') && (Instrument->Header.Signature[3] == (cte_uchar_t)'I')) && (Instrument->Header.InstrumentType == 8)) { InputDataPosition = Instrument->Header.ExtOffset; #ifndef CreamTrackerTRICode CreamTrackerCodeIndex = i; #endif if(!LoadCode(&Instrument->CodeData, CTE_TRUE)) { return CTE_FALSE; } } else if(((Instrument->Header.Signature[0] == (cte_uchar_t)'C') && (Instrument->Header.Signature[1] == (cte_uchar_t)'R') && (Instrument->Header.Signature[2] == (cte_uchar_t)'S') && (Instrument->Header.Signature[3] == (cte_uchar_t)'S')) && (Instrument->Header.InstrumentType == 9)) { InputDataPosition = Instrument->Header.ExtOffset; #ifndef CreamTrackerTRICode CreamTrackerCodeIndex = i; #endif if(!LoadCode(&Instrument->CodeData, CTE_FALSE)) { return CTE_FALSE; } PCreamTrackerCodeData CodeData = (PCreamTrackerCodeData)Instrument->CodeData; p1 = CodeData->SampleProcProcess; if(p1) { p2 = NULL; pd = (cte_pointer)&(((cte_uint8_p)(CodeData->InstanceData))[(CodeData->Instances - 1) * CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = NULL; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %1\n\t" "fldcw %2\n\t" "pushl %%eax\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %3,%%esi\n\t" "pushl %4\n\t" "movl %5,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "movl %%eax,28(%%esp)\n\t" "popal\n\t" "movl %%eax,%0\n\t" "popl %%eax\n\t" "fldcw %1\n\t" : "=m"(k) : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(p2), "m"(p1) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW push eax pushad mov edi,esp mov esi,dword ptr pd push dword ptr p2 mov eax,dword ptr p1 mov ebp,edi call eax mov esp,ebp mov dword ptr [esp+28],eax popad mov dword ptr k,eax pop eax fldcw word ptr OldCW } #endif Instrument->Header.Length = k; if(Instrument->Header.Length > 0) { Instrument->Data = GetMem(Instrument->Header.Length * (4 * sizeof(cte_float_t))); FillChar(Instrument->Data, Instrument->Header.Length * (2 * sizeof(cte_float_t)), 0); p2 = Instrument->Data; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %2,%%esi\n\t" "pushl %3\n\t" "movl %4,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" "fldcw %0\n\t" : : "m"(OldCW), "m"(CreamTrackerCW), "m"(pd), "m"(p2), "m"(p1) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW pushad mov edi,esp mov esi,dword ptr pd push dword ptr p2 mov eax,dword ptr p1 mov ebp,edi call eax mov esp,ebp popad fldcw word ptr OldCW } #endif } else { Instrument->Header.Length = 1; Instrument->Data = GetMem(1 * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, 1 * (2 * sizeof(cte_float_t)), 0); } } if(Instrument->Data) { CreamTrackerFixUpSample(Instance, Instrument); } } else #endif if(((Instrument->Header.Signature[0] == (cte_uchar_t)'S') && (Instrument->Header.Signature[1] == (cte_uchar_t)'C') && (Instrument->Header.Signature[2] == (cte_uchar_t)'R') && (Instrument->Header.Signature[3] == (cte_uchar_t)'S')) && (Instrument->Header.InstrumentType == 1)) { if ((Instrument->Header.Flags & 1) && ((Instrument->Header.LoopStart >= Instrument->Header.Length) || (Instrument->Header.LoopEnd <= Instrument->Header.LoopStart))) { Instrument->Header.Flags = Instrument->Header.Flags & ~1; } if(Instrument->Header.Length > 0) { InputDataPosition = (cte_int32_t)(((((cte_int32_t)(Instrument->Header.Offset[0])) << 16) | (((cte_int32_t)(Instrument->Header.Offset[2])) << 8) | ((cte_int32_t)(Instrument->Header.Offset[1]))) << 4); SignedOffset = -(((Instance->Header.FileFormatInformation == 2) ? 1 : 0) << (7 + (((Instrument->Header.Flags >> 2) & 1) << 3))); switch(Instrument->Header.Format) { #ifdef CreamTrackerPCM case 0: // Uncompressed non-delta PCM k = Instrument->Header.Length; if(Instrument->Header.Flags & 2) { k += k; } if(Instrument->Header.Flags & 4) { k += k; } Instrument->Data = GetMem(Instrument->Header.Length * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, Instrument->Header.Length * (2 * sizeof(cte_float_t)), 0); if((InputDataSize - InputDataPosition) < k) { Buffer = GetMem(k); FillChar(Buffer, k, 0); Move(&((cte_uint8_p)InputData)[InputDataPosition], Buffer, InputDataSize - InputDataPosition); BufferNeedToFree = CTE_TRUE; } else { Buffer = &((cte_uint8_p)InputData)[InputDataPosition]; BufferNeedToFree = CTE_FALSE; } switch(Instrument->Header.Flags & (2 | 4)) { case 0: // 8-bit mono s = Instrument->Data; sb = Buffer; for(j = 0; j < Instrument->Header.Length; j++) { cte_float_t sv = ((cte_int8_t)((cte_uint8_t)(*sb++ + ((cte_uint8_t)SignedOffset)))) / 128.0f; *s++ = sv; *s++ = sv; } break; case 2: // 8-bit stereo s = Instrument->Data; sb = Buffer; for(j = 0; j < Instrument->Header.Length; j++) { *s = ((cte_int8_t)((cte_uint8_t)(*sb++ + ((cte_uint8_t)SignedOffset)))) / 128.0f; s += 2; } s = Instrument->Data; s++; for(j = 0; j < Instrument->Header.Length; j++) { *s = ((cte_int8_t)((cte_uint8_t)(*sb++ + ((cte_uint8_t)SignedOffset)))) / 128.0f; s += 2; } break; case 4: // 16-bit mono s = Instrument->Data; sw = (cte_pointer)Buffer; for(j = 0; j < Instrument->Header.Length; j++) { cte_float_t sv = ((cte_int16_t)((cte_uint16_t)(*sw++ + ((cte_uint16_t)SignedOffset)))) / 32768.0f; *s++ = sv; *s++ = sv; } break; case 2 | 4: // 16-bit stereo s =Instrument->Data; sw = (cte_pointer)Buffer; for(j = 0; j < Instrument->Header.Length; j++) { *s = ((cte_int16_t)((cte_uint16_t)(*sw++ + ((cte_uint16_t)SignedOffset)))) / 32768.0f; s += 2; } s = Instrument->Data; s++; for(j = 0; j < Instrument->Header.Length; j++) { *s = ((cte_int16_t)((cte_uint16_t)(*sw++ + ((cte_uint16_t)SignedOffset)))) / 32768.0f; s += 2; } break; } if(BufferNeedToFree) { FreeMem(Buffer); } break; #endif #ifdef CreamTrackerDPCM4 case 4: // DPCM4 (not ADPCM4, since this variant is not adaptive!) if(!(Instrument->Header.Flags & (2 | 4))) { if((InputDataSize - InputDataPosition) >= ((cte_int32_t)sizeof(DPCM4Table) + ((Instrument->Header.Length + 1) >> 1))) { k = (Instrument->Header.Length + 1) >> 1; Instrument->Data = GetMem((k << 1) * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, (k << 1) * (2 * sizeof(cte_float_t)), 0); DPCM4Table = (cte_pointer)(&((cte_uint8_p)InputData)[InputDataPosition]); Buffer = (cte_pointer)(&((cte_uint8_p)InputData)[InputDataPosition + sizeof(DPCM4Table)]); b = 0; s = Instrument->Data; for(j = 0; j < k; j++) { b += (*DPCM4Table)[((cte_uint8_p)Buffer)[j] & 0xf]; *s++ = ((cte_int8_t)((cte_uint8_t)(((cte_uint8_t)b) + ((cte_uint8_t)SignedOffset)))) / 128.0f; b += (*DPCM4Table)[((cte_uint8_p)Buffer)[j] >> 4]; *s++ = ((cte_int8_t)((cte_uint8_t)(((cte_uint8_t)b) + ((cte_uint8_t)SignedOffset)))) / 128.0f; } } } break; #endif #ifdef CreamTrackerADPCMIMA case 5: // IMA ADPCM4 (the TRUE adaptive variant) if(Instance->CreamTrackerModule) { if((InputDataSize - InputDataPosition) >= ((((Instrument->Header.Length + 1) >> 1) + 2) << ((Instrument->Header.Flags >> 1) & 1))) { Instrument->Data = GetMem((Instrument->Header.Length | 1) * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, (Instrument->Header.Length | 1) * (2 * sizeof(cte_float_t)), 0); Buffer = (cte_pointer)(&((cte_uint8_p)InputData)[InputDataPosition]); cte_int32_t jj = (Instrument->Header.Flags >> 1) & 1; for(j = 0; j <= jj; j++) { ADPCMIMAState.PrevSample = 0; ADPCMIMAState.StepIndex = 0; b = 0; s = Instrument->Data; s += j; for(k = 0; k < Instrument->Header.Length; k++) { if(!k) { ADPCMIMAState.PrevSample = *((cte_int16_p)Buffer); Buffer += sizeof(cte_int16_t); ADPCMIMAState.StepIndex = 0; } if(k & 1) { Sample = CreamTrackerADPCMIMADecompressSample(&ADPCMIMAState, b >> 4); } else { b = *((cte_uint8_p)Buffer++); Sample = CreamTrackerADPCMIMADecompressSample(&ADPCMIMAState, b & 0xf); } if(Instrument->Header.Flags & 2) { *s = Sample / 32768.0f; s += 2; } else { *s++ = Sample / 32768.0f; *s++ = Sample / 32768.0f; } } } } } break; #endif #ifdef CreamTrackerWRS case 6: if(Instance->CreamTrackerModule) { // Sinusoidal frequency envelope dissected sample for to resynthesizing it (very good usable for vocals!) k = InputDataPosition; if(Read(&j,sizeof(cte_int32_t)) == sizeof(cte_int32_t)) { Instrument->Data = GetMem(Instrument->Header.Length * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, Instrument->Header.Length * (2 * sizeof(cte_float_t)), 0); WRSSynthese((cte_pointer)(&((cte_uint8_p)InputData)[InputDataPosition]), j, Instrument->Data, 2 * sizeof(cte_float_t), Instrument->Header.Length); InputDataPosition += j; if ((Instrument->Header.Flags & 2) && (Read(&j,sizeof(cte_int32_t)) == sizeof(cte_int32_t))) { WRSSynthese((cte_pointer)(&((cte_uint8_p)InputData)[InputDataPosition]), j, &((cte_float_p)Instrument->Data)[1], 2 * sizeof(cte_float_t), Instrument->Header.Length); InputDataPosition += j; } else { s = Instrument->Data; for(j = 0; j < Instrument->Header.Length; j++) { s[1] = s[0]; s += 2; } } Instrument->RawLen = InputDataPosition - k; if(Instrument->RawLen > 0) { Instrument->RawData = GetMem(Instrument->RawLen); Move((cte_pointer)(&((cte_uint8_p)InputData)[k]), Instrument->RawData, Instrument->RawLen); } } } break; #endif #ifdef CreamTrackerDeltaPCM case 7: // Uncompressed delta PCM k = Instrument->Header.Length; if(Instrument->Header.Flags & 2) { k += k; } if(Instrument->Header.Flags & 4) { k += k; } Instrument->Data = GetMem(Instrument->Header.Length * (2 * sizeof(cte_float_t))); FillChar(Instrument->Data, Instrument->Header.Length * (2 * sizeof(cte_float_t)), 0); if((InputDataSize - InputDataPosition) < k) { Buffer = GetMem(k); FillChar(Buffer, k, 0); Move(&((cte_uint8_p)InputData)[InputDataPosition], Buffer, InputDataSize - InputDataPosition); BufferNeedToFree = CTE_TRUE; } else { Buffer = &((cte_uint8_p)InputData)[InputDataPosition]; BufferNeedToFree = CTE_FALSE; } switch(Instrument->Header.Flags & (2 | 4)) { case 0: // 8-bit mono s = Instrument->Data; sb = Buffer; lb = 0; for(j = 0; j < Instrument->Header.Length; j++) { lb += *sb++; cte_float_t sv = ((cte_int8_t)((cte_uint8_t)(lb + ((cte_uint8_t)SignedOffset)))) / 128.0f; *s++ = sv; *s++ = sv; } break; case 2: // 8-bit stereo s = Instrument->Data; sb = Buffer; lb = 0; for(j = 0; j < Instrument->Header.Length; j++) { lb += *sb++; *s = ((cte_int8_t)((cte_uint8_t)(lb + ((cte_uint8_t)SignedOffset)))) / 128.0f; s += 2; } s = Instrument->Data; s++; lb = 0; for(j = 0; j < Instrument->Header.Length; j++) { lb += *sb++; *s = ((cte_int8_t)((cte_uint8_t)(lb + ((cte_uint8_t)SignedOffset)))) / 128.0f; s += 2; } break; case 4: // 16-bit mono s = Instrument->Data; sw = (cte_pointer)Buffer; lw = 0; for(j = 0; j < Instrument->Header.Length; j++) { lw += *sw++; cte_float_t sv = ((cte_int16_t)((cte_uint16_t)(lw + ((cte_uint16_t)SignedOffset)))) / 32768.0f; *s++ = sv; *s++ = sv; } break; case 2 | 4: // 16-bit stereo s =Instrument->Data; sw = (cte_pointer)Buffer; lw = 0; for(j = 0; j < Instrument->Header.Length; j++) { lw += *sw++; *s = ((cte_int16_t)((cte_uint16_t)(lw + ((cte_uint16_t)SignedOffset)))) / 32768.0f; s += 2; } s = Instrument->Data; s++; lw = 0; for(j = 0; j < Instrument->Header.Length; j++) { lw += *sw++; *s = ((cte_int16_t)((cte_uint16_t)(lw + ((cte_uint16_t)SignedOffset)))) / 32768.0f; s += 2; } break; } if(BufferNeedToFree) { FreeMem(Buffer); } break; #endif default: break; } if(Instrument->Data) { CreamTrackerFixUpSample(Instance, Instrument); } } } } } for(i = 0; i < 256; i++) { Pattern = &Instance->Patterns[i]; for(Row = 0; Row < 64; Row++) { for(j = 0; j < 32; j++) { PatternNote = &((*Pattern)[Row][j]); PatternNote->Note = 255; PatternNote->Instrument = 0; PatternNote->Volume = 255; PatternNote->Effect = 0; PatternNote->EffectParameter = 0; } } } if(IsStripped) { #ifdef CreamTrackerStrippedPatterns if(PatternPointers[0]) { InputDataPosition = PatternPointers[0] << 4; if(!CreamTrackerLoadPatternsAsSingleChain(Instance, &((cte_uint8_p)InputData)[InputDataPosition], InputDataSize-InputDataPosition)) { return CTE_FALSE; } } #endif } else { #ifdef CreamTrackerNormalPatterns for(i = 0; i < Instance->Header.PatNum; i++) { Pattern = &Instance->Patterns[i]; if(PatternPointers[i]) { InputDataPosition = PatternPointers[i] << 4; if(Read(&PatternLength, sizeof(cte_uint16_t)) != sizeof(cte_uint16_t)) { return CTE_FALSE; } if((InputDataSize - InputDataPosition) < PatternLength) { return CTE_FALSE; } Buffer = &((cte_uint8_p)InputData)[InputDataPosition]; j = 0; Row = 0; while(j < PatternLength) { b = Buffer[j++]; if(b) { ChannelNr = b & 0x1f; if(ChannelNr < 32) { PatternNote = &((*Pattern)[Row][ChannelNr]); if(b & 0x20) { if(j >= PatternLength) { break; } p = Buffer[j++]; if(p < 0xf0) { PatternNote->Note = ((p >> 4) * 12) + (p & 0xf); } else if(p == 0xfe) { PatternNote->Note = 0xfe; } else if((p == 0xfd) && Instance->CreamTrackerModule) { PatternNote->Note = 0xfd; } else { PatternNote->Note = 0xff; } if(j >= PatternLength) { break; } p = Buffer[j++]; PatternNote->Instrument = p; } if(b & 0x40) { if(j >= PatternLength) { break; } p = Buffer[j++]; if(p <= 64) { PatternNote->Volume = p; } } if(b & 0x80) { if(j >= PatternLength) { break; } p = Buffer[j++]; PatternNote->Effect = p; if(j >= PatternLength) { break; } p = Buffer[j++]; PatternNote->EffectParameter = p; } } else { if(b & 0x20) { if(j >= PatternLength) { break; } j++; if(j >= PatternLength) { break; } j++; } if(b & 0x40) { if(j >= PatternLength) { break; } j++; } if(b & 0x80) { if(j >= PatternLength) { break; } j++; if(j >= PatternLength) { break; } j++; } } } else { Row++; if(Row>=64) { break; } } } } } #endif } if(Instance->CreamTrackerModule && Instance->Header.Data) { InputDataPosition = Instance->Header.Data; while((InputDataPosition + (cte_int32_t)sizeof(TCreamTrackerChunk)) < InputDataSize) { if(Read(&Chunk,sizeof(TCreamTrackerChunk)) == sizeof(TCreamTrackerChunk)) { if(((Chunk.Signature[0] == (cte_uchar_t)'D') && (Chunk.Signature[1] == (cte_uchar_t)'O') && (Chunk.Signature[2] == (cte_uchar_t)'N') && (Chunk.Signature[3] == (cte_uchar_t)'E')) || (Chunk.Size < 0)) { break; } else { NextInputDataPosition = InputDataPosition + Chunk.Size; #ifdef CreamTrackerCode if(((Chunk.Signature[0] == (cte_uchar_t)'C') && (Chunk.Signature[1] == (cte_uchar_t)'O') && (Chunk.Signature[2] == (cte_uchar_t)'D') && (Chunk.Signature[3] == (cte_uchar_t)'E'))) { #ifndef CreamTrackerTRICode CreamTrackerCodeIndex = 0; #endif LoadCode(&Instance->CodeData, CTE_FALSE); } #endif InputDataPosition = NextInputDataPosition; } } else { break; } } } return CTE_TRUE; } #undef InputData #undef InputDataPosition #undef InputDataSize #undef LoadCode void CreamTrackerProcessTick(PCreamTrackerInstance Instance) { Instance->PatternRandom = ((Instance->PatternRandom * 0xcdef) + 0x1737) & 0xffff; Instance->FirstTick = CTE_FALSE; Instance->FirstRowTick = CTE_FALSE; Instance->Tick++; Instance->AbsoluteTick++; if(Instance->Tick >= (Instance->Speed + Instance->FrameDelay)) { Instance->Tick = 0; Instance->PatternDelayRowCounter--; if(Instance->PatternDelayRowCounter <= 0) { Instance->AbsoluteTick = 0; Instance->PatternDelayRowCounter = 0; Instance->FrameDelay = 0; Instance->PatternDelay = 0; Instance->FirstRowTick = CTE_TRUE; } Instance->FirstTick = CTE_TRUE; } if(Instance->FirstRowTick) { if(Instance->Playing) { #ifndef CreamTrackerCompact if(Instance->ForceOrder >= 0) { Instance->Order = !Instance->ForceOrder; Instance->NextOrder = Instance->ForceOrder; Instance->ForceOrder = -1; } if(Instance->ForceRow >= 0) { Instance->NextRow = Instance->ForceRow; Instance->ForceRow = -1; } #endif Instance->Row = Instance->NextRow; while(Instance->Order != Instance->NextOrder) { Instance->LoopRow = 0; Instance->LoopRowCounter = 0; Instance->Order = Instance->NextOrder; Instance->Pattern = 0xff; while(Instance->Order < Instance->Header.OrdNum) { switch(Instance->Orders[Instance->Order]) { case 0xfe: Instance->Order++; Instance->NextOrder = Instance->Order; break; default: Instance->Pattern = Instance->Orders[Instance->Order]; goto BreakLoop1; } } BreakLoop1: if(Instance->Order < Instance->Header.OrdNum) { Instance->TrackEnd |= ((!Instance->RepeatCounter) && Instance->RepeatCounters[Instance->Order]); Instance->RepeatCounters[Instance->Order]++; } if((Instance->Pattern == 0xff) && !Instance->PatternLoop) { if(!Instance->RepeatCounter) { Instance->TrackEnd = CTE_TRUE; } else { if(Instance->RepeatCounter > 0) { Instance->RepeatCounter--; } if(Instance->Header.OrdNum > 0) { Instance->Order = 0xff; Instance->NextOrder = 0; continue; } } } break; } Instance->Row = Instance->Row & 63; Instance->NextRow = (Instance->Row + 1) & 63; if((!Instance->NextRow) && !Instance->PatternLoop) { Instance->NextOrder = Instance->Order + 1; } if(Instance->RepeatCounter >= 0) { Instance->RepeatRowCounters[Instance->Pattern][Instance->Row]++; if(Instance->RepeatRowCounters[Instance->Pattern][Instance->Row] >= (Instance->Header.OrdNum * 64)) { Instance->TrackEnd = CTE_TRUE; } } } } } void CreamTrackerChannelVibratoCheck(PCreamTrackerChannel Channel) { Channel->VibratoPosition = ((!(Channel->VibratoWaveForm & 4)) && (Channel->VibratoPosition & 0x40)) ? 0 : (Channel->VibratoPosition & 0x3f); } void CreamTrackerChannelVibratoUpdate(PCreamTrackerInstance Instance, PCreamTrackerChannel Channel, cte_uint8_t Increment) { if((Channel->VibratoWaveForm & 3) == 3) { Channel->VibratoPosition = (Channel->VibratoPosition + ((Instance->PatternRandom >> 1) & 0xf)) & 0x3f; } Channel->VibratoPosition = (Channel->VibratoPosition + Increment) & 0x3f; } cte_uint8_t CreamTrackerProcessChannelsGetLastEffectParameter(cte_uint8_p EffectParameter, PCreamTrackerPatternNote PatternNote, PCreamTrackerChannel Channel) { if (!*EffectParameter) { PatternNote->EffectParameter = Channel->LastEffectParameter; *EffectParameter = Channel->LastEffectParameter; } return *EffectParameter; } void CreamTrackerProcessChannels(PCreamTrackerInstance Instance) { #if defined(CreamTrackerEffectExx) || defined(CreamTrackerEffectFxx) #define CreamTrackerPitchSlide #endif #if defined(CreamTrackerEffectXxx) || defined(CreamTrackerEffectS8x) #define CreamTrackerSetPanning #endif #if defined(CreamTrackerEffectDxx) || defined(CreamTrackerEffectKxx) || defined(CreamTrackerEffectLxx) #define CreamTrackerVolumeSlide #endif #if defined(CreamTrackerEffectGxx) || defined(CreamTrackerEffectLxx) #define CreamTrackerPortamento #endif #if defined(CreamTrackerEffectHxx) || defined(CreamTrackerEffectKxx) || defined(CreamTrackerEffectUxx) #define CreamTrackerVibrato #endif #ifdef CreamTrackerEffectQxx static const cte_int8_t RetrigTables[2][16] = {{0,0,0,0,0,0,10,8,0,0,0,0,0,0,24,32},{0,-1,-2,-4,-8,-16,0,0,0,1,2,4,8,16,0,0}}; #endif cte_int32_t RequestedChannelIndex, ChannelIndex, Mangitude, Fine, Speed, Depth, Value, Counter, Index, OtherValue; #ifdef CreamTrackerSetPanning cte_int32_t SetPanning; #endif PCreamTrackerPattern Pattern; PCreamTrackerPatternNote PatternNote; PCreamTrackerChannel Channel, FadeOutChannel; cte_uint8_t EffectParameter; #ifdef CreamTrackerEffectTxx cte_uint8_t TempoSlide; #endif #ifdef CreamTrackerVolumeSlide cte_uint8_t VolumeSlide; #endif #ifdef CreamTrackerEffectNxx cte_uint8_t ChannelVolumeSlide; #endif #ifdef CreamTrackerEffectQxx cte_uint8_t Retrig; #endif #ifdef CreamTrackerEffectWxx cte_uint8_t GlobalVolumeSlide; #endif #ifdef CreamTrackerEffectJxx cte_uint8_t Arpeggio; #endif #ifdef CreamTrackerEffectRxx cte_uint8_t Tremolo; #endif #ifdef CreamTrackerVibrato cte_uint32_t Vibrato; #endif #ifdef CreamTrackerPortamento cte_uint32_t Portamento; #endif #ifdef CreamTrackerPitchSlide cte_uint32_t PitchSlide; #endif #ifdef CreamTrackerEffectOxx cte_uint32_t SampleOffset; #endif cte_longbool_t Trigger, NoteChange, InstrumentChange, NoClickRemovalFadeOut; cte_double_t Volume, Pan, /*VibratoSkew,*/ Temp, PeriodMod, PeriodFactor, PeriodInvFactor, VolumeMod, VolumeFactor; #ifdef CreamTrackerCode cte_longbool_t SynthReset; cte_pointer pp, pd; #endif #define GetLastEffectParameter() CreamTrackerProcessChannelsGetLastEffectParameter(&EffectParameter, PatternNote, Channel) Pattern = (Instance->Playing && ((Instance->Pattern >= 0) && (Instance->Pattern <= 255))) ? &Instance->Patterns[Instance->Pattern] : NULL; if(Instance->FirstRowTick) { for(ChannelIndex = 0; ChannelIndex < 64; ChannelIndex++) { Channel = &Instance->Channels[ChannelIndex]; Channel->NoteOnTick = 0; Channel->NoteCutTick = 0x7ffffffful; #ifdef CreamTrackerEffectIxx Channel->TremorCounter &= 0x7f; #endif } } for(RequestedChannelIndex = 0; RequestedChannelIndex < 64; RequestedChannelIndex++) { ChannelIndex = Instance->ChannelOrder[RequestedChannelIndex]; Channel = &Instance->Channels[ChannelIndex]; PatternNote = &Channel->PatternNote; if((ChannelIndex >= 0) && (ChannelIndex <= 31)) { Channel->Enabled = !(Instance->Header.ChannelSettings[ChannelIndex] & 0x80); Channel->Muted = (Instance->Header.Panning == 0xfc) && (Instance->ChannelPannings[ChannelIndex] & 0x40); } if(Instance->FirstRowTick) { PatternNote->Note = 255; PatternNote->Instrument = 0; PatternNote->Volume = 255; PatternNote->Effect = 0; PatternNote->EffectParameter = 0; } PeriodMod = 0.0; PeriodFactor = 1.0; PeriodInvFactor = 1.0; VolumeMod = 0.0; VolumeFactor = 1.0; #ifdef CreamTrackerEffectWxx GlobalVolumeSlide = 0; #endif #ifdef CreamTrackerEffectNxx ChannelVolumeSlide = 0; #endif #ifdef CreamTrackerVolumeSlide VolumeSlide = 0; #endif #ifdef CreamTrackerPitchSlide PitchSlide = 0; #endif #ifdef CreamTrackerEffectTxx TempoSlide = 0; #endif #ifdef CreamTrackerVibrato Vibrato = 0; #endif #ifdef CreamTrackerEffectRxx Tremolo = 0; #endif #ifdef CreamTrackerEffectQxx Retrig = 0; #endif #ifdef CreamTrackerPortamento Portamento = 0; #endif #ifdef CreamTrackerEffectJxx Arpeggio = 0; #endif #ifdef CreamTrackerSetPanning SetPanning = -1; #endif #ifdef CreamTrackerEffectOxx SampleOffset = 0; #endif #ifdef CreamTrackerCode SynthReset = CTE_FALSE; #endif NoClickRemovalFadeOut = CTE_FALSE; if(Channel->Enabled) { Instance->CurrentChannel = Channel; if(!Channel->Master) { if(Instance->FirstRowTick) { if(Pattern && ((ChannelIndex >= 0) && (ChannelIndex <= 31))) { Move(&(*Pattern)[Instance->Row][ChannelIndex], PatternNote, sizeof(TCreamTrackerPatternNote)); } #ifdef CreamTrackerGUI if(Channel->HasInjectPatternNote) { Channel->PatternNote = Channel->InjectPatternNote; Channel->HasInjectPatternNote = CTE_FALSE; } #endif #ifdef CreamTrackerEffectQxx if(PatternNote->Effect != ('Q' - CreamTrackerEffectOffset)) { Channel->RetrigCounter = 0; } #endif if((PatternNote->Effect != ('H' - CreamTrackerEffectOffset)) && (PatternNote->Effect != ('U' - CreamTrackerEffectOffset)) && (PatternNote->Effect != ('K' - CreamTrackerEffectOffset)) && (PatternNote->Effect != ('R' - CreamTrackerEffectOffset))) { Channel->VibratoPosition |= 0x40; } if(PatternNote->EffectParameter) { Channel->LastEffectParameter = PatternNote->EffectParameter; } } EffectParameter = PatternNote->EffectParameter; switch(PatternNote->Effect) { #ifdef CreamTrackerEffectAxx case 'A' - CreamTrackerEffectOffset: // Set speed. If the parameter is 0, the effect is ignored. if(Instance->FirstRowTick && EffectParameter) { Instance->Speed = EffectParameter; } break; #endif #ifdef CreamTrackerEffectBxx case 'B' - CreamTrackerEffectOffset: // Order jump if(Instance->FirstRowTick) { if(!Instance->PatternLoop) { Instance->NextOrder = EffectParameter; } Instance->NextRow = 0; } break; #endif #ifdef CreamTrackerEffectCxx case 'C' - CreamTrackerEffectOffset: // Jump to row x*10 + y. The value provided is in decimal. If the row // number specified is 64 or higher, the effect is ignored. if(Instance->FirstRowTick && (((EffectParameter >> 4) < 10) && ((EffectParameter & 0xf) < 10))) { Value = ((EffectParameter >> 4) * 10) + (EffectParameter & 0xf); if((Value >= 0) && (Value <= 63)) { if(!Instance->PatternLoop) { Instance->NextOrder = Instance->Order + 1; } Instance->NextRow = Value; } } break; #endif #ifdef CreamTrackerVolumeSlide #ifdef CreamTrackerEffectDxx case 'D' - CreamTrackerEffectOffset: // Volume slide VolumeSlide = GetLastEffectParameter(); break; #endif #endif #ifdef CreamTrackerPitchSlide #ifdef CreamTrackerEffectExx case 'E' - CreamTrackerEffectOffset: // Slide down if(Channel->Active) { PitchSlide = GetLastEffectParameter(); } break; #endif #ifdef CreamTrackerEffectFxx case 'F' - CreamTrackerEffectOffset: // Slide up if(Channel->Active) { PitchSlide = GetLastEffectParameter() | 0x100; } break; #endif #endif #ifdef CreamTrackerPortamento #ifdef CreamTrackerEffectGxx case 'G' - CreamTrackerEffectOffset: // Slide to note if(EffectParameter) { Channel->LastPortamento = EffectParameter; } Portamento = Channel->LastPortamento; break; #endif #endif #ifdef CreamTrackerVibrato #ifdef CreamTrackerEffectHxx case 'H' - CreamTrackerEffectOffset: // Vibrato. This effect shares memory with Uxy. if(PatternNote->EffectParameter & 0xf) { Channel->LastVibrato = (Channel->LastVibrato & 0xf0) | (PatternNote->EffectParameter & 0x0f); } if(PatternNote->EffectParameter & 0xf0) { Channel->LastVibrato = (Channel->LastVibrato & 0x0f) | (PatternNote->EffectParameter & 0xf0); } Vibrato = Channel->LastVibrato; break; #endif #endif #ifdef CreamTrackerEffectIxx case 'I' - CreamTrackerEffectOffset: // Tremor GetLastEffectParameter(); if(EffectParameter) { Channel->TremorParameter = EffectParameter; } Channel->TremorCounter |= 0x80; break; #endif #ifdef CreamTrackerEffectJxx case 'J' - CreamTrackerEffectOffset: // Arpeggio Arpeggio = GetLastEffectParameter(); break; #endif #ifdef CreamTrackerVolumeSlide #ifdef CreamTrackerVibrato #ifdef CreamTrackerEffectKxx case 'K' - CreamTrackerEffectOffset: // H00 + Dxy // The volume slide, performed by this effect differs from Dxy in // the following ways: // 1. The first tick of this effect is ignored. This is due to a // no-op in the "first tick" pointer table. // 2. As a result of the previous point, fine slides do not work. // However, the "other" effect (H00 in case of Kxy; G00 in case of // Lxy) is also not performed when a fine volume slide is requested. Vibrato = Channel->LastVibrato; if(!Instance->FirstRowTick) { VolumeSlide = GetLastEffectParameter(); } break; #endif #endif #ifdef CreamTrackerPortamento #ifdef CreamTrackerEffectLxx case 'L' - CreamTrackerEffectOffset: // G00 + Dxy // The volume slide differs from Dxy. See Kxy for details. Portamento = Channel->LastPortamento | 0x100; if(!Instance->FirstRowTick) { VolumeSlide = GetLastEffectParameter(); } break; #endif #endif #endif #ifdef CreamTrackerEffectMxx case 'M' - CreamTrackerEffectOffset: // Channel volume if(Instance->FirstRowTick && ((PatternNote->EffectParameter >= 0) && (PatternNote->EffectParameter <= 64))) { Channel->ChannelVolume = EffectParameter; } break; #endif #ifdef CreamTrackerEffectNxx case 'N' - CreamTrackerEffectOffset: // Channel volume slide ChannelVolumeSlide = GetLastEffectParameter(); break; #endif #ifdef CreamTrackerEffectOxx case 'O' - CreamTrackerEffectOffset: // Set sample offset SampleOffset = Channel->SampleHighOffset | (PatternNote->EffectParameter << 8); break; #endif #ifdef CreamTrackerEffectPxx case 'P' - CreamTrackerEffectOffset: break; #endif #ifdef CreamTrackerEffectQxx case 'Q'-CreamTrackerEffectOffset: // Retrigger note every y ticks with volume modifier x. If the retrig value 'y' // is 0, the effect is ignored. Retrig = GetLastEffectParameter(); break; #endif #ifdef CreamTrackerEffectRxx case 'R' - CreamTrackerEffectOffset: // Tremolo. x*4 is speed, y*4 is depth. GetLastEffectParameter(); if(!(EffectParameter & 0xf0)) { EffectParameter = (EffectParameter & 0x0f) | (Channel->LastEffectParameter & 0xf0); } Channel->LastEffectParameter = EffectParameter; Channel->PatternNote.EffectParameter = EffectParameter; Tremolo = EffectParameter; break; #endif #ifdef CreamTrackerEffectSxx case 'S' - CreamTrackerEffectOffset: GetLastEffectParameter(); switch(EffectParameter & 0xf0) { #ifdef CreamTrackerEffectS1x case 0x10: if(Instance->FirstRowTick) { Channel->Glissando = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerEffectS2x case 0x20: if(Instance->FirstRowTick && (Channel->C4SpeedFactor != 0.0)) { Channel->SlideToPeriod = Channel->SlideToPeriod / Channel->C4SpeedFactor; Channel->C4SpeedFactor = ((cte_int32_t)(8363l * 16l * 1712l)) / CreamTrackerFineTuneTable[EffectParameter & 0x0f]; Channel->SlideToPeriod = Channel->SlideToPeriod * Channel->C4SpeedFactor; } break; #endif #ifdef CreamTrackerEffectS3x case 0x30: if(Instance->FirstRowTick) { Channel->VibratoWaveForm = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerEffectS4x case 0x40: if(Instance->FirstRowTick) { Channel->TremoloWaveForm = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerEffectS6x case 0x60: if(Instance->FirstRowTick) { Instance->FrameDelay = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerSetPanning #ifdef CreamTrackerEffectS8x case 0x80: if(Instance->FirstRowTick) { SetPanning = (((EffectParameter & 0x0f) * 0x100) + 8) >> 4; } break; #endif #endif #ifdef CreamTrackerEffectSAx case 0xa0: if(Instance->FirstRowTick) { Channel->SampleHighOffset = (EffectParameter & 0xf) << 16; } break; #endif #ifdef CreamTrackerEffectSBx case 0xb0: if(Instance->FirstRowTick) { if(!(EffectParameter & 0x0f)) { Instance->LoopRow = Instance->Row; } else { if(!Instance->LoopRowCounter) { Instance->LoopRowCounter = EffectParameter & 0x0f; Instance->NextRow = Instance->LoopRow; } else { Instance->LoopRowCounter--; if(Instance->LoopRowCounter > 0) { Instance->NextRow = Instance->LoopRow; } } } } break; #endif #ifdef CreamTrackerEffectSCx case 0xc0: if(Instance->FirstRowTick) { Channel->NoteCutTick = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerEffectSDx case 0xd0: if(Instance->FirstRowTick) { Channel->NoteOnTick = EffectParameter & 0x0f; } break; #endif #ifdef CreamTrackerEffectSEx case 0xe0: if(Instance->FirstRowTick) { if((!Instance->PatternDelay) && (EffectParameter & 0x0f)) { Instance->PatternDelay = (EffectParameter & 0x0f) + 1; Instance->PatternDelayRowCounter = Instance->PatternDelay; } } break; #endif } break; #endif #ifdef CreamTrackerEffectTxx case 'T' - CreamTrackerEffectOffset: if(Instance->FirstRowTick) { if((EffectParameter >= 33) && (EffectParameter <= 255)) { Instance->Tempo = EffectParameter; CreamTrackerUpdateTempo(Instance); } else { GetLastEffectParameter(); if(!(EffectParameter & 0xf0)) { EffectParameter = (EffectParameter & 0x0f) | (Channel->LastEffectParameter & 0xf0); } if(!(EffectParameter & 0x0f)) { EffectParameter = (EffectParameter & 0xf0) | (Channel->LastEffectParameter & 0x0f); } Channel->LastEffectParameter = EffectParameter; Channel->PatternNote.EffectParameter = EffectParameter; TempoSlide = EffectParameter; } } break; #endif #ifdef CreamTrackerVibrato #ifdef CreamTrackerEffectUxx case 'U' - CreamTrackerEffectOffset: if(PatternNote->EffectParameter & 0x0f) { Channel->LastVibrato = (Channel->LastVibrato & 0xf0) | (PatternNote->EffectParameter & 0x0f); } if(PatternNote->EffectParameter & 0xf0) { Channel->LastVibrato = (Channel->LastVibrato & 0x0f) | (PatternNote->EffectParameter & 0xf0); } Vibrato = Channel->LastVibrato | 0x100; break; #endif #endif #ifdef CreamTrackerEffectVxx case 'V' - CreamTrackerEffectOffset: if(Instance->FirstRowTick && ((PatternNote->EffectParameter >= 0x00) && (PatternNote->EffectParameter <= 0x80))) { Instance->GlobalVolume = PatternNote->EffectParameter; } break; #endif #ifdef CreamTrackerEffectWxx case 'W' - CreamTrackerEffectOffset: GlobalVolumeSlide = GetLastEffectParameter(); break; #endif #ifdef CreamTrackerSetPanning #ifdef CreamTrackerEffectXxx case 'X' - CreamTrackerEffectOffset: if(Instance->FirstRowTick && ((PatternNote->EffectParameter >= 0x00) && (PatternNote->EffectParameter <= 0x80))) { SetPanning = PatternNote->EffectParameter << 1; } break; #endif #endif #ifdef CreamTrackerEffectYxx case 'Y' - CreamTrackerEffectOffset: break; #endif #ifdef CreamTrackerEffectZxx case 'Z' - CreamTrackerEffectOffset: break; #endif default: #ifdef CreamTrackerCode if((PatternNote->Effect >= 0x80) && (PatternNote->Effect <= 0xe4)) { if(Instance->FirstRowTick) { Value = PatternNote->Effect & 0x7f; if ((Value >= 0) && (Value <= 63)) { Instance->ValueMemory[Value] = PatternNote->EffectParameter; } else if (((Value >= 64) && (Value <= 127)) && ((ChannelIndex >= 0) && (ChannelIndex < 32))) { Instance->ChannelValueMemory[ChannelIndex][Value - 64] = PatternNote->EffectParameter; } } } #endif break; } #ifdef CreamTrackerCode if(Instance->CreamTrackerModule && Instance->CodeData && Instance->CodeData->GlobalProcProcessPatternNote) { cte_int32_t pi0, pi1, pi2, pi3, pi4, pi5, pi6, pi7, pi8; pp = Instance->CodeData->GlobalProcProcessPatternNote; pd = (cte_pointer)&(((cte_uint8_p)(Instance->CodeData->InstanceData))[(Instance->CodeData->Instances - 1) * Instance->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = Channel; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); pi0 = Instance->Pattern; pi1 = Instance->Row; pi2 = Instance->AbsoluteTick; pi3 = ChannelIndex; pi4 = PatternNote->Note; pi5 = PatternNote->Instrument; pi6 = PatternNote->Volume; pi7 = PatternNote->Effect; pi8 = PatternNote->EffectParameter; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushal\n\t" "movl %%esp,%%edi\n\t" "mov %0,%%esi\n\t" "pushl %1\n\t" "pushl %2\n\t" "pushl %3\n\t" "pushl %4\n\t" "pushl %5\n\t" "pushl %6\n\t" "pushl %7\n\t" "pushl %8\n\t" "pushl %9\n\t" "mov %10,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" : : "m"(pd), "m"(pi8), "m"(pi7), "m"(pi6), "m"(pi5), "m"(pi4), "m"(pi3), "m"(pi2), "m"(pi1), "m"(pi0), "m"(pp) ); #else __asm { pushad mov edi,esp mov esi,dword ptr pd push dword ptr pi8 push dword ptr pi7 push dword ptr pi6 push dword ptr pi5 push dword ptr pi4 push dword ptr pi3 push dword ptr pi2 push dword ptr pi1 push dword ptr pi0 mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp popad } #endif } #endif if(Instance->AbsoluteTick == Channel->NoteOnTick) { if(PatternNote->Note == 0xfe) { Channel->NoteCutTick = Instance->AbsoluteTick; } else if(PatternNote->Note == 0xfd) { #ifdef CreamTrackerCode if(Channel->Instrument && (Channel->Instrument->Header.InstrumentType == 8) && Channel->Instrument->CodeData && Channel->Instrument->CodeData->SynthProcInstanceNoteOff && Channel->Instrument->CodeData->InstanceData) { pp = Channel->Instrument->CodeData->SynthProcInstanceNoteOff; pd = (cte_pointer)&(((cte_uint8_p)(Channel->Instrument->CodeData->InstanceData))[ChannelIndex * Channel->Instrument->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = Channel; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushal\n\t" "mov %0,%%esi\n\t" "mov %1,%%eax\n\t" "movl %%esp,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" : : "m"(pd), "m"(pp) ); #else __asm { pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad } #endif } #endif } else if((PatternNote->Note < 194) || PatternNote->Instrument) { // Note without sample results in retriggering the note without resetting the volume. // Sample without note results in resetting the volume and switching samples without // retriggering the note. If the C4 speeds differ, this could potentially be out of // tune as it does not convert the internal period values. If the note is off, it // will stay off - it will not retrigger. NoClickRemovalFadeOut = Channel->Active && (Channel->Instrument && (Channel->Instrument->Header.InstrumentType == 8) && (Channel->Instrument->Header.Flags & 128)); if( #ifdef CreamTrackerPortamento (!Portamento) && #endif !NoClickRemovalFadeOut ) { #ifdef CreamTrackerCode if(Channel->Instrument && (Channel->Instrument->Header.InstrumentType == 8) && Channel->Instrument->CodeData && Channel->Instrument->CodeData->SynthProcInstanceNoteOff && Channel->Instrument->CodeData->InstanceData) { pp = Channel->Instrument->CodeData->SynthProcInstanceNoteOff; pd = (cte_pointer)&(((cte_uint8_p)(Channel->Instrument->CodeData->InstanceData))[ChannelIndex * Channel->Instrument->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = Channel; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushal\n\t" "mov %0,%%esi\n\t" "mov %1,%%eax\n\t" "movl %%esp,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" : : "m"(pd), "m"(pp) ); #else __asm { pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad } #endif } if(Channel->Instrument && (Channel->Instrument->Header.InstrumentType == 8) && Channel->Instrument->CodeData && Channel->Instrument->CodeData->InstanceData) { Move(&((cte_uint8_p)Channel->Instrument->CodeData->InstanceData)[ChannelIndex * Channel->Instrument->CodeData->InstanceWorkDataSize], &((cte_uint8_p)Channel->Instrument->CodeData->InstanceData)[(ChannelIndex | 32) * Channel->Instrument->CodeData->InstanceWorkDataSize], Channel->Instrument->CodeData->InstanceWorkDataSize); } #endif #ifdef CreamTrackerCode Move((cte_pointer)&Instance->ChannelValueMemory[ChannelIndex], (cte_pointer)&Instance->ChannelValueMemory[ChannelIndex | 32], sizeof(TCreamTrackerChannelValueMemory)); #endif FadeOutChannel = &Instance->Channels[ChannelIndex | 32]; Move((cte_pointer)Channel, (cte_pointer)FadeOutChannel, sizeof(TCreamTrackerChannel)); FadeOutChannel->Master = Channel; #ifdef CreamTrackerCode FadeOutChannel->ValueMemory = &Instance->ChannelValueMemory[ChannelIndex | 32]; #endif FadeOutChannel->Index = ChannelIndex | 32; FadeOutChannel->NoteOnTick = 0x7fffffffl; FadeOutChannel->NoteCutTick = Instance->AbsoluteTick; #ifdef CreamTrackerEffectIxx FadeOutChannel->TremorCounter &= 0x7f; #endif FadeOutChannel->PatternNote.Effect = 0; FadeOutChannel->PatternNote.EffectParameter = 0; FadeOutChannel->LastEffectParameter = 0; Channel->LastLeft = 0; Channel->LastRight = 0; } NoteChange = PatternNote->Note < 194; InstrumentChange = CTE_FALSE; if(NoteChange) { Channel->BaseNote = PatternNote->Note; InstrumentChange = CTE_TRUE; } if(PatternNote->Instrument) { InstrumentChange |= (Channel->LastInstrument != PatternNote->Instrument); Channel->LastInstrument = PatternNote->Instrument; Channel->Instrument = &Instance->Instruments[Channel->LastInstrument]; Channel->Volume = Channel->Instrument->Header.Volume; Channel->Velocity = 64; } if(Channel->Instrument) { if(NoteChange) { if(Channel->Instrument->Header.C4Speed > 0) { Channel->C4SpeedFactor = ((cte_int32_t)(8363l * 16l * 1712l)) / (cte_double_t)Channel->Instrument->Header.C4Speed; Channel->NoteFrequencyFactor = ((cte_int32_t)(8363l * 1712l)) * (261.6255653005986 / (cte_double_t)Channel->Instrument->Header.C4Speed); } else { Channel->C4SpeedFactor = ((cte_int32_t)(8363l * 16l * 1712l)) / (cte_double_t)8363.0; Channel->NoteFrequencyFactor = 1712l * 261.6255653005986; } Channel->SlideToPeriod = Instance->NoteToPeriodTable[Channel->BaseNote] * Channel->C4SpeedFactor; if(Instance->Header.Flags & 16) { // Amiga limits if(Channel->SlideToPeriod < (907 >> 1)) { Channel->SlideToPeriod = 907 >> 1; } else if(Channel->SlideToPeriod > (1712*2)) { Channel->SlideToPeriod = 1712 * 2; } } } #ifdef CreamTrackerPortamento if(!Portamento) #endif { if(NoClickRemovalFadeOut) { Channel->LastLeft = 0; Channel->LastRight = 0; } #ifdef CreamTrackerCode SynthReset = CTE_TRUE; #endif Channel->FastRamping = CTE_FALSE; Channel->LastLeftClickRemovalFadeOut = Channel->LastLeftClickRemovalFadeOut+Channel->LastLeft; Channel->LastRightClickRemovalFadeOut = Channel->LastRightClickRemovalFadeOut+Channel->LastRight; Channel->LastLeft = 0; Channel->LastRight = 0; Channel->NoteCutCounter = 0; Channel->NewNote = CTE_TRUE; if(InstrumentChange || !Channel->Active) { #ifdef CreamTrackerEffectOxx Channel->SamplePosition.HiLo.Hi = SampleOffset; #else Channel->SamplePosition.HiLo.Hi = 0; #endif Channel->SamplePosition.HiLo.Lo = 0; } Channel->Active |= NoteChange; if(NoteChange) { Channel->StablePeriod = Channel->SlideToPeriod; } } } else { Channel->Active = CTE_FALSE; } } if(PatternNote->Volume != 255) { if(Channel->Instrument && (Channel->Instrument->Header.InstrumentType == 8) && (Channel->Instrument->Header.Flags & 1)) { Channel->Velocity = PatternNote->Volume; }else{ Channel->Volume = PatternNote->Volume; } } } } #ifdef CreamTrackerEffectQxx if(Retrig) { // Retrigger note every y ticks with volume modifier x. If the retrig value 'y' // is 0, the effect is ignored. // This effect uses a counter, that is increased on each tick. When the counter // reaches the retrig value 'y' (or becomes greater, which could happen if the // retrig value is decreased), the sample is retriggered, the note volume is // modified according to the volume modifier 'x' and the counter is reset back // to 0. The counter is reset (without retriggering the sample) also in the // following cases: // 1. before the start of playing the song // 2. when a row without the Qxx effect is encountered in the channel // However, the counter is not reset in any other cases, for example playing // a new note *with* the Qxy effect does not reset the counter. Also, this // effect is processed on every tick, including tick 0 and is entirely // independent of the song speed. A retrig of the sample can also happen on // tick 0, even when playing a new note in which case the note volume // modification occurs immediately after the new note is played. Speed = Retrig & 0x0f; if(!Speed) { Speed = 1; } if(Channel->RetrigCounter && !(Channel->RetrigCounter % Speed)) { #ifdef CreamTrackerCode SynthReset = CTE_TRUE; #endif Channel->SamplePosition.Value = 0; if(Channel->Instrument && ((Channel->PatternNote.Note < 0xf0) || Channel->PatternNote.Instrument)) { Channel->Active = CTE_TRUE; } Depth = (Retrig & 0xf0) >> 4; if(Depth & 7) { Channel->Volume = RetrigTables[0][Depth] ? (Channel->Volume*(RetrigTables[0][Depth] / 16.0)) : (Channel->Volume + RetrigTables[1][Depth]); if(Channel->Volume < 0) { Channel->Volume = 0; } else if(Channel->Volume > 64) { Channel->Volume = 64; } } } Channel->RetrigCounter++; } #endif #ifdef CreamTrackerCode if(SynthReset) { if(Channel->Instrument && Channel->Instrument->CodeData && Channel->Instrument->CodeData->SynthProcInstanceNoteOn && Channel->Instrument->CodeData->InstanceData) { pp = Channel->Instrument->CodeData->SynthProcInstanceNoteOn; pd = (cte_pointer)&(((cte_uint8_p)(Channel->Instrument->CodeData->InstanceData))[ChannelIndex * Channel->Instrument->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = Channel; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); Value = Channel->BaseNote; OtherValue = Channel->Velocity; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %0,%%esi\n\t" "pushl %2\n\t" "pushl %1\n\t" "mov %3,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" : : "m"(pd), "m"(Value), "m"(OtherValue), "m"(pp) ); #else __asm { pushad mov edi,esp mov esi,dword ptr pd push dword ptr OtherValue push dword ptr Value mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp popad } #endif } } #endif if(Channel->NoteCutCounter > 0) { VolumeFactor = 0.0; Channel->NoteCutCounter--; if(!Channel->NoteCutCounter) { Channel->Active = CTE_FALSE; Channel->BaseNote = 0xff; Channel->LastLeftClickRemovalFadeOut += Channel->LastLeft; Channel->LastRightClickRemovalFadeOut += Channel->LastRight; Channel->LastLeft = 0; Channel->LastRight = 0; } } if(Instance->AbsoluteTick == Channel->NoteCutTick) { Channel->NoteCutCounter = 1; Channel->Volume = 0.0; VolumeFactor = 0.0; Channel->FastRamping = CTE_TRUE; } #ifdef CreamTrackerSetPanning if(SetPanning >= 0) { Channel->Panning = SetPanning; if(Channel->Panning < 0) { Channel->Panning = 0; } else if(Channel->Panning > 256) { Channel->Panning = 256; } } #endif #ifdef CreamTrackerEffectJxx if(Arpeggio) { PeriodInvFactor *= Instance->NoteFactorTable[(Arpeggio >> (Channel->ArpeggioPos << 2)) & 0xf]; Channel->ArpeggioPos++; if(Channel->ArpeggioPos>2) { Channel->ArpeggioPos = 0; } } #endif #ifdef CreamTrackerVolumeSlide if(VolumeSlide) { // Volume slide. If one of the values are 0, then we slide on all nonzero ticks. // If one of the values are F, then we slide on all zero ticks. That means that D0F // slides down 15 on all ticks and DF0 slides up 15 on all ticks. // However, if fast slides are enabled (if they are set as a flag or the version // is <= 0x1300), then, unless we're doing a fineslide, we slide on all ticks. // When we do a volume slide, we slide the active volume without modifying the stored volume. // The checking order is not the same as ImpulseTracker. // Here's a full detailed description of all possible cases (0x00..0xFF) how ScreamTracker Tracker handle them: Trigger = ((Instance->Header.CWTV <= 0x1300) || (Instance->Header.Flags & 0x40)) || !Instance->FirstRowTick; cte_uint8_t VolumeSlideHi = VolumeSlide & 0xf0; cte_uint8_t VolumeSlideLo = VolumeSlide & 0x0f; if(((VolumeSlideHi >= 0x00) && (VolumeSlideHi <= 0xe0)) && ((VolumeSlideLo >= 0x01) && (VolumeSlideLo <= 0x0e))) { // D0x, 1 <= x <= 0xE: slide down by x on all nonzero ticks. Also slide on tick 0, if fast slides are enabled. // Dxy, 1 <= x <= 0xE, 1 <= y <= 0xE: ScreamTracker Tracker treats it as a slide down by y, i.e. equivalent to D0y. if(Trigger) { Channel->Volume -= VolumeSlide & 0xf; } } else if((!VolumeSlideLo) && ((VolumeSlideHi >= 0x10) && (VolumeSlideHi <= 0xe0))) { // Dx0, 1 <= x <= 0xE: slide up by x on all nonzero ticks. Also slide on tick 0, if fast slides are enabled. if(Trigger) { Channel->Volume += (VolumeSlide >> 4) & 0xf; } } else if((VolumeSlideHi == 0xf0) && ((VolumeSlideLo >= 0x01) && (VolumeSlideLo <= 0x0e))) { // DFx, 1 <= x <= 0xE: slide down by x on tick 0. if(Instance->FirstRowTick) { Channel->Volume -= VolumeSlide & 0xf; } } else if((VolumeSlideLo == 0x0f) && ((VolumeSlideHi >= 0x10) && (VolumeSlideHi <= 0xe0))) { // DxF, 1 <= x <= 0xE: slide up by x on tick 0. if(Instance->FirstRowTick) { Channel->Volume += (VolumeSlide >> 4) & 0xf; } } else if(VolumeSlide == 0xff) { // DFF: slide up by 15 on tick 0. if(Instance->FirstRowTick) { Channel->Volume += 0xf; } } else if(VolumeSlide == 0x0f) { // D0F: slide down by 15 on all ticks. Not affected at all by the fast slides flag. Channel->Volume -= 0xf; } else if(VolumeSlide == 0xf0) { // DF0: slide up by 15 on all ticks. Not affected at all by the fast slides flag. Channel->Volume += 0xf; } else { // D00: ScreamTracker Tracker uses the last nonzero effect parameter in the channel. } if(Channel->Volume < 0) { Channel->Volume = 0; } else if(Channel->Volume > 64) { Channel->Volume = 64; } } #endif #ifdef CreamTrackerPortamento if(Portamento) { // Slide to note. // Peculiarities in the ScreamTracker Tracker implementation of this effect: // 1. If the current note is empty, the destination note is set to the // last note to show up in the channel, even if it has occurred // without the Gxx effect. // 2. Gxx doesn't clear the target note when it is reached, so any // future Gxx with no note will keep sliding back to this particular // note. if((!(Portamento & 0x100)) || !Instance->FirstRowTick) { if(Channel->StablePeriod < Channel->SlideToPeriod) { Channel->StablePeriod += (cte_int32_t)(Portamento & 0xff) * 4; if(Channel->StablePeriod > Channel->SlideToPeriod) { Channel->StablePeriod = Channel->SlideToPeriod; } } else if(Channel->StablePeriod > Channel->SlideToPeriod) { Channel->StablePeriod -= (cte_int32_t)(Portamento & 0xff) * 4; if(Channel->StablePeriod < Channel->SlideToPeriod) { Channel->StablePeriod = Channel->SlideToPeriod; } } } } #endif if(Instance->Header.Flags & 16) { // Amiga limits if(Channel->StablePeriod < (907 >> 1)) { Channel->StablePeriod = 907 >> 1; } else if(Channel->StablePeriod > (1712 * 2)) { Channel->StablePeriod = 1712 * 2; } } Channel->LivePeriod = Channel->StablePeriod; #ifdef CreamTrackerPortamento if((Portamento && ((!(Portamento & 0x100)) || !Instance->FirstRowTick)) && Channel->Glissando) { for(Counter = 0; Counter < 195; Counter++) { Temp = Instance->NoteToPeriodTable[Counter] * Channel->C4SpeedFactor; if(Temp <= Channel->LivePeriod) { Channel->LivePeriod = Temp; break; } } } #endif #ifdef CreamTrackerVibrato if(Vibrato) { // Vibrato. This effect shares memory with Uxy. CreamTrackerChannelVibratoCheck(Channel); PeriodMod += WaveTables[Channel->VibratoWaveForm & 3][Channel->VibratoPosition] * (((Vibrato & 0xf) << ((((~Vibrato) & 0x100) >> 8) << 1)) / 128.0); if(!Instance->FirstRowTick) { CreamTrackerChannelVibratoUpdate(Instance, Channel, (Vibrato & 0xf0) >> 4); } } #endif #ifdef CreamTrackerEffectRxx if(Tremolo) { // Tremolo. x*4 is speed, y*4 is depth. // This effect is screwy, but not as screwy as previously documented. // 1. Get xy from the latest nonzero effect parameter to appear in the channel. // 2. On tick 1 (the second tick of the row) set the active volume to the stored // volume plus (depth * value) / (max_amplitude * 2) (Rxy peaks at 32 in each // direction), and for each nonzero tick increase the tremolo position by the // speed. The stored volume is untouched. // 3. If the song speed (not tremolo speed, but song speed - ticks per row) is 1, // the active volume is also untouched. It is not set to the stored volume! // 4. Tremolo will not work if the stored volume is 0 (or 64 - adlib only). CreamTrackerChannelVibratoCheck(Channel); VolumeMod += WaveTables[Channel->TremoloWaveForm & 3][Channel->VibratoPosition] * ((Tremolo & 0xf) / 128.0); if(!Instance->FirstRowTick) { CreamTrackerChannelVibratoUpdate(Instance,Channel,(Tremolo & 0xf0) >> 4); } } #endif #ifdef CreamTrackerEffectIxx if(Channel->TremorCounter & 0x80) { // Tremor // 1. "On" time is x + 1 ticks, "off" time is y + 1 ticks // 2. This effect is updated on every tick. // 3. Implemented with two decrementing counters per channel - // the "on" counter and the "off" counter. // 4. On each tick, if the "on" counter is greater than zero, // it is decremented and if it reaches zero, the current // volume is set to 0 and the "off" counter is set to the // "off" time (y + 1). If the "on" counter was zero in the // beginning of the update procedure, then the "off" counter // is decremented and if it reached zero (or became less than // zero), the current volume is set to the stored volume and // the "on" counter is set to the "on" time (x + 1). // 5. The "on" and "off" counters are never reset, except in the // tremor update procedure described above. ScreamTracker Tracker // doesn't even reset them on playback start. Only on tracker // startup are they reset. // 6. If the current volume was 0 at the end of the effect and // there is no tremor effect on the next row, the current // volume stays 0. It isn't reset back to the stored volume // or its previous value from before the tremor effect. // 7. The stored volume isn't modified by this effect. switch(Channel->TremorCounter) { case 0x80: Channel->TremorCounter = (Channel->TremorParameter >> 4) | 0xc0; break; case 0xc0: Channel->TremorCounter = (Channel->TremorParameter & 0xf) | 0x80; break; default: Channel->TremorCounter--; break; } if((Channel->TremorCounter & 0xc0) == 0x80) { VolumeFactor = 0; } } #endif #ifdef CreamTrackerPitchSlide if(PitchSlide) { Mangitude = PitchSlide & 0xff; switch(Mangitude & 0xf0) { case 0xe0: Mangitude = (Mangitude & 0x0f) << 2; Fine = 1; break; case 0xf0: Mangitude &= 0x0f; Fine = 2; break; default: Mangitude <<= 2; Fine = 0; break; } if(PitchSlide & 0x100) { Mangitude = -Mangitude; } if((Fine && Instance->FirstTick) || ((!Fine) && ((Instance->Speed == 1) || !Instance->FirstTick))) { Channel->StablePeriod += Mangitude; Channel->LivePeriod = Channel->StablePeriod; } } #endif #ifdef CreamTrackerEffectNxx if(ChannelVolumeSlide) { Trigger = ((Instance->Header.CWTV == 0x1330) || (Instance->Header.Flags & 0x40)) || (Instance->Tick >= Channel->NoteOnTick); if(!(ChannelVolumeSlide & 0xf0)) { if(Trigger) { Channel->ChannelVolume -= ChannelVolumeSlide & 0xf; } } else if(!(ChannelVolumeSlide & 0x0f)) { if(Trigger) { Channel->ChannelVolume += ChannelVolumeSlide >> 4; } } else if(Instance->Tick == Channel->NoteOnTick) { if((ChannelVolumeSlide & 0x0f) == 0x0f) { Channel->ChannelVolume += ChannelVolumeSlide >> 4; } else if((ChannelVolumeSlide & 0xf0) == 0xf0) { Channel->ChannelVolume -= ChannelVolumeSlide & 0xf; } } if(Channel->ChannelVolume < 0) { Channel->ChannelVolume = 0; } else if(Channel->ChannelVolume > 64) { Channel->ChannelVolume = 64; } } #endif #ifdef CreamTrackerEffectWxx if(GlobalVolumeSlide) { Trigger = ((Instance->Header.CWTV == 0x1330) || (Instance->Header.Flags & 0x40)) || (Instance->Tick >= Channel->NoteOnTick); if(!(GlobalVolumeSlide & 0xf0)) { if(Trigger) { Instance->GlobalVolume -= GlobalVolumeSlide & 0xf; } } else if(!(GlobalVolumeSlide & 0x0f)) { if(Trigger) { Instance->GlobalVolume += GlobalVolumeSlide >> 4; } } else if(Instance->Tick == Channel->NoteOnTick) { if((GlobalVolumeSlide & 0x0f) == 0x0f) { Instance->GlobalVolume += GlobalVolumeSlide >> 4; } else if((GlobalVolumeSlide & 0xf0) == 0xf0) { Instance->GlobalVolume -= GlobalVolumeSlide & 0xf; } } if(Instance->GlobalVolume < 0) { Instance->GlobalVolume = 0; } else if(Instance->GlobalVolume > 128) { Instance->GlobalVolume = 128; } } #endif #ifdef CreamTrackerEffectTxx if((!Instance->FirstTick) && TempoSlide && (((TempoSlide & 0x10) != 0) != ((TempoSlide & 0x01) != 0))) { if(TempoSlide & 0x10) { if(Instance->Tempo < 255) { Instance->Tempo++; CreamTrackerUpdateTempo(Instance); } } else if(TempoSlide & 0x01) { if(Instance->Tempo > 32) { Instance->Tempo--; CreamTrackerUpdateTempo(Instance); } } } #endif Temp = (Channel->LivePeriod + PeriodMod) * PeriodFactor; if(Instance->Header.Flags & 16) { // Amiga limits if(Temp < (907 >> 1)) { Temp = 907 >> 1; } else if(Temp > (1712 * 2)) { Temp = 1712 * 2; } } if(Temp) { Channel->SynthFrequency = (Channel->NoteFrequencyFactor / Temp) * PeriodInvFactor; Channel->SynthIncrement = (cte_float_t)(Channel->SynthFrequency * Instance->InvSampleRate); Channel->LiveIncrement = (Instance->HertzRatio / Temp) * PeriodInvFactor; Channel->Increment = CreamTrackerRound(Channel->LiveIncrement * 0x100000000ll); } else { Channel->SynthFrequency = 0.0; Channel->SynthIncrement = 0.0; Channel->LiveIncrement = 0; Channel->Increment = 0; } if(!Channel->Increment) { Channel->Active = CTE_FALSE; Channel->LastLeftClickRemovalFadeOut += Channel->LastLeft; Channel->LastRightClickRemovalFadeOut += Channel->LastRight; Channel->LastLeft = 0; Channel->LastRight = 0; } Channel->SINCCutOffLevel = 0; for(Index = 0; Index < SINCCUTOFF_LEN; Index++) { if(Channel->Increment <= ResamplerSINCCutOffIncrementTable[Index]) { Channel->SINCCutOffLevel = Index; break; } } Volume = (Channel->Volume + VolumeMod) / 64.0; if(Volume < 0.0) { Volume = 0.0; } else if(Volume > 1.0) { Volume = 1.0; } Volume *= VolumeFactor; if(Volume < 0.0) { Volume = 0.0; } else if(Volume > 1.0) { Volume = 1.0; } Pan = Channel->Panning / 256.0; if(Pan < 0.0) { Pan = 0.0; } else if(Pan > 1.0) { Pan = 1.0; } Channel->LeftVolume = (cte_float_t)(Volume * (1.0 - Pan)); Channel->RightVolume = (cte_float_t)(Volume * Pan); if(Channel->NewNote) { Channel->LastLeftClickRemovalFadeOut += Channel->LastLeft; Channel->LastRightClickRemovalFadeOut += Channel->LastRight; Channel->LastLeft = 0; Channel->LastRight = 0; if((Instance->CreamTrackerModule && Channel->Instrument && (Channel->Instrument->Header.Flags & 16)) || !Instance->CreamTrackerModule) { Channel->LeftVolumeCurrent = 0.0; Channel->RightVolumeCurrent = 0.0; } else { Channel->LeftVolumeCurrent = Channel->LeftVolume; Channel->RightVolumeCurrent = Channel->RightVolume; } Channel->LeftVolumeInc = 0.0; Channel->RightVolumeInc = 0.0; Channel->VolumeRampingRemain = 0; } if((Channel->LeftVolumeCurrent != Channel->LeftVolume) || (Channel->RightVolumeCurrent != Channel->RightVolume)) { Channel->VolumeRampingRemain = Instance->TickSamples; if(Channel->FastRamping) { Channel->VolumeRampingRemain >>= 1; if(!Channel->VolumeRampingRemain) { Channel->VolumeRampingRemain = 1; } } Channel->LeftVolumeInc = (Channel->LeftVolume-Channel->LeftVolumeCurrent)/Channel->VolumeRampingRemain; Channel->RightVolumeInc = (Channel->RightVolume-Channel->RightVolumeCurrent)/Channel->VolumeRampingRemain; } Channel->ChannelBufferVolume = Channel->ChannelVolume / 64.0f; if(Instance->VeryFirstTick) { Channel->ChannelBufferVolumeCurrent = Channel->ChannelBufferVolume; Channel->ChannelBufferVolumeRampingRemain = 0; Channel->ChannelBufferVolumeInc = 0; } else if(Channel->ChannelBufferVolumeCurrent != Channel->ChannelBufferVolume) { Channel->ChannelBufferVolumeRampingRemain = Instance->TickSamples; Channel->ChannelBufferVolumeInc = (Channel->ChannelBufferVolume - Channel->ChannelBufferVolumeCurrent) / Channel->ChannelBufferVolumeRampingRemain; } Channel->NewNote = CTE_FALSE; } } #undef GetLastEffectParameter } void CreamTrackerProcessGlobals(PCreamTrackerInstance Instance) { Instance->BufferVolume = (cte_float_t)((((Instance->Header.MasterVolume & 127) / 127.0)/* * (8.0 / 11.0) */) * (Instance->GlobalVolume / 128.0)); if(Instance->VeryFirstTick) { Instance->BufferVolumeCurrent = Instance->BufferVolume; Instance->BufferVolumeRampingRemain = 0; Instance->BufferVolumeInc = 0; } else if(Instance->BufferVolumeCurrent != Instance->BufferVolume) { Instance->BufferVolumeRampingRemain = Instance->TickSamples; Instance->BufferVolumeInc = (Instance->BufferVolume - Instance->BufferVolumeCurrent) / Instance->BufferVolumeRampingRemain; } } void CreamTrackerUpdateTick(PCreamTrackerInstance Instance) { CreamTrackerProcessTick(Instance); CreamTrackerProcessChannels(Instance); CreamTrackerProcessGlobals(Instance); Instance->VeryFirstTick = CTE_FALSE; } void CreamTrackerChannelFillBuffer(PCreamTrackerInstance Instance, PCreamTrackerChannel Channel, cte_int32_t ToDo) { const cte_double_t LocalInvPositionFactor = 1.0 / ((cte_double_t)PositionFactor); cte_int32_t Counter, BufPos, ChannelRemain, ChannelToDo, EndPos, SampleIndex, VolumeRampingRemain, VolumeRamping; cte_float_p ChannelBuffer, ChannelBuf, TempBuf; cte_float_t Left, Right, SampleInterpolation, InvSampleInterpolation, LeftVolumeCurrent, RightVolumeCurrent, LeftVolumeInc, RightVolumeInc; TCreamTrackerInt64 SamplePosition; cte_int64_t Increment; PCreamTrackerInstrument Instrument; #ifdef CreamTrackerSINCResampling cte_float_p SampleLeftData, SampleRightData; PResamplerSINCSubArray SubArray; PResamplerSINCSubSubArray SubSubArray; TCreamTrackerInt64 SecondSamplePosition; #else cte_float_p SampleData; #endif #ifdef CreamTrackerCode cte_int32_t ChannelIndex, Value; cte_float_t SynthIncrement; cte_pointer pp,pd; #endif #ifndef CreamTrackerPortable cte_int16_t OldCW; #endif if(Channel->Enabled) { #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" : : "m"(OldCW), "m"(CreamTrackerCW) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW } #endif #endif Instance->CurrentChannel = Channel; #ifdef CreamTrackerCode ChannelIndex = Channel->Index; #endif ChannelBuffer = &Channel->Buffer[0]; ChannelBuf = ChannelBuffer; TempBuf = &Channel->TempBuffer[0]; if((Channel->LastLeftClickRemovalFadeOut != 0.0) || (Channel->LastRightClickRemovalFadeOut != 0.0)) { BufPos = 0; for(Counter = 0; Counter < ToDo; Counter++) { Channel->LastLeftClickRemovalFadeOut = ((Channel->LastLeftClickRemovalFadeOut * Instance->ClickRemovalFadeOutFactor) + fDenormal) - fDenormal; Channel->LastRightClickRemovalFadeOut = ((Channel->LastRightClickRemovalFadeOut * Instance->ClickRemovalFadeOutFactor) + fDenormal) - fDenormal; ChannelBuffer[BufPos++] = Channel->LastLeftClickRemovalFadeOut; ChannelBuffer[BufPos++] = Channel->LastRightClickRemovalFadeOut; } } else { FillChar(ChannelBuffer, ToDo * (2 * sizeof(cte_float_t)), 0); } if(Channel->Active && Channel->Instrument) { Instrument = Channel->Instrument; #ifdef CreamTrackerSINCResampling if(Instrument->Data && Instrument->SINCLeftData) { SampleLeftData = &Instrument->SINCLeftData[TotalFixUpSafeAdditionalSampleLength * 2]; SampleRightData = &Instrument->SINCRightData[TotalFixUpSafeAdditionalSampleLength * 2]; } else { SampleLeftData = NULL; SampleRightData = NULL; } #else SampleData = Instrument->MixData; if(Instrument->Data && SampleData) { SampleData = &SampleData[TotalFixUpSafeAdditionalSampleLength * 4]; } #endif ChannelRemain = ToDo; while(ChannelRemain > 0) { ChannelToDo = ChannelRemain; if(!Channel->Increment) { ChannelToDo = 0; } else { if((Instrument->Header.InstrumentType == 1) || (Instrument->Header.InstrumentType == 9)) { if(Instrument->Data) { EndPos = Instrument->Header.Length; if(Instrument->Header.Flags & 1) { EndPos = Instrument->Header.LoopEnd; if(Channel->SamplePosition.HiLo.Hi >= EndPos) { Channel->SamplePosition.HiLo.Hi += Instrument->Header.LoopStart - EndPos; if(Channel->SamplePosition.HiLo.Hi < Instrument->Header.LoopStart) { Channel->SamplePosition.HiLo.Hi = Instrument->Header.LoopStart; Channel->SamplePosition.HiLo.Lo = 0; } } } if((Channel->SamplePosition.HiLo.Hi < 0) || (Channel->SamplePosition.HiLo.Hi >= Instrument->Header.Length)) { ChannelToDo = 0; } else if (((Channel->SamplePosition.Value + CreamTrackerInt64Mul(Channel->Increment, (ChannelToDo - 1))) >> 32) >= EndPos) { ChannelToDo = (cte_int32_t)((cte_int64_t)(CreamTrackerInt64Div(((((cte_int64_t)(EndPos) << 32) - Channel->SamplePosition.Value) - 1), Channel->Increment) + 1)); if(ChannelToDo > ChannelRemain) { ChannelToDo = ChannelRemain; } } } else { ChannelToDo = 0; } } } if((ChannelToDo <= 0) || (((Instrument->Header.InstrumentType == 1) || (Instrument->Header.InstrumentType == 9)) && ! #ifdef CreamTrackerSINCResampling SampleLeftData #else SampleData #endif )) { Channel->Active = CTE_FALSE; if((Channel->LastLeft != 0.0) || (Channel->LastRight != 0.0)) { Left = Channel->LastLeft; Right = Channel->LastRight; BufPos = 0; for(Counter = 0; Counter < ChannelRemain; Counter++) { Left = ((Left * Instance->ClickRemovalFadeOutFactor) + fDenormal) - fDenormal; Right = ((Right * Instance->ClickRemovalFadeOutFactor) + fDenormal) - fDenormal; ChannelBuf[BufPos++] += Left; ChannelBuf[BufPos++] += Right; } Channel->LastLeftClickRemovalFadeOut = Channel->LastLeftClickRemovalFadeOut+Left; Channel->LastRightClickRemovalFadeOut = Channel->LastRightClickRemovalFadeOut+Right; Channel->LastLeft = 0; Channel->LastRight = 0; } break; } else { switch(Instrument->Header.InstrumentType) { case 1: case 9: // Resampling Increment = Channel->Increment; SamplePosition.Value = Channel->SamplePosition.Value; #ifdef CreamTrackerSINCResampling if(Channel->Increment == 0x100000000ll) #endif { BufPos = 0; for(Counter = ChannelToDo; Counter > 0; Counter--) { SampleInterpolation = (cte_float_t)(SamplePosition.HiLo.Lo * LocalInvPositionFactor); InvSampleInterpolation = 1.0f - SampleInterpolation; #ifdef CreamTrackerSINCResampling SampleIndex = SamplePosition.HiLo.Hi; TempBuf[BufPos++] = (SampleLeftData[SampleIndex + 0] * InvSampleInterpolation) + (SampleLeftData[SampleIndex + 1] * SampleInterpolation); TempBuf[BufPos++] = (SampleRightData[SampleIndex + 0] * InvSampleInterpolation) + (SampleRightData[SampleIndex + 1] * SampleInterpolation); #else SampleIndex = SamplePosition.HiLo.Hi << 1; TempBuf[BufPos++] = (SampleData[SampleIndex + 0] * InvSampleInterpolation) + (SampleData[SampleIndex + 2] * SampleInterpolation); TempBuf[BufPos++] = (SampleData[SampleIndex + 1] * InvSampleInterpolation) + (SampleData[SampleIndex + 3] * SampleInterpolation); #endif SamplePosition.Value += Increment; } #ifdef CreamTrackerSINCResampling } else { SubArray = &((*ResamplerSINCArray)[Channel->SINCCutOffLevel]); { BufPos = 0; if(Instrument->Header.Flags & 2) { // Stereo for(Counter = ChannelToDo; Counter > 0; Counter--) { SubSubArray = &((*SubArray)[(SamplePosition.HiLo.Lo >> SINC_FRACSHIFT) & SINC_FRACMASK]); Left = Convolve(&SampleLeftData[SamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH); Right = Convolve(&SampleRightData[SamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH); if(SamplePosition.HiLo.Lo & SINC_FRACSHIFTMASK) { SampleInterpolation = (cte_float_t)((SamplePosition.HiLo.Lo & SINC_FRACSHIFTMASK) * SINC_FRACSHIFTFACTOR); SecondSamplePosition.Value = SamplePosition.Value + SINC_FRACSHIFTLENGTH; SubSubArray = &((*SubArray)[(SecondSamplePosition.HiLo.Lo >> SINC_FRACSHIFT) & SINC_FRACMASK]); Left += (Convolve(&SampleLeftData[SecondSamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH) - Left) * SampleInterpolation; Right += (Convolve(&SampleRightData[SecondSamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH) - Right) * SampleInterpolation; } TempBuf[BufPos++] = Left; TempBuf[BufPos++] = Right; SamplePosition.Value += Increment; } } else { // Mono for(Counter = ChannelToDo; Counter > 0; Counter--) { SubSubArray = &((*SubArray)[(SamplePosition.HiLo.Lo >> SINC_FRACSHIFT) & SINC_FRACMASK]); Left = Convolve(&SampleLeftData[SamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH); if(SamplePosition.HiLo.Lo & SINC_FRACSHIFTMASK) { SampleInterpolation = (cte_float_t)((SamplePosition.HiLo.Lo & SINC_FRACSHIFTMASK) * SINC_FRACSHIFTFACTOR); SecondSamplePosition.Value = SamplePosition.Value + SINC_FRACSHIFTLENGTH; SubSubArray = &((*SubArray)[(SecondSamplePosition.HiLo.Lo >> SINC_FRACSHIFT) & SINC_FRACMASK]); Left += (Convolve(&SampleLeftData[SecondSamplePosition.HiLo.Hi - SINC_HALFWIDTH], &((*SubSubArray)[0]), SINC_WIDTH) - Left) * SampleInterpolation; } TempBuf[BufPos++] = Left; TempBuf[BufPos++] = Left; SamplePosition.Value += Increment; } } } #endif } Channel->SamplePosition.Value = SamplePosition.Value; break; #ifdef CreamTrackerCode case 8: // Synthesizing if(Instance->CreamTrackerModule && Channel->Instrument && Channel->Instrument->CodeData && Channel->Instrument->CodeData->SynthProcInstanceProcess && Channel->Instrument->CodeData->InstanceData) { pp = Channel->Instrument->CodeData->SynthProcInstanceProcess; pd = (cte_pointer)&(((cte_uint8_p)(Channel->Instrument->CodeData->InstanceData))[ChannelIndex * Channel->Instrument->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = Channel; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); SynthIncrement = Channel->SynthIncrement; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushl %%eax\n\t" "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %1,%%esi\n\t" "pushl %2\n\t" "pushl %3\n\t" "pushl %4\n\t" "mov %5,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "mov %%eax,28(%%esp)\n\t" "popal\n\t" "movl %%eax,%0\n\t" "popl %%eax\n\t" : "=m"(Value) : "m"(pd), "m"(SynthIncrement), "m"(ChannelToDo), "m"(TempBuf), "m"(pp) ); #else __asm { push eax pushad mov edi,esp mov esi,dword ptr pd push dword ptr SynthIncrement push dword ptr ChannelToDo push dword ptr TempBuf mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp mov dword ptr [esp+28],eax popad mov dword ptr Value,eax pop eax } #endif if(!Value) { Channel->Increment = 0; Channel->Active = CTE_FALSE; } } else { BufPos = 0; for(Counter = ChannelToDo; Counter > 0; Counter--) { TempBuf[BufPos++] = 0.0; TempBuf[BufPos++] = 0.0; } } break; #endif default: BufPos = 0; for(Counter = ChannelToDo; Counter > 0; Counter--) { TempBuf[BufPos++] = 0.0; TempBuf[BufPos++] = 0.0; } break; } { // Volume amplifying LeftVolumeCurrent = Channel->LeftVolumeCurrent; RightVolumeCurrent = Channel->RightVolumeCurrent; LeftVolumeInc = Channel->LeftVolumeInc; RightVolumeInc = Channel->RightVolumeInc; VolumeRampingRemain = Channel->VolumeRampingRemain; if(VolumeRampingRemain || ((LeftVolumeCurrent != 1.0) || (RightVolumeCurrent != 1.0))) { BufPos = 0; for(Counter = ChannelToDo; Counter > 0; Counter--) { TempBuf[BufPos++] *= LeftVolumeCurrent; TempBuf[BufPos++] *= RightVolumeCurrent; VolumeRamping = ((cte_uint32_t)(-VolumeRampingRemain) >> 31) & 1; LeftVolumeCurrent += (LeftVolumeInc *= VolumeRamping); RightVolumeCurrent += (RightVolumeInc *= VolumeRamping); VolumeRampingRemain -= VolumeRamping; } Channel->VolumeRampingRemain = VolumeRampingRemain; if(Channel->VolumeRampingRemain) { Channel->LeftVolumeCurrent = LeftVolumeCurrent; Channel->RightVolumeCurrent = RightVolumeCurrent; } else { Channel->LeftVolumeCurrent = Channel->LeftVolume; Channel->RightVolumeCurrent = Channel->RightVolume; } } } { // Mixing Left = 0.0; Right = 0.0; { BufPos = 0; for(Counter = ChannelToDo; Counter > 0; Counter--) { Left = TempBuf[BufPos + 0]; Right = TempBuf[BufPos + 1]; ChannelBuf[BufPos++] += Left; ChannelBuf[BufPos++] += Right; } } Channel->LastLeft = Left; Channel->LastRight = Right; } ChannelBuf = &ChannelBuf[ChannelToDo * 2]; ChannelRemain -= ChannelToDo; } } } { // Channel volume amplifying LeftVolumeCurrent = Channel->ChannelBufferVolumeCurrent; LeftVolumeInc = Channel->ChannelBufferVolumeInc; VolumeRampingRemain = Channel->ChannelBufferVolumeRampingRemain; { if(VolumeRampingRemain || (LeftVolumeCurrent != 1.0)) { BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { ChannelBuffer[BufPos++] *= LeftVolumeCurrent; ChannelBuffer[BufPos++] *= LeftVolumeCurrent; VolumeRamping = ((cte_uint32_t)(-VolumeRampingRemain) >> 31) & 1; LeftVolumeCurrent += (LeftVolumeInc *= VolumeRamping); VolumeRampingRemain -= VolumeRamping; } Channel->ChannelBufferVolumeRampingRemain = VolumeRampingRemain; if(Channel->ChannelBufferVolumeRampingRemain) { Channel->ChannelBufferVolumeCurrent = LeftVolumeCurrent; } else { Channel->ChannelBufferVolumeCurrent = Channel->ChannelBufferVolume; } } } } #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldcw %0\n\t" : : "m"(OldCW) ); #else __asm { fldcw word ptr OldCW } #endif #endif } } #ifdef CreamTrackerMultithreading void CreamTrackerJobManagerProcessThread(PCreamTrackerInstance Instance, cte_int32_t ThreadNumber) { PCreamTrackerJobManager JobManager; PCreamTrackerJob Job; cte_int32_t Index; JobManager = &Instance->JobManager; while(1) { Index = CreamTrackerInterlockedDecrement(&JobManager->JobQueueIndex) + 1; if(Index >= 0) { Job = &JobManager->Jobs[Index]; switch(Job->Mode) { case ctjmCHANNELMIX: CreamTrackerChannelFillBuffer(Job->Instance, Job->Channel, Job->Samples); break; default: break; } } else { break; } } } void CTE_STDCALL CreamTrackerJobThread(PCreamTrackerJobThread JobThread) { cte_uint16_t OldCW; #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" : : "m"(OldCW), "m"(CreamTrackerCW) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW } #endif #endif CreamTrackerInterlockedIncrement(&((PCreamTrackerInstance)(JobThread->Instance))->Threads); while(!((PCreamTrackerInstance)(JobThread->Instance))->ThreadsTerminated) { WaitForSingleObject(JobThread->Event, INFINITE); if(((PCreamTrackerInstance)(JobThread->Instance))->ThreadsTerminated) { break; } else { #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldcw %0\n\t" "pushal\n\t" : : "m"(CreamTrackerCW) ); #else __asm { fldcw word ptr CreamTrackerCW pushad } #endif SIMDSetFlags(); CreamTrackerJobManagerProcessThread(JobThread->Instance, JobThread->ThreadNumber); #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__("popal"); #else __asm { popad } #endif SetEvent(JobThread->DoneEvent); } } CreamTrackerInterlockedDecrement(&((PCreamTrackerInstance)(JobThread->Instance))->Threads); #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldcw %0\n\t" : : "m"(OldCW) ); #else __asm { fldcw word ptr OldCW } #endif #endif ExitThread(0); } void CreamTrackerJobCreateThreads(PCreamTrackerInstance Instance) { cte_int32_t i, j, Cores[MaxThreads]; SYSTEM_INFO sinfo; DWORD dwProcessAffinityMask, dwSystemAffinityMask; GetSystemInfo(&sinfo); GetProcessAffinityMask(GetCurrentProcess(),&dwProcessAffinityMask,&dwSystemAffinityMask); j = 0; for(i = 0; (i < (cte_int32_t)sinfo.dwNumberOfProcessors) && (j < MaxThreads); i++) { if (dwProcessAffinityMask && (1 << i)) { Cores[j++] = i; } } Instance->JobManager.CountThreads = (j < 1) ? 1 : j; Instance->UseMultithreading = Instance->JobManager.CountThreads > 1; for(i = 0; i < Instance->JobManager.CountThreads; i++) { Instance->JobManager.Threads[i].Instance = Instance; Instance->JobManager.Threads[i].ThreadNumber = i; Instance->JobManager.Threads[i].ThreadHandle = 0; Instance->JobManager.Threads[i].ThreadID = 0; Instance->JobManager.DoneEventHandles[i] = 0; } if(Instance->JobManager.CountThreads > 1) { for(i = 0; i < Instance->JobManager.CountThreads; i++) { Instance->JobManager.Threads[i].Event = CreateEventA(NULL, 0, 0, NULL); Instance->JobManager.Threads[i].DoneEvent = CreateEventA(NULL, 0, 0, NULL); Instance->JobManager.DoneEventHandles[i] = Instance->JobManager.Threads[i].DoneEvent; Instance->JobManager.Threads[i].ThreadHandle = CreateThread(NULL, 0, (cte_pointer)&CreamTrackerJobThread, &Instance->JobManager.Threads[i], 0, &Instance->JobManager.Threads[i].ThreadID); #ifdef CreamTrackerThreadPriority SetThreadPriority(Instance->JobManager.Threads[i].ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); if(GetThreadPriority(Instance->JobManager.Threads[i].ThreadHandle) != THREAD_PRIORITY_TIME_CRITICAL) { // Fallback for no adminstrator windows users SetThreadPriority(Instance->JobManager.Threads[i].ThreadHandle, THREAD_PRIORITY_HIGHEST); if(GetThreadPriority(Instance->JobManager.Threads[i].ThreadHandle) != THREAD_PRIORITY_HIGHEST) { SetThreadPriority(Instance->JobManager.Threads[i].ThreadHandle, THREAD_PRIORITY_ABOVE_NORMAL); } } #endif #ifdef CreamTrackerThreadAffinity SetThreadIdealProcessor(Instance->JobManager.Threads[i].ThreadHandle, Cores[i]); SetThreadAffinityMask(Instance->JobManager.Threads[i].ThreadHandle, 1 << Cores[i]); #endif } } } void CreamTrackerJobWakeThreads(PCreamTrackerInstance Instance) { if(Instance->JobManager.CountThreads > 1) { cte_int32_t i; for(i = 0; i < Instance->JobManager.CountThreads; i++) { SetEvent(Instance->JobManager.Threads[i].Event); } } } void CreamTrackerJobWaitThreads(PCreamTrackerInstance Instance) { if(Instance->JobManager.CountThreads > 1) { DWORD result = WaitForMultipleObjects(Instance->JobManager.CountThreads, &Instance->JobManager.DoneEventHandles[0], TRUE, ThreadTimeOut); if(((result >= WAIT_ABANDONED_0) && (result <= (WAIT_ABANDONED_0 + (MAXIMUM_WAIT_OBJECTS - 1)))) || (result == WAIT_TIMEOUT) || (result == WAIT_FAILED)) { Instance->ThreadsTerminated = CTE_TRUE; } } } void CreamTrackerJobFreeThreads(PCreamTrackerInstance Instance) { if(Instance->JobManager.CountThreads > 1) { cte_int32_t i; for(i = 0; i < Instance->JobManager.CountThreads; i++) { if(Instance->JobManager.Threads[i].ThreadHandle) { WaitForSingleObject(Instance->JobManager.Threads[i].ThreadHandle, 25); TerminateThread(Instance->JobManager.Threads[i].ThreadHandle, 0); CloseHandle(Instance->JobManager.Threads[i].ThreadHandle); } CloseHandle(Instance->JobManager.Threads[i].Event); CloseHandle(Instance->JobManager.Threads[i].DoneEvent); } } } void CreamTrackerJobManagerInitProcessChannelMix(PCreamTrackerInstance Instance, cte_int32_t SamplesCount, cte_longbool_t NewTick) { cte_int32_t i, j; j = 0; for (i = 0; i < 64; i++) { if(Instance->Channels[i].Enabled && Instance->Channels[i].Instrument) { Instance->JobManager.Jobs[j].Mode = ctjmCHANNELMIX; Instance->JobManager.Jobs[j].Instance = Instance; Instance->JobManager.Jobs[j].Samples = SamplesCount; Instance->JobManager.Jobs[j].NewTick = NewTick; Instance->JobManager.Jobs[j].Channel = &Instance->Channels[i]; j++; } } Instance->JobManager.CountJobs = j; } void CreamTrackerJobManagerProcess(PCreamTrackerInstance Instance) { PCreamTrackerJobManager JobManager = &Instance->JobManager; if(JobManager->CountJobs > 0) { Instance->JobManager.JobQueueIndex = JobManager->CountJobs - 1; if(Instance->UseMultithreading && (Instance->JobManager.CountThreads > 1)) { CreamTrackerJobWakeThreads(Instance); CreamTrackerJobWaitThreads(Instance); } CreamTrackerJobManagerProcessThread(Instance, 0); JobManager->CountJobs = 0; } } #endif void CreamTrackerFillBuffer(PCreamTrackerInstance Instance, cte_pointer Buffer, cte_int32_t Samples) { cte_int32_t ToDo, ChannelIndex, Counter, BufPos, VolumeRampingRemain, VolumeRamping; PCreamTrackerChannel Channel; cte_float_t Left,LeftVolumeCurrent, LeftVolumeInc; cte_float_p Buf, ChannelBuffer, ChannelBuf; #ifdef CreamTrackerCode cte_pointer pp, pd, pdd; #endif #ifndef CreamTrackerPortable cte_int16_t OldCW; #endif SIMDSetFlags(); #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fstcw %0\n\t" "fldcw %1\n\t" : : "m"(OldCW), "m"(CreamTrackerCW) ); #else __asm { fstcw word ptr OldCW fldcw word ptr CreamTrackerCW } #endif #endif for(ChannelIndex = 0; ChannelIndex < 32; ChannelIndex++) { Channel = &Instance->Channels[ChannelIndex]; Instance->ChannelBuffers[ChannelIndex] = (Channel->Enabled && !Channel->Muted) ? &Instance->Channels[ChannelIndex].Buffer[0] : NULL; } Buf = Buffer; FillChar(Buf, Samples * (2 * sizeof(cte_float_t)), 0); while(Samples > 0) { if(!Instance->TickSamplesRemain) { CreamTrackerUpdateTick(Instance); Instance->TickSamplesRemain = Instance->TickSamples; } ToDo = Samples; if(ToDo > Instance->TickSamplesRemain) { ToDo = Instance->TickSamplesRemain; } if(ToDo > SubSamples) { ToDo = SubSamples; } #ifdef CreamTrackerMultithreading if(Instance->UseMultithreading) { CreamTrackerJobManagerInitProcessChannelMix(Instance, ToDo, CTE_TRUE); CreamTrackerJobManagerProcess(Instance); } else #endif { for(ChannelIndex = 0; ChannelIndex < 64; ChannelIndex++) { CreamTrackerChannelFillBuffer(Instance, &Instance->Channels[ChannelIndex], ToDo); } } { // Mixing slave channel buffers to master channel buffers for(ChannelIndex = 0; ChannelIndex < 32; ChannelIndex++) { Channel = &Instance->Channels[ChannelIndex | 0x20]; if(Channel->Master && ((PCreamTrackerChannel)(Channel->Master))->Enabled) { ChannelBuffer = &((PCreamTrackerChannel)(Channel->Master))->Buffer[0]; ChannelBuf = &Channel->Buffer[0]; BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { ChannelBuffer[BufPos + 0] += ChannelBuf[BufPos + 0]; ChannelBuffer[BufPos + 1] += ChannelBuf[BufPos + 1]; BufPos += 2; } } } } { // Mixing #ifdef CreamTrackerCode if(Instance->CreamTrackerModule && Instance->CodeData && Instance->CodeData->GlobalProcProcess) { pp = Instance->CodeData->GlobalProcProcess; pd = (cte_pointer)&(((cte_uint8_p)(Instance->CodeData->InstanceData))[(Instance->CodeData->Instances - 1) * Instance->CodeData->InstanceWorkDataSize]); ((PCreamTrackerInstanceStubData)pd)->Instance = Instance; ((PCreamTrackerInstanceStubData)pd)->Channel = NULL; pd = (cte_pointer)&(((cte_uint8_p)(pd))[sizeof(TCreamTrackerInstanceStubData)]); pdd = &Instance->ChannelBuffers; #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "pushal\n\t" "mov %%esp,%%edi\n\t" "mov %0,%%esi\n\t" "pushl %1\n\t" "pushl %2\n\t" "pushl %3\n\t" "movl %4,%%eax\n\t" "movl %%edi,%%ebp\n\t" "call *%%eax\n\t" "movl %%ebp,%%esp\n\t" "popal\n\t" : : "m"(pd), "m"(ToDo), "m"(pdd), "m"(Buf), "m"(pp) ); #else __asm { pushad mov edi,esp mov esi,dword ptr pd push dword ptr ToDo push dword ptr pdd push dword ptr Buf mov eax,dword ptr pp mov ebp,edi call eax mov esp,ebp popad } #endif } else #endif { for(ChannelIndex = 0; ChannelIndex < 32; ChannelIndex++) { Channel = &Instance->Channels[ChannelIndex]; if(Channel->Enabled && !Channel->Muted) { ChannelBuffer = &Channel->Buffer[0]; BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { Buf[BufPos + 0] += ChannelBuffer[BufPos + 0]; Buf[BufPos + 1] += ChannelBuffer[BufPos + 1]; BufPos += 2; } } } } } { LeftVolumeCurrent = Instance->BufferVolumeCurrent; LeftVolumeInc = Instance->BufferVolumeInc; VolumeRampingRemain = Instance->BufferVolumeRampingRemain; if(VolumeRampingRemain || (LeftVolumeCurrent != 1.0)) { BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { Buf[BufPos++] *= LeftVolumeCurrent; Buf[BufPos++] *= LeftVolumeCurrent; VolumeRamping = ((cte_uint32_t)(-VolumeRampingRemain) >> 31) & 1; LeftVolumeCurrent += (LeftVolumeInc *= VolumeRamping); VolumeRampingRemain -= VolumeRamping; } Instance->BufferVolumeRampingRemain = VolumeRampingRemain; Instance->BufferVolumeCurrent = Instance->BufferVolumeRampingRemain ? LeftVolumeCurrent : Instance->BufferVolume; } } if(!(Instance->Header.MasterVolume & 0x80)) { BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { Left = (Buf[BufPos + 0] + Buf[BufPos + 1]) * 0.5f; Buf[BufPos + 0] = Left; Buf[BufPos + 1] = Left; BufPos += 2; } } { BufPos = 0; for(Counter = ToDo; Counter > 0; Counter--) { Buf[BufPos + 0] = Clip(Buf[BufPos + 0], -1.0, 1.0); Buf[BufPos + 1] = Clip(Buf[BufPos + 1], -1.0, 1.0); BufPos += 2; } } Instance->TickSamplesRemain -= ToDo; Samples -= ToDo; Buf = &Buf[ToDo << 1]; } #ifdef CreamTrackerPortable #else #ifdef CTE_GCC_COMPATIBLE __asm__ __volatile__( "fldcw %0\n\t" : : "m"(OldCW) ); #else __asm { fldcw word ptr OldCW } #endif #endif } cte_int32_t CreamTrackerFillBufferOneTick(PCreamTrackerInstance Instance, cte_pointer Buffer, cte_int32_t Samples, cte_longbool_t WithTrackEnd) { cte_int32_t result = Instance->TickSamplesRemain; if(!result) { CreamTrackerUpdateTick(Instance); Instance->TickSamplesRemain = Instance->TickSamples; result = Instance->TickSamplesRemain; } if(result>Samples) { result = Samples; } if(WithTrackEnd || !Instance->TrackEnd) { CreamTrackerFillBuffer(Instance, Buffer, result); } else { result = 0; } return result; } cte_int32_t CreamTrackerCalculateLength(PCreamTrackerInstance Instance) { cte_int32_t result = 0; CreamTrackerReset(Instance); Instance->Playing = CTE_TRUE; Instance->RepeatCounter = 0; while(!Instance->TrackEnd) { CreamTrackerUpdateTick(Instance); result += Instance->TickSamples; } CreamTrackerReset(Instance); Instance->Playing = CTE_FALSE; return result; } #ifdef CreamTrackerSoundAPI #ifdef CreamTrackerCode #ifndef CreamTrackerTRICode #include "trackdata.h" #endif #endif #define SampleRate 44100 #define SynthBufferSize 2048 typedef cte_float_t TSynthEndBuffer[SynthBufferSize][2]; typedef TSynthEndBuffer *PSynthEndBuffer; TSynthEndBuffer Buffers[4]; HWAVEOUT WaveOutHandle; WAVEFORMATEX WaveFormat; WAVEHDR WaveHandler[4]; cte_pointer Data; cte_int32_t DataSize; TCreamTrackerInstance Instance; cte_longbool_t ThreadActive, ThreadReady; cte_handle_t ThreadHandle, ThreadID; void CTE_CDECL CreamTrackerSoundThread(void* Data) { DWORD BufferCounter = 0; while (ThreadActive && !ThreadReady) { sleep(1); } while ((ThreadActive && ThreadReady) && !Instance.TrackEnd) { if (WaveHandler[BufferCounter].dwFlags & WHDR_DONE) { if (waveOutUnprepareHeader(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)) != WAVERR_STILLPLAYING) { cte_int16_p sis; cte_float_p sfs; cte_int32_t i; WaveHandler[BufferCounter].dwFlags &= ~WHDR_DONE; CreamTrackerFillBuffer(&Instance, (cte_pointer)WaveHandler[BufferCounter].lpData, SynthBufferSize); sis = (cte_pointer)WaveHandler[BufferCounter].lpData; sfs = (cte_pointer)WaveHandler[BufferCounter].lpData; for (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; } waveOutPrepareHeader(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)); waveOutWrite(WaveOutHandle, &WaveHandler[BufferCounter], sizeof(WAVEHDR)); BufferCounter = (BufferCounter + 1) & 3; } } else { Sleep(1); } } ExitThread(0); } cte_longbool_t CreamTrackerSoundInit() { DWORD BufferCounter; ThreadActive = CTE_FALSE; ThreadReady = CTE_FALSE; WaveFormat.wFormatTag = 1; WaveFormat.nChannels = 2; WaveFormat.nSamplesPerSec = SampleRate; WaveFormat.nAvgBytesPerSec = SampleRate * 2 * 2; WaveFormat.nBlockAlign = 2 * 2; WaveFormat.wBitsPerSample = 2 * 8; WaveFormat.cbSize = 0; for (BufferCounter = 0; BufferCounter < 4; BufferCounter++) { WaveHandler[BufferCounter].lpData = (cte_pointer)(&(Buffers[BufferCounter])); WaveHandler[BufferCounter].dwBufferLength = sizeof(TSynthEndBuffer) >> 1; WaveHandler[BufferCounter].dwBytesRecorded = 0; WaveHandler[BufferCounter].dwUser = 0; WaveHandler[BufferCounter].dwFlags = WHDR_DONE; WaveHandler[BufferCounter].dwLoops = 0; } Data = (void*)&TrackDataBytes; DataSize = TrackDataSize; if (Data) { CreamTrackerCreate(&Instance, SampleRate); if (CreamTrackerLoad(&Instance, Data, DataSize)) { CreamTrackerReset(&Instance); Instance.Playing = CTE_TRUE; Instance.RepeatCounter = 0; waveOutOpen(&WaveOutHandle, WAVE_MAPPER, &WaveFormat, 0, 0, 0); ThreadActive = CTE_TRUE; ThreadReady = CTE_FALSE; ThreadHandle = CreateThread(NULL, 0, (cte_pointer)&CreamTrackerSoundThread, NULL, 0, &ThreadID); SetThreadPriority(ThreadHandle, THREAD_PRIORITY_TIME_CRITICAL); if (GetThreadPriority(ThreadHandle) != THREAD_PRIORITY_TIME_CRITICAL) { // Fallback for no adminstrator windows users SetThreadPriority(ThreadHandle, THREAD_PRIORITY_HIGHEST); if (GetThreadPriority(ThreadHandle) != THREAD_PRIORITY_HIGHEST) { SetThreadPriority(ThreadHandle, THREAD_PRIORITY_ABOVE_NORMAL); } } } return CTE_TRUE; } return CTE_FALSE; } void CreamTrackerSoundDone() { InterlockedExchange((LONG*)&ThreadActive, CTE_FALSE); WaitForSingleObject(ThreadHandle, 1000); TerminateThread(ThreadHandle, 0); CloseHandle(ThreadHandle); #ifndef CreamTrackerMinimalPlayer waveOutReset(WaveOutHandle); waveOutClose(WaveOutHandle); CreamTrackerDestroy(&Instance); CreamTrackerFreeMem(Data); #endif } void CreamTrackerSoundStart() { InterlockedExchange((LONG*)&ThreadReady, CTE_TRUE); } void CreamTrackerSoundStop() { InterlockedExchange((LONG*)&ThreadActive, CTE_FALSE); } cte_double_t CreamTrackerSoundGetTime() { MMTIME MMTime; if (IsSampleAccurate) { MMTime.wType = TIME_SAMPLES; if (WaveOutGetPosition(WaveOutHandle, &MMTime, sizeof(MMTIME)) == MMSYSERR_NOERROR) && (MMTime.wType == TIME_SAMPLES)){ return (CTE_DOUBLE_T)MMTime.sample / (CTE_DOUBLE_T)WaveFormat.nSamplesPerSec; } } MMTime.wType = TIME_MS; if ((WaveOutGetPosition(WaveOutHandle, &MMTime, sizeof(MMTIME)) == MMSYSERR_NOERROR) && (MMTime.wType == TIME_MS)) { return (CTE_DOUBLE_T)MMTime.ms / 1000.0; } MMTime.wType: = TIME_BYTES; if (WaveOutGetPosition(Track^.WaveOutHandle, @MMTime, sizeof(TMMTime)) = MMSYSERR_NOERROR) and (MMTime.wType = TIME_BYTES) then begin result : = (int64((MMTime.cb div Track^.WaveFormat.nBlockAlign) + Track^.FirstBufferSample) * 1000) div Track^.WaveFormat.nSamplesPerSec; exit; end; MMTime.wType: = TIME_SMPTE; if (WaveOutGetPosition(Track^.WaveOutHandle, @MMTime, sizeof(TMMTime)) = MMSYSERR_NOERROR) and (MMTime.wType = TIME_SMPTE) and(MMTime.FPS<>0) then begin result : = (int64((MMTime.hour * 60) + MMTime.min * 60) + MMTime.sec * 1000) + ((MMTime.Frame * 1000) div MMTime.FPS) + ((Track^.FirstBufferSample * 1000) div Track^.WaveFormat.nSamplesPerSec); exit; end; return (cte_double_t)((timeGetTime - FirstBufferTime) + ((FirstBufferSample * 1000ul) / WaveFormat.nSamplesPerSec)) / 1000.0; } void CreamTrackerSoundQuit() { TerminateProcess(GetCurrentProcess(), 0); ExitProcess(0); } #endif #ifdef __cplusplus } #endif