unit CreamTrackerEngine; {$undef UseTThread} {$ifdef CreamTrackerGUI} {$define UseThreading} {$define UseTThread} {$define CacheSINC} {$endif} {$undef NewDelphi} {$ifdef fpc} {$mode delphi} {$if defined(cpu386) or defined(cpuamd64)} {$asmmode intel} {$ifend} {$define caninline} {$else} {$ifdef conditionalexpressions} {$if CompilerVersion>=23.0} {$define UseTThread} {$define NewDelphi} {$ifend} {$endif} {$endif} {$m-} {$r-} {$j+} {-$define debugmem} interface {$ifdef cpu386} {$define UseAulan} {$endif} uses {$ifdef win32}Windows,{$else}{$ifdef CreamTrackerGUI}{$ifdef fpc}LCLIntf,LCLType,{$endif}{$endif}{$endif}{$ifdef UseTThread}SysUtils,Classes,SyncObjs,{$endif}{$ifdef UseAulan}TRILinker{$else}SysUtils{$endif}{$ifdef memdebug},FastMM4{$endif}; {$ifdef CreamTrackerGUI} const DefaultSharedCode:ansistring= ''+#13#10+ '// Please delete unused functions, procedures and variables for production code'+#13#10+ ''+#13#10+ #0; const DefaultGlobalCode:ansistring= ''+#13#10+ '// Please delete unused functions, procedures and variables for production code'+#13#10+ ''+#13#10+ 'external function HostGetValue(Slot as integer) as integer'+#13#10+ ''+#13#10+ 'external function HostGetSampleRate() as integer'+#13#10+ ''+#13#10+ '// Global variables'+#13#10+ 'procedure Initialize'+#13#10+ 'code'+#13#10+ ' // your global initialization code goes here (on track load)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure Deinitialize'+#13#10+ 'code'+#13#10+ ' // your global deinitialization code goes here (on track unload)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure Reset'+#13#10+ 'code'+#13#10+ ' // your global reset code goes here (on track reset)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure ProcessPatternNote(Pattern Row Tick Channel Note Instrument Volume Effect EffectParameter as integer)'+#13#10+ 'code'+#13#10+ ' // for global-based synth code implementations'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'type TBuffer as pointer to float'+#13#10+ ' TChannelBuffers as pointer to array[32] of TBuffer'+#13#10+ ''+#13#10+ 'procedure Process(OutputBuffer as TBuffer, ChannelBuffers as TChannelBuffers, ToDoCount as integer)'+#13#10+ 'variable ChannelIndex SampleIndex as integer'+#13#10+ ' Src Dest as TBuffer'+#13#10+ 'code'+#13#10+ ''+#13#10+ ' // Put here your channel-wise DSP effect code'+#13#10+ ''+#13#10+ ' // Mixing all the channel buffers to the global output buffer'+#13#10+ ' ChannelIndex = 0'+#13#10+ ' while ChannelIndex < 32 do'+#13#10+ ''+#13#10+ ' Src = ChannelBuffers^[ChannelIndex]'+#13#10+ ''+#13#10+ ' if Src <> null then // If a channel is muted or disabled, then this pointer is null'+#13#10+ ''+#13#10+ ' Dest = OutputBuffer'+#13#10+ ''+#13#10+ ' SampleIndex = 0'+#13#10+ ' while SampleIndex < ToDoCount do'+#13#10+ ''+#13#10+ ' // Left channel'+#13#10+ ' Dest^ = Dest^ + Src^'+#13#10+ ' Src = Src + sizeof(float)'+#13#10+ ' Dest = Dest + sizeof(float)'+#13#10+ ''+#13#10+ ' // Right channel'+#13#10+ ' Dest^ = Dest^ + Src^'+#13#10+ ' Src = Src + sizeof(float)'+#13#10+ ' Dest = Dest + sizeof(float)'+#13#10+ ''+#13#10+ ' SampleIndex = SampleIndex + 1'+#13#10+ ' end'+#13#10+ ''+#13#10+ ' end'+#13#10+ ''+#13#10+ ' ChannelIndex = ChannelIndex + 1'+#13#10+ ' end'+#13#10+ ''+#13#10+ ' // Put here your global output buffer DSP effect code'+#13#10+ ''+#13#10+ 'end'+#13#10+ #0; const DefaultSynthCode:ansistring= ''+#13#10+ '// Please delete unused functions, procedures and variables for production code'+#13#10+ ''+#13#10+ 'external function HostGetValue(Slot as integer) as integer'+#13#10+ ''+#13#10+ 'external function HostGetSampleRate() as integer'+#13#10+ ''+#13#10+ '// Global variables'+#13#10+ 'variable ReleaseCoef as float'+#13#10+ ''+#13#10+ 'procedure Initialize'+#13#10+ 'code'+#13#10+ ' // your global initialization code goes here (on track load)'+#13#10+ ' ReleaseCoef = exp(-(1.0f / (HostGetSampleRate() * (100.0f / 1000.0f)))) // 100ms release'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure Deinitialize'+#13#10+ 'code'+#13#10+ ' // your global deinitialization code goes here (on track unload)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure Reset'+#13#10+ 'code'+#13#10+ ' // your global reset code goes here (on track reset)'+#13#10+ 'end'+#13#10+ ''+#13#10+ '// per-channel-instance variables'+#13#10+ 'instance GlobalChannelIndex as integer'+#13#10+ ' Phase as float'+#13#10+ ' Amp as float'+#13#10+ ' NoteState as longint'+#13#10+ ''+#13#10+ 'procedure InstanceInitialize(ChannelIndex as integer)'+#13#10+ 'code'+#13#10+ ' GlobalChannelIndex = ChannelIndex'+#13#10+ ' // your per-channel-voice-instance initialization code goes here (on track load)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure InstanceDeinitialize'+#13#10+ 'code'+#13#10+ ' // your per-channel-voice-instance deinitialization code goes here (on track unload)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure InstanceReset'+#13#10+ 'code'+#13#10+ ' // your per-channel-voice-instance reset code goes here (on track reset)'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure InstanceNoteOn(Note VolumeVelocity as integer)'+#13#10+ 'code'+#13#10+ ' Phase = 0.0f'+#13#10+ ' Amp = 1.0f'+#13#10+ ' NoteState = 2'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'procedure InstanceNoteOff'+#13#10+ 'code'+#13#10+ ' if NoteState = 2 then'+#13#10+ ' NoteState = 1'+#13#10+ ' end'+#13#10+ 'end'+#13#10+ ''+#13#10+ 'function InstanceProcess(OutputBuffer as pointer to float, ToDoCount as integer, PhaseIncrement as float) as integer'+#13#10+ 'variable Value as float'+#13#10+ 'code'+#13#10+ ' // For get to the frequency, do: Frequency = PhaseIncrement * HostGetSampleRate()'+#13#10+ ''+#13#10+ ' while ToDoCount > 0 do'+#13#10+ ' ToDoCount = ToDoCount - 1'+#13#10+ ''+#13#10+ ' // Main logic'+#13#10+ ' if NoteState = 1 then'+#13#10+ ' Amp = Amp * ReleaseCoef'+#13#10+ ' if Amp < 0.0001f then'+#13#10+ ' NoteState = 0'+#13#10+ ' end'+#13#10+ ' end'+#13#10+ ' Value = (Phase - 0.5f) * Amp'+#13#10+ ' Phase = frac(Phase + PhaseIncrement)'+#13#10+ ''+#13#10+ ' // Output stereo'+#13#10+ ' OutputBuffer^ = Value'+#13#10+ ' OutputBuffer = OutputBuffer + sizeof(float)'+#13#10+ ' OutputBuffer^ = Value'+#13#10+ ' OutputBuffer = OutputBuffer + sizeof(float)'+#13#10+ ''+#13#10+ ' end'+#13#10+ ''+#13#10+ ' // return true is the note is still playing, otherwise false'+#13#10+ ' result = NoteState'+#13#10+ 'end'+#13#10+ #0; const DefaultSynthSampleCode:ansistring= ''+#13#10+ '// Please delete unused functions, procedures and variables for production code'+#13#10+ ''+#13#10+ 'function SampleProcess(OutputSampleData as pointer to float) as integer'+#13#10+ 'constant SampleRate = 44100'+#13#10+ ' SampleSize = 44100'+#13#10+ 'variable ToDoCount as integer'+#13#10+ ' Value Phase PhaseIncrement as float'+#13#10+ 'code'+#13#10+ ''+#13#10+ ' if OutputSampleData <> null then'+#13#10+ ''+#13#10+ ' Phase = 0.0f'+#13#10+ ' PhaseIncrement = (440.0f * float(2.0f ** (3.0f / 12.0f))) / SampleRate // C-4 as base frequency'+#13#10+ ''+#13#10+ ' ToDoCount = SampleSize'+#13#10+ ''+#13#10+ ' while ToDoCount > 0 do'+#13#10+ ''+#13#10+ ' ToDoCount = ToDoCount - 1'+#13#10+ ''+#13#10+ ' Value = (Phase - 0.5f) * 2.0f * sqr((ToDoCount + 0.0f) / SampleSize)'+#13#10+ ''+#13#10+ ' Phase = frac(Phase + PhaseIncrement)'+#13#10+ ''+#13#10+ ' // Output stereo'+#13#10+ ' OutputSampleData^ = Value'+#13#10+ ' OutputSampleData = OutputSampleData + sizeof(float)'+#13#10+ ' OutputSampleData^ = Value'+#13#10+ ' OutputSampleData = OutputSampleData + sizeof(float)'+#13#10+ ''+#13#10+ ' end'+#13#10+ ''+#13#10+ ' end'+#13#10+ ''+#13#10+ ' // return sample size'+#13#10+ ' result = SampleSize'+#13#10+ 'end'+#13#10+ #0; {$endif} {$ifdef CreamTrackerOscil} const SoundBufferSize=1 shl 11; SoundBufferMask=SoundBufferSize-1; OscilSize=SoundBufferSize; OscilMask=OscilSize-1; type TChannelOscilBuffer=array[0..OscilSize-1] of single; TChannelOscilBuffers=array[0..31] of TChannelOscilBuffer; PTickTimeInfoChannel=^TTickTimeInfoChannel; TTickTimeInfoChannel=record CurrentInstrument:longint; CurrentSamplePosition:longint; end; PTickTimeInfoChannels=^TTickTimeInfoChannels; TTickTimeInfoChannels=array[0..31] of TTickTimeInfoChannel; PTickTimeInfo=^TTickTimeInfo; TTickTimeInfo=record SampleIndex:longint; Order:longint; Row:longint; Tick:longint; Channels:TTickTimeInfoChannels; end; PTickTimeInfos=^TTickTimeInfos; TTickTimeInfos=array[0..$ff] of TTickTimeInfo; var OscilData:TChannelOscilBuffers; OscilCounter:array[0..31] of longint; TickTimeInfos:TTickTimeInfos; CountTickTimeInfos:longint; {$endif} const PositionFactor=int64($100000000); InvPositionFactor=1.0/PositionFactor; PositionAllRemainBits=14; PositionAllRemainFactor=1 shl PositionAllRemainBits; AddKillDenormal=1e-24; fDenormal=1e-18; FixUpSampleLength=1024; SafeAdditionalSampleLength=1024; TotalFixUpSafeAdditionalSampleLength=FixUpSampleLength+SafeAdditionalSampleLength; MaxReverbAllPassFilters=16; MaxEnvelopes=16; MaxEnvelopeNodes=128; SubSamples=1024; fCI12=1/12; fCI24=1/24; fCI64=1/64; fCI127=1/127; fCI128=1/128; fCI128_4=4/128; fCI255=1/256; fCI256=1/256; fCI256_4=4/256; ftNONE=0; ftLOWPASS=1; ftHIGHPASS=2; ftBANDPASS=3; ftBANDREJECT=4; ftPEAK=5; ftALLPASS=6; {$ifdef cpu386} CreamTrackerCW:word=$27f; {$endif} MMXExt:boolean=false; SSEExt:boolean=false; SSE2Ext:boolean=false; SSE3Ext:boolean=false; {$ifdef UseThreading} MaxThreads=64; ThreadTimeOut=1000; {$endif} type PSingleArray=^TSingleArray; TSingleArray=array[0..65535] of single; pbytebool=^bytebool; {$undef CanSSE} {$ifdef fpc} {$undef OldDelphi} {$ifdef cpu386} {$define CanSSE} {$endif} {$ifdef cpuamd64} {-$define CanSSE} {$endif} TThreadID=THandle; {$else} {$ifdef conditionalexpressions} {$ifdef cpu386} {$define CanSSE} {$endif} {$if CompilerVersion>=23.0} {$undef OldDelphi} qword=uint64; ptruint=NativeUInt; ptrint=NativeInt; TThreadID=Cardinal; {$elseif true} {$define OldDelphi} {$ifend} {$else} {$define OldDelphi} {$endif} {$endif} {$ifdef OldDelphi} qword=int64; TThreadID=THandle; {$ifdef cpu64} ptruint=qword; ptrint=int64; {$else} ptruint=longword; ptrint=longint; {$endif} {$endif} {$undef CanSINC} {$ifndef NoCreamTrackerSINC} {$ifdef NoSSE} {$undef CanSSE} {$endif} {$ifdef CanSSE} {$define CanSINC} {$endif} {$endif} PCreamTrackerValue=^TCreamTrackerValue; TCreamTrackerValue=packed record Value:byte; Slot:byte; end; PCreamTrackerInt32=^longint; PCreamTrackerInt64=^TCreamTrackerInt64; TCreamTrackerInt64=packed record case boolean of false:( Value:int64; ); true:( {$ifdef BigEndian} Hi:longint; Lo:longword; {$else} Lo:longword; Hi:longint; {$endif} ); end; PCreamTrackerBufferSample=^TCreamTrackerBufferSample; TCreamTrackerBufferSample=packed record case boolean of false:( Values:array[0..1] of single; ); true:( Left:single; Right:single; ); end; PCreamTrackerBufferSamples=^TCreamTrackerBufferSamples; TCreamTrackerBufferSamples=array[0..0] of TCreamTrackerBufferSample; PCreamTrackerChunkSignature=^TCreamTrackerChunkSignature; TCreamTrackerChunkSignature=array[0..3] of ansichar; PCreamTrackerChunk=^TCreamTrackerChunk; TCreamTrackerChunk=packed record Signature:TCreamTrackerChunkSignature; Size:longint; end; PCreamTrackerRandomGenerator=^TCreamTrackerRandomGenerator; TCreamTrackerRandomGenerator=record XorShift128x:longword; XorShift128y:longword; XorShift128z:longword; XorShift128w:longword; LCG:longword; MWCx:longword; MWCy:longword; MWCc:longword; end; PCreamTrackerPatternNote=^TCreamTrackerPatternNote; TCreamTrackerPatternNote=packed record Note:byte; Instrument:byte; Volume:byte; Effect:byte; EffectParameter:byte; end; PCreamTrackerPattern=^TCreamTrackerPattern; TCreamTrackerPattern=array[0..63,0..31] of TCreamTrackerPatternNote; PCreamTrackerPatterns=^TCreamTrackerPatterns; TCreamTrackerPatterns=array[0..255] of TCreamTrackerPattern; PCreamTrackerInstrumentHeader=^TCreamTrackerInstrumentHeader; TCreamTrackerInstrumentHeader=packed record InstrumentType:byte; FileName:array[0..11] of ansichar; Offset:array[0..2] of byte; Length:longint; LoopStart:longint; LoopEnd:longint; Volume:byte; CrossfadeType:byte; Format:byte; Flags:byte; C4Speed:longword; CrossfadeStart:longint; ExtOffset:longword; Dummy:array[0..3] of byte; SampleName:array[0..27] of ansichar; Signature:array[0..3] of ansichar; end; PCreamTrackerInstance=^TCreamTrackerInstance; PCreamTrackerChannel=^TCreamTrackerChannel; PCreamTrackerCodeData=^TCreamTrackerCodeData; TCreamTrackerCodeData=record Synth:longbool; Instances:longint; CodeText:PAnsiChar; CodeTextAllocated:longint; CodeTextSize:longint; TRIData:pointer; TRIDataSize:longint; {$ifdef UseAulan} TRIInstance:PTRIInstance; {$endif} Instance:PCreamTrackerInstance; InstanceData:pointer; InstanceDataSize:longint; InstanceWorkDataSize:longint; ProcInitialize:procedure(SampleRate:longint); stdcall; ProcDeinitialize:procedure; stdcall; ProcReset:procedure; stdcall; SynthProcInstanceInitialize:procedure(ChannelIndex:longint); stdcall; SynthProcInstanceDeinitialize:procedure; stdcall; SynthProcInstanceReset:procedure; stdcall; SynthProcInstanceNoteOn:procedure(Note:longint); stdcall; SynthProcInstanceNoteOff:procedure; stdcall; SynthProcInstanceProcess:function(Buffer:pointer;Samples:longint;PhaseIncrement:single):longint; stdcall; SampleProcProcess:function(Dest:pointer):longint; stdcall; GlobalProcProcess:procedure(GlobalBuffer,ChannelBuffers:pointer;Samples:longint); stdcall; GlobalProcProcessPatternNote:procedure(Pattern,Row,Tick,Channel,Note,Instrument,Volume,Effect,EffectParameter:longint); stdcall; end; PCreamTrackerInstrument=^TCreamTrackerInstrument; TCreamTrackerInstrument=packed record Header:TCreamTrackerInstrumentHeader; Data:pointer; {$ifdef CanSINC} SINCLeftData:PSingleArray; SINCRightData:PSingleArray; {$else} MixData:pointer; {$endif} RawData:pointer; RawLen:longint; CodeData:PCreamTrackerCodeData; end; PCreamTrackerInstruments=^TCreamTrackerInstruments; TCreamTrackerInstruments=array[1..99] of TCreamTrackerInstrument; PCreamTrackerInstanceStubData=^TCreamTrackerInstanceStubData; TCreamTrackerInstanceStubData=record Instance:PCreamTrackerInstance; Channel:PCreamTrackerChannel; end; PCreamTrackerHeader=^TCreamTrackerHeader; TCreamTrackerHeader=packed record Name:array[0..27] of ansichar; EOFChar:ansichar; Type_:byte; Dummy:word; OrdNum:word; InsNum:word; PatNum:word; Flags:word; CWTV:word; FileFormatInformation:word; Signature:array[0..3] of ansichar; GlobalVolume:byte; InitialSpeed:byte; InitialTempo:byte; MasterVolume:byte; UltraClickRemoval:byte; Panning:byte; Data:longword; RowHilightMinor:byte; RowHilightMajor:byte; Dummy2:word; Special:word; ChannelSettings:array[0..31] of byte; end; PCreamTrackerOrders=^TCreamTrackerOrders; TCreamTrackerOrders=array[0..255] of byte; PCreamTrackerBuffer=^TCreamTrackerBuffer; TCreamTrackerBuffer=array[0..(SubSamples*2)-1] of single; PCreamTrackerValueMemory=^TCreamTrackerValueMemory; TCreamTrackerValueMemory=array[0..63] of byte; PCreamTrackerChannelValueMemory=^TCreamTrackerChannelValueMemory; TCreamTrackerChannelValueMemory=array[64..127] of byte; PCreamTrackerChannelData=^TCreamTrackerChannelData; TCreamTrackerChannelData=array[0..(1 shl 16)-1] of byte; PCreamTrackerChannelDataArray=^TCreamTrackerChannelData; TCreamTrackerChannelDataArray=array[0..31] of TCreamTrackerChannelData; TCreamTrackerChannel=record Instance:PCreamTrackerInstance; Master:PCreamTrackerChannel; ValueMemory:PCreamTrackerChannelValueMemory; Index:longint; Active:longbool; Enabled:longbool; Muted:longbool; NewNote:longbool; FastRamping:longbool; C4SpeedFactor:double; NoteFrequencyFactor:double; LastLeftClickRemovalFadeOut:single; LastRightClickRemovalFadeOut:single; LastLeft:single; LastRight:single; LastInstrument:longword; Instrument:PCreamTrackerInstrument; BaseNote:longword; SamplePosition:TCreamTrackerInt64; VibratoPosition:longword; VibratoWaveForm:longword; TremoloWaveForm:longword; SynthFrequency:double; SynthIncrement:single; LivePeriod:double; SlideToPeriod:double; StablePeriod:double; LiveIncrement:double; Increment:int64; SINCCutOffLevel:longint; Volume:double; Velocity:longint; Glissando:longbool; ArpeggioPos:longword; LastEffectParameter:longword; LastVibrato:longword; LastPortamento:longword; RetrigCounter:longint; NoteOnTick:longint; NoteCutTick:longint; NoteCutCounter:longint; SampleHighOffset:longword; TremorCounter:longword; TremorParameter:longword; Panning:longint; ChannelVolume:longint; LeftVolume:single; RightVolume:single; LeftVolumeCurrent:single; RightVolumeCurrent:single; LeftVolumeInc:single; RightVolumeInc:single; VolumeRampingRemain:longint; ChannelBufferVolume:single; ChannelBufferVolumeCurrent:single; ChannelBufferVolumeInc:single; ChannelBufferVolumeRampingRemain:longint; PatternNote:TCreamTrackerPatternNote; {$ifdef CreamTrackerGUI} InjectPatternNote:TCreamTrackerPatternNote; HasInjectPatternNote:longbool; {$endif} Buffer:TCreamTrackerBuffer; TempBuffer:TCreamTrackerBuffer; end; PCreamTrackerChannels=^TCreamTrackerChannels; TCreamTrackerChannels=array[0..63] of TCreamTrackerChannel; PCreamTrackerChannelPannings=^TCreamTrackerChannelPannings; TCreamTrackerChannelPannings=array[0..31] of byte; PCreamTrackerADPCMIMAState=^TCreamTrackerADPCMIMAState; TCreamTrackerADPCMIMAState=record PrevSample:longint; StepIndex:longint; end; TCreamTrackerJobMode=(ctjmCHANNELMIX); PCreamTrackerJob=^TCreamTrackerJob; TCreamTrackerJob=record Mode:TCreamTrackerJobMode; Samples:longint; NewTick:longbool; Instance:PCreamTrackerInstance; Channel:PCreamTrackerChannel; end; TCreamTrackerJobs=array[0..63] of TCreamTrackerJob; {$ifdef UseTThread} TCreamTrackerJobThread=class(TThread) protected procedure Execute; override; public Instance:PCreamTrackerInstance; ThreadNumber:longint; Event:TEvent; DoneEvent:TEvent; constructor Create(TheInstance:PCreamTrackerInstance;TheThreadNumber:longint); destructor Destroy; override; end; {$else} PCreamTrackerJobThread=^TCreamTrackerJobThread; TCreamTrackerJobThread=record Instance:PCreamTrackerInstance; ThreadNumber:longint; ThreadHandle:THandle; ThreadID:TThreadID; Event:{$ifdef fpc}PRTLEvent{$else}THandle{$endif}; DoneEvent:{$ifdef fpc}PRTLEvent{$else}THandle{$endif}; end; {$endif} {$ifdef UseThreading} PCreamTrackerJobThreads=^TCreamTrackerJobThreads; TCreamTrackerJobThreads=array[0..MaxThreads-1] of TCreamTrackerJobThread; PCreamTrackerJobManager=^TCreamTrackerJobManager; TCreamTrackerJobManager=record Jobs:TCreamTrackerJobs; JobQueueIndex:longint; CountJobs:longint; Threads:TCreamTrackerJobThreads; CountThreads:longint; {$ifndef UseTThread} {$ifndef fpc} {$ifdef win32} DoneEventHandles:array[0..MaxThreads-1] of THandle; {$endif} {$endif} {$endif} end; {$endif} TCreamTrackerInstance=record Header:TCreamTrackerHeader; CreamTrackerModule:longbool; Patterns:TCreamTrackerPatterns; Orders:TCreamTrackerOrders; Instruments:TCreamTrackerInstruments; ChannelPannings:TCreamTrackerChannelPannings; ValueMemory:TCreamTrackerValueMemory; ChannelValueMemory:array[0..63] of TCreamTrackerChannelValueMemory; CurrentChannel:PCreamTrackerChannel; Channels:TCreamTrackerChannels; ChannelOrder:array[0..63] of longint; SampleRate:longint; InvSampleRate:double; TickSamples:longint; TickSamplesRemain:longint; HertzRatio:double; CurrentTime:double; NoteFactorTable:array[0..15] of double; NoteToPeriodTable:array[0..194] of double; ClickRemovalFadeOutFactor:single; SmoothFactor:single; DCFilterCoef:single; Speed:longint; Tempo:longint; GlobalVolume:double; NextRow:longint; NextOrder:longint; {$ifndef CreamTrackerCompact} ForceOrder:longint; ForceRow:longint; {$endif} {$ifdef CreamTrackerGUI} SamplePosition:int64; {$endif} Row:longint; Order:longint; Pattern:longint; Tick:longint; AbsoluteTick:longint; FrameDelay:longint; PatternDelay:longint; PatternDelayRowCounter:longint; FirstTick:longbool; FirstRowTick:longbool; VeryFirstTick:longbool; TrackEnd:longbool; PatternLoop:longbool; RepeatCounter:longint; RepeatCounters:array[0..255] of longint; RepeatRowCounters:array[0..255,0..63] of longint; LoopRow:longint; LoopRowCounter:longint; PatternRandom:longword; BufferVolume:single; BufferVolumeCurrent:single; BufferVolumeInc:single; BufferVolumeRampingRemain:longint; Playing:longbool; Changed:longbool; FollowSong:longbool; RowHilightMinor:longint; RowHilightMajor:longint; {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} JobManager:TCreamTrackerJobManager; Threads:longint; ThreadsTerminated:longbool; UseMultithreading:longbool; {$endif} {$endif} SharedCodeData:PCreamTrackerCodeData; CodeData:PCreamTrackerCodeData; ChannelBuffers:array[0..31] of pointer; TempBuffers:array[0..1] of TCreamTrackerBuffer; ChannelDataArray:TCreamTrackerChannelDataArray; end; function RoundToNextOfPowerTwo(x:longword):longword; function CreamTrackerADPCMIMADecompressSample(var ADPCMIMAState:TCreamTrackerADPCMIMAState;Nibble:byte):longint; {$ifdef CreamTrackerSaveRoutines} function CreamTrackerADPCMIMACompressSample(var ADPCMIMAState:TCreamTrackerADPCMIMAState;Sample:smallint):byte; {$endif} {$ifdef CanSINC} procedure InitResamplerSINC; {$endif} {$ifdef CreamTrackerGUI} function CreamTrackerGetNameCode(Name:pansichar):ansichar; {$endif} {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} procedure CreamTrackerJobCreateThreads(Instance:PCreamTrackerInstance); procedure CreamTrackerJobWakeThreads(Instance:PCreamTrackerInstance); procedure CreamTrackerJobWaitThreads(Instance:PCreamTrackerInstance); procedure CreamTrackerJobFreeThreads(Instance:PCreamTrackerInstance); procedure CreamTrackerJobManagerInitProcessChannelMix(Instance:PCreamTrackerInstance;SamplesCount:longint;NewTick:longbool); procedure CreamTrackerJobManagerProcess(Instance:PCreamTrackerInstance); {$endif} {$endif} procedure CreamTrackerCreate(var Instance:TCreamTrackerInstance;SampleRate:longint); {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerDestroy(var Instance:TCreamTrackerInstance); {$endif} procedure CreamTrackerCodeInit(var Instance:TCreamTrackerInstance;CodeData:PCreamTrackerCodeData); {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerCodeDestroy(var Instance:TCreamTrackerInstance;var CodeData:PCreamTrackerCodeData); {$endif} procedure CreamTrackerCodeReset(var Instance:TCreamTrackerInstance;CodeData:PCreamTrackerCodeData); {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerDestroyInstrument(var Instance:TCreamTrackerInstance;var Instrument:TCreamTrackerInstrument); procedure CreamTrackerDestroySampleData(var Instance:TCreamTrackerInstance;var Instrument:TCreamTrackerInstrument); {$endif} procedure CreamTrackerReset(var Instance:TCreamTrackerInstance); procedure CreamTrackerUpdateTempo(var Instance:TCreamTrackerInstance); procedure CreamTrackerFixUpSample(var Instance:TCreamTrackerInstance;Instrument:PCreamTrackerInstrument); function CreamTrackerLoadPatternsAsSingleChain(var Instance:TCreamTrackerInstance;InputData:pointer;InputDataSize:longint):longbool; function CreamTrackerLoad(var Instance:TCreamTrackerInstance;InputData:pointer;InputDataSize:longint):longbool; procedure CreamTrackerUpdateTick(var Instance:TCreamTrackerInstance); procedure CreamTrackerFillBuffer(var Instance:TCreamTrackerInstance;Buffer:pointer;Samples:longint); {$ifdef CreamTrackerSaveRoutines} function CreamTrackerFillBufferOneTick(var Instance:TCreamTrackerInstance;Buffer:pointer;Samples:longint;WithTrackEnd:longbool):longint; {$endif} function CreamTrackerCalculateLength(var Instance:TCreamTrackerInstance):longint; {$ifdef CreamTrackerSaveRoutines} function CreamTrackerSavePatternsAsSingleChain(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint):longbool; function CreamTrackerSaveCompact(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint;IsExport,CodeExport:longbool):longbool; function CreamTrackerSave(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint;IsExport,CodeExport:longbool):longbool; {$endif} implementation {$ifdef CreamTrackerGUI} uses UnitFormMain; {$endif} {$ifdef debugmem} var TotalAllocated:int64=0; procedure GetMemory(out p;const Size:ptruint); begin inc(TotalAllocated,Size); writeln('GetMem: ',Size,' ',TotalAllocated div 1024,' ',TotalAllocated div (1024*1024)); GetMem(pointer(p),Size); end; function FreeMemory(p:pointer):ptruint; var m:TMemoryManager; s:ptruint; begin GetMemoryManager(m); s:=m.MemSize(p); dec(TotalAllocated,s); result:=FreeMem(p); writeln('FreeMem: ',s,' ',TotalAllocated div 1024,' ',TotalAllocated div (1024*1024)); end; {$endif} const SINC_FRACBITS=12; SINC_LUTLEN=1 shl SINC_FRACBITS; SINC_LOG2WIDTH=7; SINC_WIDTH=1 shl SINC_LOG2WIDTH; SINC_HALFWIDTH=SINC_WIDTH div 2; SINC_FRACSHIFT=32-SINC_FRACBITS; SINC_FRACMASK=(1 shl SINC_FRACBITS)-1; SINC_FRACSHIFTLENGTH=1 shl SINC_FRACSHIFT; SINC_FRACSHIFTMASK=SINC_FRACSHIFTLENGTH-1; SINC_FRACSHIFTFACTOR=1.0/SINC_FRACSHIFTLENGTH; SINCCUTOFF_LEN=128; type PResamplerSINCSubSubArray=^TResamplerSINCSubSubArray; TResamplerSINCSubSubArray=array[0..SINC_WIDTH-1] of single; PResamplerSINCSubArray=^TResamplerSINCSubArray; TResamplerSINCSubArray=array[0..SINC_LUTLEN-1] of TResamplerSINCSubSubArray; PResamplerSINCArray=^TResamplerSINCArray; TResamplerSINCArray=array[0..SINCCUTOFF_LEN-1] of TResamplerSINCSubArray; PResamplerSINCCutOffIncrementTable=^TResamplerSINCCutOffIncrementTable; TResamplerSINCCutOffIncrementTable=array[0..SINCCUTOFF_LEN-1] of int64; const ResamplerSINCArrayInitialized:longbool=false; var ResamplerSINCArray:PResamplerSINCArray; ResamplerSINCWindowArray:TResamplerSINCSubArray; ResamplerSINCCutOffIncrementTable:TResamplerSINCCutOffIncrementTable; const WaveTables:array[0..3,0..63] of smallint= ( ( $00,$18,$31,$4a,$61,$78,$8d,$a1, $b4,$c5,$d4,$e0,$eb,$f4,$fa,$fd, $ff,$fd,$fa,$f4,$eb,$e0,$d4,$c5, $b4,$a1,$8d,$78,$61,$4a,$31,$18, $00,-$018,-$031,-$04a,-$061,-$078,-$08d,-$0a1, -$b4,-$0c5,-$0d4,-$0e0,-$0eb,-$0f4,-$0fa,-$0fd, -$ff,-$0fd,-$0fa,-$0f4,-$0eb,-$0e0,-$0d4,-$0c5, -$b4,-$0a1,-$08d,-$078,-$061,-$04a,-$031,-$018 ), ( $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff, $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff, $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff, $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff, $00,$00,$00,$00,$00,$00,$00,$00, $00,$00,$00,$00,$00,$00,$00,$00, $00,$00,$00,$00,$00,$00,$00,$00, $00,$00,$00,$00,$00,$00,$00,$00 ), ( 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 ), ( $00,$18,$31,$4a,$61,$78,$8d,$a1, $b4,$c5,$d4,$e0,$eb,$f4,$fa,$fd, $ff,$fd,$fa,$f4,$eb,$e0,$d4,$c5, $b4,$a1,$8d,$78,$61,$4a,$31,$18, $00,-$018,-$031,-$04a,-$061,-$078,-$08d,-$0a1, -$b4,-$0c5,-$0d4,-$0e0,-$0eb,-$0f4,-$0fa,-$0fd, -$ff,-$0fd,-$0fa,-$0f4,-$0eb,-$0e0,-$0d4,-$0c5, -$b4,-$0a1,-$08d,-$078,-$061,-$04a,-$031,-$018 ) ); CreamTrackerInstanceStubDataSize=SizeOf(TCreamTrackerInstanceStubData); CreamTrackerFineTuneTable:array[0..16-1] of word= (7895,7941,7985,8046,8107,8169,8232,8280,8363,8413,8463,8529,8581,8651,8723,8757); CreamTrackerADPCMIMAStepTable:array[0..88] of longint=(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); CreamTrackerADPCMIMAIndexTable:array[0..15] of longint=(-1,-1,-1,-1,2,4,6,8,-1,-1,-1,-1,2,4,6,8); CreamTrackerADPCMIMADifferenceLookUpTable:array[0..15] of longint=(1,3,5,7,9,11,13,15,-1,-3,-5,-7,-9,-11,-13,-15); function CreamTrackerADPCMIMADecompressSample(var ADPCMIMAState:TCreamTrackerADPCMIMAState;Nibble:byte):longint; begin inc(ADPCMIMAState.PrevSample,(CreamTrackerADPCMIMAStepTable[ADPCMIMAState.StepIndex]*CreamTrackerADPCMIMADifferenceLookUpTable[Nibble and $f]) div 8); if ADPCMIMAState.PrevSample<-32768 then begin ADPCMIMAState.PrevSample:=-32768; end else if ADPCMIMAState.PrevSample>32767 then begin ADPCMIMAState.PrevSample:=32767; end; ADPCMIMAState.StepIndex:=ADPCMIMAState.StepIndex+CreamTrackerADPCMIMAIndexTable[Nibble]; if ADPCMIMAState.StepIndex<0 then begin ADPCMIMAState.StepIndex:=0; end else if ADPCMIMAState.StepIndex>88 then begin ADPCMIMAState.StepIndex:=88; end; result:=ADPCMIMAState.PrevSample; end; {$ifdef CreamTrackerSaveRoutines} function CreamTrackerADPCMIMACompressSample(var ADPCMIMAState:TCreamTrackerADPCMIMAState;Sample:smallint):byte; var Delta,Nibble:longint; begin Delta:=Sample-ADPCMIMAState.PrevSample; Nibble:=(abs(Delta)*4) div CreamTrackerADPCMIMAStepTable[ADPCMIMAState.StepIndex]; if Nibble>7 then begin Nibble:=7; end; if Delta<0 then begin Nibble:=Nibble or 8; end; ADPCMIMAState.PrevSample:=ADPCMIMAState.PrevSample+((CreamTrackerADPCMIMAStepTable[ADPCMIMAState.StepIndex]*CreamTrackerADPCMIMADifferenceLookUpTable[Nibble and $f]) div 8); if ADPCMIMAState.PrevSample<-32768 then begin ADPCMIMAState.PrevSample:=-32768; end else if ADPCMIMAState.PrevSample>32767 then begin ADPCMIMAState.PrevSample:=32767; end; ADPCMIMAState.StepIndex:=ADPCMIMAState.StepIndex+CreamTrackerADPCMIMAIndexTable[Nibble]; if ADPCMIMAState.StepIndex<0 then begin ADPCMIMAState.StepIndex:=0; end else if ADPCMIMAState.StepIndex>88 then begin ADPCMIMAState.StepIndex:=88; end; result:=Nibble; end; {$endif} {$ifdef cpu386} {$ifndef ver130} function InterlockedCompareExchange64Ex(Target,NewValue,Comperand:pointer):boolean; assembler; register; asm push ebx push edi push esi mov edi,eax mov esi,edx mov edx,dword ptr [ecx+4] mov eax,dword ptr [ecx+0] mov ecx,dword ptr [esi+4] mov ebx,dword ptr [esi+0] lock cmpxchg8b [edi] setz al pop esi pop edi pop ebx end; function InterlockedCompareExchange64(var Target:int64;NewValue:int64;Comperand:int64):int64; assembler; register; asm push ebx push edi mov edi,eax mov edx,dword ptr [Comperand+4] mov eax,dword ptr [Comperand+0] mov ecx,dword ptr [NewValue+4] mov ebx,dword ptr [NewValue+0] lock cmpxchg8b [edi] pop edi pop ebx end; {$endif} {$endif} {$ifndef fpc} {$ifdef cpu386} function InterlockedDecrement(var Target:longint):longint; assembler; register; asm mov edx,$ffffffff xchg eax,edx lock xadd dword ptr [edx],eax dec eax end; function InterlockedIncrement(var Target:longint):longint; assembler; register; asm mov edx,1 xchg eax,edx lock xadd dword ptr [edx],eax inc eax end; function InterlockedExchange(var Target:longint;Source:longint):longint; assembler; register; asm lock xchg dword ptr [eax],edx mov eax,edx end; function InterlockedExchangeAdd(var Target:longint;Source:longint):longint; assembler; register; asm xchg edx,eax lock xadd dword ptr [edx],eax end; function InterlockedCompareExchange(var Target:longint;NewValue,Comperand:longint):longint; assembler; register; asm xchg ecx,eax lock cmpxchg dword ptr [ecx],edx end; {$else} function InterlockedDecrement(var Target:longint):longint; {$ifdef caninline}inline;{$endif} begin result:=Windows.InterlockedDecrement(Target); end; function InterlockedIncrement(var Target:longint):longint; {$ifdef caninline}inline;{$endif} begin result:=Windows.InterlockedIncrement(Target); end; function InterlockedExchange(var Target:longint;Source:longint):longint; {$ifdef caninline}inline;{$endif} begin result:=Windows.InterlockedExchange(Target,Source); end; function InterlockedExchangeAdd(var Target:longint;Source:longint):longint; {$ifdef caninline}inline;{$endif} begin result:=Windows.InterlockedExchangeAdd(Target,Source); end; function InterlockedCompareExchange(var Target:longint;NewValue,Comperand:longint):longint; {$ifdef caninline}inline;{$endif} begin result:=Windows.InterlockedCompareExchange(Target,NewValue,Comperand); end; {$endif} {$endif} function KillDenormal(Value:single):single; begin longword(pointer(@result)^):=longword(pointer(@Value)^) and longword($ffffffff+longword(((((longword(pointer(@Value)^) and $7f800000)+$800000) and $7f800000)-$1000000) shr 31)); end; function FastTRUNC(FloatValue:single):longint; var FloatValueCasted:longword absolute FloatValue; Exponent,Sig,IsDenormalized:longword; Shift:longint; begin Exponent:=(FloatValueCasted and $7fffffff) shr 23; Shift:=Exponent-$96; Sig:=(FloatValueCasted and $7fffff) or $00800000; IsDenormalized:=$ffffffff+(longword(Shift shr 31) shl 1); result:=((((($ffffffff+longword(longword(Exponent-$7e) shr 31)) and (Sig shr (32-Shift))) and not IsDenormalized) or (longword(Sig shl (Shift and 31)) and IsDenormalized))*(1-((FloatValueCasted shr 31) shl 1))) and ($ffffffff+(longword(longword(($9e+1)-Exponent) shr 31) shl 1)); end; function FastdBtoAmp(Value:single):single; const Factor0:single=1.6609640474436811739351597147447E-1; Factor1:single=7.02679339377207945E-1; Factor2:single=2.39338555345344262E-1; var ValueCasted:longint absolute Value; ResultCasted:longint absolute result; begin if (((ValueCasted) and $ff800000) shr 23)>$3f then begin Value:=Value*Factor0; ResultCasted:=FastTRUNC(Value); Value:=Value-ResultCasted; ResultCasted:=($7f+ResultCasted) shl 23; result:=result*(1+(Value*(Factor1+(Value*Factor2)))); end else begin result:=ord(ValueCasted=0); end; end; function RoundToNextOfPowerTwo(x:longword):longword; begin dec(x); x:=x or (x shr 1); x:=x or (x shr 2); x:=x or (x shr 4); x:=x or (x shr 8); x:=x or (x shr 16); result:=x+1; end; function log10(x:double):double; const DivLN10:double=0.4342944819; begin result:=ln(x)*DivLN10; end; function power(Base,Exponent:double):double; begin result:=exp(Exponent*ln(Base)); end; function dBToLinear(x:double):double; begin result:=exp(x*0.11512925464970228420089957273422); end; function LinearTodB(x:double):double; begin result:=ln(x)*6.02059991327962; end; function WhiteNoiseRandom(var WhiteNoiseSeed:longword):single; var WhiteNoiseValue:longword; begin WhiteNoiseSeed:=(WhiteNoiseSeed*$524281)+$3133731; WhiteNoiseValue:=(WhiteNoiseSeed and $7fffff) or $40000000; result:=single(pointer(@WhiteNoiseValue)^)-3; end; function FastSQRT(Value:single):single; const f0d5:single=0.5; var Casted:longword absolute result; begin result:=Value; Casted:=($be6eb50c-Casted) shr 1; result:=Value*(f0d5*(result*(3-(Value*sqr(result))))); end; function Clip(Value,Min,Max:single):single; {$ifdef cpu386}assembler; stdcall; const Constant0Dot5:single=0.5; asm fld dword ptr Value fld dword ptr Min fld dword ptr Max // Temp1:=abs(Value-Min); fld st(2) fsubr st(0),st(1) fabs // Temp2:=abs(Value-Max); fld st(3) fsubr st(0),st(3) fabs // result:=((Temp1+(Min+Max))-Temp2)*0.5; fadd st(0),st(3) fadd st(0),st(2) fsub st(0),st(1) fmul dword ptr Constant0Dot5 ffree st(4) ffree st(3) ffree st(2) ffree st(1) end; {$else} var Temp1,Temp2:single; begin Temp1:=abs(Value-Min); Temp2:=abs(Value-Max); result:=((Temp1+(Min+Max))-Temp2)*0.5; end; {$endif} function Clip64(Value,Min,Max:double):double; {$ifdef cpu386}assembler; stdcall; const Constant0Dot5:double=0.5; asm fld qword ptr Value fld qword ptr Min fld qword ptr Max // Temp1:=abs(Value-Min); fld st(2) fsubr st(0),st(1) fabs // Temp2:=abs(Value-Max); fld st(3) fsubr st(0),st(3) fabs // result:=((Temp1+(Min+Max))-Temp2)*0.5; fadd st(0),st(3) fadd st(0),st(2) fsub st(0),st(1) fmul qword ptr Constant0Dot5 ffree st(4) ffree st(3) ffree st(2) ffree st(1) end; {$else} var Temp1,Temp2:double; begin Temp1:=abs(Value-Min); Temp2:=abs(Value-Max); result:=((Temp1+(Min+Max))-Temp2)*0.5; end; {$endif} {$ifdef CanSSE} {$ifdef cpu386} {$ifndef ver130} function Convolve4SSE(Data,Kernel:psingle):single; assembler; register; asm movups xmm0,[eax] mulps xmm0,[edx] movhlps xmm1,xmm0 addps xmm0,xmm1 movaps xmm1,xmm0 shufps xmm1,xmm1,$55 addss xmm0,xmm1 movss dword ptr result,xmm0 end; {$endif} {$endif} {$ifdef cpuamd64} function Convolve4SSE(Data,Kernel:psingle):single; assembler; register; asm {$ifdef windows} movups xmm0,[rcx] mulps xmm0,[rdx] {$else} movups xmm0,[rdi] mulps xmm0,[rsi] {$endif} movhlps xmm1,xmm0 addps xmm0,xmm1 movaps xmm1,xmm0 shufps xmm1,xmm1,$55 addss xmm0,xmm1 movss dword ptr result,xmm0 end; {$endif} {$endif} function Convolve4(Data,Kernel:psingle):single; register; begin result:=(PSingleArray(Data)^[0]*PSingleArray(Kernel)^[0])+ (PSingleArray(Data)^[1]*PSingleArray(Kernel)^[1])+ (PSingleArray(Data)^[2]*PSingleArray(Kernel)^[2])+ (PSingleArray(Data)^[3]*PSingleArray(Kernel)^[3]); end; {$ifdef CanSSE} {$ifdef cpu386} function ConvolveSSE(Data,Kernel:psingle;Count:longint):single; assembler; register; asm push esi push edi mov esi,eax mov edi,edx xor eax,eax {$ifdef ver130} db $0f,$57,$c0 // xorps xmm0,xmm0 db $0f,$57,$c9 // xorps xmm1,xmm1 db $0f,$57,$d2 // xorps xmm2,xmm2 db $0f,$57,$db // xorps xmm3,xmm3 {$else} xorps xmm0,xmm0 xorps xmm1,xmm1 xorps xmm2,xmm2 xorps xmm3,xmm3 {$endif} mov edx,ecx shr edx,4 and ecx,15 test edx,edx jz @Loop16Done @Loop16: {$ifdef ver130} db $0f,$10,$24,$30 // movups xmm4,[esi+eax] db $0f,$59,$24,$38 // mulps xmm4,[edi+eax] db $0f,$10,$6c,$30,$10 // movups xmm5,[esi+eax+16] db $0f,$59,$6c,$38,$10 // mulps xmm5,[edi+eax+16] db $0f,$10,$74,$30,$20 // movups xmm6,[esi+eax+32] db $0f,$59,$74,$38,$20 // mulps xmm6,[edi+eax+32] db $0f,$10,$7c,$30,$30 // movups xmm7,[esi+eax+48] db $0f,$59,$7c,$38,$30 // mulps xmm7,[edi+eax+48] db $0f,$58,$c4 // addps xmm0,xmm4 db $0f,$58,$cd // addps xmm1,xmm5 db $0f,$58,$d6 // addps xmm2,xmm6 db $0f,$58,$df // addps xmm3,xmm7 {$else} movups xmm4,[esi+eax] mulps xmm4,[edi+eax] movups xmm5,[esi+eax+16] mulps xmm5,[edi+eax+16] movups xmm6,[esi+eax+32] mulps xmm6,[edi+eax+32] movups xmm7,[esi+eax+48] mulps xmm7,[edi+eax+48] addps xmm0,xmm4 addps xmm1,xmm5 addps xmm2,xmm6 addps xmm3,xmm7 {$endif} add eax,64 dec edx jnz @Loop16 @Loop16Done: test ecx,8 jz @Skip8 {$ifdef ver130} db $0f,$10,$24,$30 // movups xmm4,[esi+eax] db $0f,$59,$24,$38 // mulps xmm4,[edi+eax] db $0f,$10,$6c,$30,$10 // movups xmm5,[esi+eax+16] db $0f,$59,$6c,$38,$10 // mulps xmm5,[edi+eax+16] db $0f,$58,$c4 // addps xmm0,xmm4 db $0f,$58,$cd // addps xmm1,xmm5 {$else} movups xmm4,[esi+eax] mulps xmm4,[edi+eax] movups xmm5,[esi+eax+16] mulps xmm5,[edi+eax+16] addps xmm0,xmm4 addps xmm1,xmm5 {$endif} add eax,32 @Skip8: test ecx,4 jz @Skip4 {$ifdef ver130} db $0f,$10,$34,$30 // movups xmm6,[esi+eax] db $0f,$59,$34,$38 // mulps xmm6,[edi+eax] db $0f,$58,$d6 // addps xmm2,xmm6 {$else} movups xmm6,[esi+eax] mulps xmm6,[edi+eax] addps xmm2,xmm6 {$endif} add eax,16 @Skip4: test ecx,2 jz @Skip2 {$ifdef ver130} db $f3,$0f,$10,$3c,$30 // movss xmm7,[esi+eax] db $f3,$0f,$59,$3c,$38 // mulss xmm7,[edi+eax] db $f3,$0f,$10,$64,$30,$04 // movss xmm4,[esi+eax+4] db $f3,$0f,$59,$64,$38,$04 // mulss xmm4,[edi+eax+4] db $f3,$0f,$58,$df // addss xmm3,xmm7 db $f3,$0f,$58,$c4 // addss xmm0,xmm4 {$else} movss xmm7,[esi+eax] mulss xmm7,[edi+eax] movss xmm4,[esi+eax+4] mulss xmm4,[edi+eax+4] addss xmm3,xmm7 addss xmm0,xmm4 {$endif} add eax,8 @Skip2: test ecx,1 jz @Skip1 {$ifdef ver130} db $f3,$0f,$10,$2c,$30 // movss xmm5,[esi+eax] db $f3,$0f,$59,$2c,$38 // mulss xmm5,[edi+eax] db $f3,$0f,$58,$cd // addss xmm1,xmm5 {$else} movss xmm5,[esi+eax] mulss xmm5,[edi+eax] addss xmm1,xmm5 {$endif} add eax,4 @Skip1: {$ifdef ver130} db $0f,$58,$c1 // addps xmm0,xmm1 db $0f,$58,$d3 // addps xmm2,xmm3 db $0f,$58,$c2 // addps xmm0,xmm2 db $0f,$57,$c9 // xorps xmm1,xmm1 db $0f,$12,$c8 // movhlps xmm1,xmm0 db $0f,$58,$c1 // addps xmm0,xmm1 db $0f,$28,$c8 // movaps xmm1,xmm0 db $0f,$c6,$c9,$55 // shufps xmm1,xmm1,$55 db $f3,$0f,$58,$c1 // addss xmm0,xmm1 {$else} addps xmm0,xmm1 addps xmm2,xmm3 addps xmm0,xmm2 xorps xmm1,xmm1 movhlps xmm1,xmm0 addps xmm0,xmm1 movaps xmm1,xmm0 shufps xmm1,xmm1,$55 addss xmm0,xmm1 {$endif} pop edi pop esi movss dword ptr result,xmm0 end; {$endif} {$ifdef cpuamd64} {$undef UseXMM0to15atConvolveSSE} {$ifdef UseXMM0to15atConvolveSSE} function ConvolveSSE(Data,Kernel:psingle;Count:longint):single; register; asm {$ifndef windows} mov r8,rdx mov rcx,rdi mov rdx,rsi {$endif} xorps xmm0,xmm0 xorps xmm1,xmm1 xorps xmm2,xmm2 xorps xmm3,xmm3 xorps xmm4,xmm4 xorps xmm5,xmm5 xorps xmm6,xmm6 xorps xmm7,xmm7 xor rax,rax mov r10,r8 shr r10,5 and r8,31 test r10,r10 jz @Loop32Done @Loop32: movups xmm8,[rcx+rax] mulps xmm8,[rdx+rax] movups xmm9,[rcx+rax+16] mulps xmm9,[rdx+rax+16] movups xmm10,[rcx+rax+32] mulps xmm10,[rdx+rax+32] movups xmm11,[rcx+rax+48] mulps xmm11,[rdx+rax+48] movups xmm12,[rcx+rax+64] mulps xmm12,[rdx+rax+64] movups xmm13,[rcx+rax+80] mulps xmm13,[rdx+rax+80] movups xmm14,[rcx+rax+96] mulps xmm14,[rdx+rax+96] movups xmm15,[rcx+rax+112] mulps xmm15,[rdx+rax+112] addps xmm0,xmm8 addps xmm1,xmm9 addps xmm2,xmm10 addps xmm3,xmm11 addps xmm4,xmm12 addps xmm5,xmm13 addps xmm6,xmm14 addps xmm7,xmm15 add rax,128 dec r10 jnz @Loop32 @Loop32Done: test r8,16 jz @Skip16 movups xmm8,[rcx+rax] mulps xmm8,[rdx+rax] movups xmm9,[rcx+rax+16] mulps xmm9,[rdx+rax+16] movups xmm10,[rcx+rax+32] mulps xmm10,[rdx+rax+32] movups xmm11,[rcx+rax+48] mulps xmm11,[rdx+rax+48] addps xmm0,xmm8 addps xmm1,xmm9 addps xmm2,xmm10 addps xmm3,xmm11 add rax,64 @Skip16: test r8,8 jz @Skip8 movups xmm12,[rcx+rax] mulps xmm12,[rdx+rax] movups xmm13,[rcx+rax+16] mulps xmm13,[rdx+rax+16] addps xmm4,xmm12 addps xmm5,xmm13 add rax,32 @Skip8: test r8,4 jz @Skip4 movups xmm14,[rcx+rax] mulps xmm14,[rdx+rax] addps xmm6,xmm14 add rax,16 @Skip4: test r8,2 jz @Skip2 movss xmm15,[rcx+rax] mulss xmm15,[rdx+rax] movss xmm8,[rcx+rax+4] mulss xmm8,[rdx+rax+4] addss xmm7,xmm15 addss xmm0,xmm8 add rax,8 @Skip2: test r8,1 jz @Skip1 movss xmm9,[rcx+rax] mulss xmm9,[rdx+rax] addss xmm1,xmm9 add rax,4 @Skip1: addps xmm0,xmm4 addps xmm1,xmm5 addps xmm2,xmm6 addps xmm3,xmm7 addps xmm0,xmm1 addps xmm2,xmm3 addps xmm0,xmm2 xorps xmm1,xmm1 movhlps xmm1,xmm0 addps xmm0,xmm1 movaps xmm1,xmm0 shufps xmm1,xmm1,$55 addss xmm0,xmm1 movss dword ptr result,xmm0 end; {$else} function ConvolveSSE(Data,Kernel:psingle;Count:longint):single; register; asm {$ifndef windows} mov r8,rdx mov rcx,rdi mov rdx,rsi {$endif} xorps xmm0,xmm0 xorps xmm1,xmm1 xorps xmm2,xmm2 xorps xmm3,xmm3 xor rax,rax mov r10,r8 shr r10,4 and r8,15 test r10,r10 jz @Loop16Done @Loop16: movups xmm4,[rcx+rax] mulps xmm4,[rdx+rax] movups xmm5,[rcx+rax+16] mulps xmm5,[rdx+rax+16] movups xmm6,[rcx+rax+32] mulps xmm6,[rdx+rax+32] movups xmm7,[rcx+rax+48] mulps xmm7,[rdx+rax+48] addps xmm0,xmm4 addps xmm1,xmm5 addps xmm2,xmm6 addps xmm3,xmm7 add rax,64 dec r10 jnz @Loop16 @Loop16Done: test r8,8 jz @Skip8 movups xmm4,[rcx+rax] mulps xmm4,[rdx+rax] movups xmm5,[rcx+rax+16] mulps xmm5,[rdx+rax+16] addps xmm0,xmm4 addps xmm1,xmm5 add rax,32 @Skip8: test r8,4 jz @Skip4 movups xmm6,[rcx+rax] mulps xmm6,[rdx+rax] addps xmm2,xmm6 add rax,16 @Skip4: test r8,2 jz @Skip2 movss xmm7,[rcx+rax] mulss xmm7,[rdx+rax] movss xmm4,[rcx+rax+4] mulss xmm4,[rdx+rax+4] addss xmm3,xmm7 addss xmm0,xmm4 add rax,8 @Skip2: test r8,1 jz @Skip1 movss xmm5,[rcx+rax] mulss xmm5,[rdx+rax] addss xmm1,xmm5 add rax,4 @Skip1: addps xmm0,xmm1 addps xmm2,xmm3 addps xmm0,xmm2 xorps xmm1,xmm1 movhlps xmm1,xmm0 addps xmm0,xmm1 movaps xmm1,xmm0 shufps xmm1,xmm1,$55 addss xmm0,xmm1 movss dword ptr result,xmm0 end; {$endif} {$endif} {$endif} function Convolve(Data,Kernel:psingle;Count:longint):single; register; begin result:=0; while Count>7 do begin result:=result+((PSingleArray(Data)^[0]*PSingleArray(Kernel)^[0])+ (PSingleArray(Data)^[1]*PSingleArray(Kernel)^[1])+ (PSingleArray(Data)^[2]*PSingleArray(Kernel)^[2])+ (PSingleArray(Data)^[3]*PSingleArray(Kernel)^[3])+ (PSingleArray(Data)^[4]*PSingleArray(Kernel)^[4])+ (PSingleArray(Data)^[5]*PSingleArray(Kernel)^[5])+ (PSingleArray(Data)^[6]*PSingleArray(Kernel)^[6])+ (PSingleArray(Data)^[7]*PSingleArray(Kernel)^[7])); inc(Data,8); inc(Kernel,8); dec(Count,8); end; if Count>3 then begin result:=result+((PSingleArray(Data)^[0]*PSingleArray(Kernel)^[0])+ (PSingleArray(Data)^[1]*PSingleArray(Kernel)^[1])+ (PSingleArray(Data)^[2]*PSingleArray(Kernel)^[2])+ (PSingleArray(Data)^[3]*PSingleArray(Kernel)^[3])); inc(Data,4); inc(Kernel,4); dec(Count,4); end; if Count>2 then begin result:=result+((PSingleArray(Data)^[0]*PSingleArray(Kernel)^[0])+ (PSingleArray(Data)^[1]*PSingleArray(Kernel)^[1])); inc(Data,2); inc(Kernel,2); dec(Count,2); end; if Count>0 then begin result:=result+(Data^*Kernel^); end; end; procedure SIMDSetFlags; {$ifdef CanSSE} // Flush to Zero=Bit 15 // Underflow exception mask=Bit 11 // Denormals are zeros=Bit 6 // Denormal exception mask=Bit 8 // $8840(ftz+uem+daz+dem) and $8940(ftz+uem+daz) const DenormalsAreZero=1 shl 6; InvalidOperationExceptionMask=1 shl 7; DenormalExceptionMask=1 shl 8; DivodeByZeroExceptionMask=1 shl 9; OverflowExceptionMask=1 shl 10; UnderflowExceptionMask=1 shl 11; PrecisionMask=1 shl 12; FlushToZero=1 shl 15; SIMDFlags=InvalidOperationExceptionMask or DenormalExceptionMask or DivodeByZeroExceptionMask or OverflowExceptionMask or UnderflowExceptionMask or PrecisionMask or FlushToZero; RoundToNearest=longword(longword($ffffffff) and not ((1 shl 13) or (1 shl 14))); var SIMDCtrl:longword; begin if SSEExt then begin asm push eax stmxcsr dword ptr SIMDCtrl mov eax,dword ptr SIMDCtrl or eax,SIMDFlags and eax,RoundToNearest cmp dword ptr SSE2Ext,0 jz @NoSSE2 or eax,DenormalsAreZero // DAZ on SSE1-only CPUs is often buggy, so avoid better DAZ on SSE1-only CPUs @NoSSE2: mov dword ptr SIMDCtrl,eax ldmxcsr dword ptr SIMDCtrl pop eax end; end; end; {$else} begin end; {$endif} procedure CheckCPU; {$ifdef CanSSE} var Features,FeaturesExt:longword; {$endif} begin {$ifdef CanSSE} Features:=0; FeaturesExt:=0; asm pushad // Check for CPUID opcode pushfd pop eax mov edx,eax xor eax,$200000 push eax popfd pushfd pop eax xor eax,edx jz @NoCPUID // Get cpu features per CPUID opcode mov eax,1 cpuid mov dword ptr FeaturesExt,ecx mov dword ptr Features,edx @NoCPUID: popad end; MMXExt:=(Features and $00800000)<>0; SSEExt:=(Features and $02000000)<>0; SSE2Ext:=(Features and $04000000)<>0; SSE3Ext:=(FeaturesExt and $00000001)<>0; {$else} MMXExt:=false; SSEExt:=false; SSE2Ext:=false; SSE3Ext:=false; {$endif} end; function i0(x:double):double; const epsilon=1e-6; var u,halfx,t:double; n:longint; begin result:=1; u:=1; n:=1; halfx:=x*0.5; repeat t:=halfx/n; inc(n); u:=u*sqr(t); result:=result+u; until not (u>=(epsilon*result)); end; procedure AlignedGetMem(var p;size:longint;Align:longint=16); type ppointer=^pointer; var pp:pointer absolute p; orgptr:pointer; px:ppointer; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(pp,size+Align+SizeOf(pointer)); orgptr:=pp; inc(ppointer(pp)); if (ptruint(pp) mod longword(Align))<>0 then begin inc(ptruint(pp),longword(Align)-(ptruint(pp) mod longword(Align))); end; px:=pp; dec(px); px^:=orgptr; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; procedure AlignedFreeMem(var p); type ppointer=^pointer; var pp:pointer absolute p; px:ppointer; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(pp) then begin px:=pp; dec(px); {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(px^); pp:=nil; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$ifdef CanSINC} procedure InitResamplerSINC; {$undef CacheSINC} {$ifndef CreamTrackerCompact} {$ifdef win32} {$define CacheSINC} {$endif} {$endif} const EPSILON=1e-10; {$ifdef CacheSINC} SINCVersion:longword=2; {$endif} var Factor,CutOff,FracValue,SincValue,WindowValue{,WindowFactor},WindowParameter, OtherPosition,HalfPoints,Position,a,beta,i0beta:double; CutOffCounter,Counter,SubCounter,Points,Len:longint; {$ifdef CacheSINC} FileHandle:THandle; DataSize:longint; DataReadOrWritten,Version:longword; OK:longbool; {$endif} begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if not ResamplerSINCArrayInitialized then begin ResamplerSINCArrayInitialized:=true; AlignedGetMem(ResamplerSINCArray,SizeOf(TResamplerSINCArray)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef CacheSINC} OK:=false; FileHandle:=CreateFileA(PAnsiChar('creamtracker.sinc'),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); if FileHandle<>0 then begin DataSize:=SetFilePointer(FileHandle,0,nil,FILE_END); if DataSize=(SizeOf(longword)+SizeOf(TResamplerSINCArray)+SizeOf(TResamplerSINCCutOffIncrementTable)) then begin if SetFilePointer(FileHandle,0,nil,FILE_BEGIN)=0 then begin if ReadFile(FileHandle,Version,SizeOf(longword),DataReadOrWritten,nil) then begin if (DataReadOrWritten=SizeOf(longword)) and (Version=SINCVersion) then begin if ReadFile(FileHandle,ResamplerSINCArray^,SizeOf(TResamplerSINCArray),DataReadOrWritten,nil) then begin if DataReadOrWritten=SizeOf(TResamplerSINCArray) then begin if ReadFile(FileHandle,ResamplerSINCCutOffIncrementTable,SizeOf(TResamplerSINCCutOffIncrementTable),DataReadOrWritten,nil) then begin if DataReadOrWritten=SizeOf(TResamplerSINCCutOffIncrementTable) then begin OK:=true; end; end; end; end; end; end; end; end; CloseHandle(FileHandle); end; if not OK then {$endif}begin Points:=SINC_WIDTH; Len:=SINC_LUTLEN; HalfPoints:=Points*0.5; //WindowFactor:=(2*pi)/Points; // a:=(-20.0)*log10(1/(1 shl 16)); // 16 bits -> -96dB stopband attenuation a:=(-20.0)*log10(1/(1 shl 24)); // 24 bits -> -144dB stopband attenuation if a>50.0 then begin beta:=0.1102*(a-8.7); end else if a>=21.0 then begin beta:=(0.5842*power(a-21.0,0.4))+(0.07886*(a-21.0)); end else begin beta:=0.0; end; i0beta:=i0(beta); for Counter:=0 to Len-1 do begin FracValue:=(Counter/Len)-0.5; for SubCounter:=0 to Points-1 do begin OtherPosition:=SubCounter-FracValue; Position:=OtherPosition-HalfPoints; WindowParameter:=Position/HalfPoints; if abs(WindowParameter)<1.0 then begin WindowValue:=i0(beta*sqrt(1.0-sqr(WindowParameter)))/i0beta; end else begin WindowValue:=0.0;//i0(0)/i0beta; end; { WindowParameter:=OtherPosition*WindowFactor; WindowValue:=(0.42-(0.50*cos(WindowParameter)))+(0.08*cos(2.0*WindowParameter));{} ResamplerSINCWindowArray[Counter,SubCounter]:=WindowValue; end; end; for CutOffCounter:=0 to SINCCUTOFF_LEN-1 do begin if CutOffCounter=0 then begin Factor:=1.0; end else begin Factor:=1.0*power(2.0,CutOffCounter/12.0); end; ResamplerSINCCutOffIncrementTable[CutOffCounter]:=round(int64($100000000)*Factor); CutOff:=1.0/Factor; for Counter:=0 to Len-1 do begin FracValue:=(Counter/Len)-0.5; for SubCounter:=0 to Points-1 do begin OtherPosition:=SubCounter-FracValue; Position:=OtherPosition-HalfPoints; if abs(Position)0 then begin if WriteFile(FileHandle,SINCVersion,SizeOf(longword),DataReadOrWritten,nil) then begin if DataReadOrWritten=SizeOf(longword) then begin if WriteFile(FileHandle,ResamplerSINCArray^,SizeOf(TResamplerSINCArray),DataReadOrWritten,nil) then begin if DataReadOrWritten=SizeOf(TResamplerSINCArray) then begin if WriteFile(FileHandle,ResamplerSINCCutOffIncrementTable,SizeOf(TResamplerSINCCutOffIncrementTable),DataReadOrWritten,nil) then begin if DataReadOrWritten=SizeOf(TResamplerSINCCutOffIncrementTable) then begin OK:=true; end; end; end; end; end; end; CloseHandle(FileHandle); if not OK then begin Windows.DeleteFileA(PAnsiChar('creamtracker.sinc')); end; end; {$endif} end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$ifndef CreamTrackerMinimalPlayer} procedure DoneResamplerSINC; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if ResamplerSINCArrayInitialized then begin AlignedFreeMem(ResamplerSINCArray); ResamplerSINCArrayInitialized:=false; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$endif} {$endif} procedure CreamTrackerRandomInit(var RandomGenerator:TCreamTrackerRandomGenerator;Seed:longword); begin RandomGenerator.XorShift128x:=$c8a2d216 xor Seed; RandomGenerator.XorShift128y:=($b46f23c7*Seed) xor Seed; RandomGenerator.XorShift128z:=($a54c2364 xor Seed)*Seed; RandomGenerator.XorShift128w:=$8c1ca70c+Seed; RandomGenerator.LCG:=$72c1a602 xor Seed; RandomGenerator.MWCx:=$a182f231 xor Seed; RandomGenerator.MWCy:=$80d3c173-Seed; RandomGenerator.MWCc:=0; end; function CreamTrackerRandomGet(var RandomGenerator:TCreamTrackerRandomGenerator):longword; begin result:=RandomGenerator.XorShift128x xor (RandomGenerator.XorShift128x shl 11); RandomGenerator.XorShift128x:=RandomGenerator.XorShift128y; RandomGenerator.XorShift128y:=RandomGenerator.XorShift128z; RandomGenerator.XorShift128z:=RandomGenerator.XorShift128w; RandomGenerator.XorShift128w:=(RandomGenerator.XorShift128w xor (RandomGenerator.XorShift128w shr 19)) xor (result xor (result shr 8)); RandomGenerator.LCG:=(RandomGenerator.LCG*1664525)+1013904223; result:=RandomGenerator.MWCx+RandomGenerator.MWCy+RandomGenerator.MWCc; RandomGenerator.MWCx:=RandomGenerator.MWCy; RandomGenerator.MWCc:=result shr 31; RandomGenerator.MWCy:=result and $7fffffff; result:=RandomGenerator.LCG+RandomGenerator.XorShift128w+(RandomGenerator.MWCx shl 1); end; function WRSSynthese(InputData:pointer;InputDataSize:longint;OutputData:pointer;OutputDataStepSize,OutputLength:longint):boolean; const pi2=pi*2.0; type PLongwords=^TLongwords; TLongwords=array[0..65535] of longword; PSinusoidFrame=^TSinusoidFrame; TSinusoidFrame=record Amplitude:single; Frequency:single; Phase:single; Active:longbool; end; PSinusoidFrameTrack=^TSinusoidFrameTrack; TSinusoidFrameTrack=array[0..0] of TSinusoidFrame; PSinusoidFrameTracks=^TSinusoidFrameTracks; TSinusoidFrameTracks=array[0..0] of PSinusoidFrameTrack; var SinusoidFrames:PSinusoidFrameTracks; TrackLengths:PLongwords; SampleRate,NewLength,OriginalLength,FrameSize,Parts,NumTracks:longint; PitchScale,TimeScale:single; HasPhases:boolean; function Load:boolean; var InputDataPosition:longint; function Read(var Data;Bytes:longint):longint; begin result:=InputDataSize-InputDataPosition; if result<0 then begin result:=0; end else if result>Bytes then begin result:=Bytes; end; if result>0 then begin Move(PAnsiChar(InputData)[InputDataPosition],Data,result); inc(InputDataPosition,result); end; end; function ReadByte:byte; begin Read(result,SizeOf(byte)); end; function ReadInteger:longint; begin Read(result,SizeOf(longint)); end; function ReadFloat:single; begin Read(result,SizeOf(single)); end; var ByteIndex,Frame,Track,NumFrames,Count,Index:longint; Value,Flags:byte; SinusoidFrame:PSinusoidFrame; MaxAmplitude,Float32:single; Temp:PLongwords; RandomGenerator:TCreamTrackerRandomGenerator; HighQualityAmplitudes:boolean; begin InputDataPosition:=0; CreamTrackerRandomInit(RandomGenerator,0); SampleRate:=ReadInteger; OriginalLength:=ReadInteger; FrameSize:=ReadInteger; Parts:=ReadInteger; PitchScale:=ReadFloat; TimeScale:=ReadFloat; NumTracks:=ReadInteger; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(SinusoidFrames,NumTracks*SizeOf(PSinusoidFrameTrack)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(SinusoidFrames^,NumTracks*SizeOf(PSinusoidFrameTrack),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} MaxAmplitude:=ReadFloat; Flags:=ReadByte; HasPhases:=(Flags and 1)<>0; HighQualityAmplitudes:=(Flags and 2)<>0; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(TrackLengths,NumTracks*SizeOf(longword)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(TrackLengths^,NumTracks*SizeOf(longword),AnsiChar(#0)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Count:=0; for ByteIndex:=0 to 3 do begin Value:=0; for Track:=0 to NumTracks-1 do begin inc(Value,ReadByte); TrackLengths^[Track]:=TrackLengths^[Track] or (Value shl (ByteIndex shl 3)); end; end; for Track:=0 to NumTracks-1 do begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(SinusoidFrames^[Track],TrackLengths^[Track]*SizeOf(TSinusoidFrame)); FillChar(SinusoidFrames^[Track]^,TrackLengths^[Track]*SizeOf(TSinusoidFrame),#0); inc(Count,TrackLengths^[Track]); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Temp,Count*SizeOf(longword)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Temp^,Count*SizeOf(longword),AnsiChar(#0)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for ByteIndex:=0 to 1 do begin Index:=0; Value:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin inc(Value,ReadByte); Temp^[Index]:=Temp^[Index] or (Value shl (ByteIndex shl 3)); inc(Index); end; end; end; Index:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin SinusoidFrame:=@SinusoidFrames^[Track]^[Frame]; SinusoidFrame^.Frequency:=(Temp^[Index]/65535)*SampleRate; inc(Index); end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if HighQualityAmplitudes then begin FillChar(Temp^,Count*SizeOf(longword),AnsiChar(#0)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for ByteIndex:=0 to 1 do begin Index:=0; Value:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin inc(Value,ReadByte); Temp^[Index]:=Temp^[Index] or (Value shl (ByteIndex shl 3)); inc(Index); end; end; end; Index:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin SinusoidFrame:=@SinusoidFrames^[Track]^[Frame]; SinusoidFrame^.Amplitude:=(Temp^[Index]/65535)*MaxAmplitude; inc(Index); end; end; end else begin Value:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin inc(Value,ReadByte); SinusoidFrame:=@SinusoidFrames^[Track]^[Frame]; SinusoidFrame^.Amplitude:=(Value/255)*MaxAmplitude; end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if HasPhases then begin Value:=0; for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin inc(Value,ReadByte); SinusoidFrame:=@SinusoidFrames^[Track]^[Frame]; SinusoidFrame^.Phase:=(Value/255)*pi2; end; end; end else begin for Track:=0 to NumTracks-1 do begin NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin SinusoidFrame:=@SinusoidFrames^[Track]^[Frame]; longword(pointer(@Float32)^):=((CreamTrackerRandomGet(RandomGenerator) shr 9) and $7fffff) or $3f800000; SinusoidFrame^.Phase:=(Float32-1.0)*pi2; end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(Temp) then begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Temp); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; result:=true; end; function MagTodB(x:double):double; begin if x<0.00001 then begin result:=0.0; end else begin result:=20.0*log10(x*100000.0); end; end; procedure Synthese(Pitch:single=1); var NumFrames,Track,Frame,Sample,PartSize,PartPos,SampleIndex,IntPartSize,CurrentPartSize:longint; SinusoidFrame:PSinusoidFrame; CurrentPhase,PrevAmp,CurrentFreq,PrevFreq,PrevPhase,InstPhase,InstAmp,AmpInc, CurrentAmplitude,InstFreq,FreqInc,VolumeScale,FloatPartSize,FloatPartSizeFrac:double; Samples:PSingleArray; XorShift128x,XorShift128y,XorShift128z,XorShift128w,LCG,MWCx,MWCy,MWCc,Temp:longword; Float32:single; begin NewLength:=round(OriginalLength*TimeScale); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Samples,NewLength*SizeOf(single)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} XorShift128x:=$c8a2d216; XorShift128y:=$b46f23c7; XorShift128z:=$a54c2364; XorShift128w:=$8c1ca70c; LCG:=$72c1a602; MWCx:=$a182f231; MWCy:=$80d3c173; MWCc:=0; Pitch:=Pitch*PitchScale; for Sample:=0 to OriginalLength-1 do begin Samples^[Sample]:=0.0; end; PartSize:=FrameSize div Parts; FloatPartSize:=PartSize*TimeScale; IntPartSize:=trunc(FloatPartSize); FloatPartSizeFrac:=frac(FloatPartSize); VolumeScale:=1.0/(FrameSize*0.25); for Track:=0 to NumTracks-1 do begin PrevAmp:=0; PrevFreq:=0; PrevPhase:=0; Sample:=0; FloatPartSize:=0; NumFrames:=TrackLengths^[Track]; for Frame:=0 to NumFrames-1 do begin CurrentPartSize:=IntPartSize+trunc(FloatPartSize); FloatPartSize:=frac(FloatPartSize+FloatPartSizeFrac); if (Frame-Parts)<0 then begin Sample:=(Frame-Parts)*PartSize; end else if (Frame-Parts)=0 then begin Sample:=0; end; SinusoidFrame:=@SinusoidFrames[Track]^[Frame]; CurrentFreq:=(SinusoidFrame^.Frequency/SampleRate)*Pitch; if (CurrentFreq>=0) and (CurrentFreq<0.5) then begin CurrentFreq:=CurrentFreq*pi2; CurrentAmplitude:=SinusoidFrame^.Amplitude; end else begin CurrentFreq:=0.0; CurrentAmplitude:=0.0; end; if (CurrentAmplitude>0.0) or (PrevAmp>0.0) then begin if HasPhases then begin CurrentPhase:=SinusoidFrame^.Phase; end else begin Temp:=XorShift128x xor (XorShift128x shl 11); XorShift128x:=XorShift128y; XorShift128y:=XorShift128z; XorShift128z:=XorShift128w; XorShift128w:=(XorShift128w xor (XorShift128w shr 19)) xor (Temp xor (Temp shr 8)); LCG:=(LCG*1664525)+1013904223; Temp:=MWCx+MWCy+MWCc; MWCx:=MWCy; MWCc:=Temp shr 31; MWCy:=Temp and $7fffffff; Temp:=(LCG+XorShift128w+(MWCx shl 1)) shr 24; longword(pointer(@Float32)^):=((Temp shr 9) and $7fffff) or $40000000; CurrentPhase:=(Float32-3)*pi; end; if PrevAmp<=0 then begin PrevFreq:=CurrentFreq; PrevPhase:=CurrentPhase-(CurrentFreq*CurrentPartSize); while PrevPhase>=pi do begin PrevPhase:=PrevPhase-pi2; end; while PrevPhase<-pi do begin PrevPhase:=PrevPhase+pi2; end; end else if (Frame>0) and (CurrentAmplitude<=0) then begin CurrentFreq:=((SinusoidFrames^[Track]^[Frame-1].Frequency/SampleRate)*Pitch)*pi2; CurrentPhase:=PrevPhase+(PrevFreq*CurrentPartSize); while CurrentPhase>=pi do begin CurrentPhase:=CurrentPhase-pi2; end; while CurrentPhase<-pi do begin CurrentPhase:=CurrentPhase+pi2; end; end; InstAmp:=MagTodB(PrevAmp); AmpInc:=(MagTodB(CurrentAmplitude)-InstAmp)/CurrentPartSize; InstFreq:=PrevFreq; FreqInc:=(CurrentFreq-PrevFreq)/CurrentPartSize; for PartPos:=0 to CurrentPartSize-1 do begin InstAmp:=InstAmp+AmpInc; InstFreq:=InstFreq+FreqInc; PrevPhase:=PrevPhase+InstFreq; while PrevPhase>=pi do begin PrevPhase:=PrevPhase-pi2; end; while PrevPhase<-pi do begin PrevPhase:=PrevPhase+pi2; end; InstPhase:=PrevPhase; SampleIndex:=Sample+PartPos; if ((SampleIndex>=0) and (SampleIndex=0.00001) then begin Samples^[SampleIndex]:=Samples^[SampleIndex]+((sin(InstPhase)*(0.00001*power(10.0,InstAmp*0.05)))*VolumeScale); end; end; end; PrevFreq:=CurrentFreq; PrevAmp:=CurrentAmplitude; inc(Sample,CurrentPartSize); end; end; for Sample:=0 to NewLength-1 do begin if Sample0 then begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instance.SharedCodeData^.CodeText,Instance.SharedCodeData^.CodeTextSize); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Move(DefaultSharedCode[1],Instance.SharedCodeData^.CodeText[0],Instance.SharedCodeData^.CodeTextSize); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instance.CodeData,SizeOf(TCreamTrackerCodeData)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instance.CodeData^,SizeOf(TCreamTrackerCodeData),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instance.CodeData^.CodeTextAllocated:=length(DefaultGlobalCode); Instance.CodeData^.CodeTextSize:=Instance.CodeData^.CodeTextAllocated; if Instance.CodeData^.CodeTextSize>0 then begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instance.CodeData^.CodeText,Instance.CodeData^.CodeTextSize); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Move(DefaultGlobalCode[1],Instance.CodeData^.CodeText[0],Instance.CodeData^.CodeTextSize); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$endif} {$ifndef CreamTrackerCompact} Instance.RowHilightMinor:=4; Instance.RowHilightMajor:=16; Instance.Header.InitialSpeed:=6; Instance.Header.InitialTempo:=125; Instance.Header.GlobalVolume:=128; Instance.Header.MasterVolume:=$80 or 48; Instance.Header.Panning:=$fc; {$endif} Instance.CreamTrackerModule:=true; Instance.SampleRate:=SampleRate; Instance.InvSampleRate:=1.0/SampleRate; Instance.ClickRemovalFadeOutFactor:=exp(-1/(SampleRate*0.002825)); //power(0.992,44100/SampleRate); Instance.SmoothFactor:=1-exp(-1/(SampleRate*0.002255555));// power(0.99,44100/SampleRate); Instance.DCFilterCoef:=1.0-((pi*2.0)*(94.0/SampleRate)); // -3dB @ 10 Hz (250*10Hz/40Hz) for i:=0 to 15 do begin // pow(2.0,i/12) Instance.NoteFactorTable[i]:=exp(i*Log2Div12); end; for i:=0 to 194 do begin // 1.0/pow(2.0,(i/12.0)) Instance.NoteToPeriodTable[i]:=exp(i*MinusLog2Div12); end; for i:=low(TCreamTrackerPatterns) to high(TCreamTrackerPatterns) do begin Pattern:=@Instance.Patterns[i]; for Row:=0 to 63 do begin for j:=0 to 31 do begin PatternNote:=@Pattern^[Row,j]; PatternNote^.Note:=255; PatternNote^.Instrument:=0; PatternNote^.Volume:=255; PatternNote^.Effect:=0; PatternNote^.EffectParameter:=0; end; end; end; Instance.Header.OrdNum:=1; Instance.Orders[0]:=0; for i:=0 to 31 do begin Instance.Header.ChannelSettings[i]:=((((i and 15) shr 1) or ((i and 1) shl 3)) and (-((not (i shr 4)) and 1))) or (i and (-((i shr 4) and 1))); Instance.ChannelPannings[i]:=$27 or $80; end; for i:=0 to 63 do begin Instance.ChannelOrder[i]:=i; end; for i:=0 to 31 do begin Instance.ChannelBuffers[i]:=@Instance.Channels[i].Buffer[0]; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerReset(Instance); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} CreamTrackerJobCreateThreads(@Instance); {$endif} {$endif} {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerDestroy(var Instance:TCreamTrackerInstance); var i:longint; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} if Instance.UseMultithreading then begin Instance.ThreadsTerminated:=true; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerJobWakeThreads(@Instance); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if Instance.UseMultithreading then begin i:=0; while (Instance.Threads>0) and (i<1000) do begin Sleep(10); inc(i,10); end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerJobFreeThreads(@Instance); {$endif} {$endif} {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=low(TCreamTrackerInstruments) to high(TCreamTrackerInstruments) do begin CreamTrackerDestroyInstrument(Instance,Instance.Instruments[i]); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(Instance.CodeData) then begin CreamTrackerCodeDestroy(Instance,Instance.CodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instance.CodeData:=nil; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(Instance.SharedCodeData) then begin CreamTrackerCodeDestroy(Instance,Instance.SharedCodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instance.SharedCodeData:=nil; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instance,SizeOf(TCreamTrackerInstance),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$endif} const Seed:longword=$321277da; function RandomFloat:single; stdcall; var RandomValue:longword; resultCasted:single absolute result; begin Seed:=(Seed*1664525)+1013904223; RandomValue:=((Seed shr 9) and $7fffff) or $40000000; resultCasted:=((single(pointer(@RandomValue)^)-3.0)+1.0)*0.5; end; {$ifdef UseAulan} procedure TRIHostRandom; assembler; asm call RandomFloat push eax fld dword ptr [esp] pop eax end; procedure TRIHostKillDenormal(Value:pointer); stdcall; begin longword(Value^):=longword(Value^) and longword($ffffffff+longword(((((longword(Value^) and $7f800000)+$800000) and $7f800000)-$1000000) shr 31)); end; procedure TRIHostDenormalKill(Value:single); assembler; stdcall; asm mov eax,dword ptr Value mov edx,eax and eax,$7f800000 add eax,$00800000 and eax,$7f800000 sub eax,$01000000 shr eax,31 dec eax and eax,edx push eax fld dword ptr [esp] pop eax end; function TRIHostGetSampleRateEx(InstanceStubData:PCreamTrackerInstanceStubData):longint; stdcall; begin if assigned(InstanceStubData) then begin result:=InstanceStubData^.Instance^.SampleRate; end else begin result:=0; end; end; function TRIHostGetSampleRate:longint; stdcall; asm lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetSampleRateEx end; function TRIHostGetValueEx(InstanceStubData:PCreamTrackerInstanceStubData;Slot:longint):longint; stdcall; begin result:=0; if assigned(InstanceStubData) then begin if assigned(InstanceStubData^.Instance) and (Slot in [0..63]) then begin result:=InstanceStubData^.Instance^.ValueMemory[Slot]; end else if assigned(InstanceStubData^.Channel) and (Slot in [64..127]) then begin result:=InstanceStubData^.Channel^.ValueMemory^[Slot]; end; end; end; function TRIHostGetValue(Slot:longint):longint; stdcall; asm push dword ptr Slot lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetValueEx end; procedure TRIHostSetValueEx(InstanceStubData:PCreamTrackerInstanceStubData;Slot,Value:longint); stdcall; begin if assigned(InstanceStubData) then begin if assigned(InstanceStubData^.Instance) and (Slot in [0..63]) then begin InstanceStubData^.Instance^.ValueMemory[Slot]:=Value; end else if assigned(InstanceStubData^.Channel) and (Slot in [64..127]) then begin InstanceStubData^.Channel^.ValueMemory^[Slot]:=Value; end; end; end; procedure TRIHostSetValue(Slot,Value:longint); stdcall; asm push dword ptr Value push dword ptr Slot lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostSetValueEx end; function TRIHostGetChannelValueEx(InstanceStubData:PCreamTrackerInstanceStubData;Channel,Slot:longint):longint; stdcall; begin result:=0; if assigned(InstanceStubData) and assigned(InstanceStubData^.Instance) then begin if Slot in [0..63] then begin result:=InstanceStubData^.Instance^.ValueMemory[Slot]; end else if (Channel in [0..63]) and (Slot in [64..127]) then begin result:=InstanceStubData^.Instance.Channels[Channel].ValueMemory^[Slot]; end; end; end; function TRIHostGetChannelValue(Channel,Slot:longint):longint; stdcall; asm push dword ptr Slot push dword ptr Channel lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetChannelValueEx end; procedure TRIHostSetChannelValueEx(InstanceStubData:PCreamTrackerInstanceStubData;Channel,Slot,Value:longint); stdcall; begin if assigned(InstanceStubData) and assigned(InstanceStubData^.Instance) then begin if Slot in [0..63] then begin InstanceStubData^.Instance^.ValueMemory[Slot]:=Value; end else if (Channel in [0..63]) and (Slot in [64..127]) then begin InstanceStubData^.Instance.Channels[Channel].ValueMemory^[Slot]:=Value; end; end; end; procedure TRIHostSetChannelValue(Channel,Slot,Value:longint); stdcall; asm push dword ptr Value push dword ptr Slot push dword ptr Channel lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostSetChannelValueEx end; function TRIHostGetMemory(Size:longint):pointer; stdcall; begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(result,Size); end; procedure TRIHostFreeMemory(ThePointer:pointer); stdcall; begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(ThePointer); end; procedure TRIHostZeroMemory(ThePointer:pointer;Size:longint); stdcall; begin FillChar(ThePointer^,Size,AnsiChar(#0)); end; procedure TRIHostCopyMemory(FromPointer,ToPointer:pointer;Size:longint); stdcall; begin Move(FromPointer^,ToPointer^,Size); end; function TRIHostGetSampleDataEx(InstanceStubData:PCreamTrackerInstanceStubData;InstrumentIndex:longint):pointer; stdcall; begin if assigned(InstanceStubData) and (InstrumentIndex>=1) and (InstrumentIndex<=99) then begin result:=InstanceStubData^.Instance.Instruments[InstrumentIndex].Data; end else begin result:=nil; end; end; function TRIHostGetSampleData(InstrumentIndex:longint):pointer; stdcall; asm push dword ptr InstrumentIndex lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetSampleDataEx end; function TRIHostGetSampleSizeEx(InstanceStubData:PCreamTrackerInstanceStubData;InstrumentIndex:longint):longint; stdcall; begin if assigned(InstanceStubData) and (InstrumentIndex>=1) and (InstrumentIndex<=99) and assigned(InstanceStubData^.Instance.Instruments[InstrumentIndex].Data) then begin result:=InstanceStubData^.Instance.Instruments[InstrumentIndex].Header.Length; end else begin result:=0; end; end; function TRIHostGetSampleSize(InstrumentIndex:longint):longint; stdcall; asm push dword ptr InstrumentIndex lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetSampleSizeEx end; procedure TRIHostDebugInt(const Value:longint); stdcall; begin {$ifdef CreamTrackerGUI} SendMessage(FormMain.Handle,WM_DEBUGINT,Value,0); {$endif} end; procedure TRIHostDebugFloat(const Value:single); stdcall; begin {$ifdef CreamTrackerGUI} SendMessage(FormMain.Handle,WM_DEBUGFLOAT,longint(pointer(@Value)^),0); {$endif} end; function TRIHostGetChannelDataEx(InstanceStubData:PCreamTrackerInstanceStubData;ChannelIndex:longint):pointer; stdcall; begin if assigned(InstanceStubData) and assigned(InstanceStubData^.Instance) and ((ChannelIndex>=0) and (ChannelIndex<32)) then begin result:=@InstanceStubData^.Instance^.ChannelDataArray[ChannelIndex and 31]; end else begin result:=nil; end; end; function TRIHostGetChannelData(ChannelIndex:longint):pointer; stdcall; asm push dword ptr ChannelIndex lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetChannelDataEx end; function TRIHostGetCurrentChannelDataEx(InstanceStubData:PCreamTrackerInstanceStubData):pointer; stdcall; var ChannelIndex:longint; begin if assigned(InstanceStubData) and assigned(InstanceStubData^.Instance) and assigned(InstanceStubData^.Channel) and not assigned(InstanceStubData^.Channel^.Master) then begin result:=@InstanceStubData^.Instance^.ChannelDataArray[ChannelIndex and 31]; end else begin result:=nil; end; end; function TRIHostGetCurrentChannelData:pointer; stdcall; asm lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetCurrentChannelDataEx end; function TRIHostGetCurrentMasterChannelDataEx(InstanceStubData:PCreamTrackerInstanceStubData):pointer; stdcall; var ChannelIndex:longint; begin if assigned(InstanceStubData) and assigned(InstanceStubData^.Instance) and assigned(InstanceStubData^.Channel) then begin if assigned(InstanceStubData^.Channel^.Master) then begin ChannelIndex:=InstanceStubData^.Channel^.Master^.Index; end else begin ChannelIndex:=InstanceStubData^.Channel^.Index; end; result:=@InstanceStubData^.Instance^.ChannelDataArray[ChannelIndex and 31]; end else begin result:=nil; end; end; function TRIHostGetCurrentMasterChannelData:pointer; stdcall; asm lea eax,[esi-CreamTrackerInstanceStubDataSize] push eax call TRIHostGetCurrentMasterChannelDataEx end; type TCreamTrackerComplex=packed record Re,Im:single; end; PCreamTrackerComplex=^TCreamTrackerComplex; TCreamTrackerComplexArray=array[0..65535] of TCreamTrackerComplex; PCreamTrackerComplexArray=^TCreamTrackerComplexArray; procedure TRIHostFFT(InBuffer,OutBuffer:PCreamTrackerComplexArray;NumSamples,Inverse:longint); stdcall; var NumBits,i,j,k,n,BlockSize,BlockEnd:longint; delta_angle,delta_ar,alpha,beta,tr,ti,ar,ai,Angle:single; begin Angle:=6.28318530718; if Inverse<>0 then begin Angle:=-Angle; end; NumBits:=0; for i:=0 to 30 do begin if (NumSamples and (1 shl i))<>0 then begin NumBits:=i; break; end; end; for i:=0 to NumSamples-1 do begin n:=i; j:=0; for k:=0 to NumBits-1 do begin j:=(j shl 1) or (n and 1); n:=n shr 1; end; OutBuffer^[j].Re:=InBuffer^[i].Re; OutBuffer^[j].Im:=InBuffer^[i].Im; end; BlockEnd:=1; BlockSize:=2; while BlockSize<=NumSamples do begin delta_angle:=Angle/BlockSize; alpha:=sin(0.5*delta_angle); alpha:=2.0*sqr(alpha); beta:=sin(delta_angle); i:=0; while i0 then begin CodeData^.InstanceDataSize:=longint(ThePointer^); end; end else begin if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INITIALIZE') or{$endif} ComparePAnsiChar(Name,#$81) then begin addr(CodeData^.ProcInitialize):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$DEINITIALIZE') or{$endif} ComparePAnsiChar(Name,#$82) then begin addr(CodeData^.ProcDeinitialize):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$RESET') or{$endif} ComparePAnsiChar(Name,#$83) then begin addr(CodeData^.ProcReset):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$SAMPLEPROCESS') or{$endif} ComparePAnsiChar(Name,#$84) then begin addr(CodeData^.SampleProcProcess):=ThePointer; end else begin if CodeData^.Synth then begin if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCEINITIALIZE') or{$endif} ComparePAnsiChar(Name,#$85) then begin addr(CodeData^.SynthProcInstanceInitialize):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCEDEINITIALIZE') or{$endif} ComparePAnsiChar(Name,#$86) then begin addr(CodeData^.SynthProcInstanceDeinitialize):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCERESET') or{$endif} ComparePAnsiChar(Name,#$87) then begin addr(CodeData^.SynthProcInstanceReset):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCENOTEON') or{$endif} ComparePAnsiChar(Name,#$88) then begin addr(CodeData^.SynthProcInstanceNoteOn):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCENOTEOFF') or{$endif} ComparePAnsiChar(Name,#$89) then begin addr(CodeData^.SynthProcInstanceNoteOff):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$INSTANCEPROCESS') or{$endif} ComparePAnsiChar(Name,#$8a) then begin addr(CodeData^.SynthProcInstanceProcess):=ThePointer; end; end else begin if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$PROCESS') or{$endif} ComparePAnsiChar(Name,#$8b) then begin addr(CodeData^.GlobalProcProcess):=ThePointer; end else if {$ifndef CreamTrackerCompact}ComparePAnsiChar(Name,'CODE$FUNCTION$PROCESSPATTERNNOTE') or{$endif} ComparePAnsiChar(Name,#$8c) then begin addr(CodeData^.GlobalProcProcessPatternNote):=ThePointer; end; end; end; end; end; end; {$endif} procedure CreamTrackerCodeInit(var Instance:TCreamTrackerInstance;CodeData:PCreamTrackerCodeData); var pp,pd:pointer; i,SampleRate:longint; OldCW:word; begin if assigned(CodeData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if CodeData^.Synth then begin CodeData^.Instances:=65; end else begin CodeData^.Instances:=1; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.Instance:=nil; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef UseAulan} if assigned(CodeData^.TRIInstance) then begin TRIFree(CodeData^.TRIInstance); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.TRIInstance:=nil; end; {$endif} if assigned(CodeData^.InstanceData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(CodeData^.InstanceData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.InstanceData:=nil; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef UseAulan} if assigned(CodeData^.TRIData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.Instance:=@Instance; CodeData^.InstanceDataSize:=0; CodeData^.InstanceWorkDataSize:=0; addr(CodeData^.ProcInitialize):=nil; addr(CodeData^.ProcDeinitialize):=nil; addr(CodeData^.ProcReset):=nil; addr(CodeData^.SynthProcInstanceInitialize):=nil; addr(CodeData^.SynthProcInstanceDeinitialize):=nil; addr(CodeData^.SynthProcInstanceReset):=nil; addr(CodeData^.SynthProcInstanceNoteOn):=nil; addr(CodeData^.SynthProcInstanceNoteOff):=nil; addr(CodeData^.SynthProcInstanceProcess):=nil; addr(CodeData^.SampleProcProcess):=nil; addr(CodeData^.GlobalProcProcess):=nil; addr(CodeData^.GlobalProcProcessPatternNote):=nil; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.TRIInstance:=TRILink(CodeData^.TRIData,CodeData^.TRIDataSize,CodeData,GetExternalPointer,SetPublicPointer); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(CodeData^.TRIInstance) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.InstanceWorkDataSize:=SizeOf(TCreamTrackerInstanceStubData)+CodeData^.InstanceDataSize; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if CodeData^.InstanceWorkDataSize>0 then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(CodeData^.InstanceData,CodeData^.InstanceWorkDataSize*CodeData^.Instances); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(CodeData^.InstanceData^,CodeData^.InstanceWorkDataSize*CodeData^.Instances,#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end else begin CodeData^.InstanceData:=nil; end; {$ifdef CreamTrackerGUI} try {$endif} if assigned(addr(CodeData^.ProcInitialize)) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} pp:=addr(CodeData^.ProcInitialize); pd:=@PAnsiChar(CodeData^.InstanceData)[(CodeData^.Instances-1)*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=nil; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; SampleRate:=Instance.SampleRate; 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 end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if CodeData^.Synth and assigned(addr(CodeData^.SynthProcInstanceInitialize)) and assigned(CodeData^.InstanceData) then begin pp:=addr(CodeData^.SynthProcInstanceInitialize); for i:=0 to 63 do begin pd:=@PAnsiChar(CodeData^.InstanceData)[i*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=@Instance.Channels[i]; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; 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 end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef CreamTrackerGUI} finally end; {$endif} end else begin CodeData^.Instance:=nil; CodeData^.InstanceDataSize:=0; CodeData^.InstanceWorkDataSize:=0; end; end; {$else} CodeData^.Instance:=nil; CodeData^.InstanceDataSize:=0; CodeData^.InstanceWorkDataSize:=0; {$endif} end; end; {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerCodeDestroy(var Instance:TCreamTrackerInstance;var CodeData:PCreamTrackerCodeData); var i:longint; pp,pd:pointer; OldCW:word; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(CodeData) then begin {$ifdef CreamTrackerGUI} try {$endif} if CodeData^.Synth then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(addr(CodeData^.SynthProcInstanceDeinitialize)) and assigned(CodeData^.InstanceData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef UseAulan} pp:=addr(CodeData^.SynthProcInstanceDeinitialize); for i:=0 to 63 do begin pd:=@PAnsiChar(CodeData^.InstanceData)[i*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=@Instance.Channels[i]; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; 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 end; end; {$endif} end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(addr(CodeData^.ProcDeinitialize)) then begin {$ifdef UseAulan} pp:=addr(CodeData^.ProcDeinitialize); pd:=@PAnsiChar(CodeData^.InstanceData)[(CodeData^.Instances-1)*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=nil; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; 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 end; {$endif} {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef CreamTrackerGUI} finally end; {$endif} if assigned(CodeData^.CodeText) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(CodeData^.CodeText); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.CodeText:=nil; end; if assigned(CodeData^.TRIData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(CodeData^.TRIData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.TRIData:=nil; end; {$ifdef UseAulan} if assigned(CodeData^.TRIInstance) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} TRIFree(CodeData^.TRIInstance); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.TRIInstance:=nil; end; {$endif} if assigned(CodeData^.InstanceData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(CodeData^.InstanceData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.InstanceData:=nil; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(CodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData:=nil; end; end; {$endif} procedure CreamTrackerCodeReset(var Instance:TCreamTrackerInstance;CodeData:PCreamTrackerCodeData); {$ifdef UseAulan} var pd,pp:pointer; i:longint; OldCW:word; begin {$ifdef CreamTrackerGUI} try {$endif} if assigned(CodeData) and assigned(CodeData^.Instance) and assigned(CodeData^.TRIData) and assigned(CodeData^.TRIInstance) then begin if assigned(CodeData^.InstanceData) and (CodeData^.InstanceWorkDataSize>0) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(CodeData^.InstanceData^,CodeData^.InstanceWorkDataSize*CodeData^.Instances,#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; if assigned(addr(CodeData^.ProcReset)) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} pp:=addr(CodeData^.ProcReset); pd:=@PAnsiChar(CodeData^.InstanceData)[(CodeData^.Instances-1)*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=nil; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; 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 end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; if CodeData^.Synth then begin if assigned(addr(CodeData^.SynthProcInstanceReset)) and (assigned(CodeData^.InstanceData) and (CodeData^.InstanceWorkDataSize>0)) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} pp:=addr(CodeData^.SynthProcInstanceReset); for i:=0 to 63 do begin pd:=@PAnsiChar(CodeData^.InstanceData)[i*CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=@Instance.Channels[i]; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; 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 end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; end; end; {$ifdef CreamTrackerGUI} finally end; {$endif} end; {$else} begin end; {$endif} {$ifndef CreamTrackerMinimalPlayer} procedure CreamTrackerDestroyInstrument(var Instance:TCreamTrackerInstance;var Instrument:TCreamTrackerInstrument); begin if assigned(Instrument.Data) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.Data); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.Data:=nil; end; {$ifdef CanSINC} if assigned(Instrument.SINCLeftData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.SINCLeftData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.SINCLeftData:=nil; end; Instrument.SINCRightData:=nil; {$else} if assigned(Instrument.MixData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.MixData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.MixData:=nil; end; {$endif} if assigned(Instrument.RawData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.RawData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.RawData:=nil; end; if assigned(Instrument.CodeData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerCodeDestroy(Instance,Instrument.CodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.CodeData:=nil; end; end; procedure CreamTrackerDestroySampleData(var Instance:TCreamTrackerInstance;var Instrument:TCreamTrackerInstrument); begin if assigned(Instrument.Data) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.Data); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.Data:=nil; end; {$ifdef CanSINC} if assigned(Instrument.SINCLeftData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.SINCLeftData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.SINCLeftData:=nil; end; Instrument.SINCRightData:=nil; {$else} if assigned(Instrument.MixData) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument.MixData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instrument.MixData:=nil; end; {$endif} end; {$endif} procedure CreamTrackerReset(var Instance:TCreamTrackerInstance); var i,j:longint; Channel:PCreamTrackerChannel; PatternNote:PCreamTrackerPatternNote; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instance.ValueMemory,SizeOf(TCreamTrackerValueMemory),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=0 to 31 do begin Channel:=@Instance.Channels[i]; FillChar(Channel^,SizeOf(TCreamTrackerChannel),#0); Channel^.Instance:=@Instance; Channel^.ValueMemory:=@Instance.ChannelValueMemory[i]; Channel^.Index:=i; Channel^.Active:=false; Channel^.BaseNote:=255; Channel^.NoteCutCounter:=0; Channel^.ChannelVolume:=64; Channel^.Enabled:=(Instance.Header.ChannelSettings[i] and $80)=0; Channel^.Muted:=(Instance.Header.Panning=$fc) and ((Instance.ChannelPannings[i] and $40)<>0); if (Instance.Header.Panning=$fc) and ((Instance.ChannelPannings[i] and $80)<>0) then begin Channel^.Panning:=128; end else if (Instance.Header.Panning=$fc) and ((Instance.ChannelPannings[i] and $20)<>0) then begin Channel^.Panning:=(((Instance.ChannelPannings[i] and $f) shl 8)+8) div 15; end else begin case Instance.Header.ChannelSettings[i] and $7f of 0..7:begin Channel^.Panning:=64; end; 8..15:begin Channel^.Panning:=192; end; else begin Channel^.Panning:=128; end; end; end; Channel^.Glissando:=false; Channel^.RetrigCounter:=0; PatternNote:=@Channel^.PatternNote; PatternNote^.Note:=255; PatternNote^.Instrument:=0; PatternNote^.Volume:=255; PatternNote^.Effect:=0; PatternNote^.EffectParameter:=0; Instance.ChannelOrder[i]:=i; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} i:=0; while (i+1)<32 do begin if (Instance.Header.ChannelSettings[Instance.ChannelOrder[i]] and $1f)>(Instance.Header.ChannelSettings[Instance.ChannelOrder[i+1]] and $1f) then begin j:=Instance.ChannelOrder[i]; Instance.ChannelOrder[i]:=Instance.ChannelOrder[i+1]; Instance.ChannelOrder[i+1]:=j; if i>0 then begin dec(i); end else begin inc(i); end; end else begin inc(i); end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=32 to 63 do begin Channel:=@Instance.Channels[i]; Channel^:=Instance.Channels[i-32]; Channel^.Instance:=@Instance; Channel^.Master:=@Instance.Channels[i-32]; Channel^.ValueMemory:=@Instance.ChannelValueMemory[i]; Channel^.Index:=i; Instance.ChannelOrder[i]:=i; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if Instance.Header.InitialSpeed=0 then begin Instance.Speed:=6; end else begin Instance.Speed:=Instance.Header.InitialSpeed; end; if Instance.Header.InitialTempo<33 then begin Instance.Tempo:=125; end else begin Instance.Tempo:=Instance.Header.InitialTempo; end; 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} {$ifdef CreamTrackerGUI} Instance.SamplePosition:=0; {$endif} Instance.Row:=0; Instance.Order:=$ff; Instance.Pattern:=$ff; Instance.Tick:=$3fffffff; Instance.AbsoluteTick:=$3fffffff; Instance.FrameDelay:=0; Instance.PatternDelay:=0; Instance.PatternDelayRowCounter:=0; Instance.FirstTick:=false; Instance.FirstRowTick:=false; Instance.VeryFirstTick:=true; Instance.TrackEnd:=false; Instance.PatternLoop:=false; Instance.RepeatCounter:=-1; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instance.RepeatCounters,SizeOf(Instance.RepeatCounters),#0); FillChar(Instance.RepeatRowCounters,SizeOf(Instance.RepeatRowCounters),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instance.LoopRow:=0; Instance.LoopRowCounter:=0; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instance.ChannelDataArray,SizeOf(TCreamTrackerChannelDataArray),#0); CreamTrackerCodeReset(Instance,Instance.CodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=low(TCreamTrackerInstruments) to high(TCreamTrackerInstruments) do begin CreamTrackerCodeReset(Instance,Instance.Instruments[i].CodeData); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; procedure CreamTrackerUpdateTempo(var Instance:TCreamTrackerInstance); begin if Instance.Tempo<32 then begin Instance.Tempo:=32; end; Instance.TickSamples:=(Instance.SampleRate*(5*128)) div (Instance.Tempo shl 8); // (2.5*SampleRate)/Tempo end; procedure CreamTrackerFixUpSample(var Instance:TCreamTrackerInstance;Instrument:PCreamTrackerInstrument); var {$ifdef CanSINC}MixData,{$endif}Src,Dst:PSingleArray; Index,Index2,Index3,First,Last,Len,Channel,Tries:longint; s0,s1,x:single; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if assigned(Instrument^.Data) and (Instrument^.Header.Length>0) then begin if Instrument^.Header.LoopStart<0 then begin Instrument^.Header.LoopStart:=0; end else if Instrument^.Header.LoopStart>=Instrument^.Header.Length then begin Instrument^.Header.LoopStart:=Instrument^.Header.Length-1; end; if Instrument^.Header.LoopEnd<0 then begin Instrument^.Header.LoopEnd:=0; end else if Instrument^.Header.LoopEnd>=Instrument^.Header.Length then begin Instrument^.Header.LoopEnd:=Instrument^.Header.Length; end; if Instrument^.Header.CrossfadeStart=Instrument^.Header.LoopEnd then begin Instrument^.Header.CrossfadeStart:=Instrument^.Header.LoopEnd-1; end; {$ifndef CanSINC} if assigned(Instrument^.MixData) then begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(Instrument^.MixData); end; {$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}({$ifndef CanSINC}Instrument^.{$endif}MixData,(Instrument^.Header.Length+(TotalFixUpSafeAdditionalSampleLength*4))*(2*SizeOf(single))); FillChar({$ifndef CanSINC}Instrument^.{$endif}MixData^,(Instrument^.Header.Length+(TotalFixUpSafeAdditionalSampleLength*4))*(2*SizeOf(single)),#0); Src:=pointer(Instrument^.Data); Dst:=pointer(@PSingleArray(pointer({$ifndef CanSINC}Instrument^.{$endif}MixData))^[TotalFixUpSafeAdditionalSampleLength*4]); Move(Src^,Dst^,Instrument^.Header.Length*(2*SizeOf(single))); Last:=Instrument^.Header.Length-1; for Index:=1 to FixUpSampleLength-1 do begin Dst^[((-Index) shl 1)+0]:=Src^[0]; Dst^[((-Index) shl 1)+1]:=Src^[1]; Dst^[((Last+Index) shl 1)+0]:=Src^[(Last shl 1)+0]; Dst^[((Last+Index) shl 1)+1]:=Src^[(Last shl 1)+1]; end; if (Instrument^.Header.Flags and 1)<>0 then begin if Instrument^.Header.LoopStart>=Instrument^.Header.LoopEnd then begin Instrument^.Header.Flags:=Instrument^.Header.Flags and not 1; end else begin if (Instrument^.Header.CrossfadeType and 3) in [1..2] then begin Len:=Instrument^.Header.LoopEnd-Instrument^.Header.CrossfadeStart; Index2:=Instrument^.Header.LoopStart-Len; for Channel:=0 to 1 do begin for Index:=0 to Len-1 do begin s0:=Src^[((Instrument^.Header.CrossfadeStart+Index) shl 1)+Channel]; Index3:=Index2+Index; Tries:=10; while Tries>0 do begin dec(Tries); if Index3<0 then begin Index3:=-Index3; end else if Index3>Instrument^.Header.LoopEnd then begin Index3:=Instrument^.Header.LoopEnd-(Index3-Instrument^.Header.LoopEnd); end else begin break; end; end; if Index3<0 then begin Index3:=0; end else if Index3>Instrument^.Header.LoopEnd then begin Index3:=Instrument^.Header.LoopEnd; end; s1:=Src^[(Index3 shl 1)+Channel]; x:=Index/Len; case Instrument^.Header.CrossfadeType and 3 of 1:begin Dst^[((Instrument^.Header.CrossfadeStart+Index) shl 1)+Channel]:=(s0*(1.0-x))+(s1*x); end; 2:begin x:=(x*0.5)*pi; Dst^[((Instrument^.Header.CrossfadeStart+Index) shl 1)+Channel]:=(s0*cos(x))+(s1*sin(x)); end; end; end; end; end; First:=Instrument^.Header.LoopStart; Last:=Instrument^.Header.LoopEnd; if (First=0) and (Last<=Instrument^.Header.Length)) then begin for Index:=0 to FixUpSampleLength-1 do begin Index2:=First+Index; if Index2>=Last then begin Index2:=Index2+(First-Last); if Index20 then begin EffectParameterStream:=p; end else begin EffectParameterStream:=nil; end; if RowDeltaStreamSize>0 then begin 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 to RowDeltaStreamSize-1 do begin inc(RowIndex,RowDeltaStream^[RowDeltaStreamIndex]); inc(LastFlags,FlagStream^[FlagStreamIndex]); inc(FlagStreamIndex); case LastFlags of 64:begin // Skip pattern Instance.Orders[RowIndex shr 6]:=$fe; end; 64 or 32:begin // Track end Instance.Orders[RowIndex shr 6]:=$ff; end; 128:begin // Channel end RowIndex:=0; inc(ChannelIndex); if ChannelIndex>=32 then begin break; end; end; else begin PatternIndex:=RowIndex shr 6; Instance.Orders[RowIndex shr 6]:=PatternIndex; Pattern:=@Instance.Patterns[PatternIndex]; PatternNote:=@Pattern^[RowIndex and $3f,ChannelIndex]; if (LastFlags and 1)<>0 then begin inc(LastNote,NoteStream^[NoteStreamIndex]); inc(NoteStreamIndex); PatternNote^.Note:=LastNote; end; if (LastFlags and 2)<>0 then begin inc(LastInstrument,InstrumentStream^[InstrumentStreamIndex]); inc(InstrumentStreamIndex); PatternNote^.Instrument:=LastInstrument; end; if (LastFlags and 4)<>0 then begin inc(LastVolume,VolumeStream^[VolumeStreamIndex]); inc(VolumeStreamIndex); PatternNote^.Volume:=LastVolume; end; if (LastFlags and 8)<>0 then begin inc(LastEffect,EffectStream^[EffectStreamIndex]); inc(EffectStreamIndex); PatternNote^.Effect:=LastEffect; end; if (LastFlags and 16)<>0 then begin inc(LastEffectParameter,EffectParameterStream^[EffectParameterStreamIndex]); inc(EffectParameterStreamIndex); PatternNote^.EffectParameter:=LastEffectParameter; end; end; end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; function CreamTrackerLoad(var Instance:TCreamTrackerInstance;InputData:pointer;InputDataSize:longint):longbool; type psingle=^single; psmallint=^smallint; pword=^word; PDPCM4Table=^TDPCM4Table; TDPCM4Table=array[0..15] of byte; var InputDataPosition,NextInputDataPosition:longint; function Read(var Data;Bytes:longint):longint; begin result:=InputDataSize-InputDataPosition; if result<0 then begin result:=0; end else if result>Bytes then begin result:=Bytes; end; if result>0 then begin Move(PAnsiChar(InputData)[InputDataPosition],Data,result); inc(InputDataPosition,result); end; end; function LoadCode(var CodeData:PCreamTrackerCodeData;Synth:longbool):boolean; begin result:=false; {$ifndef CreamTrackerMinimalPlayer} if assigned(CodeData) then begin CreamTrackerCodeDestroy(Instance,CodeData); end; {$endif} {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(CodeData,SizeOf(TCreamTrackerCodeData)); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(CodeData^,SizeOf(TCreamTrackerCodeData),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CodeData^.Synth:=Synth; if Read(CodeData^.TRIDataSize,SizeOf(longint))<>SizeOf(longint) then begin exit; end; if Read(CodeData^.CodeTextSize,SizeOf(longint))<>SizeOf(longint) then begin exit; end; if CodeData^.TRIDataSize>0 then begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(CodeData^.TRIData,CodeData^.TRIDataSize); if Read(CodeData^.TRIData^,CodeData^.TRIDataSize)<>CodeData^.TRIDataSize then begin exit; end; end; CodeData^.CodeTextAllocated:=CodeData^.CodeTextSize; if CodeData^.CodeTextSize>0 then begin {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(CodeData^.CodeText,CodeData^.CodeTextSize); if Read(CodeData^.CodeText^,CodeData^.CodeTextSize)<>CodeData^.CodeTextSize then begin exit; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerCodeInit(Instance,CodeData); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} result:=true; end; var i,j,k,Row,SignedOffset,Sample:longint; InstrumentPointers:array[1..99] of word; PatternPointers:array[0..255] of word; Instrument:PCreamTrackerInstrument; Pattern:PCreamTrackerPattern; PatternNote:PCreamTrackerPatternNote; PatternLength:word; Buffer:PAnsiChar; b,ChannelNr,p:byte; s:psingle; sb:pbyte; sw:pword; DPCM4Table:PDPCM4Table; ADPCMIMAState:TCreamTrackerADPCMIMAState; //d:shortint; Chunk:TCreamTrackerChunk; BufferNeedToFree,IsStripped:boolean; lb:byte; lw:word; OldCW:word; p1,p2,pd:pointer; begin result:=false; InputDataPosition:=0; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if Read(Instance.Header,SizeOf(TCreamTrackerHeader))<>SizeOf(TCreamTrackerHeader) then begin exit; end; if (((Instance.Header.Signature[0]<>'S') or (Instance.Header.Signature[1]<>'C') or (Instance.Header.Signature[2]<>'R') or (Instance.Header.Signature[3]<>'M')) and ((Instance.Header.Signature[0]<>'C') or (Instance.Header.Signature[1]<>'R') or (not (Instance.Header.Signature[2] in ['E','S'])) or (Instance.Header.Signature[3]<>'M'))) or (Instance.Header.EOFChar<>#$1a) or (Instance.Header.Type_<>$10) or (Instance.Header.OrdNum>256) or (Instance.Header.InsNum>99) or (Instance.Header.PatNum>256) then begin exit; end; Instance.CreamTrackerModule:=(Instance.Header.Signature[0]='C') and (Instance.Header.Signature[1]='R') and (Instance.Header.Signature[2] in ['E','S']) and (Instance.Header.Signature[3]='M'); IsStripped:=Instance.CreamTrackerModule and (Instance.Header.Signature[2]='S'); if Instance.CreamTrackerModule then begin Instance.RowHilightMinor:=Instance.Header.RowHilightMinor; Instance.RowHilightMajor:=Instance.Header.RowHilightMajor; end; i:=Instance.Header.OrdNum; if i=0 then begin i:=1; end; FillChar(Instance.Orders,SizeOf(TCreamTrackerOrders),#$ff); if IsStripped then begin Instance.Orders[0]:=0; end else begin if Read(Instance.Orders,i)<>i then begin exit; end; if (Instance.Header.OrdNum and 1)<>0 then begin if (InputDataPosition(Instance.Header.InsNum*SizeOf(word)) then begin exit; end; if IsStripped then begin if Read(PatternPointers,SizeOf(word))<>SizeOf(word) then begin exit; end; end else begin if Read(PatternPointers,Instance.Header.PatNum*SizeOf(word))<>(Instance.Header.PatNum*SizeOf(word)) then begin exit; end; end; FillChar(Instance.ChannelPannings,SizeOf(TCreamTrackerChannelPannings),#0); if Instance.Header.Panning=$fc then begin if Read(Instance.ChannelPannings,SizeOf(TCreamTrackerChannelPannings))<>SizeOf(TCreamTrackerChannelPannings) then begin exit; end; if not Instance.CreamTrackerModule then begin for i:=0 to 31 do begin Instance.ChannelPannings[i]:=Instance.ChannelPannings[i] and not ($40 or $80); end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=1 to Instance.Header.InsNum do begin if InstrumentPointers[i]<>0 then begin Instrument:=@Instance.Instruments[i]; InputDataPosition:=InstrumentPointers[i] shl 4; if Read(Instrument^.Header,SizeOf(TCreamTrackerInstrumentHeader))<>SizeOf(TCreamTrackerInstrumentHeader) then begin exit; end; if ((Instrument^.Header.Signature[0]='C') and (Instrument^.Header.Signature[1]='R') and (Instrument^.Header.Signature[2]='S') and (Instrument^.Header.Signature[3]='I')) and (Instrument^.Header.InstrumentType=8) then begin InputDataPosition:=Instrument^.Header.ExtOffset; if not LoadCode(Instrument^.CodeData,true) then begin exit; end; end else if ((Instrument^.Header.Signature[0]='C') and (Instrument^.Header.Signature[1]='R') and (Instrument^.Header.Signature[2]='S') and (Instrument^.Header.Signature[3]='S')) and (Instrument^.Header.InstrumentType=9) then begin InputDataPosition:=Instrument^.Header.ExtOffset; if not LoadCode(Instrument^.CodeData,false) then begin exit; end; {$ifdef UseAulan} p1:=addr(Instrument^.CodeData.SampleProcProcess); if assigned(p1) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} pd:=@PAnsiChar(Instrument^.CodeData^.InstanceData)[(Instrument^.CodeData^.Instances-1)*Instrument^.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=nil; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; p2:=nil; 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 end; Instrument^.Header.Length:=k; if Instrument^.Header.Length>0 then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,Instrument^.Header.Length*(4*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,Instrument^.Header.Length*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} p2:=Instrument^.Data; 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 end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end else begin Instrument^.Header.Length:=1; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,1*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,1*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; end; {$endif} if assigned(Instrument^.Data) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} CreamTrackerFixUpSample(Instance,Instrument); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; end else if ((Instrument^.Header.Signature[0]='S') and (Instrument^.Header.Signature[1]='C') and (Instrument^.Header.Signature[2]='R') and (Instrument^.Header.Signature[3]='S')) and (Instrument^.Header.InstrumentType=1) then begin if ((Instrument^.Header.Flags and 1)<>0) and ((Instrument^.Header.LoopStart>=Instrument^.Header.Length) or (Instrument^.Header.LoopEnd<=Instrument^.Header.LoopStart)) then begin Instrument^.Header.Flags:=Instrument^.Header.Flags and not 1; end; if Instrument^.Header.Length>0 then begin InputDataPosition:=((Instrument^.Header.Offset[0] shl 16) or (Instrument^.Header.Offset[2] shl 8) or Instrument^.Header.Offset[1]) shl 4; SignedOffset:=-((ord(Instance.Header.FileFormatInformation=2) and 1) shl (7+(((Instrument^.Header.Flags shr 2) and 1) shl 3))); case Instrument^.Header.Format of 0:begin // Uncompressed non-delta PCM k:=Instrument^.Header.Length; if (Instrument^.Header.Flags and 2)<>0 then begin inc(k,k); end; if (Instrument^.Header.Flags and 4)<>0 then begin inc(k,k); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,Instrument^.Header.Length*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,Instrument^.Header.Length*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if (InputDataSize-InputDataPosition)=(SizeOf(DPCM4Table)+((Instrument^.Header.Length+1) shr 1)) then begin k:=(Instrument^.Header.Length+1) shr 1; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,(k shl 1)*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,(k shl 1)*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} DPCM4Table:=pointer(@PAnsiChar(InputData)[InputDataPosition]); Buffer:=pointer(@PAnsiChar(InputData)[InputDataPosition+SizeOf(DPCM4Table)]); b:=0; s:=Instrument^.Data; for j:=0 to k-1 do begin inc(b,DPCM4Table^[byte(Buffer[j]) and $f]); s^:=shortint(byte(byte(b)+byte(SignedOffset)))/128.0; inc(s); inc(b,DPCM4Table^[byte(Buffer[j]) shr 4]); s^:=shortint(byte(byte(b)+byte(SignedOffset)))/128.0; inc(s); end; end; end; end; 5:begin // IMA ADPCM4 (the TRUE adaptive variant) if Instance.CreamTrackerModule then begin if (InputDataSize-InputDataPosition)>=((((Instrument^.Header.Length+1) shr 1)+2) shl ((Instrument^.Header.Flags shr 1) and 1)) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,(Instrument^.Header.Length or 1)*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,(Instrument^.Header.Length or 1)*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Buffer:=pointer(@PAnsiChar(InputData)[InputDataPosition]); for j:=0 to (Instrument^.Header.Flags shr 1) and 1 do begin ADPCMIMAState.PrevSample:=0; ADPCMIMAState.StepIndex:=0; b:=0; s:=Instrument^.Data; inc(s,j); for k:=0 to Instrument^.Header.Length-1 do begin if k=0 then begin ADPCMIMAState.PrevSample:=smallint(pointer(Buffer)^); inc(Buffer,SizeOf(smallint)); ADPCMIMAState.StepIndex:=0; end; if (k and 1)=0 then begin b:=byte(pointer(Buffer)^); inc(Buffer,SizeOf(byte)); Sample:=CreamTrackerADPCMIMADecompressSample(ADPCMIMAState,b and $f); end else begin Sample:=CreamTrackerADPCMIMADecompressSample(ADPCMIMAState,b shr 4); end; if (Instrument^.Header.Flags and 2)=0 then begin s^:=Sample/32768.0; inc(s); s^:=Sample/32768.0; inc(s); end else begin s^:=Sample/32768.0; inc(s,2); end; end; end; end; end; end; 6:begin if Instance.CreamTrackerModule then begin // Sinusoidal frequency envelope dissected sample for to resynthesizing it (very good usable for vocals!) k:=InputDataPosition; if Read(j,SizeOf(longint))=SizeOf(longint) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,Instrument^.Header.Length*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,Instrument^.Header.Length*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} WRSSynthese(pointer(@PAnsiChar(InputData)[InputDataPosition]),j,Instrument^.Data,2*SizeOf(single),Instrument^.Header.Length); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} inc(InputDataPosition,j); if ((Instrument^.Header.Flags and 2)<>0) and (Read(j,SizeOf(longint))=SizeOf(longint)) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} WRSSynthese(pointer(@PAnsiChar(InputData)[InputDataPosition]),j,@PSingleArray(Instrument^.Data)^[1],2*SizeOf(single),Instrument^.Header.Length); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} inc(InputDataPosition,j); end else begin s:=Instrument^.Data; for j:=0 to Instrument^.Header.Length-1 do begin PSingleArray(s)^[1]:=PSingleArray(s)^[0]; inc(s,2); end; end; Instrument^.RawLen:=InputDataPosition-k; if Instrument^.RawLen>0 then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.RawData,Instrument^.RawLen); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Move(PAnsiChar(InputData)[k],Instrument^.RawData^,Instrument^.RawLen); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; end; end; end; 7:begin // Uncompressed delta PCM k:=Instrument^.Header.Length; if (Instrument^.Header.Flags and 2)<>0 then begin inc(k,k); end; if (Instrument^.Header.Flags and 4)<>0 then begin inc(k,k); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(Instrument^.Data,Instrument^.Header.Length*(2*SizeOf(single))); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FillChar(Instrument^.Data^,Instrument^.Header.Length*(2*SizeOf(single)),#0); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if (InputDataSize-InputDataPosition)0 then begin InputDataPosition:=PatternPointers[0] shl 4; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if not CreamTrackerLoadPatternsAsSingleChain(Instance,pointer(@PAnsiChar(InputData)[InputDataPosition]),InputDataSize-InputDataPosition) then begin exit; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; end else begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} for i:=0 to Instance.Header.PatNum-1 do begin Pattern:=@Instance.Patterns[i]; if PatternPointers[i]<>0 then begin InputDataPosition:=PatternPointers[i] shl 4; if Read(PatternLength,SizeOf(word))<>SizeOf(word) then begin exit; end; if (InputDataSize-InputDataPosition)0 then begin ChannelNr:=b and $1f; if ChannelNr<32 then begin PatternNote:=@Pattern^[Row,ChannelNr]; if (b and $20)<>0 then begin if j>=PatternLength then begin break; end; p:=byte(Buffer[j]); inc(j); if p<$f0 then begin PatternNote^.Note:=((p shr 4)*12)+(p and $f); end else if p=$fe then begin PatternNote^.Note:=$fe; end else if (p=$fd) and Instance.CreamTrackerModule then begin PatternNote^.Note:=$fd; end else begin PatternNote^.Note:=$ff; end; if j>=PatternLength then begin break; end; p:=byte(Buffer[j]); inc(j); PatternNote^.Instrument:=p; end; if (b and $40)<>0 then begin if j>=PatternLength then begin break; end; p:=byte(Buffer[j]); inc(j); if p<=64 then begin PatternNote^.Volume:=p; end; end; if (b and $80)<>0 then begin if j>=PatternLength then begin break; end; p:=byte(Buffer[j]); inc(j); PatternNote^.Effect:=p; if j>=PatternLength then begin break; end; p:=byte(Buffer[j]); inc(j); PatternNote^.EffectParameter:=p; end; end else begin if (b and $20)<>0 then begin if j>=PatternLength then begin break; end; inc(j); if j>=PatternLength then begin break; end; inc(j); end; if (b and $40)<>0 then begin if j>=PatternLength then begin break; end; inc(j); end; if (b and $80)<>0 then begin if j>=PatternLength then begin break; end; inc(j); if j>=PatternLength then begin break; end; inc(j); end; end; end else begin inc(Row); if Row>=64 then begin break; end; end; end; end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; if Instance.CreamTrackerModule and (Instance.Header.Data<>0) then begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} InputDataPosition:=Instance.Header.Data; while (InputDataPosition+SizeOf(TCreamTrackerChunk))=(Instance.Speed+Instance.FrameDelay) then begin Instance.Tick:=0; dec(Instance.PatternDelayRowCounter); if Instance.PatternDelayRowCounter<=0 then begin Instance.AbsoluteTick:=0; Instance.PatternDelayRowCounter:=0; Instance.FrameDelay:=0; Instance.PatternDelay:=0; Instance.FirstRowTick:=true; end; Instance.FirstTick:=true; end; if Instance.FirstRowTick then begin if Instance.Playing then begin {$ifndef CreamTrackerCompact} if Instance.ForceOrder>=0 then begin Instance.Order:=not Instance.ForceOrder; Instance.NextOrder:=Instance.ForceOrder; Instance.ForceOrder:=-1; end; if Instance.ForceRow>=0 then begin Instance.NextRow:=Instance.ForceRow; Instance.ForceRow:=-1; end; {$endif} Instance.Row:=Instance.NextRow; while Instance.Order<>Instance.NextOrder do begin Instance.LoopRow:=0; Instance.LoopRowCounter:=0; Instance.Order:=Instance.NextOrder; Instance.Pattern:=$ff; while Instance.Order0)); inc(Instance.RepeatCounters[Instance.Order]); end; if (Instance.Pattern=$ff) and not Instance.PatternLoop then begin if Instance.RepeatCounter=0 then begin Instance.TrackEnd:=true; end else begin if Instance.RepeatCounter>0 then begin dec(Instance.RepeatCounter); end; if Instance.Header.OrdNum>0 then begin Instance.Order:=$ff; Instance.NextOrder:=0; continue; end; end; end; break; end; Instance.Row:=Instance.Row and 63; Instance.NextRow:=(Instance.Row+1) and 63; if (Instance.NextRow=0) and not Instance.PatternLoop then begin Instance.NextOrder:=Instance.Order+1; end; if Instance.RepeatCounter>=0 then begin inc(Instance.RepeatRowCounters[Instance.Pattern,Instance.Row]); if Instance.RepeatRowCounters[Instance.Pattern,Instance.Row]>=(Instance.Header.OrdNum*64) then begin Instance.TrackEnd:=true; end; end; end; end; end; procedure CreamTrackerChannelVibratoCheck(Channel:PCreamTrackerChannel); begin if ((Channel^.VibratoWaveForm and 4)=0) and ((Channel^.VibratoPosition and $40)<>0) then begin Channel^.VibratoPosition:=0; end else begin Channel^.VibratoPosition:=Channel^.VibratoPosition and $3f; end; end; procedure CreamTrackerChannelVibratoUpdate(var Instance:TCreamTrackerInstance;Channel:PCreamTrackerChannel;Increment:byte); begin if (Channel^.VibratoWaveForm and 3)=3 then begin Channel^.VibratoPosition:=(Channel^.VibratoPosition+((Instance.PatternRandom shr 1) and $f)) and $3f; end; Channel^.VibratoPosition:=(Channel^.VibratoPosition+Increment) and $3f; end; procedure CreamTrackerProcessChannels(var Instance:TCreamTrackerInstance); const RetrigTables:array[0..1,0..16-1] of shortint=((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)); CreamTrackerEffectOffset=64; var RequestedChannelIndex,ChannelIndex,Mangitude,Fine,Speed,Depth,Value,Counter, Index,SetPanning,OtherValue,pi0,pi1,pi2,pi3,pi4,pi5,pi6,pi7,pi8:longint; Pattern:PCreamTrackerPattern; PatternNote:PCreamTrackerPatternNote; Channel,FadeOutChannel:PCreamTrackerChannel; EffectParameter,GlobalVolumeSlide,ChannelVolumeSlide,VolumeSlide,TempoSlide,Tremolo, Retrig,Arpeggio:byte; PitchSlide,Vibrato,Portamento,SampleOffset:longword; Trigger,NoteChange,InstrumentChange,SynthReset,NoClickRemovalFadeOut:boolean; Volume,Pan,VibratoSkew,Temp,PeriodMod,PeriodFactor,PeriodInvFactor, VolumeMod,VolumeFactor:double; pp,pd:pointer; function GetLastEffectParameter:byte; begin if EffectParameter=0 then begin PatternNote^.EffectParameter:=Channel^.LastEffectParameter; EffectParameter:=Channel^.LastEffectParameter; end; result:=EffectParameter; end; begin if Instance.Playing and (Instance.Pattern in [low(TCreamTrackerPatterns)..high(TCreamTrackerPatterns)]) then begin Pattern:=@Instance.Patterns[Instance.Pattern]; end else begin Pattern:=nil; end; if Instance.FirstRowTick then begin for ChannelIndex:=0 to 63 do begin Channel:=@Instance.Channels[ChannelIndex]; Channel^.NoteOnTick:=0; Channel^.NoteCutTick:=$7fffffff; Channel^.TremorCounter:=Channel^.TremorCounter and $7f; end; end; for RequestedChannelIndex:=0 to 63 do begin ChannelIndex:=Instance.ChannelOrder[RequestedChannelIndex]; Channel:=@Instance.Channels[ChannelIndex]; PatternNote:=@Channel^.PatternNote; if ChannelIndex in [0..31] then begin Channel^.Enabled:=(Instance.Header.ChannelSettings[ChannelIndex] and $80)=0; Channel^.Muted:=(Instance.Header.Panning=$fc) and ((Instance.ChannelPannings[ChannelIndex] and $40)<>0); end; if Instance.FirstRowTick then begin PatternNote^.Note:=255; PatternNote^.Instrument:=0; PatternNote^.Volume:=255; PatternNote^.Effect:=0; PatternNote^.EffectParameter:=0; end; PeriodMod:=0.0; PeriodFactor:=1.0; PeriodInvFactor:=1.0; VolumeMod:=0.0; VolumeFactor:=1.0; GlobalVolumeSlide:=0; ChannelVolumeSlide:=0; VolumeSlide:=0; PitchSlide:=0; TempoSlide:=0; Vibrato:=0; Tremolo:=0; Retrig:=0; Portamento:=0; Arpeggio:=0; SetPanning:=-1; SampleOffset:=0; SynthReset:=false; NoClickRemovalFadeOut:=false; if NoClickRemovalFadeOut then begin end; if Channel^.Enabled then begin Instance.CurrentChannel:=Channel; if not assigned(Channel^.Master) then begin if Instance.FirstRowTick then begin if assigned(Pattern) and (ChannelIndex in [0..31]) then begin PatternNote^:=Pattern^[Instance.Row,ChannelIndex]; end; {$ifdef CreamTrackerGUI} if Channel^.HasInjectPatternNote then begin Channel^.PatternNote:=Channel^.InjectPatternNote; Channel^.HasInjectPatternNote:=false; end; {$endif} if PatternNote^.Effect<>(ord('Q')-CreamTrackerEffectOffset) then begin Channel^.RetrigCounter:=0; end; if not (PatternNote^.Effect in [ord('H')-CreamTrackerEffectOffset,ord('U')-CreamTrackerEffectOffset,ord('K')-CreamTrackerEffectOffset,ord('R')-CreamTrackerEffectOffset]) then begin Channel^.VibratoPosition:=Channel^.VibratoPosition or $40; end; if PatternNote^.EffectParameter<>0 then begin Channel^.LastEffectParameter:=PatternNote^.EffectParameter; end; end; EffectParameter:=PatternNote^.EffectParameter; case PatternNote^.Effect of ord('A')-CreamTrackerEffectOffset:begin // Set speed. If the parameter is 0, the effect is ignored. if Instance.FirstRowTick and (EffectParameter<>0) then begin Instance.Speed:=EffectParameter; end; end; ord('B')-CreamTrackerEffectOffset:begin // Order jump if Instance.FirstRowTick then begin if not Instance.PatternLoop then begin Instance.NextOrder:=EffectParameter; end; Instance.NextRow:=0; end; end; ord('C')-CreamTrackerEffectOffset:begin // 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 and (((EffectParameter shr 4)<10) and ((EffectParameter and $f)<10)) then begin Value:=(((EffectParameter shr 4)*10)+(EffectParameter and $f)); if Value in [0..63] then begin if not Instance.PatternLoop then begin Instance.NextOrder:=Instance.Order+1; end; Instance.NextRow:=Value; end; end; end; ord('D')-CreamTrackerEffectOffset:begin // Volume slide VolumeSlide:=GetLastEffectParameter; end; ord('E')-CreamTrackerEffectOffset:begin // Slide down if Channel^.Active then begin PitchSlide:=GetLastEffectParameter; end; end; ord('F')-CreamTrackerEffectOffset:begin // Slide up if Channel^.Active then begin PitchSlide:=GetLastEffectParameter or $100; end; end; ord('G')-CreamTrackerEffectOffset:begin // Slide to note if EffectParameter<>0 then begin Channel^.LastPortamento:=EffectParameter; end; Portamento:=Channel^.LastPortamento; end; ord('H')-CreamTrackerEffectOffset:begin // Vibrato. This effect shares memory with Uxy. if (PatternNote^.EffectParameter and $0f)<>0 then begin Channel^.LastVibrato:=(Channel^.LastVibrato and $f0) or (PatternNote^.EffectParameter and $0f); end; if (PatternNote^.EffectParameter and $f0)<>0 then begin Channel^.LastVibrato:=(Channel^.LastVibrato and $0f) or (PatternNote^.EffectParameter and $f0); end; Vibrato:=Channel^.LastVibrato; end; ord('I')-CreamTrackerEffectOffset:begin // Tremor GetLastEffectParameter; if EffectParameter<>0 then begin Channel^.TremorParameter:=EffectParameter; end; Channel^.TremorCounter:=Channel^.TremorCounter or $80; end; ord('J')-CreamTrackerEffectOffset:begin // Arpeggio Arpeggio:=GetLastEffectParameter; end; ord('K')-CreamTrackerEffectOffset:begin // 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 not Instance.FirstRowTick then begin VolumeSlide:=GetLastEffectParameter; end; end; ord('L')-CreamTrackerEffectOffset:begin // G00 + Dxy // The volume slide differs from Dxy. See Kxy for details. Portamento:=Channel^.LastPortamento or $100; if not Instance.FirstRowTick then begin VolumeSlide:=GetLastEffectParameter; end; end; ord('M')-CreamTrackerEffectOffset:begin // Channel volume if Instance.FirstRowTick and (PatternNote^.EffectParameter in [$00..$40]) then begin Channel^.ChannelVolume:=EffectParameter; end; end; ord('N')-CreamTrackerEffectOffset:begin // Channel volume slide ChannelVolumeSlide:=GetLastEffectParameter; end; ord('O')-CreamTrackerEffectOffset:begin // Set sample offset SampleOffset:=Channel^.SampleHighOffset or (PatternNote^.EffectParameter shl 8); end; ord('P')-CreamTrackerEffectOffset:begin end; ord('Q')-CreamTrackerEffectOffset:begin // Retrigger note every y ticks with volume modifier x. If the retrig value 'y' // is 0, the effect is ignored. Retrig:=GetLastEffectParameter; end; ord('R')-CreamTrackerEffectOffset:begin // Tremolo. x*4 is speed, y*4 is depth. GetLastEffectParameter; if (EffectParameter and $f0)=0 then begin EffectParameter:=(EffectParameter and $0f) or (Channel^.LastEffectParameter and $f0); end; Channel^.LastEffectParameter:=EffectParameter; Channel^.PatternNote.EffectParameter:=EffectParameter; Tremolo:=EffectParameter; end; ord('S')-CreamTrackerEffectOffset:begin GetLastEffectParameter; case EffectParameter and $f0 of $10:begin if Instance.FirstRowTick then begin Channel^.Glissando:=(EffectParameter and $0f)<>0; end; end; $20:begin if Instance.FirstRowTick and (Channel^.C4SpeedFactor<>0.0) then begin Channel^.SlideToPeriod:=Channel^.SlideToPeriod/Channel^.C4SpeedFactor; Channel^.C4SpeedFactor:=longint(8363*16*1712)/CreamTrackerFineTuneTable[EffectParameter and $0f]; Channel^.SlideToPeriod:=Channel^.SlideToPeriod*Channel^.C4SpeedFactor; end; end; $30:begin if Instance.FirstRowTick then begin Channel^.VibratoWaveForm:=EffectParameter and $0f; end; end; $40:begin if Instance.FirstRowTick then begin Channel^.TremoloWaveForm:=EffectParameter and $0f; end; end; $60:begin if Instance.FirstRowTick then begin Instance.FrameDelay:=EffectParameter and $0f; end; end; $80:begin if Instance.FirstRowTick then begin SetPanning:=(((EffectParameter and $0f)*$100)+8) shr 4; end; end; $a0:begin if Instance.FirstRowTick then begin Channel^.SampleHighOffset:=(EffectParameter and $f) shl 16; end; end; $b0:begin if Instance.FirstRowTick then begin if (EffectParameter and $0f)=0 then begin Instance.LoopRow:=Instance.Row; end else begin if Instance.LoopRowCounter=0 then begin Instance.LoopRowCounter:=EffectParameter and $0f; Instance.NextRow:=Instance.LoopRow; end else begin dec(Instance.LoopRowCounter); if Instance.LoopRowCounter>0 then begin Instance.NextRow:=Instance.LoopRow; end; end; end; end; end; $c0:begin if Instance.FirstRowTick then begin Channel^.NoteCutTick:=EffectParameter and $0f; end; end; $d0:begin if Instance.FirstRowTick then begin Channel^.NoteOnTick:=EffectParameter and $0f; end; end; $e0:begin if Instance.FirstRowTick then begin if (Instance.PatternDelay=0) and ((EffectParameter and $0f)<>0) then begin Instance.PatternDelay:=(EffectParameter and $0f)+1; Instance.PatternDelayRowCounter:=Instance.PatternDelay; end; end; end; end; end; ord('T')-CreamTrackerEffectOffset:begin if Instance.FirstRowTick then begin if EffectParameter in [33..255] then begin Instance.Tempo:=EffectParameter; CreamTrackerUpdateTempo(Instance); end else begin GetLastEffectParameter; if (EffectParameter and $f0)=0 then begin EffectParameter:=(EffectParameter and $0f) or (Channel^.LastEffectParameter and $f0); end; if (EffectParameter and $0f)=0 then begin EffectParameter:=(EffectParameter and $f0) or (Channel^.LastEffectParameter and $0f); end; Channel^.LastEffectParameter:=EffectParameter; Channel^.PatternNote.EffectParameter:=EffectParameter; TempoSlide:=EffectParameter; end; end; end; ord('U')-CreamTrackerEffectOffset:begin if (PatternNote^.EffectParameter and $0f)<>0 then begin Channel^.LastVibrato:=(Channel^.LastVibrato and $f0) or (PatternNote^.EffectParameter and $0f); end; if (PatternNote^.EffectParameter and $f0)<>0 then begin Channel^.LastVibrato:=(Channel^.LastVibrato and $0f) or (PatternNote^.EffectParameter and $f0); end; Vibrato:=Channel^.LastVibrato or $100; end; ord('V')-CreamTrackerEffectOffset:begin if Instance.FirstRowTick and (PatternNote^.EffectParameter in [$00..$80]) then begin Instance.GlobalVolume:=PatternNote^.EffectParameter; end; end; ord('W')-CreamTrackerEffectOffset:begin GlobalVolumeSlide:=GetLastEffectParameter; end; ord('X')-CreamTrackerEffectOffset:begin if Instance.FirstRowTick and (PatternNote^.EffectParameter in [$00..$80]) then begin SetPanning:=PatternNote^.EffectParameter shl 1; end; end; ord('Y')-CreamTrackerEffectOffset:begin end; ord('Z')-CreamTrackerEffectOffset:begin end; $80..$e4:begin if Instance.FirstRowTick then begin Value:=PatternNote^.Effect and $7f; if Value in [0..63] then begin Instance.ValueMemory[Value]:=PatternNote^.EffectParameter; end else if (Value in [64..127]) and (ChannelIndex in [0..31]) then begin Instance.ChannelValueMemory[ChannelIndex,Value]:=PatternNote^.EffectParameter; end; end; end; end; {$ifdef UseAulan} if Instance.CreamTrackerModule and assigned(Instance.CodeData) and assigned(addr(Instance.CodeData^.GlobalProcProcessPatternNote)) then begin pp:=addr(Instance.CodeData^.GlobalProcProcessPatternNote); pd:=@PAnsiChar(Instance.CodeData^.InstanceData)[(Instance.CodeData^.Instances-1)*Instance.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=Channel; pd:=@PAnsiChar(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; 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 end; end; {$endif} if Instance.AbsoluteTick=Channel^.NoteOnTick then begin if PatternNote^.Note=$fe then begin Channel^.NoteCutTick:=Instance.AbsoluteTick; end else if PatternNote^.Note=$fd then begin {$ifdef UseAulan} if assigned(Channel^.Instrument) and (Channel^.Instrument^.Header.InstrumentType=8) and assigned(Channel^.Instrument^.CodeData) and assigned(addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOff)) and assigned(Channel^.Instrument^.CodeData^.InstanceData) then begin pp:=addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOff); pd:=@PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[ChannelIndex*Channel^.Instrument^.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=Channel; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; asm pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad end; end; {$endif} end else if (PatternNote^.Note<194) or (PatternNote^.Instrument<>0) then begin // 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 and (assigned(Channel^.Instrument) and (Channel^.Instrument^.Header.InstrumentType=8) and ((Channel^.Instrument^.Header.Flags and 128)<>0)); if (Portamento=0) and not NoClickRemovalFadeOut then begin {$ifdef UseAulan} if assigned(Channel^.Instrument) and (Channel^.Instrument^.Header.InstrumentType=8) and assigned(Channel^.Instrument^.CodeData) and assigned(addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOff)) and assigned(Channel^.Instrument^.CodeData^.InstanceData) then begin pp:=addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOff); pd:=@PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[ChannelIndex*Channel^.Instrument^.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=Channel; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; asm pushad mov esi,dword ptr pd mov eax,dword ptr pp mov ebp,esp call eax mov esp,ebp popad end; end; {$endif} if assigned(Channel^.Instrument) and (Channel^.Instrument^.Header.InstrumentType=8) and assigned(Channel^.Instrument^.CodeData) and assigned(Channel^.Instrument^.CodeData^.InstanceData) then begin Move(PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[ChannelIndex*Channel^.Instrument^.CodeData^.InstanceWorkDataSize], PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[longint(ChannelIndex or 32)*Channel^.Instrument^.CodeData^.InstanceWorkDataSize], Channel^.Instrument^.CodeData^.InstanceWorkDataSize); end; Instance.ChannelValueMemory[ChannelIndex or 32]:=Instance.ChannelValueMemory[ChannelIndex]; FadeOutChannel:=@Instance.Channels[ChannelIndex or 32]; FadeOutChannel^:=Channel^; FadeOutChannel^.Master:=Channel; FadeOutChannel^.ValueMemory:=@Instance.ChannelValueMemory[ChannelIndex or 32]; FadeOutChannel^.Index:=ChannelIndex or 32; FadeOutChannel^.NoteOnTick:=$7fffffff; FadeOutChannel^.NoteCutTick:=Instance.AbsoluteTick; FadeOutChannel^.TremorCounter:=FadeOutChannel^.TremorCounter and $7f; FadeOutChannel^.PatternNote.Effect:=0; FadeOutChannel^.PatternNote.EffectParameter:=0; FadeOutChannel^.LastEffectParameter:=0; Channel^.LastLeft:=0; Channel^.LastRight:=0; end; NoteChange:=PatternNote^.Note<194; InstrumentChange:=false; if NoteChange then begin Channel^.BaseNote:=PatternNote^.Note; InstrumentChange:=true; end; if PatternNote^.Instrument<>0 then begin InstrumentChange:=InstrumentChange or (Channel^.LastInstrument<>PatternNote^.Instrument); Channel^.LastInstrument:=PatternNote^.Instrument; Channel^.Instrument:=@Instance.Instruments[Channel^.LastInstrument]; Channel^.Volume:=Channel^.Instrument^.Header.Volume; Channel^.Velocity:=64; end; if assigned(Channel^.Instrument) then begin if NoteChange then begin if Channel^.Instrument^.Header.C4Speed>0 then begin Channel^.C4SpeedFactor:=longint(8363*16*1712)/Channel^.Instrument^.Header.C4Speed; Channel^.NoteFrequencyFactor:=longint(8363*1712)*(261.6255653005986/Channel^.Instrument^.Header.C4Speed); end else begin Channel^.C4SpeedFactor:=longint(8363*16*1712)/8363; Channel^.NoteFrequencyFactor:=1712*261.6255653005986; end; Channel^.SlideToPeriod:=Instance.NoteToPeriodTable[Channel^.BaseNote]*Channel^.C4SpeedFactor; if (Instance.Header.Flags and 16)<>0 then begin // Amiga limits if Channel^.SlideToPeriod<(907 div 2) then begin Channel^.SlideToPeriod:=907 div 2; end else if Channel^.SlideToPeriod>(1712*2) then begin Channel^.SlideToPeriod:=1712*2; end; end; end; if Portamento=0 then begin if NoClickRemovalFadeOut then begin Channel^.LastLeft:=0; Channel^.LastRight:=0; end; SynthReset:=true; Channel^.FastRamping:=false; Channel^.LastLeftClickRemovalFadeOut:=Channel^.LastLeftClickRemovalFadeOut+Channel^.LastLeft; Channel^.LastRightClickRemovalFadeOut:=Channel^.LastRightClickRemovalFadeOut+Channel^.LastRight; Channel^.LastLeft:=0; Channel^.LastRight:=0; Channel^.NoteCutCounter:=0; Channel^.NewNote:=true; if InstrumentChange or not Channel^.Active then begin Channel^.SamplePosition.Hi:=SampleOffset; Channel^.SamplePosition.Lo:=0; end; Channel^.Active:=Channel^.Active or NoteChange; if NoteChange then begin Channel^.StablePeriod:=Channel^.SlideToPeriod; end; end; end else begin Channel^.Active:=false; end; end; if PatternNote^.Volume<>255 then begin if assigned(Channel^.Instrument) and (Channel^.Instrument^.Header.InstrumentType=8) and ((Channel^.Instrument^.Header.Flags and 1)<>0) then begin Channel^.Velocity:=PatternNote^.Volume; end else begin Channel^.Volume:=PatternNote^.Volume; end; end; end; end; if Retrig<>0 then begin // 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 and $0f; if Speed=0 then begin Speed:=1; end; if (Channel^.RetrigCounter<>0) and ((Channel^.RetrigCounter mod Speed)=0) then begin SynthReset:=true; Channel^.SamplePosition.Value:=0; if assigned(Channel^.Instrument) and ((Channel^.PatternNote.Note<$f0) or (Channel^.PatternNote.Instrument<>0)) then begin Channel^.Active:=true; end; Depth:=(Retrig and $f0) shr 4; if (Depth and 7)<>0 then begin if RetrigTables[0,Depth]<>0 then begin Channel^.Volume:=Channel^.Volume*(RetrigTables[0,Depth]/16.0); end else begin Channel^.Volume:=Channel^.Volume+RetrigTables[1,Depth]; end; if Channel^.Volume<0 then begin Channel^.Volume:=0; end else if Channel^.Volume>64 then begin Channel^.Volume:=64; end; end; end; inc(Channel^.RetrigCounter); end; if SynthReset then begin if assigned(Channel^.Instrument) and assigned(Channel^.Instrument^.CodeData) and assigned(addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOn)) and assigned(Channel^.Instrument^.CodeData^.InstanceData) then begin {$ifdef UseAulan} pp:=addr(Channel^.Instrument^.CodeData^.SynthProcInstanceNoteOn); pd:=@PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[ChannelIndex*Channel^.Instrument^.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=Channel; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; Value:=Channel^.BaseNote; OtherValue:=Channel^.Velocity; 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 end; {$endif} end; end; if Channel^.NoteCutCounter>0 then begin VolumeFactor:=0.0; dec(Channel^.NoteCutCounter); if Channel^.NoteCutCounter=0 then begin Channel^.Active:=false; Channel^.BaseNote:=$ff; Channel^.LastLeftClickRemovalFadeOut:=Channel^.LastLeftClickRemovalFadeOut+Channel^.LastLeft; Channel^.LastRightClickRemovalFadeOut:=Channel^.LastRightClickRemovalFadeOut+Channel^.LastRight; Channel^.LastLeft:=0; Channel^.LastRight:=0; end; end; if Instance.AbsoluteTick=Channel^.NoteCutTick then begin Channel^.NoteCutCounter:=1; Channel^.Volume:=0.0; VolumeFactor:=0.0; Channel^.FastRamping:=true; end; if SetPanning>=0 then begin Channel^.Panning:=SetPanning; if Channel^.Panning<0 then begin Channel^.Panning:=0; end else if Channel^.Panning>256 then begin Channel^.Panning:=256; end; end; if Arpeggio<>0 then begin PeriodInvFactor:=PeriodInvFactor*Instance.NoteFactorTable[(Arpeggio shr (Channel^.ArpeggioPos shl 2)) and $f]; inc(Channel^.ArpeggioPos); if Channel^.ArpeggioPos>2 then begin Channel^.ArpeggioPos:=0; end; end; if VolumeSlide<>0 then begin // 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<=$1300) or ((Instance.Header.Flags and $40)<>0)) or not Instance.FirstRowTick; if ((VolumeSlide and $f0) in [$00..$e0]) and ((VolumeSlide and $0f) in [$01..$0e]) then begin // 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 then begin Channel^.Volume:=Channel^.Volume-(VolumeSlide and $f); end; end else if ((VolumeSlide and $0f)=0) and ((VolumeSlide and $f0) in [$10..$e0]) then begin // Dx0, 1 <= x <= 0xE: slide up by x on all nonzero ticks. Also slide on tick 0, if fast slides are enabled. if Trigger then begin Channel^.Volume:=Channel^.Volume+((VolumeSlide shr 4) and $f); end; end else if ((VolumeSlide and $f0)=$f0) and ((VolumeSlide and $0f) in [$01..$0e]) then begin // DFx, 1 <= x <= 0xE: slide down by x on tick 0. if Instance.FirstRowTick then begin Channel^.Volume:=Channel^.Volume-(VolumeSlide and $f); end; end else if ((VolumeSlide and $0f)=$0f) and ((VolumeSlide and $f0) in [$10..$e0]) then begin // DxF, 1 <= x <= 0xE: slide up by x on tick 0. if Instance.FirstRowTick then begin Channel^.Volume:=Channel^.Volume+((VolumeSlide shr 4) and $f); end; end else if VolumeSlide=$ff then begin // DFF: slide up by 15 on tick 0. if Instance.FirstRowTick then begin Channel^.Volume:=Channel^.Volume+$f; end; end else if VolumeSlide=$0f then begin // D0F: slide down by 15 on all ticks. Not affected at all by the fast slides flag. Channel^.Volume:=Channel^.Volume-$f; end else if VolumeSlide=$f0 then begin // DF0: slide up by 15 on all ticks. Not affected at all by the fast slides flag. Channel^.Volume:=Channel^.Volume+$f; end else begin // D00: ScreamTracker Tracker uses the last nonzero effect parameter in the channel. end; if Channel^.Volume<0 then begin Channel^.Volume:=0; end else if Channel^.Volume>64 then begin Channel^.Volume:=64; end; end; if Portamento<>0 then begin // 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 and $100)=0) or not Instance.FirstRowTick then begin if Channel^.StablePeriodChannel^.SlideToPeriod then begin Channel^.StablePeriod:=Channel^.SlideToPeriod; end; end else if Channel^.StablePeriod>Channel^.SlideToPeriod then begin Channel^.StablePeriod:=Channel^.StablePeriod-((Portamento and $ff)*4); if Channel^.StablePeriod0 then begin // Amiga limits if Channel^.StablePeriod<(907 div 2) then begin Channel^.StablePeriod:=907 div 2; end else if Channel^.StablePeriod>(1712*2) then begin Channel^.StablePeriod:=1712*2; end; end; Channel^.LivePeriod:=Channel^.StablePeriod; if ((Portamento<>0) and (((Portamento and $100)=0) or not Instance.FirstRowTick)) and Channel^.Glissando then begin for Counter:=low(Instance.NoteToPeriodTable) to high(Instance.NoteToPeriodTable) do begin Temp:=Instance.NoteToPeriodTable[Counter]*Channel^.C4SpeedFactor; if Temp<=Channel^.LivePeriod then begin Channel^.LivePeriod:=Temp; break; end; end; end; if Vibrato<>0 then begin // Vibrato. This effect shares memory with Uxy. CreamTrackerChannelVibratoCheck(Channel); PeriodMod:=PeriodMod+(WaveTables[Channel^.VibratoWaveForm and 3,Channel^.VibratoPosition]*(((Vibrato and $f) shl ((((not Vibrato) and $100) shr 8) shl 1))/128.0)); if not Instance.FirstRowTick then begin CreamTrackerChannelVibratoUpdate(Instance,Channel,(Vibrato and $f0) shr 4); end; end; if Tremolo<>0 then begin // 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:=VolumeMod+(WaveTables[Channel^.TremoloWaveForm and 3,Channel^.VibratoPosition]*((Tremolo and $f)/128.0)); if not Instance.FirstRowTick then begin CreamTrackerChannelVibratoUpdate(Instance,Channel,(Tremolo and $f0) shr 4); end; end; if (Channel^.TremorCounter and $80)<>0 then begin // 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. case Channel^.TremorCounter of $80:begin Channel^.TremorCounter:=(Channel^.TremorParameter shr 4) or $c0; end; $c0:begin Channel^.TremorCounter:=(Channel^.TremorParameter and $f) or $80; end; else begin dec(Channel^.TremorCounter); end; end; if (Channel^.TremorCounter and $c0)=$80 then begin VolumeFactor:=0; end; end; if PitchSlide<>0 then begin Mangitude:=PitchSlide and $ff; case Mangitude and $f0 of $e0:begin Mangitude:=(Mangitude and $0f) shl 2; Fine:=1; end; $f0:begin Mangitude:=Mangitude and $0f; Fine:=2; end; else begin Mangitude:=Mangitude shl 2; Fine:=0; end; end; if (PitchSlide and $100)<>0 then begin Mangitude:=-Mangitude; end; if ((Fine<>0) and Instance.FirstTick) or ((Fine=0) and ((Instance.Speed=1) or not Instance.FirstTick)) then begin Channel^.StablePeriod:=Channel^.StablePeriod+Mangitude; Channel^.LivePeriod:=Channel^.StablePeriod; end; end; if ChannelVolumeSlide<>0 then begin Trigger:=((Instance.Header.CWTV=$1330) or ((Instance.Header.Flags and $40)<>0)) or (Instance.Tick>=Channel^.NoteOnTick); if (ChannelVolumeSlide and $f0)=0 then begin if Trigger then begin dec(Channel^.ChannelVolume,ChannelVolumeSlide and $f); end; end else if (ChannelVolumeSlide and $0f)=0 then begin if Trigger then begin inc(Channel^.ChannelVolume,ChannelVolumeSlide shr 4); end; end else if Instance.Tick=Channel^.NoteOnTick then begin if (ChannelVolumeSlide and $0f)=$0f then begin inc(Channel^.ChannelVolume,ChannelVolumeSlide shr 4); end else if (ChannelVolumeSlide and $f0)=$f0 then begin dec(Channel^.ChannelVolume,ChannelVolumeSlide and $f); end; end; if Channel^.ChannelVolume<0 then begin Channel^.ChannelVolume:=0; end else if Channel^.ChannelVolume>64 then begin Channel^.ChannelVolume:=64; end; end; if GlobalVolumeSlide<>0 then begin Trigger:=((Instance.Header.CWTV=$1330) or ((Instance.Header.Flags and $40)<>0)) or (Instance.Tick>=Channel^.NoteOnTick); if (GlobalVolumeSlide and $f0)=0 then begin if Trigger then begin Instance.GlobalVolume:=Instance.GlobalVolume-(GlobalVolumeSlide and $f); end; end else if (GlobalVolumeSlide and $0f)=0 then begin if Trigger then begin Instance.GlobalVolume:=Instance.GlobalVolume+(GlobalVolumeSlide shr 4); end; end else if Instance.Tick=Channel^.NoteOnTick then begin if (GlobalVolumeSlide and $0f)=$0f then begin Instance.GlobalVolume:=Instance.GlobalVolume+(GlobalVolumeSlide shr 4); end else if (GlobalVolumeSlide and $f0)=$f0 then begin Instance.GlobalVolume:=Instance.GlobalVolume-(GlobalVolumeSlide and $f); end; end; if Instance.GlobalVolume<0 then begin Instance.GlobalVolume:=0; end else if Instance.GlobalVolume>128 then begin Instance.GlobalVolume:=128; end; end; if (not Instance.FirstTick) and (TempoSlide<>0) and (((TempoSlide and $10)<>0)<>((TempoSlide and $01)<>0)) then begin if (TempoSlide and $10)<>0 then begin if Instance.Tempo<255 then begin inc(Instance.Tempo); CreamTrackerUpdateTempo(Instance); end; end else if (TempoSlide and $01)<>0 then begin if Instance.Tempo>32 then begin dec(Instance.Tempo); CreamTrackerUpdateTempo(Instance); end; end; end; Temp:=(Channel^.LivePeriod+PeriodMod)*PeriodFactor; if (Instance.Header.Flags and 16)<>0 then begin // Amiga limits if Temp<(907 div 2) then begin Temp:=907 div 2; end else if Temp>(1712*2) then begin Temp:=1712*2; end; end; if Temp<>0 then begin Channel^.SynthFrequency:=(Channel^.NoteFrequencyFactor/Temp)*PeriodInvFactor; Channel^.SynthIncrement:=Channel^.SynthFrequency*Instance.InvSampleRate; Channel^.LiveIncrement:=(Instance.HertzRatio/Temp)*PeriodInvFactor; Channel^.Increment:=round(Channel^.LiveIncrement*int64($100000000)); end else begin Channel^.SynthFrequency:=0.0; Channel^.SynthIncrement:=0.0; Channel^.LiveIncrement:=0; Channel^.Increment:=0; end; if Channel^.Increment=0 then begin Channel^.Active:=false; Channel^.LastLeftClickRemovalFadeOut:=Channel^.LastLeftClickRemovalFadeOut+Channel^.LastLeft; Channel^.LastRightClickRemovalFadeOut:=Channel^.LastRightClickRemovalFadeOut+Channel^.LastRight; Channel^.LastLeft:=0; Channel^.LastRight:=0; end; Channel^.SINCCutOffLevel:=0; for Index:=0 to SINCCUTOFF_LEN-1 do begin if Channel^.Increment<=ResamplerSINCCutOffIncrementTable[Index] then begin Channel^.SINCCutOffLevel:=Index; break; end; end; Volume:=(Channel^.Volume+VolumeMod)/64.0; if Volume<0.0 then begin Volume:=0.0; end else if Volume>1.0 then begin Volume:=1.0; end; Volume:=Volume*VolumeFactor; if Volume<0.0 then begin Volume:=0.0; end else if Volume>1.0 then begin Volume:=1.0; end; Pan:=Channel^.Panning/256.0; if Pan<0.0 then begin Pan:=0.0; end else if Pan>1.0 then begin Pan:=1.0; end; Channel^.LeftVolume:=Volume*(1.0-Pan); Channel^.RightVolume:=Volume*Pan; if Channel^.NewNote then begin Channel^.LastLeftClickRemovalFadeOut:=Channel^.LastLeftClickRemovalFadeOut+Channel^.LastLeft; Channel^.LastRightClickRemovalFadeOut:=Channel^.LastRightClickRemovalFadeOut+Channel^.LastRight; Channel^.LastLeft:=0; Channel^.LastRight:=0; if (Instance.CreamTrackerModule and assigned(Channel^.Instrument) and ((Channel^.Instrument^.Header.Flags and 16)<>0)) or not Instance.CreamTrackerModule then begin Channel^.LeftVolumeCurrent:=0.0; Channel^.RightVolumeCurrent:=0.0; end else begin Channel^.LeftVolumeCurrent:=Channel^.LeftVolume; Channel^.RightVolumeCurrent:=Channel^.RightVolume; end; Channel^.LeftVolumeInc:=0.0; Channel^.RightVolumeInc:=0.0; Channel^.VolumeRampingRemain:=0; end; if (Channel^.LeftVolumeCurrent<>Channel^.LeftVolume) or (Channel^.RightVolumeCurrent<>Channel^.RightVolume) then begin Channel^.VolumeRampingRemain:=Instance.TickSamples; if Channel^.FastRamping then begin Channel^.VolumeRampingRemain:=Channel^.VolumeRampingRemain shr 1; if Channel^.VolumeRampingRemain=0 then begin Channel^.VolumeRampingRemain:=1; end; end; Channel^.LeftVolumeInc:=(Channel^.LeftVolume-Channel^.LeftVolumeCurrent)/Channel^.VolumeRampingRemain; Channel^.RightVolumeInc:=(Channel^.RightVolume-Channel^.RightVolumeCurrent)/Channel^.VolumeRampingRemain; end; Channel^.ChannelBufferVolume:=Channel^.ChannelVolume/64.0; if Instance.VeryFirstTick then begin Channel^.ChannelBufferVolumeCurrent:=Channel^.ChannelBufferVolume; Channel^.ChannelBufferVolumeRampingRemain:=0; Channel^.ChannelBufferVolumeInc:=0; end else if Channel^.ChannelBufferVolumeCurrent<>Channel^.ChannelBufferVolume then begin Channel^.ChannelBufferVolumeRampingRemain:=Instance.TickSamples; Channel^.ChannelBufferVolumeInc:=(Channel^.ChannelBufferVolume-Channel^.ChannelBufferVolumeCurrent)/Channel^.ChannelBufferVolumeRampingRemain; end; Channel^.NewNote:=false; end; end; end; procedure CreamTrackerProcessGlobals(var Instance:TCreamTrackerInstance); const c8d11=8.0/11.0; begin Instance.BufferVolume:=(((Instance.Header.MasterVolume and 127)/127.0){*c8d11})*(Instance.GlobalVolume/128.0); if Instance.VeryFirstTick then begin Instance.BufferVolumeCurrent:=Instance.BufferVolume; Instance.BufferVolumeRampingRemain:=0; Instance.BufferVolumeInc:=0; end else if Instance.BufferVolumeCurrent<>Instance.BufferVolume then begin Instance.BufferVolumeRampingRemain:=Instance.TickSamples; Instance.BufferVolumeInc:=(Instance.BufferVolume-Instance.BufferVolumeCurrent)/Instance.BufferVolumeRampingRemain; end; end; procedure CreamTrackerUpdateTick(var Instance:TCreamTrackerInstance); begin CreamTrackerProcessTick(Instance); CreamTrackerProcessChannels(Instance); CreamTrackerProcessGlobals(Instance); Instance.VeryFirstTick:=false; end; {$ifdef CanSSE} procedure MixBufferSSE(Src,Dest:pointer;SamplesCount:longint); assembler; stdcall; asm mov eax,dword ptr Src mov edx,dword ptr Dest xchg eax,edx mov ecx,dword ptr SamplesCount test ecx,ecx jz @Done add ecx,ecx test eax,15 jnz @Unaligned test edx,15 jnz @Unaligned @Aligned: push ecx shr ecx,4 jz @SkipHurgeLoopAligned @HurgeLoopAligned: movaps xmm0,[eax] movaps xmm4,[edx] addps xmm0,xmm4 movaps xmm1,[eax+16] movaps xmm4,[edx+16] addps xmm1,xmm4 movaps xmm2,[eax+32] movaps xmm4,[edx+32] addps xmm2,xmm4 movaps xmm3,[eax+48] movaps xmm4,[edx+48] addps xmm3,xmm4 movaps [eax],xmm0 movaps [eax+16],xmm1 movaps [eax+32],xmm2 movaps [eax+48],xmm3 add eax,64 add edx,64 dec ecx jnz @HurgeLoopAligned @SkipHurgeLoopAligned: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoopAligned @LargeLoopAligned: movaps xmm0,[eax] movaps xmm4,[edx] addps xmm0,xmm4 movaps [eax],xmm0 add eax,16 add edx,16 dec ecx jnz @LargeLoopAligned @SkipLargeLoopAligned: pop ecx and ecx,3 jz @Done jmp @SmallLoop @Unaligned: push ecx shr ecx,4 jz @SkipHurgeLoop @HurgeLoop: movups xmm0,[eax] movups xmm4,[edx] addps xmm0,xmm4 movups xmm1,[eax+16] movups xmm4,[edx+16] addps xmm1,xmm4 movups xmm2,[eax+32] movups xmm4,[edx+32] addps xmm2,xmm4 movups xmm3,[eax+48] movups xmm4,[edx+48] addps xmm3,xmm4 movups [eax],xmm0 movups [eax+16],xmm1 movups [eax+32],xmm2 movups [eax+48],xmm3 add eax,64 add edx,64 dec ecx jnz @HurgeLoop @SkipHurgeLoop: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoop @LargeLoop: movups xmm0,[eax] movups xmm4,[edx] addps xmm0,xmm4 movups [eax],xmm0 add eax,16 add edx,16 dec ecx jnz @LargeLoop @SkipLargeLoop: pop ecx and ecx,3 jz @Done @SmallLoop: movss xmm0,dword ptr [eax] addss xmm0,dword ptr [edx] movss dword ptr [eax],xmm0 add eax,4 add edx,4 dec ecx jnz @SmallLoop @Done: end; procedure FillFloatSSE(Buffer:pointer;Value:single;Count:longint); assembler; stdcall; asm mov eax,dword ptr Buffer mov ecx,dword ptr Count test ecx,ecx jz @Done movss xmm0,dword ptr Value shufps xmm0,xmm0,0 test eax,15 jnz @Unaligned @Aligned: push ecx shr ecx,4 jz @SkipHurgeLoopAligned @HurgeLoopAligned: movaps [eax],xmm0 movaps [eax+16],xmm0 movaps [eax+32],xmm0 movaps [eax+48],xmm0 add eax,64 dec ecx jnz @HurgeLoopAligned @SkipHurgeLoopAligned: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoopAligned @LargeLoopAligned: movaps [eax],xmm0 add eax,16 dec ecx jnz @LargeLoopAligned @SkipLargeLoopAligned: pop ecx and ecx,3 jz @Done jmp @SmallLoop @Unaligned: push ecx shr ecx,4 jz @SkipHurgeLoop @HurgeLoop: movups [eax],xmm0 movups [eax+16],xmm0 movups [eax+32],xmm0 movups [eax+48],xmm0 add eax,64 dec ecx jnz @HurgeLoop @SkipHurgeLoop: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoop @LargeLoop: movups [eax],xmm0 add eax,16 dec ecx jnz @LargeLoop @SkipLargeLoop: pop ecx and ecx,3 jz @Done @SmallLoop: movss dword ptr [eax],xmm0 add eax,4 dec ecx jnz @SmallLoop @Done: end; procedure BufferMulFloatSSE(Buffer:pointer;Value:single;Count:longint); assembler; stdcall; asm mov eax,dword ptr Buffer mov ecx,dword ptr Count test ecx,ecx jz @Done add ecx,ecx movss xmm4,dword ptr Value shufps xmm4,xmm4,0 test eax,15 jnz @Unaligned @Aligned: push ecx shr ecx,4 jz @SkipHurgeLoopAligned @HurgeLoopAligned: movaps xmm0,[eax] mulps xmm0,xmm4 movaps xmm1,[eax+16] mulps xmm1,xmm4 movaps xmm2,[eax+32] mulps xmm2,xmm4 movaps xmm3,[eax+48] mulps xmm3,xmm4 movaps [eax],xmm0 movaps [eax+16],xmm1 movaps [eax+32],xmm2 movaps [eax+48],xmm3 add eax,64 dec ecx jnz @HurgeLoopAligned @SkipHurgeLoopAligned: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoopAligned @LargeLoopAligned: movaps xmm0,[eax] mulps xmm0,xmm4 movaps [eax],xmm0 add eax,16 dec ecx jnz @LargeLoopAligned @SkipLargeLoopAligned: pop ecx and ecx,3 jz @Done jmp @SmallLoop @Unaligned: push ecx shr ecx,4 jz @SkipHurgeLoop @HurgeLoop: movups xmm0,[eax] mulps xmm0,xmm4 movups xmm1,[eax+16] mulps xmm1,xmm4 movups xmm2,[eax+32] mulps xmm2,xmm4 movups xmm3,[eax+48] mulps xmm3,xmm4 movups [eax],xmm0 movups [eax+16],xmm1 movups [eax+32],xmm2 movups [eax+48],xmm3 add eax,64 dec ecx jnz @HurgeLoop @SkipHurgeLoop: pop ecx and ecx,15 jz @Done push ecx shr ecx,2 jz @SkipLargeLoop @LargeLoop: movups xmm0,[eax] mulps xmm0,xmm4 movups [eax],xmm0 add eax,16 dec ecx jnz @LargeLoop @SkipLargeLoop: pop ecx and ecx,3 jz @Done @SmallLoop: movss xmm0,dword ptr [eax] mulss xmm0,xmm4 movss dword ptr [eax],xmm0 add eax,4 dec ecx jnz @SmallLoop @Done: end; {$endif} procedure CreamTrackerChannelFillBuffer(var Instance:TCreamTrackerInstance;Channel:PCreamTrackerChannel;ToDo:longint); {$ifdef CanSSE} const sseP1:array[0..3] of single=(1.0,1.0,1.0,1.0); sseM1:array[0..3] of single=(-1.0,-1.0,-1.0,-1.0); sseHALF:array[0..3] of single=(0.5,0.5,0.5,0.5); {$endif} var ChannelIndex,Counter,BufPos,ChannelRemain,ChannelToDo,EndPos,SampleIndex,Value, VolumeRampingRemain,VolumeRamping:longint; ChannelBuffer,ChannelBuf,TempBuf:PSingleArray; Left,Right,SynthIncrement:single; SamplePosition,SecondSamplePosition:TCreamTrackerInt64; Increment:int64; Instrument:PCreamTrackerInstrument; {$ifdef CanSINC}SampleLeftData,SampleRightData{$else}SampleData{$endif}:PSingleArray; SampleInterpolation,InvSampleInterpolation, LeftVolumeCurrent,RightVolumeCurrent,LeftVolumeInc,RightVolumeInc:single; pp,pd:pointer; SubArray:PResamplerSINCSubArray; SubSubArray:PResamplerSINCSubSubArray; {$ifdef cpu386} OldCW:word; {$endif} begin if Channel^.Enabled then begin {$ifdef cpu386} asm fstcw word ptr OldCW fldcw word ptr CreamTrackerCW end; {$endif} Instance.CurrentChannel:=Channel; ChannelIndex:=Channel^.Index; ChannelBuffer:=pointer(@Channel^.Buffer); ChannelBuf:=ChannelBuffer; TempBuf:=pointer(@Channel^.TempBuffer); if (Channel^.LastLeftClickRemovalFadeOut<>0) or (Channel^.LastRightClickRemovalFadeOut<>0) then begin BufPos:=0; for Counter:=1 to ToDo do begin Channel^.LastLeftClickRemovalFadeOut:=(((Channel^.LastLeftClickRemovalFadeOut*Instance.ClickRemovalFadeOutFactor)+fDenormal)-fDenormal); Channel^.LastRightClickRemovalFadeOut:=(((Channel^.LastRightClickRemovalFadeOut*Instance.ClickRemovalFadeOutFactor)+fDenormal)-fDenormal); ChannelBuffer^[BufPos+0]:=Channel^.LastLeftClickRemovalFadeOut; ChannelBuffer^[BufPos+1]:=Channel^.LastRightClickRemovalFadeOut; inc(BufPos,2); end; end else begin {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin FillFloatSSE(ChannelBuffer,0.0,ToDo*2); end else{$endif}{$endif} begin FillChar(ChannelBuffer^,ToDo*(2*SizeOf(Single)),#0); end; end; if Channel^.Active and assigned(Channel^.Instrument) then begin Instrument:=Channel^.Instrument; {$ifdef CanSINC} if assigned(Instrument^.Data) and assigned(Instrument^.SINCLeftData) then begin SampleLeftData:=pointer(@Instrument^.SINCLeftData^[TotalFixUpSafeAdditionalSampleLength*2]); SampleRightData:=pointer(@Instrument^.SINCRightData^[TotalFixUpSafeAdditionalSampleLength*2]); end else begin SampleLeftData:=nil; SampleRightData:=nil; end; {$else} SampleData:=Instrument^.MixData; if assigned(Instrument^.Data) and assigned(SampleData) then begin SampleData:=pointer(@SampleData^[TotalFixUpSafeAdditionalSampleLength*4]); end; {$endif} ChannelRemain:=ToDo; while ChannelRemain>0 do begin ChannelToDo:=ChannelRemain; if Channel^.Increment=0 then begin ChannelToDo:=0; end else begin if Instrument^.Header.InstrumentType in [1,9] then begin if assigned(Instrument^.Data) then begin EndPos:=Instrument^.Header.Length; if (Instrument^.Header.Flags and 1)<>0 then begin EndPos:=Instrument^.Header.LoopEnd; if Channel^.SamplePosition.Hi>=EndPos then begin Channel^.SamplePosition.Hi:=Channel^.SamplePosition.Hi+(Instrument^.Header.LoopStart-EndPos); if Channel^.SamplePosition.Hi=Instrument^.Header.Length) then begin ChannelToDo:=0; end else if ((Channel^.SamplePosition.Value+(Channel^.Increment*(ChannelToDo-1))) shr 32)>=EndPos then begin ChannelToDo:=((((int64(EndPos) shl 32)-Channel^.SamplePosition.Value)-1) div Channel^.Increment)+1; if ChannelToDo>ChannelRemain then begin ChannelToDo:=ChannelRemain; end; end; end else begin ChannelToDo:=0; end; end; end; if (ChannelToDo<=0) or ((Instrument^.Header.InstrumentType in [1,9]) and not assigned({$ifdef CanSINC}SampleLeftData{$else}SampleData{$endif})) then begin Channel^.Active:=false; if (Channel^.LastLeft<>0) or (Channel^.LastRight<>0) then begin Left:=Channel^.LastLeft; Right:=Channel^.LastRight; BufPos:=0; for Counter:=1 to ChannelRemain do begin Left:=(((Left*Instance.ClickRemovalFadeOutFactor)+fDenormal)-fDenormal); Right:=(((Right*Instance.ClickRemovalFadeOutFactor)+fDenormal)-fDenormal); ChannelBuf^[BufPos+0]:=ChannelBuf^[BufPos+0]+Left; ChannelBuf^[BufPos+1]:=ChannelBuf^[BufPos+1]+Right; inc(BufPos,2); end; Channel^.LastLeftClickRemovalFadeOut:=Channel^.LastLeftClickRemovalFadeOut+Left; Channel^.LastRightClickRemovalFadeOut:=Channel^.LastRightClickRemovalFadeOut+Right; Channel^.LastLeft:=0; Channel^.LastRight:=0; end; break; end else begin case Instrument^.Header.InstrumentType of 1,9:begin // Resampling Increment:=Channel^.Increment; SamplePosition.Value:=Channel^.SamplePosition.Value; {$ifdef CanSINC}if Channel^.Increment=int64($100000000) then{$endif}begin BufPos:=0; for Counter:=1 to ChannelToDo do begin SampleInterpolation:=SamplePosition.Lo*InvPositionFactor; InvSampleInterpolation:=1.0-SampleInterpolation; {$ifdef CanSINC} SampleIndex:=SamplePosition.Hi; TempBuf^[BufPos+0]:=(SampleLeftData^[SampleIndex+0]*InvSampleInterpolation)+(SampleLeftData^[SampleIndex+1]*SampleInterpolation); TempBuf^[BufPos+1]:=(SampleRightData^[SampleIndex+0]*InvSampleInterpolation)+(SampleRightData^[SampleIndex+1]*SampleInterpolation);{} {$else} SampleIndex:=SamplePosition.Hi shl 1; TempBuf^[BufPos+0]:=(SampleData^[SampleIndex+0]*InvSampleInterpolation)+(SampleData^[SampleIndex+2]*SampleInterpolation); TempBuf^[BufPos+1]:=(SampleData^[SampleIndex+1]*InvSampleInterpolation)+(SampleData^[SampleIndex+3]*SampleInterpolation); {$endif} inc(BufPos,2); inc(SamplePosition.Value,Increment); end; {$ifdef CanSINC} end else begin SubArray:=@ResamplerSINCArray^[Channel^.SINCCutOffLevel]; {$ifdef CanSSE} if SSEExt then begin BufPos:=0; if (Instrument^.Header.Flags and 2)<>0 then begin // Stereo for Counter:=1 to ChannelToDo do begin SubSubArray:=@SubArray^[(SamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=ConvolveSSE(pointer(@SampleLeftData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); Right:=ConvolveSSE(pointer(@SampleRightData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); if (SamplePosition.Lo and SINC_FRACSHIFTMASK)<>0 then begin SampleInterpolation:=(SamplePosition.Lo and SINC_FRACSHIFTMASK)*SINC_FRACSHIFTFACTOR; SecondSamplePosition.Value:=SamplePosition.Value+SINC_FRACSHIFTLENGTH; SubSubArray:=@SubArray^[(SecondSamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Left+((ConvolveSSE(pointer(@SampleLeftData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Left)*SampleInterpolation); Right:=Right+((ConvolveSSE(pointer(@SampleRightData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Right)*SampleInterpolation); end; TempBuf^[BufPos+0]:=Left; TempBuf^[BufPos+1]:=Right; inc(BufPos,2); inc(SamplePosition.Value,Increment); end; end else begin // Mono for Counter:=1 to ChannelToDo do begin SubSubArray:=@SubArray^[(SamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=ConvolveSSE(pointer(@SampleLeftData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); if (SamplePosition.Lo and SINC_FRACSHIFTMASK)<>0 then begin SampleInterpolation:=(SamplePosition.Lo and SINC_FRACSHIFTMASK)*SINC_FRACSHIFTFACTOR; SecondSamplePosition.Value:=SamplePosition.Value+SINC_FRACSHIFTLENGTH; SubSubArray:=@SubArray^[(SecondSamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Left+((ConvolveSSE(pointer(@SampleLeftData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Left)*SampleInterpolation); end; TempBuf^[BufPos+0]:=Left; TempBuf^[BufPos+1]:=Left; inc(BufPos,2); inc(SamplePosition.Value,Increment); end; end; end else {$endif}begin BufPos:=0; if (Instrument^.Header.Flags and 2)<>0 then begin // Stereo for Counter:=1 to ChannelToDo do begin SubSubArray:=@SubArray^[(SamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Convolve(pointer(@SampleLeftData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); Right:=Convolve(pointer(@SampleRightData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); if (SamplePosition.Lo and SINC_FRACSHIFTMASK)<>0 then begin SampleInterpolation:=(SamplePosition.Lo and SINC_FRACSHIFTMASK)*SINC_FRACSHIFTFACTOR; SecondSamplePosition.Value:=SamplePosition.Value+SINC_FRACSHIFTLENGTH; SubSubArray:=@SubArray^[(SecondSamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Left+((Convolve(pointer(@SampleLeftData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Left)*SampleInterpolation); Right:=Right+((Convolve(pointer(@SampleRightData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Right)*SampleInterpolation); end; TempBuf^[BufPos+0]:=Left; TempBuf^[BufPos+1]:=Right; inc(BufPos,2); inc(SamplePosition.Value,Increment); end; end else begin // Mono for Counter:=1 to ChannelToDo do begin SubSubArray:=@SubArray^[(SamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Convolve(pointer(@SampleLeftData^[SamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH); if (SamplePosition.Lo and SINC_FRACSHIFTMASK)<>0 then begin SampleInterpolation:=(SamplePosition.Lo and SINC_FRACSHIFTMASK)*SINC_FRACSHIFTFACTOR; SecondSamplePosition.Value:=SamplePosition.Value+SINC_FRACSHIFTLENGTH; SubSubArray:=@SubArray^[(SecondSamplePosition.Lo shr SINC_FRACSHIFT) and SINC_FRACMASK]; Left:=Left+((Convolve(pointer(@SampleLeftData^[SecondSamplePosition.Hi-SINC_HALFWIDTH]),pointer(@SubSubArray^[0]),SINC_WIDTH)-Left)*SampleInterpolation); end; TempBuf^[BufPos+0]:=Left; TempBuf^[BufPos+1]:=Left; inc(BufPos,2); inc(SamplePosition.Value,Increment); end; end; end; {$endif} end; Channel^.SamplePosition.Value:=SamplePosition.Value; end; 8:begin // Synthesizing if Instance.CreamTrackerModule and assigned(Channel^.Instrument) and assigned(Channel^.Instrument^.CodeData) and assigned(addr(Channel^.Instrument^.CodeData^.SynthProcInstanceProcess)) and assigned(Channel^.Instrument^.CodeData^.InstanceData) then begin {$ifdef UseAulan} pp:=addr(Channel^.Instrument^.CodeData^.SynthProcInstanceProcess); pd:=@PAnsiChar(Channel^.Instrument^.CodeData^.InstanceData)[ChannelIndex*Channel^.Instrument^.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=Channel; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; SynthIncrement:=Channel^.SynthIncrement; 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 end; if Value=0 then begin Channel^.Increment:=0; Channel^.Active:=false; end; {$else} Channel^.Increment:=0; Channel^.Active:=false; {$endif} end else begin BufPos:=0; for Counter:=1 to ChannelToDo do begin TempBuf^[BufPos+0]:=0.0; TempBuf^[BufPos+1]:=0.0; inc(BufPos,2); end; end; end else begin BufPos:=0; for Counter:=1 to ChannelToDo do begin TempBuf^[BufPos+0]:=0.0; TempBuf^[BufPos+1]:=0.0; inc(BufPos,2); end; end; end; begin // Volume amplifying LeftVolumeCurrent:=Channel^.LeftVolumeCurrent; RightVolumeCurrent:=Channel^.RightVolumeCurrent; LeftVolumeInc:=Channel^.LeftVolumeInc; RightVolumeInc:=Channel^.RightVolumeInc; VolumeRampingRemain:=Channel^.VolumeRampingRemain; if (VolumeRampingRemain<>0) or ((LeftVolumeCurrent<>1.0) or (RightVolumeCurrent<>1.0)) then begin BufPos:=0; for Counter:=1 to ChannelToDo do begin TempBuf^[BufPos+0]:=TempBuf^[BufPos+0]*LeftVolumeCurrent; TempBuf^[BufPos+1]:=TempBuf^[BufPos+1]*RightVolumeCurrent; inc(BufPos,2); VolumeRamping:=((-VolumeRampingRemain) shr 31) and 1; longword(pointer(@LeftVolumeInc)^):=longword(pointer(@LeftVolumeInc)^) and longword(-VolumeRamping); longword(pointer(@RightVolumeInc)^):=longword(pointer(@RightVolumeInc)^) and longword(-VolumeRamping); LeftVolumeCurrent:=LeftVolumeCurrent+LeftVolumeInc; RightVolumeCurrent:=RightVolumeCurrent+RightVolumeInc; dec(VolumeRampingRemain,VolumeRamping); end; Channel^.VolumeRampingRemain:=VolumeRampingRemain; if Channel^.VolumeRampingRemain=0 then begin Channel^.LeftVolumeCurrent:=Channel^.LeftVolume; Channel^.RightVolumeCurrent:=Channel^.RightVolume; end else begin Channel^.LeftVolumeCurrent:=LeftVolumeCurrent; Channel^.RightVolumeCurrent:=RightVolumeCurrent; end; end; end; begin // Mixing Left:=0; Right:=0; {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin if ChannelToDo>0 then begin MixBufferSSE(TempBuf,ChannelBuf,ChannelToDo); BufPos:=(ChannelToDo-1) shl 1; Left:=TempBuf^[BufPos+0]; Right:=TempBuf^[BufPos+1]; end; end else{$endif}{$endif} begin BufPos:=0; for Counter:=1 to ChannelToDo do begin Left:=TempBuf^[BufPos+0]; Right:=TempBuf^[BufPos+1]; ChannelBuf^[BufPos+0]:=ChannelBuf^[BufPos+0]+Left; ChannelBuf^[BufPos+1]:=ChannelBuf^[BufPos+1]+Right; inc(BufPos,2); end; end; Channel^.LastLeft:=Left; Channel^.LastRight:=Right; end; ChannelBuf:=@ChannelBuf[ChannelToDo*2]; dec(ChannelRemain,ChannelToDo); end; end; end; begin // Channel volume amplifying LeftVolumeCurrent:=Channel^.ChannelBufferVolumeCurrent; LeftVolumeInc:=Channel^.ChannelBufferVolumeInc; VolumeRampingRemain:=Channel^.ChannelBufferVolumeRampingRemain; {$ifdef CanSSE} {$ifdef cpu386} if SSEExt and (VolumeRampingRemain=0) then begin if LeftVolumeCurrent<>1.0 then begin BufferMulFloatSSE(ChannelBuffer,LeftVolumeCurrent,ToDo); end; end else{$endif}{$endif} begin if (VolumeRampingRemain<>0) or (LeftVolumeCurrent<>1.0) then begin BufPos:=0; for Counter:=1 to ToDo do begin ChannelBuffer^[BufPos+0]:=ChannelBuffer^[BufPos+0]*LeftVolumeCurrent; ChannelBuffer^[BufPos+1]:=ChannelBuffer^[BufPos+1]*LeftVolumeCurrent; inc(BufPos,2); VolumeRamping:=((-VolumeRampingRemain) shr 31) and 1; longword(pointer(@LeftVolumeInc)^):=longword(pointer(@LeftVolumeInc)^) and longword(-VolumeRamping); LeftVolumeCurrent:=LeftVolumeCurrent+LeftVolumeInc; dec(VolumeRampingRemain,VolumeRamping); end; Channel^.ChannelBufferVolumeRampingRemain:=VolumeRampingRemain; if Channel^.ChannelBufferVolumeRampingRemain=0 then begin Channel^.ChannelBufferVolumeCurrent:=Channel^.ChannelBufferVolume; end else begin Channel^.ChannelBufferVolumeCurrent:=LeftVolumeCurrent; end; end; end; end; {$ifdef cpu386} asm fldcw word ptr OldCW end; {$endif} end; end; {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} procedure CreamTrackerJobManagerProcessThread(Instance:PCreamTrackerInstance;ThreadNumber:longint); var JobManager:PCreamTrackerJobManager; Job:PCreamTrackerJob; Index:longint; begin JobManager:=@Instance^.JobManager; while true do begin Index:=InterlockedDecrement(JobManager^.JobQueueIndex); if Index>=0 then begin Job:=@JobManager^.Jobs[Index]; case Job^.Mode of ctjmCHANNELMIX:begin CreamTrackerChannelFillBuffer(Job^.Instance^,Job^.Channel,Job^.Samples); end; end; end else begin break; end; end; end; {$ifdef UseTThread} constructor TCreamTrackerJobThread.Create(TheInstance:PCreamTrackerInstance;TheThreadNumber:longint); begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Instance:=TheInstance; ThreadNumber:=TheThreadNumber; Event:=TEvent.Create(nil,false,false,''); DoneEvent:=TEvent.Create(nil,false,false,''); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} inherited Create(false); end; destructor TCreamTrackerJobThread.Destroy; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} FreeAndNil(Event); FreeAndNil(DoneEvent); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} inherited Destroy; end; procedure TCreamTrackerJobThread.Execute; {$ifdef cpu386} var OldFCW:word; {$endif} {$ifdef UseSSE} var OldSIMDCtrl:longword; {$endif} begin {$ifdef cpu386} asm fstcw word ptr OldFCW fldcw word ptr CreamTrackerCW end; {$endif} {$ifdef UseSSE} if SSEExt then begin asm stmxcsr dword ptr OldSIMDCtrl end; SIMDSetFlags; end; {$endif} InterlockedIncrement(Instance^.Threads); while not Instance^.ThreadsTerminated do begin Event.WaitFor(INFINITE); if Instance^.ThreadsTerminated then begin break; end else begin {$ifdef cpu386} asm fldcw word ptr CreamTrackerCW end; {$endif} {$ifdef UseSSE} if SSEExt then begin SIMDSetFlags; end; {$endif} CreamTrackerJobManagerProcessThread(Instance,ThreadNumber); DoneEvent.SetEvent; end; end; InterlockedDecrement(Instance^.Threads); {$ifdef UseSSE} if SSEExt then begin asm ldmxcsr dword ptr OldSIMDCtrl end; end; {$endif} {$ifdef cpu386} asm fldcw word ptr OldFCW end; {$endif} end; {$else} {$ifdef WIN32ReallyPlain} procedure CreamTrackerJobThread(JobThread:PCreamTrackerJobThread); stdcall; {$else} function CreamTrackerJobThread(JobThread:PCreamTrackerJobThread):longint; {$endif} {$ifdef cpu386} var OldFCW:word; {$endif} {$ifdef UseSSE} var OldSIMDCtrl:longword; {$endif} begin {$ifdef cpu386} asm fstcw word ptr OldFCW fldcw word ptr CreamTrackerCW end; {$endif} {$ifdef UseSSE} if SSEExt then begin asm stmxcsr dword ptr OldSIMDCtrl end; SIMDSetFlags; end; {$endif} InterlockedIncrement(JobThread^.Instance^.Threads); while not JobThread^.Instance^.ThreadsTerminated do begin {$ifdef fpc} RTLEventWaitFor(JobThread^.Event); {$else} WaitForSingleObject(JobThread^.Event,INFINITE); {$endif} if JobThread^.Instance^.ThreadsTerminated then begin break; end else begin {$ifdef cpu386} asm fldcw word ptr CreamTrackerCW end; {$endif} {$ifdef UseSSE} if SSEExt then begin SIMDSetFlags; end; {$endif} asm pushad end; CreamTrackerJobManagerProcessThread(JobThread^.Instance,JobThread^.ThreadNumber); asm popad end; {$ifdef fpc} RTLEventSetEvent(JobThread^.DoneEvent); {$else} SetEvent(JobThread^.DoneEvent); {$endif} end; end; InterlockedDecrement(JobThread^.Instance^.Threads); {$ifdef UseSSE} if SSEExt then begin asm ldmxcsr dword ptr OldSIMDCtrl end; end; {$endif} {$ifdef cpu386} asm fldcw word ptr OldFCW end; {$endif} {$ifdef WIN32ReallyPlain} ExitThread(0); {$else} EndThread(0); result:=0; {$endif} end; {$endif} procedure CreamTrackerJobCreateThreads(Instance:PCreamTrackerInstance); var i,j:longint; {$ifdef win32} sinfo:SYSTEM_INFO; dwProcessAffinityMask,dwSystemAffinityMask:ptruint; {$endif} Cores:array[0..MaxThreads-1] of longint; begin {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} {$ifdef win32} GetSystemInfo(sinfo); GetProcessAffinityMask(GetCurrentProcess,dwProcessAffinityMask,dwSystemAffinityMask); j:=0; for i:=0 to sinfo.dwNumberOfProcessors-1 do begin if (dwProcessAffinityMask and (1 shl i))<>0 then begin Cores[j]:=i; inc(j); if j>=MaxThreads then begin break; end; end; end; Instance^.JobManager.CountThreads:=j; if Instance^.JobManager.CountThreads<1 then begin Instance^.JobManager.CountThreads:=1; end else if Instance^.JobManager.CountThreads>MaxThreads then begin Instance^.JobManager.CountThreads:=MaxThreads; end; {$ifdef NewDelphi} Cores[0]:=0; Instance^.JobManager.CountThreads:=1; {$endif} {$else} Cores[0]:=0; Instance^.JobManager.CountThreads:=1; {$endif} {$ifdef memdebugsinglecore} Cores[0]:=0; Instance^.JobManager.CountThreads:=1; {$endif} Instance^.UseMultithreading:=Instance^.JobManager.CountThreads>1; //Track^.JobManager.CountThreads:=1; for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef UseTThread} Instance^.JobManager.Threads[i]:=nil; {$else} Instance^.JobManager.Threads[i].Instance:=Instance; Instance^.JobManager.Threads[i].ThreadNumber:=i; Instance^.JobManager.Threads[i].ThreadHandle:=0; Instance^.JobManager.Threads[i].ThreadID:=0; {$ifdef win32} {$ifndef fpc} Instance^.JobManager.DoneEventHandles[i]:=0; {$endif} {$endif} {$endif} end; if Instance^.JobManager.CountThreads>1 then begin for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef UseTThread} Instance^.JobManager.Threads[i]:=TCreamTrackerJobThread.Create(Instance,i); Instance^.JobManager.Threads[i].Priority:=tpHigher; {$ifdef win32} {$ifdef CreamTrackerUseThreadCPUCorePinning} {$ifdef fpc} SetThreadAffinityMask(Instance^.JobManager.Threads[i].Handle,1 shl Cores[i]); {$else} SetThreadIdealProcessor(Instance^.JobManager.Threads[i].Handle,Cores[i]); {$endif} {$endif} {$endif} {$else} Instance^.JobManager.Threads[i].Event:={$ifdef fpc}RTLEventCreate{$else}CreateEventA(nil,false,false,''){$endif}; Instance^.JobManager.Threads[i].DoneEvent:={$ifdef fpc}RTLEventCreate{$else}CreateEventA(nil,false,false,''){$endif}; {$ifdef win32} {$ifndef fpc} Instance^.JobManager.DoneEventHandles[i]:=Instance^.JobManager.Threads[i].DoneEvent; {$endif} {$endif} {$ifdef win32ReallyPlain} Instance^.JobManager.Threads[i].ThreadHandle:=CreateThread(nil,0,@CreamTrackerJobThread,@Instance^.JobManager.Threads[i],0,Instance^.JobManager.Threads[i].ThreadID); {$else} Instance^.JobManager.Threads[i].ThreadHandle:=BeginThread(nil,0,@CreamTrackerJobThread,@Instance^.JobManager.Threads[i],0,Instance^.JobManager.Threads[i].ThreadID); {$endif} {$ifdef win32} SetThreadPriority(Instance^.JobManager.Threads[i].ThreadHandle,THREAD_PRIORITY_TIME_CRITICAL); if GetThreadPriority(Instance^.JobManager.Threads[i].ThreadHandle)<>THREAD_PRIORITY_TIME_CRITICAL then begin // 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 then begin SetThreadPriority(Instance^.JobManager.Threads[i].ThreadHandle,THREAD_PRIORITY_ABOVE_NORMAL); end; end; {$ifdef CreamTrackerUseThreadCPUCorePinning} {$ifdef fpc} SetThreadAffinityMask(Instance^.JobManager.Threads[i].ThreadHandle,1 shl Cores[i]); {$else} SetThreadIdealProcessor(Instance^.JobManager.Threads[i].ThreadHandle,Cores[i]); {$endif} {$endif} {$endif} {$endif} end; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; procedure CreamTrackerJobWakeThreads(Instance:PCreamTrackerInstance); var i:longint; begin if Instance^.JobManager.CountThreads>1 then begin for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef UseTThread} Instance^.JobManager.Threads[i].Event.SetEvent; {$else} {$ifdef fpc} RTLEventSetEvent(Instance^.JobManager.Threads[i].Event); {$else} SetEvent(Instance^.JobManager.Threads[i].Event); {$endif} {$endif} end; end; end; procedure CreamTrackerJobWaitThreads(Instance:PCreamTrackerInstance); var i:longint; begin if Instance^.JobManager.CountThreads>1 then begin {$ifdef UseTThread} for i:=0 to Instance^.JobManager.CountThreads-1 do begin case Instance^.JobManager.Threads[i].DoneEvent.WaitFor(ThreadTimeOut) of wrSignaled:begin end; wrTimeout,wrAbandoned,wrError:begin Instance.ThreadsTerminated:=true; end; end; end; {$else} {$ifdef fpc} for i:=0 to Instance^.JobManager.CountThreads-1 do begin RTLEventWaitFor(Instance^.JobManager.Threads[i].DoneEvent); end; {$else} {$ifdef win32} case WaitForMultipleObjects(Instance^.JobManager.CountThreads,@Instance^.JobManager.DoneEventHandles[0],true,ThreadTimeOut) of WAIT_ABANDONED_0..(WAIT_ABANDONED_0+(MAXIMUM_WAIT_OBJECTS-1)),WAIT_TIMEOUT,WAIT_FAILED:begin Instance.ThreadsTerminated:=true; end; end; {$else} for i:=0 to Instance^.JobManager.CountThreads-1 do begin case WaitForSingleObject(Instance^.JobManager.Threads[i].DoneEvent,ThreadTimeOut) of WAIT_ABANDONED,WAIT_TIMEOUT,WAIT_FAILED:begin Instance.ThreadsTerminated:=true; end; end; end; {$endif} {$endif} {$endif} end; if Instance.ThreadsTerminated then begin Instance.UseMultithreading:=false; for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef fpc} System.KillThread(Instance^.JobManager.Threads[i].ThreadID); {$else} {$ifdef UseTThread} TerminateThread(Instance^.JobManager.Threads[i].Handle,0); {$else} TerminateThread(Instance^.JobManager.Threads[i].ThreadHandle,0); {$endif} {$endif} {$ifndef UseTThread} {$ifdef win32} if Instance^.JobManager.Threads[i].ThreadHandle<>0 then begin CloseHandle(Instance^.JobManager.Threads[i].ThreadHandle); Instance^.JobManager.Threads[i].ThreadHandle:=0; end; {$endif} {$endif} end; end; end; procedure CreamTrackerJobFreeThreads(Instance:PCreamTrackerInstance); var i:longint; begin if Instance^.JobManager.CountThreads>1 then begin for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef UseTThread} Instance^.JobManager.Threads[i].Terminate; Instance^.JobManager.Threads[i].WaitFor; {$else} if Instance^.JobManager.Threads[i].ThreadHandle<>0 then begin {$ifdef win32} WaitForSingleObject(Instance^.JobManager.Threads[i].ThreadHandle,25); {$endif} {$ifdef fpc} System.KillThread(Instance^.JobManager.Threads[i].ThreadID); {$else} TerminateThread(Instance^.JobManager.Threads[i].ThreadHandle,0); {$endif} {$ifdef win32} CloseHandle(Instance^.JobManager.Threads[i].ThreadHandle); {$endif} end; {$endif} end; for i:=0 to Instance^.JobManager.CountThreads-1 do begin {$ifdef UseTThread} FreeAndNil(Instance^.JobManager.Threads[i]); {$else} {$ifdef fpc} RTLEventDestroy(Instance^.JobManager.Threads[i].Event); RTLEventDestroy(Instance^.JobManager.Threads[i].DoneEvent); {$else} CloseHandle(Instance^.JobManager.Threads[i].Event); CloseHandle(Instance^.JobManager.Threads[i].DoneEvent); {$endif} {$endif} end; end; end; procedure CreamTrackerJobManagerInitProcessChannelMix(Instance:PCreamTrackerInstance;SamplesCount:longint;NewTick:longbool); var i,j:longint; begin j:=0; for i:=0 to 63 do begin if Instance^.Channels[i].Enabled and assigned(Instance^.Channels[i].Instrument) then begin 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]; inc(j); end; end; Instance^.JobManager.CountJobs:=j; end; procedure CreamTrackerJobManagerProcess(Instance:PCreamTrackerInstance); var JobManager:PCreamTrackerJobManager; begin JobManager:=@Instance^.JobManager; if JobManager^.CountJobs>0 then begin Instance^.JobManager.JobQueueIndex:=JobManager^.CountJobs; if Instance^.UseMultithreading and (Instance^.JobManager.CountThreads>1) then begin CreamTrackerJobWakeThreads(Instance); CreamTrackerJobWaitThreads(Instance); end; CreamTrackerJobManagerProcessThread(Instance,0); JobManager^.CountJobs:=0; end; end; {$endif} {$endif} procedure CreamTrackerFillBuffer(var Instance:TCreamTrackerInstance;Buffer:pointer;Samples:longint); {$ifdef CanSSE} const sseP1:array[0..3] of single=(1.0,1.0,1.0,1.0); sseM1:array[0..3] of single=(-1.0,-1.0,-1.0,-1.0); sseHALF:array[0..3] of single=(0.5,0.5,0.5,0.5); {$endif} var ToDo,ChannelIndex,Counter,BufPos,VolumeRampingRemain,VolumeRamping{$ifdef CreamTrackerOscil},BufferOffset{$endif}:longint; Channel:PCreamTrackerChannel; Left,LeftVolumeCurrent,LeftVolumeInc:single; Buf,ChannelBuffer,ChannelBuf:PSingleArray; pp,pd,pdd:pointer; {$ifdef CreamTrackerOscil} TickTimeInfo:PTickTimeInfo; TickTimeInfoChannel:PTickTimeInfoChannel; {$endif} {$ifdef cpu386} OldCW:word; {$endif} begin SIMDSetFlags; {$ifdef cpu386} asm fstcw word ptr OldCW fldcw word ptr CreamTrackerCW end; {$endif} for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex]; if Channel^.Enabled and not Channel^.Muted then begin Instance.ChannelBuffers[ChannelIndex]:=@Instance.Channels[ChannelIndex].Buffer[0]; end else begin Instance.ChannelBuffers[ChannelIndex]:=nil; end; end; Buf:=Buffer; {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin FillFloatSSE(Buf,0.0,Samples*2); end else{$endif}{$endif} begin FillChar(Buf^,Samples*(2*SizeOf(Single)),#0); end; {$ifdef CreamTrackerOscil} BufferOffset:=0; CountTickTimeInfos:=0; {$endif} while Samples>0 do begin if Instance.TickSamplesRemain=0 then begin CreamTrackerUpdateTick(Instance); Instance.TickSamplesRemain:=Instance.TickSamples; end; {$ifdef CreamTrackerOscil} if CountTickTimeInfosInstance.TickSamplesRemain then begin ToDo:=Instance.TickSamplesRemain; end; if ToDo>SubSamples then begin ToDo:=SubSamples; end; {$ifndef CreamTrackerMinimalPlayer} {$ifdef UseThreading} if Instance.UseMultithreading then begin CreamTrackerJobManagerInitProcessChannelMix(@Instance,ToDo,true); CreamTrackerJobManagerProcess(@Instance); end else{$endif}{$endif} begin for ChannelIndex:=0 to 63 do begin CreamTrackerChannelFillBuffer(Instance,@Instance.Channels[ChannelIndex],ToDo); end; end; begin // Mixing slave channel buffers to master channel buffers for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex or $20]; if assigned(Channel^.Master) and Channel^.Master^.Enabled then begin ChannelBuffer:=pointer(@Channel^.Master^.Buffer); ChannelBuf:=pointer(@Channel^.Buffer); BufPos:=0; for Counter:=1 to ToDo do begin ChannelBuffer^[BufPos+0]:=ChannelBuffer^[BufPos+0]+ChannelBuf^[BufPos+0]; ChannelBuffer^[BufPos+1]:=ChannelBuffer^[BufPos+1]+ChannelBuf^[BufPos+1]; inc(BufPos,2); end; end; end; end; begin // Mixing if Instance.CreamTrackerModule and assigned(Instance.CodeData) and assigned(addr(Instance.CodeData^.GlobalProcProcess)) then begin {$ifdef UseAulan} { // Why? It needs only too much CPU time :-) So commenting out... for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex]; if Channel^.Muted or not Channel^.Enabled then begin ChannelBuf:=pointer(@Channel^.Buffer); BufPos:=0; for Counter:=1 to ToDo do begin ChannelBuffer^[BufPos+0]:=0.0; ChannelBuffer^[BufPos+1]:=0.0; inc(BufPos,2); end; end; end;} pp:=addr(Instance.CodeData^.GlobalProcProcess); pd:=@PAnsiChar(Instance.CodeData^.InstanceData)[(Instance.CodeData^.Instances-1)*Instance.CodeData^.InstanceWorkDataSize]; PCreamTrackerInstanceStubData(pd)^.Instance:=@Instance; PCreamTrackerInstanceStubData(pd)^.Channel:=nil; pd:=@PAnsiChar(pd)[SizeOf(TCreamTrackerInstanceStubData)]; pdd:=@Instance.ChannelBuffers; 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 end; {$else} for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex]; if Channel^.Muted or not Channel^.Enabled then begin ChannelBuf:=pointer(@Channel^.Buffer); BufPos:=0; for Counter:=1 to ToDo do begin ChannelBuffer^[BufPos+0]:=0.0; ChannelBuffer^[BufPos+1]:=0.0; inc(BufPos,2); end; end; end; {$endif} end else begin for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex]; if Channel^.Enabled and not Channel^.Muted then begin ChannelBuffer:=pointer(@Channel.Buffer); {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin MixBufferSSE(ChannelBuffer,Buf,ToDo); end else{$endif}{$endif} begin BufPos:=0; for Counter:=1 to ToDo do begin Buf^[BufPos+0]:=Buf^[BufPos+0]+ChannelBuffer^[BufPos+0]; Buf^[BufPos+1]:=Buf^[BufPos+1]+ChannelBuffer^[BufPos+1]; inc(BufPos,2); end; end; end; end; end; end; {$ifdef CreamTrackerOscil} begin // Oscil stuff for ChannelIndex:=0 to 31 do begin Channel:=@Instance.Channels[ChannelIndex]; if Channel^.Enabled and not Channel^.Muted then begin ChannelBuffer:=pointer(@Channel^.Buffer); BufPos:=0; for Counter:=1 to ToDo do begin OscilData[ChannelIndex,OscilCounter[ChannelIndex] and OscilMask]:=(ChannelBuffer^[BufPos+0]+ChannelBuffer^[BufPos+1]);//*0.5; OscilCounter[ChannelIndex]:=(OscilCounter[ChannelIndex]+1) and OscilMask; inc(BufPos,2); end; end else begin for Counter:=1 to ToDo do begin OscilData[ChannelIndex,OscilCounter[ChannelIndex] and OscilMask]:=0.0; OscilCounter[ChannelIndex]:=(OscilCounter[ChannelIndex]+1) and OscilMask; end; end; end; end; {$endif} begin LeftVolumeCurrent:=Instance.BufferVolumeCurrent; LeftVolumeInc:=Instance.BufferVolumeInc; VolumeRampingRemain:=Instance.BufferVolumeRampingRemain; {$ifdef CanSSE} {$ifdef cpu386} if SSEExt and (VolumeRampingRemain=0) then begin if LeftVolumeCurrent<>1.0 then begin BufferMulFloatSSE(Buf,LeftVolumeCurrent,ToDo); end; end else{$endif}{$endif} begin if (VolumeRampingRemain<>0) or (LeftVolumeCurrent<>1.0) then begin BufPos:=0; for Counter:=1 to ToDo do begin Buf^[BufPos+0]:=Buf^[BufPos+0]*LeftVolumeCurrent; Buf^[BufPos+1]:=Buf^[BufPos+1]*LeftVolumeCurrent; inc(BufPos,2); VolumeRamping:=((-VolumeRampingRemain) shr 31) and 1; longword(pointer(@LeftVolumeInc)^):=longword(pointer(@LeftVolumeInc)^) and longword(-VolumeRamping); LeftVolumeCurrent:=LeftVolumeCurrent+LeftVolumeInc; dec(VolumeRampingRemain,VolumeRamping); end; Instance.BufferVolumeRampingRemain:=VolumeRampingRemain; if Instance.BufferVolumeRampingRemain=0 then begin Instance.BufferVolumeCurrent:=Instance.BufferVolume; end else begin Instance.BufferVolumeCurrent:=LeftVolumeCurrent; end; end; end; end; if (Instance.Header.MasterVolume and $80)=0 then begin {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin asm push esi push ecx mov ecx,dword ptr ToDo test ecx,ecx jz @Done add ecx,ecx mov esi,dword ptr Buf movups xmm7,[sseHALF] push ecx shr ecx,4 jecxz @Skip16 @Loop16: movups xmm0,[esi] movaps xmm4,xmm0 shufps xmm4,xmm4,$b1 addps xmm0,xmm4 mulps xmm0,xmm7 movups xmm1,[esi+16] movaps xmm5,xmm1 shufps xmm5,xmm5,$b1 addps xmm1,xmm5 mulps xmm1,xmm7 movups xmm2,[esi+32] movaps xmm6,xmm2 shufps xmm6,xmm6,$b1 addps xmm2,xmm6 mulps xmm2,xmm7 movups xmm3,[esi+48] movaps xmm4,xmm3 shufps xmm4,xmm4,$b1 addps xmm3,xmm4 mulps xmm3,xmm7 movups [esi],xmm0 movups [esi+16],xmm1 movups [esi+32],xmm2 movups [esi+48],xmm3 add esi,64 dec ecx jnz @Loop16 @Skip16: pop ecx and ecx,15 push ecx shr ecx,3 jecxz @Skip8 @Loop8: movups xmm0,[esi] movaps xmm4,xmm0 shufps xmm4,xmm4,$b1 addps xmm0,xmm4 mulps xmm0,xmm7 movups xmm1,[esi+16] movaps xmm5,xmm1 shufps xmm5,xmm5,$b1 addps xmm1,xmm5 mulps xmm1,xmm7 movups [esi],xmm0 movups [esi+16],xmm1 add esi,32 dec ecx jnz @Loop8 @Skip8: pop ecx and ecx,7 push ecx shr ecx,2 jecxz @Skip4 @Loop4: movups xmm0,[esi] movaps xmm4,xmm0 shufps xmm4,xmm4,$b1 addps xmm0,xmm4 mulps xmm0,xmm7 movups [esi],xmm0 add esi,16 dec ecx jnz @Loop4 @Skip4: pop ecx and ecx,3 shr ecx,1 jecxz @Skip2 @Loop2: movss xmm0,dword ptr [esi] addss xmm0,dword ptr [esi+4] mulss xmm0,xmm7 movss dword ptr [esi],xmm0 movss dword ptr [esi+4],xmm0 add esi,8 dec ecx jnz @Loop2 @Skip2: @Done: pop ecx pop esi end; end else {$endif}{$endif}begin BufPos:=0; for Counter:=1 to ToDo do begin Left:=(Buf^[BufPos]+Buf^[BufPos+1])*0.5; Buf^[BufPos+0]:=Left; Buf^[BufPos+1]:=Left; inc(BufPos,2); end; end; end; {$ifndef CreamTrackerGUI} begin {$ifdef CanSSE} {$ifdef cpu386} if SSEExt then begin asm push esi push ecx mov ecx,dword ptr ToDo test ecx,ecx jz @Done add ecx,ecx mov esi,dword ptr Buf movups xmm6,[sseM1] movups xmm7,[sseP1] push ecx shr ecx,4 jecxz @Skip16 @Loop16: movups xmm0,[esi] maxps xmm0,xmm6 minps xmm0,xmm7 movups xmm1,[esi+16] maxps xmm1,xmm6 minps xmm1,xmm7 movups xmm2,[esi+32] maxps xmm2,xmm6 minps xmm2,xmm7 movups xmm3,[esi+48] maxps xmm3,xmm6 minps xmm3,xmm7 movups [esi],xmm0 movups [esi+16],xmm1 movups [esi+32],xmm2 movups [esi+48],xmm3 add esi,64 dec ecx jnz @Loop16 @Skip16: pop ecx and ecx,15 push ecx shr ecx,3 jecxz @Skip8 @Loop8: movups xmm0,[esi] maxps xmm0,xmm6 minps xmm0,xmm7 movups xmm1,[esi+16] maxps xmm1,xmm6 minps xmm1,xmm7 movups [esi],xmm0 movups [esi+16],xmm1 add esi,32 dec ecx jnz @Loop8 @Skip8: pop ecx and ecx,7 push ecx shr ecx,2 jecxz @Skip4 @Loop4: movups xmm0,[esi] maxps xmm0,xmm6 minps xmm0,xmm7 movups [esi],xmm0 add esi,16 dec ecx jnz @Loop4 @Skip4: pop ecx and ecx,3 jecxz @Skip1 @Loop1: movss xmm0,dword ptr [esi] maxss xmm0,xmm6 minss xmm0,xmm7 movss dword ptr [esi],xmm0 add esi,4 dec ecx jnz @Loop1 @Skip1: @Done: pop ecx pop esi end; end else {$endif}{$endif}begin BufPos:=0; for Counter:=1 to ToDo do begin Buf^[BufPos+0]:=Clip(Buf^[BufPos+0],-1,1); Buf^[BufPos+1]:=Clip(Buf^[BufPos+1],-1,1); inc(BufPos,2); end; end; end; {$endif} {$ifdef CreamTrackerGUI} inc(Instance.SamplePosition,ToDo); {$endif} dec(Instance.TickSamplesRemain,ToDo); dec(Samples,ToDo); {$ifdef CreamTrackerOscil} inc(BufferOffset,ToDo); {$endif} Buf:=pointer(@Buf[ToDo shl 1]); end; {$ifdef cpu386} asm fldcw word ptr OldCW end; {$endif} end; {$ifdef CreamTrackerSaveRoutines} function CreamTrackerFillBufferOneTick(var Instance:TCreamTrackerInstance;Buffer:pointer;Samples:longint;WithTrackEnd:longbool):longint; begin result:=Instance.TickSamplesRemain; if result=0 then begin CreamTrackerUpdateTick(Instance); Instance.TickSamplesRemain:=Instance.TickSamples; result:=Instance.TickSamplesRemain; end; if result>Samples then begin result:=Samples; end; if WithTrackEnd or not Instance.TrackEnd then begin CreamTrackerFillBuffer(Instance,Buffer,result); end else begin result:=0; end; end; {$endif} function CreamTrackerCalculateLength(var Instance:TCreamTrackerInstance):longint; begin result:=0; CreamTrackerReset(Instance); Instance.Playing:=true; Instance.RepeatCounter:=0; while not Instance.TrackEnd do begin CreamTrackerUpdateTick(Instance); inc(result,Instance.TickSamples); end; CreamTrackerReset(Instance); Instance.Playing:=false; end; {$ifdef CreamTrackerSaveRoutines} function CreamTrackerSavePatternsAsSingleChain(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint):longbool; type PStreamBytes=^TStreamBytes; TStreamBytes=array[0..(256*64*32)-1] of byte; var OutputAllocated:longint; function Write(const Src;Bytes:longint):longint; begin if (OutputSize+Bytes)>=OutputAllocated then begin while (OutputSize+Bytes)>=OutputAllocated do begin inc(OutputAllocated,OutputAllocated); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} ReallocMem(OutputData,OutputAllocated); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; Move(Src,PAnsiChar(OutputData)[OutputSize],Bytes); inc(OutputSize,Bytes); result:=Bytes; end; var Channel,CountOrders,OrderCounter,PatternIndex,RowCounter,RowIndex,LastRowIndex, RowDeltaStreamSize,FlagStreamSize,NoteStreamSize,InstrumentStreamSize,VolumeStreamSize, EffectStreamSize,EffectParameterStreamSize:longint; RowDeltaStream,FlagStream,NoteStream,InstrumentStream,VolumeStream,EffectStream,EffectParameterStream:PStreamBytes; Flags,LastFlags,LastNote,LastInstrument,LastVolume,LastEffect,LastEffectParameter:byte; PatternNote:PCreamTrackerPatternNote; Pattern:PCreamTrackerPattern; begin result:=true; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} New(RowDeltaStream); New(FlagStream); New(NoteStream); New(InstrumentStream); New(VolumeStream); New(EffectStream); New(EffectParameterStream); try {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} OutputSize:=0; OutputAllocated:=65536; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(OutputData,OutputAllocated); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} RowDeltaStreamSize:=0; FlagStreamSize:=0; NoteStreamSize:=0; InstrumentStreamSize:=0; VolumeStreamSize:=0; EffectStreamSize:=0; EffectParameterStreamSize:=0; CountOrders:=0; for OrderCounter:=length(Instance.Orders)-1 downto 0 do begin if Instance.Orders[OrderCounter]<>$ff then begin CountOrders:=OrderCounter+1; break; end; end; if CountOrders=0 then begin CountOrders:=1; end; LastFlags:=0; LastNote:=0; LastInstrument:=0; LastVolume:=0; LastEffect:=0; LastEffectParameter:=0; for Channel:=0 to 31 do begin LastRowIndex:=0; RowIndex:=0; for OrderCounter:=0 to CountOrders-1 do begin PatternIndex:=Instance.Orders[OrderCounter]; case PatternIndex of $00..$fd:begin // Normal pattern Pattern:=@Instance.Patterns[PatternIndex]; for RowCounter:=0 to 63 do begin PatternNote:=@Pattern^[RowCounter,Channel]; Flags:=0; if PatternNote^.Note<>255 then begin Flags:=Flags or 1; end; if PatternNote^.Instrument<>0 then begin Flags:=Flags or 2; end; if PatternNote^.Volume<>255 then begin Flags:=Flags or 4; end; if PatternNote^.Effect in [1..254] then begin Flags:=Flags or 8; end; if PatternNote^.EffectParameter<>0 then begin Flags:=Flags or 16; end; if (Flags=0) and ((LastRowIndex+64)<=RowIndex) then begin Flags:=Flags or 32; end; if Flags<>0 then begin RowDeltaStream^[FlagStreamSize]:=RowIndex-LastRowIndex; inc(RowDeltaStreamSize); LastRowIndex:=RowIndex; FlagStream^[FlagStreamSize]:=Flags-LastFlags; inc(FlagStreamSize); LastFlags:=Flags; if (Flags and 1)<>0 then begin NoteStream^[NoteStreamSize]:=PatternNote^.Note-LastNote; inc(NoteStreamSize); LastNote:=PatternNote^.Note; end; if (Flags and 2)<>0 then begin InstrumentStream^[InstrumentStreamSize]:=PatternNote^.Instrument-LastInstrument; inc(InstrumentStreamSize); LastInstrument:=PatternNote^.Instrument; end; if (Flags and 4)<>0 then begin VolumeStream^[VolumeStreamSize]:=PatternNote^.Volume-LastVolume; inc(VolumeStreamSize); LastVolume:=PatternNote^.Volume; end; if (Flags and 8)<>0 then begin EffectStream^[EffectStreamSize]:=PatternNote^.Effect-LastEffect; inc(EffectStreamSize); LastEffect:=PatternNote^.Effect; end; if (Flags and 16)<>0 then begin EffectParameterStream^[EffectParameterStreamSize]:=PatternNote^.EffectParameter-LastEffectParameter; inc(EffectParameterStreamSize); LastEffectParameter:=PatternNote^.EffectParameter; end; end; inc(RowIndex); end; end; $fe:begin // Skip pattern RowDeltaStream^[FlagStreamSize]:=RowIndex-LastRowIndex; inc(RowDeltaStreamSize); LastRowIndex:=RowIndex; inc(RowIndex,64); FlagStream^[FlagStreamSize]:=64-LastFlags; inc(FlagStreamSize); LastFlags:=64; end; $ff:begin // Track end RowDeltaStream^[FlagStreamSize]:=RowIndex-LastRowIndex; inc(RowDeltaStreamSize); LastRowIndex:=RowIndex; inc(RowIndex,64); FlagStream^[FlagStreamSize]:=(64 or 32)-LastFlags; inc(FlagStreamSize); LastFlags:=64 or 32; end; end; end; // Channel end RowDeltaStream^[FlagStreamSize]:=RowIndex-LastRowIndex; inc(RowDeltaStreamSize); FlagStream^[FlagStreamSize]:=128-LastFlags; inc(FlagStreamSize); LastFlags:=128; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} Write(RowDeltaStreamSize,SizeOf(longint)); Write(FlagStreamSize,SizeOf(longint)); Write(NoteStreamSize,SizeOf(longint)); Write(InstrumentStreamSize,SizeOf(longint)); Write(VolumeStreamSize,SizeOf(longint)); Write(EffectStreamSize,SizeOf(longint)); Write(EffectParameterStreamSize,SizeOf(longint)); Write(RowDeltaStream^,RowDeltaStreamSize); Write(FlagStream^,FlagStreamSize); Write(NoteStream^,NoteStreamSize); Write(InstrumentStream^,InstrumentStreamSize); Write(VolumeStream^,VolumeStreamSize); Write(EffectStream^,EffectStreamSize); Write(EffectParameterStream^,EffectParameterStreamSize); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} ReallocMem(OutputData,OutputSize); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} finally Dispose(RowDeltaStream); Dispose(FlagStream); Dispose(NoteStream); Dispose(InstrumentStream); Dispose(VolumeStream); Dispose(EffectStream); Dispose(EffectParameterStream); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; function CreamTrackerSaveCompact(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint;IsExport,CodeExport:longbool):longbool; var OutputAllocated:longint; function Write(const Src;Bytes:longint):longint; begin if (OutputSize+Bytes)>=OutputAllocated then begin while (OutputSize+Bytes)>=OutputAllocated do begin inc(OutputAllocated,OutputAllocated); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} ReallocMem(OutputData,OutputAllocated); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; Move(Src,PAnsiChar(OutputData)[OutputSize],Bytes); inc(OutputSize,Bytes); result:=Bytes; end; var Len:longint; b:byte; DataPointer:pointer; begin result:=false; OutputSize:=0; OutputAllocated:=65536; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(OutputData,OutputAllocated); b:=Instance.Header.InitialSpeed; Write(b,sizeof(byte)); b:=Instance.Header.InitialTempo; Write(b,sizeof(byte)); b:=Instance.Header.GlobalVolume; Write(b,sizeof(byte)); b:=Instance.Header.MasterVolume; Write(b,sizeof(byte)); DataPointer:=nil; if CreamTrackerSavePatternsAsSingleChain(Instance,DataPointer,Len) then begin Write(DataPointer^,Len); result:=true; end; if assigned(DataPointer) then begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(DataPointer); end; end; function CreamTrackerSave(var Instance:TCreamTrackerInstance;var OutputData:pointer;var OutputSize:longint;IsExport,CodeExport:longbool):longbool; var OutputAllocated:longint; function Write(const Src;Bytes:longint):longint; begin if (OutputSize+Bytes)>=OutputAllocated then begin while (OutputSize+Bytes)>=OutputAllocated do begin inc(OutputAllocated,OutputAllocated); end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} ReallocMem(OutputData,OutputAllocated); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} end; Move(Src,PAnsiChar(OutputData)[OutputSize],Bytes); inc(OutputSize,Bytes); result:=Bytes; end; procedure ParaAlign(b:byte); begin while (OutputSize and 15)<>0 do begin Write(b,SizeOf(byte)); end; end; procedure SaveCode(CodeData:PCreamTrackerCodeData;Synth:longbool); var li,TempSize:longint; TempPointer:pointer; begin TempPointer:=nil; if assigned(CodeData) then begin if IsExport then begin if CodeExport and assigned(CodeData^.TRIData) and (CodeData^.TRIDataSize>0) then begin TRIStrip(CodeData^.TRIData,CodeData^.TRIDataSize,TempPointer,TempSize,@CreamTrackerGetNameCode); end else begin TempSize:=0; end; Write(TempSize,SizeOf(longint)); li:=0; Write(li,SizeOf(longint)); if assigned(TempPointer) and (TempSize>0) then begin Write(TempPointer^,TempSize); end; end else begin Write(CodeData^.TRIDataSize,SizeOf(longint)); Write(CodeData^.CodeTextSize,SizeOf(longint)); if assigned(CodeData^.TRIData) and (CodeData^.TRIDataSize>0) then begin Write(CodeData^.TRIData^,CodeData^.TRIDataSize); end; if assigned(CodeData^.CodeText) and (CodeData^.CodeTextSize>0) then begin Write(CodeData^.CodeText^,CodeData^.CodeTextSize); end; end; end else begin li:=0; Write(li,SizeOf(longint)); li:=0; Write(li,SizeOf(longint)); end; if assigned(TempPointer) then begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(TempPointer); end; end; var i,j,k,h,ParaOfs,Len:longint; ADPCMIMAState:TCreamTrackerADPCMIMAState; Okay,DoFlush:boolean; Pattern:PCreamTrackerPattern; PatternNote:PCreamTrackerPatternNote; PatternNoteEx:TCreamTrackerPatternNote; Instrument:PCreamTrackerInstrument; InstrumentPointers:array[1..99] of word; PatternPointers:array[0..255] of word; b,lb:byte; w,lw:word; si:smallint; s:PSingleArray; ss:PSingle; PatternBuffer:PAnsiChar; v:single; Chunk:TCreamTrackerChunk; DataPointer:pointer; Header:TCreamTrackerHeader; InstrumentHeader:TCreamTrackerInstrumentHeader; begin result:=false; OutputSize:=0; OutputAllocated:=65536; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(OutputData,OutputAllocated); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} if Instance.CreamTrackerModule then begin Instance.Header.Signature[0]:='C'; Instance.Header.Signature[1]:='R'; Instance.Header.Signature[2]:='E'; Instance.Header.Signature[3]:='M'; Instance.Header.RowHilightMinor:=Instance.RowHilightMinor; Instance.Header.RowHilightMajor:=Instance.RowHilightMajor; end else begin Instance.Header.Signature[0]:='S'; Instance.Header.Signature[1]:='C'; Instance.Header.Signature[2]:='R'; Instance.Header.Signature[3]:='M'; Instance.Header.RowHilightMinor:=0; Instance.Header.RowHilightMajor:=0; end; Instance.Header.EOFChar:=#$1a; Instance.Header.Type_:=$10; Instance.Header.OrdNum:=0; for i:=length(Instance.Orders)-1 downto 0 do begin if Instance.Orders[i]<>$ff then begin Instance.Header.OrdNum:=i+1; break; end; end; if Instance.Header.OrdNum=0 then begin Instance.Header.OrdNum:=1; end; if (Instance.Header.OrdNum and 1)<>0 then begin inc(Instance.Header.OrdNum); end; Instance.Header.InsNum:=0; for i:=99 downto 1 do begin if (Instance.Instruments[i].Header.InstrumentType<>0) or (Instance.Instruments[i].Header.Length>0) or (Instance.Instruments[i].Header.FileName[0]<>#0) or (Instance.Instruments[i].Header.SampleName[0]<>#0) then begin Instance.Header.InsNum:=i; break; end; end; Instance.Header.PatNum:=0; for i:=length(Instance.Patterns)-1 downto 0 do begin if i<$fe then begin Okay:=false; for j:=0 to Instance.Header.OrdNum-1 do begin if Instance.Orders[j]=i then begin Okay:=true; break; end; end; if not Okay then begin Pattern:=@Instance.Patterns[i]; for j:=0 to 63 do begin for k:=0 to 31 do begin PatternNote:=@Pattern^[j,k]; if (PatternNote^.Note<>255) or (PatternNote^.Instrument<>0) or (PatternNote^.Volume<>255) or (PatternNote^.Effect in [1..254]) or (PatternNote^.EffectParameter<>0) then begin Okay:=true; break; end; if Okay then begin break; end; end; end; end; if Okay then begin Instance.Header.PatNum:=i+1; break; end; end; end; Instance.Header.CWTV:=$7001; Instance.Header.FileFormatInformation:=2; Instance.Header.UltraClickRemoval:=16; Header:=Instance.Header; if IsExport then begin FillChar(Header.Name,SizeOf(Header.Name),AnsiChar(#0)); Header.Signature[0]:='C'; Header.Signature[1]:='R'; Header.Signature[2]:='S'; Header.Signature[3]:='M'; Header.RowHilightMinor:=Instance.RowHilightMinor; Header.RowHilightMajor:=Instance.RowHilightMajor; end; if Write(Header,SizeOf(TCreamTrackerHeader))<>SizeOf(TCreamTrackerHeader) then begin exit; end; if not IsExport then begin if Write(Instance.Orders,Instance.Header.OrdNum)<>Instance.Header.OrdNum then begin exit; end; end; FillChar(InstrumentPointers,SizeOf(InstrumentPointers),#0); FillChar(PatternPointers,SizeOf(PatternPointers),#0); ParaOfs:=OutputSize; if Write(InstrumentPointers,Instance.Header.InsNum*SizeOf(word))<>(Instance.Header.InsNum*SizeOf(word)) then begin exit; end; if IsExport then begin if Write(PatternPointers,SizeOf(word))<>SizeOf(word) then begin exit; end; end else begin if Write(PatternPointers,Instance.Header.PatNum*SizeOf(word))<>(Instance.Header.PatNum*SizeOf(word)) then begin exit; end; end; if Instance.Header.Panning=$fc then begin if Write(Instance.ChannelPannings,SizeOf(TCreamTrackerChannelPannings))<>SizeOf(TCreamTrackerChannelPannings) then begin exit; end; end; {if Instance.Header.CWTV>=$7001 then begin SaveCode(Instance.CodeData,false); end;} ParaAlign(0); for i:=1 to Instance.Header.InsNum do begin Okay:=false; Instrument:=@Instance.Instruments[i]; case Instrument^.Header.InstrumentType of 1:begin Instrument^.Header.Signature[0]:='S'; Instrument^.Header.Signature[1]:='C'; Instrument^.Header.Signature[2]:='R'; Instrument^.Header.Signature[3]:='S'; case Instrument^.Header.Format of 0:begin Okay:=true; end; 4:begin Instrument^.Header.Format:=0; Instrument^.Header.Flags:=Instrument^.Header.Flags and not (2 or 4); Okay:=true; end; 5:begin if not Instance.CreamTrackerModule then begin Instrument^.Header.Format:=0; Instrument^.Header.Flags:=Instrument^.Header.Flags or 4; end; Okay:=true; end; 6:begin if not Instance.CreamTrackerModule then begin Instrument^.Header.Format:=0; Instrument^.Header.Flags:=Instrument^.Header.Flags or 4; end; Okay:=true; end; 7:begin Okay:=true; end; end; end; 2..7:begin Instrument^.Header.Signature[0]:='S'; Instrument^.Header.Signature[1]:='C'; Instrument^.Header.Signature[2]:='R'; Instrument^.Header.Signature[3]:='I'; Okay:=true; end; 8:begin Instrument^.Header.Signature[0]:='C'; Instrument^.Header.Signature[1]:='R'; Instrument^.Header.Signature[2]:='S'; Instrument^.Header.Signature[3]:='I'; Instrument^.Header.ExtOffset:=0; Okay:=true; end; 9:begin Instrument^.Header.Signature[0]:='C'; Instrument^.Header.Signature[1]:='R'; Instrument^.Header.Signature[2]:='S'; Instrument^.Header.Signature[3]:='S'; Instrument^.Header.ExtOffset:=0; Instrument^.Header.Format:=0; Instrument^.Header.Flags:=Instrument^.Header.Flags or (2 or 4); Okay:=true; end; else begin Instrument^.Header.Signature[0]:=#0; Instrument^.Header.Signature[1]:=#0; Instrument^.Header.Signature[2]:=#0; Instrument^.Header.Signature[3]:=#0; Okay:=true; end; end; if Okay then begin InstrumentPointers[i]:=OutputSize shr 4; InstrumentHeader:=Instrument^.Header; if IsExport then begin FillChar(InstrumentHeader.FileName,SizeOf(InstrumentHeader.FileName),AnsiChar(#0)); FillChar(InstrumentHeader.SampleName,SizeOf(InstrumentHeader.SampleName),AnsiChar(#0)); end; if Write(InstrumentHeader,SizeOf(TCreamTrackerInstrumentHeader))<>SizeOf(TCreamTrackerInstrumentHeader) then begin exit; end; ParaAlign(0); end; end; if IsExport then begin PatternPointers[0]:=OutputSize shr 4; DataPointer:=nil; if CreamTrackerSavePatternsAsSingleChain(Instance,DataPointer,Len) then begin Write(DataPointer^,Len); end; if assigned(DataPointer) then begin {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(DataPointer); end; ParaAlign(0); end else begin for i:=0 to Instance.Header.PatNum-1 do begin Okay:=false; for j:=0 to Instance.Header.OrdNum-1 do begin if Instance.Orders[j]=i then begin Okay:=true; break; end; end; if not Okay then begin Pattern:=@Instance.Patterns[i]; for j:=0 to 63 do begin for k:=0 to 31 do begin PatternNote:=@Pattern^[j,k]; if (PatternNote^.Note<>255) or (PatternNote^.Instrument<>0) or (PatternNote^.Volume<>255) or (PatternNote^.Effect in [1..254]) or (PatternNote^.EffectParameter<>0) then begin Okay:=true; break; end; if Okay then begin break; end; end; end; end; if Okay then begin PatternPointers[i]:=OutputSize shr 4; Pattern:=@Instance.Patterns[i]; {$ifdef debugmem}GetMemory{$else}GetMem{$endif}(PatternBuffer,65536);//(64*((32*6)+1))); PatternNote:=@PatternNoteEx; Len:=0; for j:=0 to 63 do begin for k:=0 to 31 do begin PatternNoteEx:=Pattern^[j,k]; b:=k; if (PatternNote^.Note<>$ff) or (PatternNote^.Instrument<>0) then begin b:=b or $20; end; if (PatternNote^.Note=$fd) and not Instance.CreamTrackerModule then begin PatternNote^.Note:=$fe; end; if PatternNote^.Volume<=64 then begin b:=b or $40; end; if (PatternNote^.Effect in [1..254]) or (PatternNote^.EffectParameter<>0) then begin b:=b or $80; end; if (b and ($20 or $40 or $80))<>0 then begin byte(PatternBuffer[Len]):=b; inc(Len); if (b and $20)<>0 then begin if PatternNote^.Note<$f0 then begin byte(PatternBuffer[Len]):=((PatternNote^.Note mod 12) and $f) or ((PatternNote^.Note div 12) shl 4); end else begin byte(PatternBuffer[Len]):=PatternNote^.Note; end; inc(Len); byte(PatternBuffer[Len]):=PatternNote^.Instrument; inc(Len); end; if (b and $40)<>0 then begin byte(PatternBuffer[Len]):=PatternNote^.Volume; inc(Len); end; if (b and $80)<>0 then begin byte(PatternBuffer[Len]):=PatternNote^.Effect; inc(Len); byte(PatternBuffer[Len]):=PatternNote^.EffectParameter; inc(Len); end; end; end; byte(PatternBuffer[Len]):=0; inc(Len); end; w:=Len; Write(w,SizeOf(word)); Write(PatternBuffer^,Len); {$ifdef debugmem}FreeMemory{$else}FreeMem{$endif}(PatternBuffer); ParaAlign(0); end; end; end; for i:=1 to Instance.Header.InsNum do begin Instrument:=@Instance.Instruments[i]; case Instrument^.Header.InstrumentType of 1:begin Instrument^.Header.Offset[0]:=OutputSize shr 20; Instrument^.Header.Offset[2]:=OutputSize shr 12; Instrument^.Header.Offset[1]:=OutputSize shr 4; case Instrument^.Header.Format of 0:begin // Uncompressed non-delta PCM case Instrument^.Header.Flags and (2 or 4) of 0:begin // 8-bit mono s:=Instrument^.Data; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(((s^[(j shl 1) or 0]+s^[(j shl 1) or 1])*0.5)*128.0); if k<-128 then begin k:=-128; end else if k>127 then begin k:=127; end; b:=k+128; Write(b,SizeOf(byte)); end; end; 2:begin // 8-bit stereo s:=Instrument^.Data; for h:=0 to 1 do begin for j:=0 to Instrument^.Header.Length-1 do begin k:=round(s^[(j shl 1) or h]*128); if k<-128 then begin k:=-128; end else if k>127 then begin k:=127; end; b:=k+128; Write(b,SizeOf(byte)); end; end; end; 4:begin // 16-bit mono s:=Instrument^.Data; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(((s^[(j shl 1) or 0]+s^[(j shl 1) or 1])*0.5)*32768); if k<-32768 then begin k:=-32768; end else if k>32727 then begin k:=32727; end; w:=k+32768; Write(w,SizeOf(word)); end; end; 2 or 4:begin // 16-bit stereo s:=Instrument^.Data; for h:=0 to 1 do begin for j:=0 to Instrument^.Header.Length-1 do begin k:=round(s^[(j shl 1) or h]*32768); if k<-32768 then begin k:=-32768; end else if k>32727 then begin k:=32727; end; w:=k+32768; Write(w,SizeOf(word)); end; end; end; end; end; 5:begin // IMA ADPCM4 (the TRUE adaptive variant) if Instance.CreamTrackerModule then begin for j:=0 to (Instrument^.Header.Flags shr 1) and 1 do begin ADPCMIMAState.PrevSample:=0; ADPCMIMAState.StepIndex:=0; b:=0; ss:=Instrument^.Data; inc(ss,j); DoFlush:=false; for k:=0 to Instrument^.Header.Length-1 do begin if (Instrument^.Header.Flags and 2)=0 then begin v:=ss^; inc(ss); v:=(v+ss^)*0.5; inc(ss); end else begin v:=ss^; inc(ss,2); end; h:=round(v*32768.0); if h<-32768 then begin h:=-32768; end else if h>32767 then begin h:=32767; end; if k=0 then begin ADPCMIMAState.PrevSample:=k; ADPCMIMAState.StepIndex:=0; si:=k; Write(si,SizeOf(smallint)); end; if (k and 1)=0 then begin b:=CreamTrackerADPCMIMACompressSample(ADPCMIMAState,h); DoFlush:=true; end else begin DoFlush:=false; b:=b or (CreamTrackerADPCMIMACompressSample(ADPCMIMAState,h) shl 4); Write(b,sizeof(byte)); end; end; if DoFlush then begin Write(b,sizeof(byte)); end; end; end; end; 6:begin // Sinusoidal frequency envelope dissected sample for to resynthesizing it (very good usable for vocals!) if Instance.CreamTrackerModule and assigned(Instrument^.RawData) and (Instrument^.RawLen>0) then begin Write(Instrument^.RawData^,Instrument^.RawLen); end; end; 7:begin // Uncompressed delta PCM case Instrument^.Header.Flags and (2 or 4) of 0:begin // 8-bit mono s:=Instrument^.Data; lb:=0; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(((s^[(j shl 1) or 0]+s^[(j shl 1) or 1])*0.5)*128.0); if k<-128 then begin k:=-128; end else if k>127 then begin k:=127; end; b:=byte(k+128)-lb; Write(b,SizeOf(byte)); lb:=byte(k+128); end; end; 2:begin // 8-bit stereo s:=Instrument^.Data; for h:=0 to 1 do begin lb:=0; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(s^[(j shl 1) or h]*128); if k<-128 then begin k:=-128; end else if k>127 then begin k:=127; end; b:=byte(k+128)-lb; Write(b,SizeOf(byte)); lb:=byte(k+128); end; end; end; 4:begin // 16-bit mono s:=Instrument^.Data; lw:=0; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(((s^[(j shl 1) or 0]+s^[(j shl 1) or 1])*0.5)*32768); if k<-32768 then begin k:=-32768; end else if k>32727 then begin k:=32727; end; w:=word(k+32768)-lw; Write(w,SizeOf(word)); lw:=word(k+32768); end; end; 2 or 4:begin // 16-bit stereo s:=Instrument^.Data; for h:=0 to 1 do begin lw:=0; for j:=0 to Instrument^.Header.Length-1 do begin k:=round(s^[(j shl 1) or h]*32768); if k<-32768 then begin k:=-32768; end else if k>32727 then begin k:=32727; end; w:=word(k+32768)-lw; Write(w,SizeOf(word)); lw:=word(k+32768); end; end; end; end; end; end; ParaAlign($80); end; 8:begin Instrument^.Header.Offset[0]:=OutputSize shr 20; Instrument^.Header.Offset[2]:=OutputSize shr 12; Instrument^.Header.Offset[1]:=OutputSize shr 4; Instrument^.Header.ExtOffset:=OutputSize; Instrument^.Header.ExtOffset:=OutputSize; Instrument^.Header.ExtOffset:=OutputSize; SaveCode(Instrument^.CodeData,true); ParaAlign($80); end; 9:begin Instrument^.Header.Offset[0]:=OutputSize shr 20; Instrument^.Header.Offset[2]:=OutputSize shr 12; Instrument^.Header.Offset[1]:=OutputSize shr 4; Instrument^.Header.ExtOffset:=OutputSize; Instrument^.Header.ExtOffset:=OutputSize; Instrument^.Header.ExtOffset:=OutputSize; SaveCode(Instrument^.CodeData,false); ParaAlign($80); end; end; end; j:=OutputSize; OutputSize:=ParaOfs; if Write(InstrumentPointers,Instance.Header.InsNum*SizeOf(word))<>(Instance.Header.InsNum*SizeOf(word)) then begin exit; end; if IsExport then begin if Write(PatternPointers,SizeOf(word))<>SizeOf(word) then begin exit; end; end else begin if Write(PatternPointers,Instance.Header.PatNum*SizeOf(word))<>(Instance.Header.PatNum*SizeOf(word)) then begin exit; end; end; for i:=1 to Instance.Header.InsNum do begin if InstrumentPointers[i]<>0 then begin Instrument:=@Instance.Instruments[i]; OutputSize:=InstrumentPointers[i] shl 4; if Write(Instrument^.Header,SizeOf(TCreamTrackerInstrumentHeader))<>SizeOf(TCreamTrackerInstrumentHeader) then begin exit; end; if IsExport then begin OutputSize:=(InstrumentPointers[i] shl 4)+(PAnsiChar(pointer(@Instrument^.Header.FileName))-PAnsiChar(pointer(@Instrument^.Header))); for k:=0 to 11 do begin b:=0; if Write(b,SizeOf(byte))<>SizeOf(byte) then begin exit; end; end; OutputSize:=(InstrumentPointers[i] shl 4)+(PAnsiChar(pointer(@Instrument^.Header.SampleName))-PAnsiChar(pointer(@Instrument^.Header))); for k:=0 to 27 do begin b:=0; if Write(b,SizeOf(byte))<>SizeOf(byte) then begin exit; end; end; end; end; end; OutputSize:=j; if Instance.CreamTrackerModule then begin Instance.Header.Data:=OutputSize; Header.Data:=OutputSize; if not IsExport then begin j:=OutputSize; Chunk.Signature[0]:='S'; Chunk.Signature[1]:='H'; Chunk.Signature[2]:='C'; Chunk.Signature[3]:='O'; Chunk.Size:=0; if Write(Chunk,SizeOf(TCreamTrackerChunk))<>SizeOf(TCreamTrackerChunk) then begin exit; end; SaveCode(Instance.SharedCodeData,false); k:=OutputSize; OutputSize:=j; Chunk.Size:=k-(j+SizeOf(TCreamTrackerChunk)); if Write(Chunk,SizeOf(TCreamTrackerChunk))<>SizeOf(TCreamTrackerChunk) then begin exit; end; OutputSize:=k; end; begin j:=OutputSize; Chunk.Signature[0]:='C'; Chunk.Signature[1]:='O'; Chunk.Signature[2]:='D'; Chunk.Signature[3]:='E'; Chunk.Size:=0; if Write(Chunk,SizeOf(TCreamTrackerChunk))<>SizeOf(TCreamTrackerChunk) then begin exit; end; SaveCode(Instance.CodeData,false); k:=OutputSize; OutputSize:=j; Chunk.Size:=k-(j+SizeOf(TCreamTrackerChunk)); if Write(Chunk,SizeOf(TCreamTrackerChunk))<>SizeOf(TCreamTrackerChunk) then begin exit; end; OutputSize:=k; end; begin Chunk.Signature[0]:='D'; Chunk.Signature[1]:='O'; Chunk.Signature[2]:='N'; Chunk.Signature[3]:='E'; Chunk.Size:=0; if Write(Chunk,SizeOf(TCreamTrackerChunk))<>SizeOf(TCreamTrackerChunk) then begin exit; end; end; j:=OutputSize; OutputSize:=0; if Write(Header,SizeOf(TCreamTrackerHeader))<>SizeOf(TCreamTrackerHeader) then begin exit; end; OutputSize:=j; end; {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} ReallocMem(OutputData,OutputSize); {$ifdef memdebug}ScanMemoryPoolForCorruptions;{$endif} result:=true; end; {$endif} initialization CheckCPU; SIMDSetFlags; {$ifdef CanSINC} {$ifndef Intro} {$ifndef CreamTrackerGUI} InitResamplerSINC; {$endif} {$endif} {$endif} finalization {$ifdef CanSINC} DoneResamplerSINC; {$endif} end.