PDA

View Full Version : Sample Compression Format for IT (Impulse Tracker) Modules



WILL
13-06-2006, 11:59 PM
This is an age old problem that I've had since I first started working on the OpenMOD library some few years ago.

What are all the compression formats used on the samples used in a Impulse Tracker module?

I will post the exact issues I had with loading and playing back samples from the library. (still in development, but halted due to time constraints and other projects) I have to look it up in the source. And since I'm in the process of converting all my sources to FPC/Lazarus, I don't have a running demo app. to test things out yet.

One thing I do remember quite specifically though was that when I took a mod that was created (seemingly) with Impulse Tracker it's self and then tried to load one saved (modified or created by) ModPlug Tracker it would cease to load properly.

fragle
15-06-2006, 11:56 AM
Here comes my highly random guess: maybe it was because of ADPCM compression MODPlug can use to reduce sample size? Maybe it gets applied by default on saving in MP Tracker? Though it seems Impulse Tracker has its own sample compression as well... Dunno >_<

WILL
15-06-2006, 12:51 PM
Ah, found it! :)

Now this is very very OLD Delphi produced code. So you'll have to forgive any lax conventions here.

I obviously am rewritting it, but the fact that I was unable to get IT samples to playback made me wonder. The problem quite simply was that Impulse Tracker prodced modules would load and MODPlug would not. Where as, only in IT did I have trouble decoding or playing back samples. S3M played them back just fine. Don't recall getting to the point of trying XM samples though.

I'll also include the S3MData.pas and ITData.pas sources for the load routines. IT had some packing/unpacking routines that I think may have tricked me aswell perhaps someone can point out points about the proper method for the use of these aswell. :)

unit Unit1;

interface

uses
// Standard Libs
SysUtils, Types, Classes, Variants,
// CLX Cross-Platform Libs
Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ExtCtrls,
// OpenGL Libs
al, oooal, altypes, alut,
// OpenMod Libs
S3MData, ITData, XMData, ModTools;

type
TForm1 = class(TForm)
SongNameBox: TEdit;
Label1: TLabel;
OrdersList: TListBox;
SamplesBox: TListBox;
SampleAddressBox: TListBox;
ChannelsBox: TEdit;
PatternsBox: TEdit;
SongLengthBox: TEdit;
Label4: TLabel;
Label5: TLabel;
Label2: TLabel;
Label3: TLabel;
Label6: TLabel;
Label7: TLabel;
Button1: TButton;
OpenMODDlg: TOpenDialog;
PlaySample: TSpeedButton;
Timer1: TTimer;
ModTypeBox: TEdit;
Label8: TLabel;
procedure Button1Click(Sender: TObject);
procedure SamplesBoxDblClick(Sender: TObject);
procedure PlaySampleClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure OrdersListDblClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

ModType: Integer;
S3M: S3M_Mixer;
IT: IT_Mixer;
XM: XM_Mixer;

{-------}
// AudioSample: TSDL_AudioSpec;

Sample: TALObject;

// OpenAL Possitioning Settings
listenerpos: array [0..2] of TALfloat= ( 0.0, 0.0, 0.0);
listenervel: array [0..2] of TALfloat= ( 0.0, 0.0, 0.0);
listenerori: array [0..5] of TALfloat= ( 0.0, 0.0, -1.0, 0.0, 1.0, 0.0);
playbuf : array[0..7] of TAluint;
source : array[0..7] of TALuint;

{--------}

implementation

uses Unit2, Unit3;

{$R *.xfm}


procedure TForm1.Button1Click(Sender: TObject);
var i, j: Integer;
TempStr: String;
begin
if (OpenMODDlg.Execute) then
begin
ModType := GetModType(OpenMODDlg.FileName);
ModTypeBox.Text := MODTypeString[ModType];
if (ModType = mtS3M) then
begin
S3M := S3M_Mixer.Create;

S3M.LoadFromFile(OpenMODDlg.FileName);


SongNameBox.Text := GetModString(S3M.Name);
ChannelsBox.Text := IntToStr(S3M.NumberOfChannels);
PatternsBox.Text := IntToStr(S3M.Header.PatNum);
//PatternsBox.Text := IntToStr(S3M.NumberOfPatterns);
SongLengthBox.Text := IntToStr(S3M.SongLength);

OrdersList.Clear;
for i := 0 to S3M.SongLength - 1 do
OrdersList.Items.Add(IntToStr(S3M.Orders[i]));

SamplesBox.Clear;
SampleAddressBox.Clear;
for i := 0 to S3M.Header.InsNum - 1 do
begin
SamplesBox.Items.Add(GetModString(S3M.Instruments[i].SampleName));
SampleAddressBox.Items.Add(IntToHex(S3M.InsPtr[i] * 16, 8));
end;
S3M.Free;
end;
if (ModType = mtIT) then
begin
IT := IT_Mixer.Create;

IT.LoadFromFile(OpenMODDlg.FileName);


SongNameBox.Text := GetModString(IT.Name);
ChannelsBox.Text := IntToStr(IT.NumberOfChannels);
PatternsBox.Text := IntToStr(IT.Header.PatternNumber);

SamplesBox.Clear;
SampleAddressBox.Clear;
for i := 0 to IT.Header.SampleNumber - 1 do
begin
SamplesBox.Items.Add(GetModString(IT.Samples[i].SampleName));
SampleAddressBox.Items.Add(IntToHex(IT.SamPtr[i], 8));
end;
IT.Free;
end;
if (ModType = mtXM) then
begin
XM := XM_Mixer.Create;

XM.LoadFromFile(OpenMODDlg.FileName);

SongNameBox.Text := GetModString(XM.Name);
ChannelsBox.Text := IntToStr(XM.Header.NumOfChannels);
PatternsBox.Text := IntToStr(XM.Header.NumOfPatterns);

XM.Free;
end;
end;
end;

procedure TForm1.SamplesBoxDblClick(Sender: TObject);
type
TSample = Array[0..65528] of Byte; { One sample }
TFixedSample = Array[0..65528] of SmallInt; { One sample }
var
i: Integer;
Samp: TSample;
FixedSamp: TFixedSample;
Sample_Data: ^TSample; { Pointers to actual sample data }
Sample_Size: Word;
FixedSample_Data: ^TFixedSample; { Pointers to actual sample data }
FixedSample_Size: Word;
AudioFormat: Integer;
begin
SamplePreview.Show;

if (ModType = mtS3M) then
DrawSample(S3M.Instruments[SamplesBox.ItemIndex].SampleData,
S3M.Instruments[SamplesBox.ItemIndex].SampleLength, False);
{ if (ModType = mtIT) then
DrawSample(IT.Samples[SamplesBox.ItemIndex].SampleData,
IT.Samples[SamplesBox.ItemIndex].Length);}


if (Sample <> nil) then
Sample.Free;

Sample := TALObject.Create;

// Retrive Sample Data
if (ModType = mtS3M) then
Sample.LoadFromBuffer2(S3M.Instruments[SamplesBox.ItemIndex].SampleData,
S3M.Instruments[SamplesBox.ItemIndex].SampleLength,
8392, AL_FORMAT_MONO8);

if (ModType = mtIT) then
begin
if (IT.Samples[SamplesBox.ItemIndex].isStereo) then
if (IT.Samples[SamplesBox.ItemIndex].is16Bit) then
AudioFormat := AL_FORMAT_STEREO16
else
AudioFormat := AL_FORMAT_STEREO8
else
if (IT.Samples[SamplesBox.ItemIndex].is16Bit) then
AudioFormat := AL_FORMAT_MONO16
else
AudioFormat := AL_FORMAT_MONO8;

Sample.LoadFromBuffer2(IT.Samples[SamplesBox.ItemIndex].SampleData,
IT.Samples[SamplesBox.ItemIndex].Length,
// IT.Samples[SamplesBox.ItemIndex].C5Speed,
8392,
AudioFormat);
end;

Sample.Update;
Sample.Play;
end;

procedure TForm1.PlaySampleClick(Sender: TObject);
//var AquiredSample: TSDL_AudioSpec;
// AudioPtr, AquiredPtr: PSDL_AudioSpec;
// err: Integer;
begin
// AudioSample.freq := 22050;
// AudioSample.format := AUDIO_U8;
// if (S3M.isMono) then
// AudioSample.channels := 1
// else
// AudioSample.channels := 2;
// AudioSample.silence
// AudioSample.samples := S3M.Instruments[SamplesBox.ItemIndex].SampleLength;
// AudioSample.size := S3M.Instruments[SamplesBox.ItemIndex].SampleLength;
// AudioSample.userdata := S3M.Instruments[SamplesBox.ItemIndex].SampleData;

// AudioSample.callback :=
// AudioPtr := Addr(AudioSample);
// AquiredPtr := Addr(AquiredSample);
// err := SDL_OpenAudio(AudioPtr, AquiredPtr);
// if (err <> 0) then
// ShowMessage('Error Opening Audio!');
end;

procedure TForm1.FormCreate(Sender: TObject);
var
argv: array of PChar;
loop: integer;
begin
AlutInit(nil, argv);

AlGenSources(8, @source);
AlGenBuffers(8, @playbuf);

//set up listener
AlListenerfv(AL_POSITION, @listenerpos);
AlListenerfv(AL_VELOCITY, @listenervel);
AlListenerfv(AL_ORIENTATION, @listenerori);

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
AlDeleteSources(8, @source);
AlDeleteBuffers(8, @playbuf);

if (Sample <> nil) then
begin
if (Sample.playing) then
Sample.Stop;
Sample.Destroy;
end;

AlutExit();
end;

end.

unit S3MData;

interface

uses
CommonData, ModTools;

type
S3M_Header = Record
SongName : Array[1 .. 28] of Char;
Unknown1 : Byte;{should be '1A' in hex}
Typ : Byte;{File type: 16=module,17=song}
Unknown2 : Word;{Unknown! For Expansion Useless}
OrdNum : Word;{Number of orders in file}
InsNum : Word;{Number of instruments in file}
PatNum : Word;{Number of patterns in file}
Flags : Word;
TrackerVersion : Word;{Created with tracker / version}
FileFormatVersion : Word;
SCRM : Array[1 .. 4] of Char;
GlobalVolume : Byte;
InitialSpeed : Byte;
InitialTempo : Byte;
MasterVolume : Byte;
UltraClickRemoval : Byte;
DefaultPanning : Byte;
Unknown3 : Array[1 .. 8] of Byte;{For expansion, ignore}
Special1 : Word;
end;
S3M_Instrument = Record
InsType : Byte;
FileName : Array[1 .. 12] of Char;
MemSeg : Integer;
SampleLength : Word;
LoopBegin : Word;
LoopEnd : Word;
Volume : Byte;
PackingScheme : Byte; {0 = unpacked, 1 = DP30ADPCM packing}
Flags : Byte; {BYTE AND 1 = Loop on,BYTE AND 2 = Stereo sample (not supported),
BYTE AND 4 = 16 Bit sample (not supported)}
C2SPD : Word;
SampleName : Array[1 .. 28] of Char;
SCRS : Array[1 .. 4] of Char;{'SCRS'}

{Flags}
isLoop : Boolean;
isStereo : Boolean;
is16Bit : Boolean;

SampleData : Pointer;
end;
{ TODO 1 : Add field to indicate END OF ROW }
S3M_Note = Record
Channel : Byte;
Note : Byte;
Instrument : Byte;
Volume : Byte;
Effect : Byte;
EffectParameter : Byte;

SetVolume : Boolean;
LastNote : Boolean;
end;
S3M_Pattern = class(TObject)
NumberOfNotes: Integer;
Notes: Array of S3M_Note;
end;
S3M_Mixer = class(TObject)
Name: String;
NumberOfChannels: Integer;
NumberOfPatterns: Integer;
SongLength: Integer;
Header: S3M_Header;
// Channel Settings: Disabled >= 16 > Right(Enabled) > 7 >= Left(Enabled)
ChannelSettings: Array[0 .. 31] of Byte;

Panning: Array[0 .. 31] of Byte;
isMono: Boolean;

ReMap: Array[0 .. 31] of Byte;
PanValue: Array[0 .. 31] of Pan_Type;

Orders: Array[0 .. 255] of Byte;

Instruments: Array[0 .. 99] of S3M_Instrument;

Patterns: Array[0 .. 253] of S3M_Pattern;

// To be moved after development debugging!!!
InsPtr: Array[0..99] of Word;
PatPtr: Array[0..255] of Word;
// To be moved after development debugging!!!

procedure LoadFromFile(FileName: String);
end;


implementation


{S3M_Mixer Functions}
procedure S3M_Mixer.LoadFromFile(FileName: String);
var FileStream: File;
OldByteBuffer,
ByteBuffer, ByteBuffer2: Byte;
WordBuffer, WordBuffer2: Word;
i, j: Integer;
NoteIndex, RowIndex: Integer;
DumpNote : Boolean;
RecordBlankRow: Boolean;
begin
{Read Header From File}
AssignFile(FileStream, FileName);
Reset(FileStream, 1);

BlockRead(FileStream, Header, SizeOf(Header));

{Display S3M Information}
if (Header.SCRM = 'SCRM') then
begin
{SongName}
Name := Header.SongName;
end;

{Read Channel Settings}
// Channel Settings: Disabled >= 16 > Right(Enabled) > 7 >= Left(Enabled)
NumberOfChannels := 0;
for i := 0 to 31 do
ReMap[i] := 255;
for i := 0 to 31 do
begin
BlockRead(FileStream, ChannelSettings[i], SizeOf(ChannelSettings[i]));
if (ChannelSettings[i] < 16) then
begin
ReMap[i] := NumberOfChannels;
if (ChannelSettings[i] <= 7) then
PanValue[NumberOfChannels] := Left
else
PanValue[NumberOfChannels] := Right;
inc(NumberOfChannels);
end;
end;

{Read Orders}
SongLength := 0;
NumberOfPatterns := 0;
for i := 0 to Header.OrdNum - 1 do
begin
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
if (ByteBuffer = 255) then
Break;
if (ByteBuffer < 254) then
begin
Orders[SongLength] := ByteBuffer;

if (Orders[SongLength] > NumberOfPatterns) then
NumberOfPatterns := Orders[SongLength];
inc(SongLength);
end;
end;

{ParaPointers}
for i := 0 to Header.InsNum - 1 do
BlockRead(FileStream, InsPtr[i], SizeOf(InsPtr[i]));
for i := 0 to Header.PatNum do
BlockRead(FileStream, PatPtr[i], SizeOf(PatPtr[i]));

{Default Pan Positions}
if (Header.DefaultPanning = 252) then
for i := 0 to 31 do
begin
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
Panning[i] := ByteBuffer AND $0F;
end;

{Mono}
isMono := False;
if ((Header.MasterVolume AND 128) > 0) then
isMono := True;

{Instruments}
for i := 0 to Header.InsNum - 1 do
begin
Seek(FileStream, InsPtr[i] * 16);
BlockRead(FileStream, Instruments[i].InsType, SizeOf(Instruments[i].InsType));
j := 1;
repeat
BlockRead(FileStream, Instruments[i].FileName[j], SizeOf(Instruments[i].FileName[j]));
inc(j);
until (j = 13);

BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));

Instruments[i].MemSeg := (ByteBuffer shl 16) + WordBuffer;

BlockRead(FileStream, Instruments[i].SampleLength, SizeOf(Instruments[i].SampleLength));
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
Instruments[i].SampleLength := Instruments[i].SampleLength + (WordBuffer shl 8);

BlockRead(FileStream, Instruments[i].LoopBegin, SizeOf(Instruments[i].LoopBegin));
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
Instruments[i].LoopBegin := Instruments[i].LoopBegin + (WordBuffer shl 8);

BlockRead(FileStream, Instruments[i].LoopEnd, SizeOf(Instruments[i].LoopEnd));
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
Instruments[i].LoopEnd := Instruments[i].LoopEnd + (WordBuffer shl 8);

BlockRead(FileStream, Instruments[i].Volume, SizeOf(Instruments[i].Volume));
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));

{0 = unpacked, 1 = DP30ADPCM packing}
BlockRead(FileStream, Instruments[i].PackingScheme, SizeOf(Instruments[i].PackingScheme));

BlockRead(FileStream, Instruments[i].Flags, SizeOf(Instruments[i].Flags));
if (Instruments[i].Flags AND 1 = 1) then
Instruments[i].isLoop := True
else
Instruments[i].isLoop := False;
if (Instruments[i].Flags AND 2 = 1) then
Instruments[i].isStereo := True
else
Instruments[i].isStereo := False;
if (Instruments[i].Flags AND 4 = 1) then
Instruments[i].is16Bit := True
else
Instruments[i].is16Bit := False;

BlockRead(FileStream, Instruments[i].C2SPD, SizeOf(Instruments[i].C2SPD));
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
Instruments[i].C2SPD := Instruments[i].C2SPD + (WordBuffer shl 8);

for j := 1 to 6 do
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));

BlockRead(FileStream, Instruments[i].SampleName, SizeOf(Instruments[i].SampleName));
BlockRead(FileStream, Instruments[i].SCRS, SizeOf(Instruments[i].SCRS));
end;

{Patterns}
for i := 0 to Header.PatNum - 1 do
begin
Seek(FileStream, PatPtr[i] * 16);
// This is the length of the packed pattern data - No need to store this data
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));

// Init next Pattern
Patterns[i] := S3M_Pattern.Create;

Patterns[i].NumberOfNotes := NumberOfChannels * 64;
SetLength(Patterns[i].Notes, Patterns[i].NumberOfNotes);

RowIndex := 1;
NoteIndex := 0;
DumpNote := False;
RecordBlankRow := False;
OldByteBuffer := 1; // Init to something other than 0 so no blank rows will be recorded.
repeat
// for j := 0 to (Patterns[i].NumberOfNotes) - 1 do
begin
repeat
begin
DumpNote := False;
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));

if (OldByteBuffer = 0) and (ByteBuffer = 0) and (not RecordBlankRow) then
RecordBlankRow := True
else
RecordBlankRow := False;

OldByteBuffer := ByteBuffer;

// if byte = 0 then end of row! if previous Byte read was an end of Row marker and LastNote was recorded mark new note
if (ByteBuffer > 0) or (RecordBlankRow) then
begin
// Check to make sure that Note is on a valid channel!
if (ReMap[ByteBuffer AND 31] > NumberOfChannels) then
begin
DumpNote := True;

if (ByteBuffer AND 32 > 0) then
BlockRead(FileStream, WordBuffer2, SizeOf(ByteBuffer2));
if (ByteBuffer AND 64 > 0) then
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
if (ByteBuffer AND 128 > 0) then
BlockRead(FileStream, WordBuffer2, SizeOf(ByteBuffer2));
end
else
begin
Patterns[i].Notes[NoteIndex].Channel := ReMap[ByteBuffer AND 31];

if (ByteBuffer AND 32 > 0) then
begin
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
Patterns[i].Notes[NoteIndex].Note := ByteBuffer2;
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
Patterns[i].Notes[NoteIndex].Instrument := ByteBuffer2;
end;
if (ByteBuffer AND 64 > 0) then
begin
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
Patterns[i].Notes[NoteIndex].Volume := ByteBuffer2;
Patterns[i].Notes[NoteIndex].SetVolume := True;
end
else
Patterns[i].Notes[NoteIndex].SetVolume := False;
if (ByteBuffer AND 128 > 0) then
begin
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
Patterns[i].Notes[NoteIndex].Effect := ByteBuffer2;
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
Patterns[i].Notes[NoteIndex].EffectParameter := ByteBuffer2;
end;

inc(NoteIndex);
end;

// if End of Row marker was set and new blank row is recorded
if (RecordBlankRow) then
Patterns[i].Notes[NoteIndex - 1].LastNote := True;
end
else
begin
inc(RowIndex);
Patterns[i].Notes[NoteIndex - 1].LastNote := True;
end;
end;
until (DumpNote = False);
if (RowIndex > 64) then
Break;
end;
until (NoteIndex > Patterns[i].NumberOfNotes - 1);

// Real NumberOfNotes
Patterns[i].NumberOfNotes := NoteIndex;
end;

{Samples}
for i := 0 to Header.InsNum - 1 do
begin
Seek(FileStream, Instruments[i].MemSeg * 16);
GetMem(Instruments[i].SampleData, Instruments[i].SampleLength);

BlockRead(FileStream, Instruments[i].SampleData^, Instruments[i].SampleLength);
end;

CloseFile(FileStream);
end;

end.

unit ITData;
interface

uses
CommonData, ModTools;


type
IT_Header = Record
IMPM : Array[1 .. 4] of Char;
SongName : Array[1 .. 26] of Char;
PatternHilight : Word;{Useless for playback}
OrderNumber : Word;
InstrumentNumber : Word;
SampleNumber : Word;
PatternNumber : Word;
CreatedWith : Word;
CompatableWith : Word;
Flags : Word;
Special : Word;
GlobalVolume : Byte;
MixingVolume : Byte;
InitialSpeed : Byte;
InitialTempo : Byte;
PanningSeparation : Byte;{Panning separation between channels (0->128, 128 is max sep.)}
PitchWheelDepth : Byte;
MessageLength : Word;
MessageOffset : LongWord;
Reservered1 : LongWord;
end;
IT_Instrument_Old = Record {cmwt < $200}
IMPI : Array[1 .. 4] of Char;
DOSFileName : Array[1 .. 12] of Char;
Reserved1 : Byte; {should be $00}
Flag : Byte; {Bit#0: ON = Use volume envelope
Bit#1: ON = Use volume loop
Bit#2: ON = Use sustain volume loop}
VolumeLoopStart : Byte; {Volume loop start (node number)}
VolumeLoopEnd : Byte; {Volume loop end (node number)}
SustainLoopStart : Byte; {Sustain loop start (node number)}
SustainLoopEnd : Byte; {Sustain loop end (node number)}
Unknown1 : Word;
FadeOut : Word;
NewNoteAction : Byte; {New note action:
0 = Note cut
1 = Note continue
2 = Note off
3 = Note fade}
DuplicateNoteCheck : Byte; {Duplicate note check (0 = Off, 1 = On)}
TrackerVersion : Word; {Tracker version used to save the instrument. This is only
used in the instrument files.}
NumberOfSamples : Byte; {Number of samples associated with instrument. This is only
used in the instrument files.}
Reserved2 : Byte;
InstrumentName : Array[1 .. 26] of Char;
Reserved3 : Array[1 .. 6] of Byte;
NoteSampleKeyboardTable : Array[1 .. 240] of Byte;
VolumeEnvelope : Array[1 .. 200] of Byte;
NodePoints : Array[1 .. 25, 1 .. 2] of Byte;{Node points (25x2 bytes)
Node data: Tick THEN magnitude}
end;
IT_Envelope_Node = Record
Value : Integer; {0->64 for vol, -32->+32 for panning or pitch}
Tick : Word; {for tick number (0->9999)}
end;
IT_Envelope = Record
Flag : Byte;
NumberOfNodePoints : Byte;
LoopBegin : Byte;
LoopEnd : Byte;
SustainLoopBegin : Byte;
SustainLoopEnd : Byte;
Nodes : Array[0 .. 24] of IT_Envelope_Node;
end;
IT_Instrument = Record
IMPI : Array[1 .. 4] of Char;
DOSFileName : Array[1 .. 12] of Char;
Reserved1 : Byte; {should be $00}
NewNoteAction : Byte; {New Note Action
0 = Cut
1 = Continue
2 = Note off
3 = Note fade}
DuplicateCheckType : Byte; {Duplicate Check Type
0 = Off
1 = Note
2 = Sample
3 = Instrument}
DuplicateCheckAction : Byte; {Duplicate Check Action
0 = Cut
1 = Note Off
2 = Note fade}
FadeOut : Word;
PitchPanSeparation : Byte; {Pitch-Pan separation, range -32 -> +32}
PitchPanCenter : Byte; {Pitch-Pan center: C-0 to B-9 represented as 0->119 inclusive}
GlobalVolume : Byte; {Global Volume, 0->128}
DefaultPan : Byte; {Default Pan, 0->64, &128 => Don't use}
RandomVolume : Byte; {Random volume variation (percentage)}
RandomPanning : Byte; {Random panning variation (panning change - not implemented yet)}
TrackerVersion : Word; {Tracker version used to save the instrument. This is only
used in the instrument files.}
NumberOfSamples : Byte; {Number of samples associated with instrument. This is only
used in the instrument files.}
Reserved2 : Byte;
InstrumentName : Array[1 .. 26] of Char;
Reserved3 : Word;
MIDIChannel : Byte; {MIDI Channel}
MIDIProgram : Byte; {MIDI Program (Instrument)}
Reserved4 : Word;
NoteSampleKeyboardTable : Array[0 ..119, 0 .. 1] of Byte;
{Each note of the instrument is first converted to a sample number
and a note (C-0 -> B-9). These are stored as note/sample byte pairs
(note first, range 0->119 for C-0 to B-9, sample ranges from
1-99, 0=no sample)}
Envelopes : Array[0 .. 2] of IT_Envelope;
end;
IT_Sample = Record
IMPS : Array[1 .. 4] of Char;
DOSFileName : Array[1 .. 12] of Char;
Reserved1 : Byte; {should be $00}
GlobalVolume : Byte; {Global volume for instrument, ranges from 0->64}
Flags : Byte; {Bit#0: ON = Sample associated with header.
OFF= No sample. (No sampledata either!)
Bit#1: ON = 16-bit, OFF = 8-bit.
Bit#2: ON = Stereo, OFF = Mono.
(Stereo samples are not supported yet.)
Bit#3: RESERVED
Bit#4: ON = Use looping
Bit#5: ON = Use sustain loop
Bit#6: ON = Ping Pong (bi-directional) loop,
OFF= Forwards loop
Bit#7: ON = Ping Pong Sustain loop,
OFF= Forwards Sustain loop}
Volume : Byte; {Default volume for instrument}
SampleName : Array[1 .. 26] of Char;
CreatedWith : Byte; {Created with tracker version.}
DefaultPanning : Byte; {Default panning.
Bits 0->6 = Pan value,
Bit 7. ON to USE (opposite of inst)}
Length : LongWord; {Length of sample in SAMPLES, NOT bytes!}
LoopStart : LongWord; {Start of loop (no of samples in, not bytes)}
LoopEnd : LongWord; {Sample no. AFTER end of loop}
C5Speed : LongWord; {Number of bytes a second for C-5 (ranges from 0->9999999)}
SustainLoopStart : LongWord; {Start of sustain loop}
SustainLoopEnd : LongWord; {Sample no. AFTER end of sustain loop}
SamplePointer : Integer; {'Long' Offset of sample in file. 32-bit signed}
VibratoSpeed : Byte; {Vibrato Speed, ranges from 0->64}
VibratoDepth : Byte; {Vibrato Depth, ranges from 0->64}
VibratoRate : Byte; {Vibrato Rate, rate at which vibrato is applied (0->64)}
VibratoType : Byte; {Vibrato waveform type.
0 = Sine wave
1 = Ramp down
2 = Square wave
3 = Random (speed is irrelevant)}

{Flags}
isSample : Boolean;
is16Bit : Boolean;
isStereo : Boolean;
isLoop : Boolean;
isSustainLoop : Boolean;
isPingPongLoop : Boolean;
isPingPongSustainLoop : Boolean;

SampleLength : Integer; // Length in Bytes!
SampleData : Pointer; // Pointer To Sample Data
end;
IT_Note = Record
Channel : Byte;
Note : Byte;
Instrument : Byte;
VolumePan : Byte;
Effect : Byte;
EffectParameter : Byte;
lastNote : Boolean;
lastInstrument : Boolean;
lastVolumePan : Boolean;
lastEffect : Boolean;
end;
IT_Pattern = class(TObject)
NumberOfRows : Word; {Ranges from 32->200}
NumberOfNotes : Integer;
Notes: Array of IT_Note;
end;
IT_Mixer = class(TObject)
Name : String;
NumberOfChannels : Integer;
NumberOfInstruments : Integer;
NumberOfSamples : Integer;
NumberOfPatterns : Integer;
SongLength : Integer;

Header: IT_Header;

ChannelPan : Array[0 .. 63] of Byte;
{Each byte contains a panning value for a channel. Ranges from
0 (absolute left) to 64 (absolute right). 32 = central pan,
100 = Surround sound.
+128 = disabled channel}
ChannelVolume : Array[0 .. 63] of Byte; {0 to 64}

NumberOfOrders: Word;
Orders: Array[0 .. 255] of Byte;

InsPtr: Array[0 .. 255] of LongWord;
SamPtr: Array[0 .. 255] of LongWord;
PatPtr: Array[0 .. 255] of LongWord;

Instruments: Array[0 .. 255] of IT_Instrument;

Samples: Array[0 .. 255] of IT_Sample;

Patterns: Array[0 .. 253] of IT_Pattern;

procedure LoadFromFile(FileName: String);
end;


implementation

(*
function ITReadBits(bitbuf: Cardinal; bitnum: Integer; ibuf, n: Byte): Cardinal;
//-----------------------------------------------------------------
var
retval: Cardinal;
i: Integer;
begin
retval := 0;
i := n;

if (n > 0) then
begin
repeat
begin
if (bitnum = 0) then
begin
bitbuf := *ibuf++;
bitnum := 8;
end;
retval := retval shr 1;
retval := retval OR (bitbuf shl 31);
bitbuf := bitbuf shr 1;
bitnum := bitnum - 1;
dec(i);
end;
until (i = 0);
i := n;
end;
Result := (retval shr (32-i));
end;


// Unpack 8 Bit IT Sample
procedure ITUnpack8Bit(Sample: Pointer; dwLen: Integer; MemFile: Integer; MemLength: Integer; b215: Boolean);
//-------------------------------------------------------------------------------------------
var
// FileStream: File;
pDst: Pointer;
pSrc: Integer;
wHdr: Integer;
wCount: Integer;
bitbuf: Integer;
bitnum:Integer;
bLeft, bTemp, bTemp2: Byte;
{---}
d,
dwPos: Integer;
wBits: Word;
begin
pDst := pSample;
pSrc := MemFile;
wHdr := 0;
wCount := 0;
bitbuf := 0;
bitnum := 0;
bLeft := 0;
bTemp := 0;
bTemp2 := 0;

while (dwLen > 0) do
begin
if (wCount = 0) then
begin
wCount := $8000;
BlockRead(FileStream, wHdr, 2); // wHdr = *((LPWORD)pSrc);
pSrc := pSrc + 2;
bLeft := 9;
bTemp := 0;
bTemp2 := 0;
bitbuf := 0;
bitnum := 0;
end;
d := wCount;
if (d > dwLen) then
d := dwLen;
// Unpacking
dwPos := 0;
repeat
begin
wBits := (WORD)ITReadBits(bitbuf, bitnum, pSrc, bLeft);
if (bLeft < 7) then
begin
DWORD i = 1 << (bLeft-1);
DWORD j = wBits & 0xFFFF;
if (i != j) then
goto UnpackByte;
wBits = (WORD)(ITReadBits(bitbuf, bitnum, pSrc, 3) + 1) & 0xFF;
bLeft = ((BYTE)wBits < bLeft) ? (BYTE)wBits : (BYTE)((wBits+1) & 0xFF);
goto Next;
end;
if (bLeft < 9) then
begin
WORD i = (0xFF >> (9 - bLeft)) + 4;
WORD j = i - 8;
if ((wBits <= j) || (wBits > i))
goto UnpackByte;
wBits -= j;
bLeft = ((BYTE)(wBits & 0xFF) < bLeft) ? (BYTE)(wBits & 0xFF) : (BYTE)((wBits+1) & 0xFF);
goto Next;
end;
if (bLeft >= 10) then
goto SkipByte;
if (wBits >= 256) then
begin
bLeft = (BYTE)(wBits + 1) & 0xFF;
goto Next;
end;
UnpackByte:
if (bLeft < 8) then
begin
BYTE shift = 8 - bLeft;
char c = (char)(wBits << shift);
c >>= shift;
wBits = (WORD)c;
end;
wBits += bTemp;
bTemp = (BYTE)wBits;
bTemp2 += bTemp;
//#ifdef IT215_SUPPORT
pDst[dwPos] = (b215) ? bTemp2 : bTemp;
//#else
pDst[dwPos] = bTemp;
//#endif
SkipByte:
dwPos++;
Next:
if (pSrc >= lpMemFile+dwMemLength+1) then
return;
end;
until (dwPos >= d);
// Move On
wCount -= d;
dwLen -= d;
pDst += d;
end;
end;
*)

{IT_Mixer Functions}
procedure IT_Mixer.LoadFromFile(FileName: String);
var FileStream: File;
ByteBuffer, ByteBuffer2, ByteBuffer3: Byte;
ShortIntBuffer: ShortInt;
WordBuffer, WordBuffer2: Word;
i, j, k: Integer;
RowIndex: Integer;
DumpNote : Boolean;
HeaderSize: Integer;
HighestChannel: Integer;
NumberOfRows: Integer;
FileLength: Integer;
Flags: Integer;
Len: Integer;
begin
{Check that file Exists}
// if (FileExists(FileName)) then
// begin
{Open File}
AssignFile(FileStream, FileName);
Reset(FileStream, 1);

{Read File Length}
FileLength := FileSize(FileStream);

{Read Header From File}
BlockRead(FileStream, Header, SizeOf(Header));


{Get IT Song Name}
if (Header.IMPM = 'IMPM') then
begin
{SongName}
Name := Header.SongName;
end;

{Read Channel Panning}
for i := 0 to 63 do
BlockRead(FileStream, ChannelPan[i], SizeOf(ChannelPan[i]));

{Read Channel Volumes}
for i := 0 to 63 do
BlockRead(FileStream, ChannelVolume[i], SizeOf(ChannelVolume[i]));

{Read Orders}
for i := 0 to Header.OrderNumber - 1 do
BlockRead(FileStream, Orders[i], SizeOf(Orders[i]));

{Read Instrument ParaPointers}
for i := 0 to Header.InstrumentNumber - 1 do
BlockRead(FileStream, InsPtr[i], SizeOf(InsPtr[i]));

{Read Sample ParaPointers}
for i := 0 to Header.SampleNumber - 1 do
BlockRead(FileStream, SamPtr[i], SizeOf(SamPtr[i]));

{Read Pattern ParaPointers}
for i := 0 to Header.PatternNumber - 1 do
BlockRead(FileStream, PatPtr[i], SizeOf(PatPtr[i]));


{Read Instruments} // Only Reads New Instrument Type!!!
if (Header.CompatableWith >= $200) then
for i := 0 to Header.InstrumentNumber - 1 do
begin
Seek(FileStream, InsPtr[i]);

BlockRead(FileStream, Instruments[i].IMPI, SizeOf(Instruments[i].IMPI));
BlockRead(FileStream, Instruments[i].DOSFileName, SizeOf(Instruments[i].DOSFileName));
BlockRead(FileStream, Instruments[i].Reserved1, SizeOf(Instruments[i].Reserved1));
BlockRead(FileStream, Instruments[i].NewNoteAction, SizeOf(Instruments[i].NewNoteAction));
BlockRead(FileStream, Instruments[i].DuplicateCheckType, SizeOf(Instruments[i].DuplicateCheckType));
BlockRead(FileStream, Instruments[i].DuplicateCheckAction, SizeOf(Instruments[i].DuplicateCheckAction));
BlockRead(FileStream, Instruments[i].FadeOut, SizeOf(Instruments[i].FadeOut));
BlockRead(FileStream, Instruments[i].PitchPanSeparation, SizeOf(Instruments[i].PitchPanSeparation));
BlockRead(FileStream, Instruments[i].PitchPanCenter, SizeOf(Instruments[i].PitchPanCenter));
BlockRead(FileStream, Instruments[i].GlobalVolume, SizeOf(Instruments[i].GlobalVolume));
BlockRead(FileStream, Instruments[i].DefaultPan, SizeOf(Instruments[i].DefaultPan));
BlockRead(FileStream, Instruments[i].RandomVolume, SizeOf(Instruments[i].RandomVolume));
BlockRead(FileStream, Instruments[i].RandomPanning, SizeOf(Instruments[i].RandomPanning));
BlockRead(FileStream, Instruments[i].TrackerVersion, SizeOf(Instruments[i].TrackerVersion));
BlockRead(FileStream, Instruments[i].NumberOfSamples, SizeOf(Instruments[i].NumberOfSamples));
BlockRead(FileStream, Instruments[i].Reserved2, SizeOf(Instruments[i].Reserved2));
BlockRead(FileStream, Instruments[i].InstrumentName, SizeOf(Instruments[i].InstrumentName));
BlockRead(FileStream, Instruments[i].Reserved3, SizeOf(Instruments[i].Reserved3));
BlockRead(FileStream, Instruments[i].MIDIChannel, SizeOf(Instruments[i].MIDIChannel));
BlockRead(FileStream, Instruments[i].MIDIProgram, SizeOf(Instruments[i].MIDIProgram));
BlockRead(FileStream, Instruments[i].Reserved4, SizeOf(Instruments[i].Reserved4));

// Read Note/Sample Keyboard Table
for j := 0 to 119 do
for k := 0 to 1 do
BlockRead(FileStream, Instruments[i].NoteSampleKeyboardTable[j][k], SizeOf(Instruments[i].NoteSampleKeyboardTable[j][k]));

// Read Envelopes
for j := 0 to 2 do
begin
BlockRead(FileStream, Instruments[i].Envelopes[j].Flag, SizeOf(Instruments[i].Envelopes[j].Flag));
BlockRead(FileStream, Instruments[i].Envelopes[j].NumberOfNodePoints, SizeOf(Instruments[i].Envelopes[j].NumberOfNodePoints));
BlockRead(FileStream, Instruments[i].Envelopes[j].LoopBegin, SizeOf(Instruments[i].Envelopes[j].LoopBegin));
BlockRead(FileStream, Instruments[i].Envelopes[j].LoopEnd, SizeOf(Instruments[i].Envelopes[j].LoopEnd));
BlockRead(FileStream, Instruments[i].Envelopes[j].SustainLoopBegin, SizeOf(Instruments[i].Envelopes[j].SustainLoopBegin));
BlockRead(FileStream, Instruments[i].Envelopes[j].SustainLoopEnd, SizeOf(Instruments[i].Envelopes[j].SustainLoopEnd));

// Read Envelope Nodes
for k := 0 to 24 do
begin
if (j = 0) then // First Envelope for Volume(0 to 64)
begin
BlockRead(FileStream, ByteBuffer, 1);
Instruments[i].Envelopes[j].Nodes[k].Value := ByteBuffer;
end
else // Second & Third Envelopes for Panning & Pitch, respectively(-32 to +32)
begin
BlockRead(FileStream, ShortIntBuffer, 1);
Instruments[i].Envelopes[j].Nodes[k].Value := ShortIntBuffer;
end;
BlockRead(FileStream, Instruments[i].Envelopes[j].Nodes[k].Tick, SizeOf(Instruments[i].Envelopes[j].Nodes[k].Tick));
end;

// Skip 1 Byte
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
end;

// Skip 7 Wasted Bytes
for j := 0 to 6 do
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
end;

{Read Sample Headers}
for i := 0 to Header.SampleNumber - 1 do
begin
Seek(FileStream, SamPtr[i]);

BlockRead(FileStream, Samples[i].IMPS, SizeOf(Samples[i].IMPS));
BlockRead(FileStream, Samples[i].DOSFileName, SizeOf(Samples[i].DOSFileName));
BlockRead(FileStream, Samples[i].Reserved1, SizeOf(Samples[i].Reserved1));
BlockRead(FileStream, Samples[i].GlobalVolume, SizeOf(Samples[i].GlobalVolume));

// Sample Flags
BlockRead(FileStream, Samples[i].Flags, SizeOf(Samples[i].Flags));
//Bit#0: ON = Sample associated with header.
// OFF= No sample. (No sampledata either!)
if (Samples[i].Flags AND 1 > 0) then
Samples[i].isSample := True
else
Samples[i].isSample := False;
//Bit#1: ON = 16-bit, OFF = 8-bit.
if (Samples[i].Flags AND 2 > 0) then
Samples[i].is16Bit := True
else
Samples[i].is16Bit := False;
//Bit#2: ON = Stereo, OFF = Mono.
//(Stereo samples are not supported yet.)
if (Samples[i].Flags AND 4 > 0) then
Samples[i].isStereo := True
else
Samples[i].isStereo := False;
//Bit#4: ON = Use looping
if (Samples[i].Flags AND 16 > 0) then
Samples[i].isLoop := True
else
Samples[i].isLoop := False;
//Bit#5: ON = Use sustain loop
if (Samples[i].Flags AND 32 > 0) then
Samples[i].isSustainLoop := True
else
Samples[i].isSustainLoop := False;
//Bit#6: ON = Ping Pong (bi-directional) loop,
// OFF= Forwards loop
if (Samples[i].Flags AND 64 > 0) then
Samples[i].isPingPongLoop := True
else
Samples[i].isPingPongLoop := False;
//Bit#7: ON = Ping Pong Sustain loop,
// OFF= Forwards Sustain loop
if (Samples[i].Flags AND 128 > 0) then
Samples[i].isPingPongSustainLoop := True
else
Samples[i].isPingPongSustainLoop := False;

BlockRead(FileStream, Samples[i].Volume, SizeOf(Samples[i].Volume));
BlockRead(FileStream, Samples[i].SampleName, SizeOf(Samples[i].SampleName));
BlockRead(FileStream, Samples[i].CreatedWith, SizeOf(Samples[i].CreatedWith));
BlockRead(FileStream, Samples[i].DefaultPanning, SizeOf(Samples[i].DefaultPanning));
BlockRead(FileStream, Samples[i].Length, SizeOf(Samples[i].Length));
BlockRead(FileStream, Samples[i].LoopStart, SizeOf(Samples[i].LoopStart));
BlockRead(FileStream, Samples[i].LoopEnd, SizeOf(Samples[i].LoopEnd));
BlockRead(FileStream, Samples[i].C5Speed, SizeOf(Samples[i].C5Speed));
BlockRead(FileStream, Samples[i].SustainLoopStart, SizeOf(Samples[i].SustainLoopStart));
BlockRead(FileStream, Samples[i].SustainLoopEnd, SizeOf(Samples[i].SustainLoopEnd));
BlockRead(FileStream, Samples[i].SamplePointer, SizeOf(Samples[i].SamplePointer));

BlockRead(FileStream, Samples[i].VibratoSpeed, SizeOf(Samples[i].VibratoSpeed));
BlockRead(FileStream, Samples[i].VibratoDepth, SizeOf(Samples[i].VibratoDepth));
BlockRead(FileStream, Samples[i].VibratoRate, SizeOf(Samples[i].VibratoRate));
BlockRead(FileStream, Samples[i].VibratoType, SizeOf(Samples[i].VibratoType));
end;

{Determine Number Of Channels from Pattern Data}
HighestChannel := -1;
for i := 0 to Header.PatternNumber - 1 do
begin
Seek(FileStream, PatPtr[i]);
// This is the length of the packed pattern data - No need to store this data
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
// Read Number Of Rows for Pattern
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
NumberOfRows := WordBuffer;
// 4 Wasted Bytes!
for j := 0 to 1 do
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));
// Read Notes
j := 0;
repeat
begin
// Get Next Channel Marker
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
if (ByteBuffer > 0) then
begin
if ((ByteBuffer - 1) AND 63 > HighestChannel) then
HighestChannel := (ByteBuffer - 1) AND 63;
// IF note has a mask
if (ByteBuffer AND 128 > 0) then
begin
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));
// Has Note
if (ByteBuffer2 AND 1 > 0) then
BlockRead(FileStream, ByteBuffer3, SizeOf(ByteBuffer3));
// Has Instrument
if (ByteBuffer2 AND 2 > 0) then
BlockRead(FileStream, ByteBuffer3, SizeOf(ByteBuffer3));
// Has Volume/Pan
if (ByteBuffer2 AND 4 > 0) then
BlockRead(FileStream, ByteBuffer3, SizeOf(ByteBuffer3));
// Has Effect & Parameter
if (ByteBuffer2 AND 8 > 0) then
begin
BlockRead(FileStream, ByteBuffer3, SizeOf(ByteBuffer3));
BlockRead(FileStream, ByteBuffer3, SizeOf(ByteBuffer3));
end;
end;
end;
inc(j);
end;
until (j > NumberOfRows);
end;
NumberOfChannels := HighestChannel + 1;

{Read Patterns}
for i := 0 to Header.PatternNumber - 1 do
begin
Seek(FileStream, PatPtr[i]);
// This is the length of the packed pattern data - No need to store this data
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));

// Init next Pattern
Patterns[i] := IT_Pattern.Create;

BlockRead(FileStream, Patterns[i].NumberOfRows, SizeOf(Patterns[i].NumberOfRows));

// 4 Wasted Bytes!
for j := 0 to 1 do
BlockRead(FileStream, WordBuffer, SizeOf(WordBuffer));

Patterns[i].NumberOfNotes := NumberOfChannels * Patterns[i].NumberOfRows;
SetLength(Patterns[i].Notes, Patterns[i].NumberOfNotes);

// Read Notes
j := 0;
repeat
begin
BlockRead(FileStream, ByteBuffer, SizeOf(ByteBuffer));
if (ByteBuffer > 0) then
begin
Patterns[i].Notes[j].Channel := (ByteBuffer - 1) AND 63;

// IF note has a mask
if (ByteBuffer AND 128 > 0) then
begin
BlockRead(FileStream, ByteBuffer2, SizeOf(ByteBuffer2));

// Has Note
if (ByteBuffer2 AND 1 > 0) then
BlockRead(FileStream, Patterns[i].Notes[j].Note, SizeOf(Patterns[i].Notes[j].Note));

// Has Instrument
if (ByteBuffer2 AND 2 > 0) then
BlockRead(FileStream, Patterns[i].Notes[j].Instrument, SizeOf(Patterns[i].Notes[j].Instrument));

// Has Volume/Pan
if (ByteBuffer2 AND 4 > 0) then
BlockRead(FileStream, Patterns[i].Notes[j].VolumePan, SizeOf(Patterns[i].Notes[j].VolumePan));

// Has Effect & Parameter
if (ByteBuffer2 AND 8 > 0) then
begin
BlockRead(FileStream, Patterns[i].Notes[j].Effect, SizeOf(Patterns[i].Notes[j].Effect));
BlockRead(FileStream, Patterns[i].Notes[j].EffectParameter, SizeOf(Patterns[i].Notes[j].EffectParameter));
end;

// Last Note
if (ByteBuffer2 AND 16 > 0) then
Patterns[i].Notes[j].lastNote := True
else
Patterns[i].Notes[j].lastNote := False;

// Last Instrument
if (ByteBuffer2 AND 32 > 0) then
Patterns[i].Notes[j].lastInstrument := True
else
Patterns[i].Notes[j].lastInstrument := False;

// Last Volume/Pan
if (ByteBuffer2 AND 64 > 0) then
Patterns[i].Notes[j].lastVolumePan := True
else
Patterns[i].Notes[j].lastVolumePan := False;

// Last Effect & Parameter
if (ByteBuffer2 AND 128 > 0) then
Patterns[i].Notes[j].lastEffect := True
else
Patterns[i].Notes[j].lastEffect := False;
end;
end;
inc(j);
end;
until (j > Patterns[i].NumberOfRows);
end;

{Read Samples}
for i := 0 to Header.SampleNumber - 1 do
if (Samples[i].SamplePointer > 0) and
(Samples[i].SamplePointer < FileLength) and
(Samples[i].Length > 0) then
begin
// //
// Determine Audio Compression //
// //
if (Samples[i].Length > MAX_SAMPLE_LENGTH) then
Samples[i].Length := MAX_SAMPLE_LENGTH;
if (Samples[i].CreatedWith AND 1 > 0) then
flags := RS_PCM8S
else
flags := RS_PCM8U;
if (Samples[i].Flags AND 2 > 0) then
begin
flags := flags + 5;
if (Samples[i].Flags AND 4 > 0) then
flags := flags OR RSF_STEREO;
Samples[i].is16Bit := True;
// IT 2.14 16-bit packed sample ?
if (Samples[i].Flags AND 8 > 0) then
if (Header.CompatableWith >= $215) and
(Samples[i].CreatedWith AND 4 > 0) then
flags := RS_IT21516
else
flags := RS_IT21416;
end
else
begin
if (Samples[i].Flags AND 4 > 0) then
flags := flags OR RSF_STEREO;
if (Samples[i].CreatedWith = $FF) then
flags := RS_ADPCM4
else
// IT 2.14 8-bit packed sample ?
if (Samples[i].Flags AND 8 > 0) then
if (Header.CompatableWith >= $215) and
(Samples[i].CreatedWith AND 4 > 0) then
flags := RS_IT2158
else
flags := RS_IT2148;
end;
// ReadSample(&Ins[nsmp + 1], flags, (LPSTR)(lpStream + Samples[i].SamplePointer),
// FileLength - Samples[i].SamplePointer);
// //
// Decompress Sample Data //
// //
Len := 0;
Samples[i].SampleLength := Samples[i].Length + 6;
if (flags AND RSF_16BIT > 0) then
begin
Samples[i].SampleLength := Samples[i].SampleLength * 2;
Samples[i].is16Bit := True;
end;
if (flags AND RSF_STEREO > 0) then
begin
Samples[i].SampleLength := Samples[i].SampleLength * 2;
Samples[i].isStereo := True;
end;

// IT 2.14 compressed samples
case (flags) of
RS_IT2148, RS_IT21416, RS_IT2158, RS_IT21516:
begin
Len := FileLength - Samples[i].SamplePointer;
if (Len < 4) then
Continue;
if (flags = RS_IT2148) or (flags = RS_IT2158) then
begin
//ITUnpack8Bit(Samples[i].SampleData, Samples[i].Length, Samples[i].SamplePointer,
// Len, (flags = RS_IT2158))
end
else
begin
//ITUnpack16Bit(Samples[i].SampleData, Samples[i].Length, Samples[i].SamplePointer,
// Len, (flags = RS_IT21516));
end;
Continue;
end;
end;
end;

for i := 0 to Header.SampleNumber - 1 do
if (Samples[i].isSample) and (Samples[i].Length > 0) and
(Samples[i].SamplePointer + Samples[i].Length <= FileSize(FileStream)) then
begin
{Allocate Memory for Sample Data And assign SampleData pointer to it}
// HeaderSize := 192 + Header.OrderNumber + Header.InstrumentNumber * 4 +
// Header.SampleNumber * 4 + Header.PatternNumber * 4;
HeaderSize := 0;
Seek(FileStream, Samples[i].SamplePointer - HeaderSize);
GetMem(Samples[i].SampleData, Samples[i].SampleLength);

BlockRead(FileStream, Samples[i].SampleData^, Samples[i].SampleLength);
end;

CloseFile(FileStream);
end;

end.

WILL
17-06-2006, 06:50 AM
Nobody huh?

I know it's a nice big chunk of code, but it's not that complex. I've commented it well enough to understand what part is being loaded, it's just long. :)

Oh and yes, it uses noeska's OpenAL headers for audio playback. :)

BeRo
28-06-2006, 05:55 PM
Directly from BeRoTracker src (sorry for the german variable names etc. from my old coding style, the code base is very old)


TYPE TITSignatur=ARRAY[1..4] OF CHAR;

TITHeader=PACKED RECORD
Signatur:TITSignatur; {IMPM}
Name:ARRAY[1..26] OF CHAR;
RowHilightMinor,RowHilightMajor:BYTE;
TrackLaenge:WORD;
AnzahlDerInstrumente:WORD;
AnzahlDerSamples:WORD;
AnzahlDerPatterns:WORD;
TrackerVersion:WORD;
KompatibelMitVersion:WORD;
Flags:WORD;
SpezialFlag:WORD;
GlobalLautstaerke:BYTE; {0-128}
MixingLautstaerke:BYTE; {0-128}
Speed:BYTE;
BPM:BYTE;
PanSeparation:BYTE; {0-128,128=Max. Separation}
MIDIPitchDepth:BYTE;
KommentarLaenge:WORD;
KommentarPosition:LONGWORD;
ExtendedDataOffset:LONGWORD; {Reserviert}
KanalPan:ARRAY[0..64-1] OF BYTE;
KanalLautstaerke:ARRAY[0..64-1] OF BYTE;
END;

TITEnvNode=PACKED RECORD
Env:BYTE;
KnotenPunkt:WORD;
END;

TITEnvelope=PACKED RECORD
Flags:BYTE;
AnzahlDerKnoten:BYTE;
SchleifeStart:BYTE;
SchleifeEnde:BYTE;
SustainSchleifeStart:BYTE;
SustainSchleifeEnde:BYTE;
Knoten:ARRAY[0..25-1] OF TITEnvNode;
Reserviert:BYTE;
END;

TITAlterInstrumentHeader=PACKED RECORD
Signatur:TITSignatur; {IMPI}
DateiName:ARRAY[1..12] OF CHAR;
LeerUndReserviert:BYTE;
Flags:BYTE;
VolSchleifeStart:BYTE;
VolSchleifeEnde:BYTE;
SustainSchleifeStart:BYTE;
SustainSchleifeEnde:BYTE;
Reserviert:WORD;
FadeOut:WORD;
NNA:BYTE;
DNC:BYTE;
TrackerVersion:WORD;
AnzahlDerSamples:BYTE;
Reserviert2:BYTE;
Name:ARRAY[1..26] OF CHAR;
Reserviert3:ARRAY[0..6-1] OF CHAR;
NoteSampleTabelle:ARRAY[0..240-1] OF BYTE;
VolEnvelope:ARRAY[0..200-1] OF BYTE;
KnotenPunkte:ARRAY[0..2*25-1] OF BYTE;
END;

TITInstrumentHeader=PACKED RECORD
Signatur:TITSignatur; {IMPI}
DateiName:ARRAY[1..12] OF CHAR;
LeerUndReserviert:BYTE;
NNA:BYTE;
DCT:BYTE;
DCA:BYTE;
FadeOut:WORD;
PPS:BYTE;
PPC:BYTE;
GbV:BYTE;
DfP:BYTE;
RV:BYTE;
RP:BYTE;
TrackerVersion:WORD;
AnzahlDerSamples:BYTE;
Reserviert:BYTE;
Name:ARRAY[1..26] OF CHAR;
IFC:BYTE;
IFR:BYTE;
MCh:BYTE;
MPr:BYTE;
MidiBank:WORD;
NoteSampleTabelle:ARRAY[0..240-1] OF BYTE;
VolEnv:TITEnvelope;
PanEnv:TITEnvelope;
PitchEnv:TITEnvelope;
Leer:LONGWORD;
END;

TITPattern=PACKED RECORD
DatenGroesse:WORD;
Zeilen:WORD;
Reserviert:LONGWORD;
END;

TITSampleHeader=PACKED RECORD
Signatur:TITSignatur; {IMPS}
DateiName:ARRAY[1..12] OF CHAR;
LeerUndReserviert:BYTE;
GvL:BYTE;
Flags:BYTE;
Lautstaerke:BYTE;
Name:ARRAY[1..26] OF CHAR;
KonvertierFlag:BYTE;
Panning:BYTE;
Laenge:LONGWORD;
SchleifeStart:LONGWORD;
SchleifeEnde:LONGWORD;
C5Speed:LONGWORD;
SustainSchleifeStart:LONGWORD;
SustainSchleifeEnde:LONGWORD;
SampleDatenPosition:LONGWORD;
VibratoSpeed:BYTE;
VibratoDepth:BYTE;
VibratoRate:BYTE;
VibratoWaveForm:BYTE;
END;



FUNCTION TEngineSample.LeseITBits(ABitWidth:BYTE):LONGWORD;
VAR Wert:LONGWORD;
B:BYTE;
BitWidth:BYTE;
BEGIN
Wert:=0;
IF LONGWORD(SourcePosition)<LONGWORD(SourceEnd) THEN BEGIN
BitWidth:=ABitWidth;
B:=0;
WHILE BitWidth>SourceRemainBits DO BEGIN
Wert:=Wert OR (SourcePosition^ SHL B);
IF LONGWORD(SourcePosition)>=LONGWORD(SourceEnd) THEN BEGIN
RESULT:=Wert;
EXIT;
END;
INC(LONGWORD(SourcePosition));
INC(B,SourceRemainBits);
DEC(BitWidth,SourceRemainBits);
SourceRemainBits:=8;
END;
Wert:=Wert OR ((SourcePosition^ AND ((1 SHL BitWidth)-1)) SHL B);
SourcePosition^:=SourcePosition^ SHR BitWidth;
DEC(SourceRemainBits,BitWidth);
END;
RESULT:=Wert;
END;

FUNCTION TEngineSample.LeseITBlock(Daten:TEngineStream):BOO LEAN;
VAR S:WORD;
BEGIN
RESULT:=FALSE;
IF Daten.Read(S,2)<>2 THEN EXIT;
GETMEM(SourceBuffer,S);
IF NOT ASSIGNED(SourceBuffer) THEN EXIT;
IF Daten.Read(SourceBuffer^,S)<>S THEN BEGIN
FREEMEM(SourceBuffer);
EXIT;
END;
SourcePosition:=SourceBuffer;
LONGWORD(SourceEnd):=LONGWORD(SourceBuffer)+S;
SourceRemainBits:=8;
RESULT:=TRUE;
END;

FUNCTION TEngineSample.GebeITBlockFrei:BOOLEAN;
BEGIN
RESULT:=FALSE;
IF ASSIGNED(SourceBuffer) THEN BEGIN
FREEMEM(SourceBuffer);
SourceBuffer:=NIL;
RESULT:=TRUE;
EXIT;
END;
END;

PROCEDURE TEngineSample.LeseITSample8BitPacked(Daten:TEngine Stream;Groesse:LONGWORD;bIT215,Stereo:BOOLEAN);
VAR DestPos:PSHORTINT;
BlockLength:WORD;
BlockPosition:WORD;
Width:BYTE;
Value:WORD;
D1:SHORTINT;
D2:SHORTINT;
V:SHORTINT;
L:LONGINT;
Border:BYTE;
Shift:BYTE;
BEGIN
Bits:=8;
IF Stereo THEN BEGIN
Kaenale:=2;
END ELSE BEGIN
Kaenale:=1;
END;
Vergroessern(Groesse);
FILLCHAR(DataPointer^,Groesse,#0);

DestPos:=DataPointer;

L:=Groesse;
WHILE L>0 DO BEGIN
IF NOT LeseITBlock(Daten) THEN EXIT;
IF L<$8000 THEN BEGIN
BlockLength:=L;
END ELSE BEGIN
BlockLength:=$8000;
END;
BlockPosition:=0;

Width:=9;
D1:=0;
D2:=0;
WHILE BlockPosition<BlockLength DO BEGIN
Value:=LeseITBits(Width);
IF Width<7 THEN BEGIN
IF Value=(1 SHL (Width-1)) THEN BEGIN
Value:=LeseITBits(3)+1;
IF Value<Width THEN BEGIN
Width:=Value;
END ELSE BEGIN
Width:=Value+1;
END;
CONTINUE;
END;
END ELSE IF Width<9 THEN BEGIN
Border:=($FF SHR (9-Width))-4;
IF (Value>Border) AND (Value<=(Border+8)) THEN BEGIN
DEC(Value,Border);
IF Value<Width THEN BEGIN
Width:=Value;
END ELSE BEGIN
Width:=Value+1;
END;
CONTINUE;
END;
END ELSE IF Width=9 THEN BEGIN
IF (Value AND $100)<>0 THEN BEGIN
Width:=(Value+1) AND $FF;
CONTINUE;
END;
END ELSE BEGIN
GebeITBlockFrei;
EXIT;
END;
IF Width<8 THEN BEGIN
Shift:=8-Width;
V:=SHORTINT(Value SHL Shift);
V:=V SHR Shift;
END ELSE BEGIN
V:=SHORTINT(Value);
END;
INC(D1,V);
INC(D2,D1);
IF bIT215 THEN BEGIN
DestPos^:=D2;
INC(DestPos);
END ELSE BEGIN
DestPos^:=D1;
INC(DestPos);
END;
INC(BlockPosition);
END;
GebeITBlockFrei;
DEC(L,BlockLength);
END;
ConvertStereo;
END;

PROCEDURE TEngineSample.LeseITSample16BitPacked(Daten:TEngin eStream;Groesse:LONGWORD;bIT215,Stereo:BOOLEAN);
VAR DestPos:PSMALLINT;
BlockLength:WORD;
BlockPosition:WORD;
Width:BYTE;
Value:LONGWORD;
D1:SMALLINT;
D2:SMALLINT;
V:SMALLINT;
L:LONGINT;
Border:WORD;
Shift:BYTE;
BEGIN
Bits:=16;
IF Stereo THEN BEGIN
Kaenale:=2;
END ELSE BEGIN
Kaenale:=1;
END;
Vergroessern(Groesse);
FILLCHAR(DataPointer^,Groesse,#0);

DestPos:=DataPointer;

L:=Groesse SHR 1;
WHILE L>0 DO BEGIN
IF NOT LeseITBlock(Daten) THEN EXIT;
IF L<$4000 THEN BEGIN
BlockLength:=L;
END ELSE BEGIN
BlockLength:=$4000;
END;
BlockPosition:=0;

Width:=17;
D1:=0;
D2:=0;
WHILE BlockPosition<BlockLength DO BEGIN
Value:=LeseITBits(Width);
IF Width<7 THEN BEGIN
IF Value=(1 SHL (Width-1)) THEN BEGIN
Value:=LeseITBits(4)+1;
IF Value<Width THEN BEGIN
Width:=Value;
END ELSE BEGIN
Width:=Value+1;
END;
CONTINUE;
END;
END ELSE IF Width<17 THEN BEGIN
Border:=($FFFF SHR (17-Width))-8;
IF (Value>Border) AND (Value<=(Border+16)) THEN BEGIN
DEC(Value,Border);
IF Value<Width THEN BEGIN
Width:=Value;
END ELSE BEGIN
Width:=Value+1;
END;
CONTINUE;
END;
END ELSE IF Width=17 THEN BEGIN
IF (Value AND $10000)<>0 THEN BEGIN
Width:=(Value+1) AND $FF;
CONTINUE;
END;
END ELSE BEGIN
GebeITBlockFrei;
EXIT;
END;
IF Width<16 THEN BEGIN
Shift:=16-Width;
V:=SMALLINT(Value SHL Shift);
V:=V SHR Shift;
END ELSE BEGIN
V:=SMALLINT(Value);
END;
INC(D1,V);
INC(D2,D1);
IF bIT215 THEN BEGIN
DestPos^:=D2;
INC(DestPos);
END ELSE BEGIN
DestPos^:=D1;
INC(DestPos);
END;
INC(BlockPosition);
END;
GebeITBlockFrei;
DEC(L,BlockLength);
END;
ConvertStereo;
END;

PROCEDURE TEngineSample.LeseSample8BitADPCM4(Daten:TEngineSt ream;Groesse:LONGWORD;Signed,Stereo:BOOLEAN);
VAR B:PSHORTINT;
RB:BYTE;
D:SHORTINT;
I,L:LONGWORD;
Tabelle:ARRAY[0..15] OF BYTE;
BEGIN
Bits:=8;
IF Stereo THEN BEGIN
Kaenale:=2;
END ELSE BEGIN
Kaenale:=1;
END;
Vergroessern(Groesse);
Daten.Read(Tabelle,16);
I:=0;
L:=(Groesse+1) DIV 2;
B:=DataPointer;
D:=0;
WHILE I<L DO BEGIN
RB:=Daten.ReadByte;
D:=SHORTINT(BYTE(BYTE(D)+Tabelle[RB AND $F]));
B^:=D;
INC(B);
D:=SHORTINT(BYTE(BYTE(D)+Tabelle[RB SHR 4]));
B^:=D;
INC(B);
INC(I);
END;
IF NOT Signed THEN BEGIN
I:=0;
B:=DataPointer;
WHILE I<Groesse DO BEGIN
BYTE(B^):=BYTE(B^)+128;
INC(B);
INC(I);
END;
END;
ConvertStereo;
END;

PROCEDURE TEngineSample.ConvertStereo;
VAR Counter,SampleSize,SampleLength:INTEGER;
SampleBuffer:POINTER;
PQB,PZB:PBYTE;
PQW,PZW:PWORD;
BEGIN
TRY
IF (Bits IN [8,16]) AND (Kaenale=2) THEN BEGIN
SampleSize:=RealSize;
SampleLength:=SampleSize DIV (2*(Bits DIV 8));
GETMEM(SampleBuffer,SampleSize);
IF Bits=8 THEN BEGIN
PQB:=DataPointer;
PZB:=SampleBuffer;
FOR Counter:=1 TO SampleLength DO BEGIN
PZB^:=PQB^;
INC(PQB);
INC(PZB,2);
END;
PZB:=SampleBuffer;
INC(PZB);
FOR Counter:=1 TO SampleLength DO BEGIN
PZB^:=PQB^;
INC(PQB);
INC(PZB,2);
END;
END ELSE IF Bits=16 THEN BEGIN
PQW:=DataPointer;
PZW:=SampleBuffer;
FOR Counter:=1 TO SampleLength DO BEGIN
PZW^:=PQW^;
INC(PQW);
INC(PZW,2);
END;
PZW:=SampleBuffer;
INC(PZW);
FOR Counter:=1 TO SampleLength DO BEGIN
PZW^:=PQW^;
INC(PQW);
INC(PZW,2);
END;
END;
MOVE(SampleBuffer^,DataPointer^,SampleSize);
FREEMEM(SampleBuffer);
END;
EXCEPT
END;
END;



FUNCTION TEngineTrack.LadeIT(Daten:TEngineStream):BOOLEAN;
VAR Header:TITHeader;
I,N,AltePosition,NLP,NLS:INTEGER;
J,Note,Ins,EV,L,LP,LW,ExtendedDataOffset:LONGWORD;
W:WORD;
C:CHAR;
IOfs,SOfs,POfs:ARRAY[0..256] OF LONGWORD;
PatOrd:ARRAY[0..255] OF BYTE;
AltesInstrument:TITAlterInstrumentHeader;
NeuesInstrument:TITInstrumentHeader;
EinSample:TITSampleHeader;
Signatur:TITSignatur;
C32:ARRAY[0..32-1] OF CHAR;
FUNCTION LesePattern(PI:BYTE):BOOLEAN;
VAR LeereNote,EineNote:TEnginePatternNote;
ITPattern:TITPattern;
Row,J,K:WORD;
KanalMaske:ARRAY[0..63] OF BYTE;
LetzterWert:ARRAY[0..63] OF TEnginePatternNote;
EinByte,EinWeiteresByte,Flag,KanalNr:BYTE;
PDaten:TEngineStream;
PDatenBuffer:POINTER;
BEGIN
RESULT:=TRUE;
IF POfs[PI]=0 THEN BEGIN
IF TrackerModus THEN BEGIN
Pattern[PI].Vergroessern(64,AnzahlDerKaenale);
END ELSE BEGIN
Pattern[PI].Vergroessern(0,AnzahlDerKaenale);
END;
EXIT;
END;

Daten.Seek(POfs[PI]);
IF Daten.Read(ITPattern,SIZEOF(TITPattern))<>SIZEOF(TITPattern) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

SwapLittleEndianData16(ITPattern.DatenGroesse);
SwapLittleEndianData16(ITPattern.Zeilen);
SwapLittleEndianData32(ITPattern.Reserviert);

IF (Daten.Position+ITPattern.DatenGroesse)>Daten.Size THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

GETMEM(PDatenBuffer,ITPattern.DatenGroesse);
IF Daten.Read(PDatenBuffer^,ITPattern.DatenGroesse)<>ITPattern.DatenGroesse THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
PDaten:=TEngineStream.Create;
PDaten.Seek(0);
PDaten.Write(PDatenBuffer^,ITPattern.DatenGroesse) ;
FREEMEM(PDatenBuffer,ITPattern.DatenGroesse);

Pattern[PI].Vergroessern(ITPattern.Zeilen,AnzahlDerKaenale);

LeereNote.Note:=0;
LeereNote.SampleInstrument:=0;
LeereNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_KEI N;
LeereNote.Lautstaerke:=0;
LeereNote.Panning:=$FF;
LeereNote.Filter:=$FF;
LeereNote.Effekt:=EFFEKT_KEIN;
LeereNote.EffektParameter:=0;

FOR J:=0 TO AnzahlDerKaenale-1 DO BEGIN
FOR K:=0 TO Pattern[I].AnzahlDerZeilen-1 DO Pattern[PI].SetzeNote(K,J,LeereNote);
END;

PDaten.Seek(0);
Row:=0;
FOR J:=0 TO 63 DO KanalMaske[J]:=0;
FILLCHAR(LetzterWert,SIZEOF(LetzterWert),#0);
WHILE (Row<Pattern[PI].AnzahlDerZeilen) AND (PDaten.Position<PDaten.Size) DO BEGIN
IF PDaten.Read(EinByte,1)<>1 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
Flag:=EinByte;
IF Flag=0 THEN BEGIN
INC(Row);
END ELSE BEGIN
KanalNr:=EinByte AND $7F;
IF KanalNr<>0 THEN KanalNr:=(Flag-1) AND $3F;

EineNote:=LeereNote;

IF (Flag AND $80)<>0 THEN BEGIN
IF PDaten.Read(EinByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
KanalMaske[KanalNr]:=EinByte;
END;

IF (KanalMaske[KanalNr] AND 1)<>0 THEN BEGIN
IF PDaten.Read(EinByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
IF KanalNr<AnzahlDerKaenale THEN BEGIN
IF EinByte<$80 THEN INC(EinByte);
EineNote.Note:=EinByte;
LetzterWert[KanalNr].Note:=EinByte;
END;
END;

IF (KanalMaske[KanalNr] AND 2)<>0 THEN BEGIN
IF PDaten.Read(EinByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
IF KanalNr<AnzahlDerKaenale THEN BEGIN
EineNote.SampleInstrument:=EinByte;
LetzterWert[KanalNr].SampleInstrument:=EinByte;
END;
END;

IF (KanalMaske[KanalNr] AND 4)<>0 THEN BEGIN
IF PDaten.Read(EinByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
IF KanalNr<AnzahlDerKaenale THEN BEGIN
IF EinByte<=64 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_VOLU ME;
EineNote.Lautstaerke:=EinByte;
END ELSE IF EinByte<75 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_FINE VOLUP;
EineNote.Lautstaerke:=EinByte-65;
END ELSE IF EinByte<85 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_FINE VOLDOWN;
EineNote.Lautstaerke:=EinByte-75;
END ELSE IF EinByte<95 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_VOLS LIDEUP;
EineNote.Lautstaerke:=EinByte-85;
END ELSE IF EinByte<105 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_VOLS LIDEDOWN;
EineNote.Lautstaerke:=EinByte-95;
END ELSE IF EinByte<115 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_PORT ADOWN;
EineNote.Lautstaerke:=EinByte-105;
END ELSE IF EinByte<125 THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_PORT AUP;
EineNote.Lautstaerke:=EinByte-115;
END ELSE IF (EinByte>=128) AND (EinByte<=192) THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_PANN ING;
EineNote.Lautstaerke:=EinByte-128;
END ELSE IF (EinByte>=193) AND (EinByte<=202) THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_TONE PORTAMENTO;
EineNote.Lautstaerke:=ImpulseTrackerPortaVolCmd[EinByte-193];
END ELSE IF (EinByte>=203) AND (EinByte<=212) THEN BEGIN
EineNote.LautstaerkeBefehl:=LAUTSTAERKEEFFEKT_VIBR ATO;
EineNote.Lautstaerke:=EinByte-203;
END;
LetzterWert[KanalNr].LautstaerkeBefehl:=EineNote.LautstaerkeBefehl;
LetzterWert[KanalNr].Lautstaerke:=EineNote.Lautstaerke;
END;
END;

IF (KanalMaske[KanalNr] AND 8)<>0 THEN BEGIN
IF PDaten.Read(EinByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
IF PDaten.Read(EinWeiteresByte,1)=0 THEN BEGIN
RESULT:=FALSE;
BREAK;
END;
IF KanalNr<AnzahlDerKaenale THEN BEGIN
IF EinByte<>0 THEN BEGIN
EineNote.Effekt:=EinByte;
EineNote.EffektParameter:=EinWeiteresByte;
KonvertiereS3MEffekt(EineNote,TRUE);
LetzterWert[KanalNr].Effekt:=EineNote.Effekt;
LetzterWert[KanalNr].EffektParameter:=EineNote.EffektParameter;
END;
END;
END;

IF ((KanalMaske[KanalNr] AND $10)<>0) AND (KanalNr<AnzahlDerKaenale) THEN BEGIN
EineNote.Note:=LetzterWert[KanalNr].Note;
END;

IF ((KanalMaske[KanalNr] AND $20)<>0) AND (KanalNr<AnzahlDerKaenale) THEN BEGIN
EineNote.SampleInstrument:=LetzterWert[KanalNr].SampleInstrument;
END;

IF ((KanalMaske[KanalNr] AND $40)<>0) AND (KanalNr<AnzahlDerKaenale) THEN BEGIN
EineNote.LautstaerkeBefehl:=LetzterWert[KanalNr].LautstaerkeBefehl;
EineNote.Lautstaerke:=LetzterWert[KanalNr].Lautstaerke;
END;

IF ((KanalMaske[KanalNr] AND $80)<>0) AND (KanalNr<AnzahlDerKaenale) THEN BEGIN
EineNote.Effekt:=LetzterWert[KanalNr].Effekt;
EineNote.EffektParameter:=LetzterWert[KanalNr].EffektParameter;
END;

IF KanalNr<AnzahlDerKaenale THEN Pattern[PI].SetzeNote(Row,KanalNr,EineNote);
END;
END;
PDaten.Free;
END;
BEGIN
IF (Daten.Size<$100) OR NOT ASSIGNED(Daten) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

Daten.Seek(0);
IF Daten.Read(Header,SIZEOF(TITHeader))<>SIZEOF(TITHeader) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
SwapLittleEndianData16(Header.TrackLaenge);
SwapLittleEndianData16(Header.AnzahlDerInstrumente );
SwapLittleEndianData16(Header.AnzahlDerSamples);
SwapLittleEndianData16(Header.AnzahlDerPatterns);
SwapLittleEndianData16(Header.TrackerVersion);
SwapLittleEndianData16(Header.KompatibelMitVersion );
SwapLittleEndianData16(Header.Flags);
SwapLittleEndianData16(Header.SpezialFlag);
SwapLittleEndianData16(Header.KommentarLaenge);
SwapLittleEndianData32(Header.KommentarPosition);
SwapLittleEndianData32(Header.ExtendedDataOffset);
IF (Header.Signatur<>'IMPM') OR (Header.AnzahlDerInstrumente>MaximumAnzahlDerDateiInstrumente) OR (Header.AnzahlDerSamples>MaximumAnzahlDerDateiSamples) OR (Header.AnzahlDerPatterns>MaximumAnzahlDerDateiPatterns) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

RowHilightMinor:=Header.RowHilightMinor;
RowHilightMajor:=Header.RowHilightMajor;

ExtendedDataOffset:=Header.ExtendedDataOffset;

AnzahlDerInstrumente:=Header.AnzahlDerInstrumente;
AnzahlDerSamples:=Header.AnzahlDerSamples;
AnzahlDerPatterns:=Header.AnzahlDerPatterns;

Stereo:=(Header.Flags AND $1)<>0;
NullVolumeOptimization:=(Header.Flags AND $2)<>0;
Instruments:=(Header.Flags AND $4)<>0;
LinearSlides:=(Header.Flags AND $8)<>0;
OldEffects:=(Header.Flags AND $10)<>0;
CompatibleMode:=(Header.Flags AND $20)<>0;
EmbeddedMIDIConfig:=(Header.Flags AND $80)<>0;
ExtendedFilterRange:=(Header.Flags AND $1000)<>0;
StartNullFilterOptimization:=(Header.Flags AND $8000)=0;
OriginalImpulseTrackerFilter:=((Header.TrackerVers ion>=$214) AND (Header.TrackerVersion<=$216)) OR (Header.KompatibelMitVersion>=$214);

Name:='';
FOR I:=1 TO 26 DO IF Header.Name[I]<>#0 THEN Name:=Name+Header.Name[I];

IF Header.GlobalLautstaerke<>0 THEN BEGIN
StartGlobalLautstaerke:=Header.GlobalLautstaerke SHL 1;
IF StartGlobalLautstaerke=0 THEN StartGlobalLautstaerke:=256;
IF StartGlobalLautstaerke>256 THEN StartGlobalLautstaerke:=256;
END;

IF Header.Speed<>0 THEN StartSpeed:=Header.Speed;
IF Header.BPM<>0 THEN StartBPM:=Header.BPM;
Speed:=StartSpeed;
BPM:=StartBPM;
StartRowsPerBeat:=0;
RowsPerBeat:=StartRowsPerBeat;

MixingLautstaerke:=Header.MixingLautstaerke;

SetTrackChannels(64);

AnzahlDerKaenale:=0;
FOR I:=0 TO 63 DO IF Header.KanalPan[I]<>$FF THEN BEGIN
KanalEinstellungen[I].Lautstaerke:=Header.KanalLautstaerke[I];
KanalEinstellungen[I].Pan:=128;
KanalEinstellungen[I].Mute:=(Header.KanalPan[I] AND $80)<>0;
N:=Header.KanalPan[I] AND $7F;
IF N<=64 THEN KanalEinstellungen[I].Pan:=N SHL 2;
KanalEinstellungen[I].Surround:=N=100;
INC(AnzahlDerKaenale);
END;
IF AnzahlDerKaenale<1 THEN AnzahlDerKaenale:=1;

SetTrackChannels(AnzahlDerKaenale);

IF ((Header.SpezialFlag AND 1)<>0) AND (Header.KommentarLaenge<>0) AND (INTEGER(Header.KommentarPosition+Header.Kommentar Laenge)<Daten.Size) THEN BEGIN
AltePosition:=Daten.Position;
IF LONGWORD(Daten.Seek(Header.KommentarPosition))<>Header.KommentarPosition THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
J:=0;
WHILE J<Header.KommentarLaenge DO BEGIN
IF Daten.Read(C,1)<>1 THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
Message:=Message+C;
{$IFDEF vcltracker}
IF C=#13 THEN Message:=Message+#10;
{$ENDIF}
INC(J);
END;
Daten.Seek(AltePosition);
END;

IF Header.TrackLaenge>256 THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

TrackLaenge:=Header.TrackLaenge-1;
TrackRealLaenge:=TrackLaenge;
IF Daten.Read(PatOrd,Header.TrackLaenge)<>Header.TrackLaenge THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
PatternOrder.KopierenVonByte(PatOrd,TrackLaenge);

IF Daten.Read(IOfs,4*Header.AnzahlDerInstrumente)<>(4*Header.AnzahlDerInstrumente) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF Daten.Read(SOfs,4*Header.AnzahlDerSamples)<>(4*Header.AnzahlDerSamples) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF Daten.Read(POfs,4*Header.AnzahlDerPatterns)<>(4*Header.AnzahlDerPatterns) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF Daten.Read(W,SIZEOF(WORD))<>SIZEOF(WORD) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;

IF (Daten.Position+(W SHL 3))<Daten.Size THEN BEGIN
Daten.Seek(Daten.Position+(W SHL 3));
END;

IF EmbeddedMIDIConfig THEN BEGIN
LP:=Daten.Position;
IF Daten.Read(MIDIConfig,SIZEOF(TMIDIConfig))<>SIZEOF(TMIDIConfig) THEN BEGIN
{ ResetMIDIConfig;
Daten.Seek(LP);}
RESULT:=FALSE;
EXIT;
END;
END;

LP:=Daten.Position;

FOR I:=1 TO Header.AnzahlDerInstrumente DO BEGIN
Daten.Seek(IOfs[I-1]);
IF NOT ASSIGNED(Instrument[I]) THEN Instrument[I]:=TEngineInstrument.Create(I);
Instrument[I].Clear;
IF (Header.TrackerVersion<$200) THEN BEGIN
IF Daten.Read(AltesInstrument,SIZEOF(TITAlterInstrume ntHeader))<>SIZEOF(TITAlterInstrumentHeader) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF AltesInstrument.Signatur<>'IMPI' THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
SwapLittleEndianData16(AltesInstrument.Reserviert) ;
SwapLittleEndianData16(AltesInstrument.FadeOut);
SwapLittleEndianData16(AltesInstrument.TrackerVers ion);
Instrument[I].Name:=TRIMRIGHT(AltesInstrument.Name);
Instrument[I].DateiName:=TRIMRIGHT(AltesInstrument.DateiName);
Instrument[I].FadeOut:=AltesInstrument.FadeOut SHL 6;
Instrument[I].GlobalLautstaerke:=128;
FOR J:=0 TO 119 DO BEGIN
Note:=AltesInstrument.NoteSampleTabelle[J*2];
Ins:=AltesInstrument.NoteSampleTabelle[J*2+1];
IF Ins<MaximumAnzahlDerDateiSamples THEN BEGIN
Instrument[I].NoteMapper.SampleMap[J]:=Ins;
END;
IF Note<128 THEN BEGIN
Instrument[I].NoteMapper.NoteMap[J]:=Note+1;
END ELSE IF Note>=$FE THEN BEGIN
Instrument[I].NoteMapper.NoteMap[J]:=Note;
END;
END;
Instrument[I].VolEnv.Aktiv:=(AltesInstrument.Flags AND 1)<>0;
Instrument[I].VolEnv.Schleife:=(AltesInstrument.Flags AND 2)<>0;
Instrument[I].VolEnv.SustainSchleife:=(AltesInstrument.Flags AND 4)<>0;
Instrument[I].VolEnv.SchleifeStart:=AltesInstrument.VolSchleife Start;
Instrument[I].VolEnv.SchleifeEnde:=AltesInstrument.VolSchleifeE nde;
Instrument[I].VolEnv.SustainSchleifeStart:=AltesInstrument.Sust ainSchleifeStart;
Instrument[I].VolEnv.SustainSchleifeEnde:=AltesInstrument.Susta inSchleifeEnde;
FOR ev:=0 TO 25-1 DO BEGIN
Instrument[I].VolEnv.Knoten[ev]:=AltesInstrument.KnotenPunkte[ev*2];
Instrument[I].VolEnv.Env[ev]:=AltesInstrument.KnotenPunkte[ev*2+1];
IF Instrument[I].VolEnv.Knoten[ev]=$FE THEN BEGIN
Instrument[I].VolEnv.AnzahlDerKnoten:=ev;
BREAK;
END;
END;
Instrument[I].NNA:=AltesInstrument.NNA;
Instrument[I].DCT:=AltesInstrument.DNC;
Instrument[I].Pan:=$80;
END ELSE BEGIN
IF Daten.Read(NeuesInstrument,SIZEOF(TITInstrumentHea der))<>SIZEOF(TITInstrumentHeader) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF NeuesInstrument.Signatur<>'IMPI' THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
SwapLittleEndianData16(NeuesInstrument.FadeOut);
SwapLittleEndianData16(NeuesInstrument.TrackerVers ion);
SwapLittleEndianData16(NeuesInstrument.MIDIBank);
Instrument[I].Name:=TRIMRIGHT(NeuesInstrument.Name);
Instrument[I].DateiName:=TRIMRIGHT(NeuesInstrument.DateiName);
Instrument[I].MIDIProgramm:=NeuesInstrument.MPr;
Instrument[I].MIDIKanal:=NeuesInstrument.MCh;
Instrument[I].MIDIBank:=NeuesInstrument.MIDIBank;
Instrument[I].FadeOut:=NeuesInstrument.FadeOut SHL 5;

IF (Instrument[I].MIDIProgramm<>0) OR (Instrument[I].MIDIKanal<>0) OR (NeuesInstrument.MIDIBank<>0) THEN BEGIN
DoImportMPFX:=TRUE;
END;

Instrument[I].GlobalLautstaerke:=NeuesInstrument.GbV;
IF Instrument[I].GlobalLautstaerke>128 THEN Instrument[I].GlobalLautstaerke:=128;
FOR J:=0 TO 119 DO BEGIN
Note:=NeuesInstrument.NoteSampleTabelle[J*2];
Ins:=NeuesInstrument.NoteSampleTabelle[J*2+1];
Instrument[I].NoteMapper.SampleMap[J]:=0;
Instrument[I].NoteMapper.NoteMap[J]:=0;
IF Ins<MaximumAnzahlDerDateiSamples THEN BEGIN
Instrument[I].NoteMapper.SampleMap[J]:=Ins;
END;
IF Note<128 THEN BEGIN
Instrument[I].NoteMapper.NoteMap[J]:=Note+1;
END ELSE IF Note>=$FE THEN BEGIN
Instrument[I].NoteMapper.NoteMap[J]:=Note;
END;
END;

Instrument[I].VolEnv.Aktiv:=(NeuesInstrument.VolEnv.Flags AND 1)<>0;
Instrument[I].VolEnv.Schleife:=(NeuesInstrument.VolEnv.Flags AND 2)<>0;
Instrument[I].VolEnv.SustainSchleife:=(NeuesInstrument.VolEnv.F lags AND 4)<>0;
Instrument[I].VolEnv.Carry:=(NeuesInstrument.VolEnv.Flags AND 8)<>0;

Instrument[I].PanEnv.Aktiv:=(NeuesInstrument.PanEnv.Flags AND 1)<>0;
Instrument[I].PanEnv.Schleife:=(NeuesInstrument.PanEnv.Flags AND 2)<>0;
Instrument[I].PanEnv.SustainSchleife:=(NeuesInstrument.PanEnv.F lags AND 4)<>0;
Instrument[I].PanEnv.Carry:=(NeuesInstrument.PanEnv.Flags AND 8)<>0;

Instrument[I].PitchEnv.Aktiv:=(NeuesInstrument.PitchEnv.Flags AND 1)<>0;
Instrument[I].PitchEnv.Schleife:=(NeuesInstrument.PitchEnv.Flag s AND 2)<>0;
Instrument[I].PitchEnv.SustainSchleife:=(NeuesInstrument.PitchE nv.Flags AND 4)<>0;
Instrument[I].PitchEnv.Carry:=(NeuesInstrument.PitchEnv.Flags AND 8)<>0;
Instrument[I].PitchEnvAlsFilterEnv:=(NeuesInstrument.PitchEnv.F lags AND 128)<>0;

Instrument[I].VolEnv.AnzahlDerKnoten:=NeuesInstrument.VolEnv.An zahlDerKnoten;
IF Instrument[I].VolEnv.AnzahlDerKnoten>25 THEN Instrument[I].VolEnv.AnzahlDerKnoten:=25;

Instrument[I].PanEnv.AnzahlDerKnoten:=NeuesInstrument.PanEnv.An zahlDerKnoten;
IF Instrument[I].PanEnv.AnzahlDerKnoten>25 THEN Instrument[I].PanEnv.AnzahlDerKnoten:=25;

Instrument[I].PitchEnv.AnzahlDerKnoten:=NeuesInstrument.PitchEn v.AnzahlDerKnoten;
IF Instrument[I].PitchEnv.AnzahlDerKnoten>25 THEN Instrument[I].PitchEnv.AnzahlDerKnoten:=25;

Instrument[I].VolEnv.SchleifeStart:=NeuesInstrument.VolEnv.Schl eifeStart;
Instrument[I].VolEnv.SchleifeEnde:=NeuesInstrument.VolEnv.Schle ifeEnde;
Instrument[I].VolEnv.SustainSchleifeStart:=NeuesInstrument.VolE nv.SustainSchleifeStart;
Instrument[I].VolEnv.SustainSchleifeEnde:=NeuesInstrument.VolEn v.SustainSchleifeEnde;

Instrument[I].PanEnv.SchleifeStart:=NeuesInstrument.PanEnv.Schl eifeStart;
Instrument[I].PanEnv.SchleifeEnde:=NeuesInstrument.PanEnv.Schle ifeEnde;
Instrument[I].PanEnv.SustainSchleifeStart:=NeuesInstrument.PanE nv.SustainSchleifeStart;
Instrument[I].PanEnv.SustainSchleifeEnde:=NeuesInstrument.PanEn v.SustainSchleifeEnde;

Instrument[I].PitchEnv.SchleifeStart:=NeuesInstrument.PitchEnv. SchleifeStart;
Instrument[I].PitchEnv.SchleifeEnde:=NeuesInstrument.PitchEnv.S chleifeEnde;
Instrument[I].PitchEnv.SustainSchleifeStart:=NeuesInstrument.Pi tchEnv.SustainSchleifeStart;
Instrument[I].PitchEnv.SustainSchleifeEnde:=NeuesInstrument.Pit chEnv.SustainSchleifeEnde;

FOR ev:=0 TO 25-1 DO BEGIN
Instrument[I].VolEnv.Knoten[ev]:=SwapWordLittleEndian(NeuesInstrument.VolEnv.Knot en[ev].KnotenPunkt);
Instrument[I].VolEnv.Env[ev]:=NeuesInstrument.VolEnv.Knoten[ev].Env;
Instrument[I].PanEnv.Knoten[ev]:=SwapWordLittleEndian(NeuesInstrument.PanEnv.Knot en[ev].KnotenPunkt);
Instrument[I].PanEnv.Env[ev]:=NeuesInstrument.PanEnv.Knoten[ev].Env+32;
Instrument[I].PitchEnv.Knoten[ev]:=SwapWordLittleEndian(NeuesInstrument.PitchEnv.Kn oten[ev].KnotenPunkt);
Instrument[I].PitchEnv.Env[ev]:=NeuesInstrument.PitchEnv.Knoten[ev].Env+32;
END;

Instrument[I].NNA:=NeuesInstrument.NNA;
Instrument[I].DCT:=NeuesInstrument.DCT;
Instrument[I].DNA:=NeuesInstrument.DCA;
Instrument[I].PanEnv.PitchCenter:=NeuesInstrument.PPC;
Instrument[I].PanEnv.PitchSeparation:=NeuesInstrument.PPS;
Instrument[I].IFC:=NeuesInstrument.IFC;
Instrument[I].IFR:=NeuesInstrument.IFR;
Instrument[I].VolEnv.Swing:=NeuesInstrument.rv;
Instrument[I].PanEnv.Swing:=NeuesInstrument.rp;
Instrument[I].Pan:=(NeuesInstrument.dfp AND $7F) SHL 2;
IF Instrument[I].Pan>256 THEN Instrument[I].Pan:=128;
Instrument[I].PanningAktiv:=NeuesInstrument.dfp<$80;
END;
IF (Instrument[I].VolEnv.SchleifeStart>=25) OR (Instrument[I].VolEnv.SchleifeEnde>=25) THEN Instrument[I].VolEnv.Schleife:=FALSE;
IF (Instrument[I].VolEnv.SustainSchleifeStart>=25) OR (Instrument[I].VolEnv.SustainSchleifeEnde>=25) THEN Instrument[I].VolEnv.SustainSchleife:=FALSE;
IF (Instrument[I].PanEnv.SchleifeStart>=25) OR (Instrument[I].PanEnv.SchleifeEnde>=25) THEN Instrument[I].PanEnv.Schleife:=FALSE;
IF (Instrument[I].PanEnv.SustainSchleifeStart>=25) OR (Instrument[I].PanEnv.SustainSchleifeEnde>=25) THEN Instrument[I].PanEnv.SustainSchleife:=FALSE;
IF (Instrument[I].PitchEnv.SchleifeStart>=25) OR (Instrument[I].PitchEnv.SchleifeEnde>=25) THEN Instrument[I].PitchEnv.Schleife:=FALSE;
IF (Instrument[I].PitchEnv.SustainSchleifeStart>=25) OR (Instrument[I].PitchEnv.SustainSchleifeEnde>=25) THEN Instrument[I].PitchEnv.SustainSchleife:=FALSE;
END;

FOR I:=1 TO Header.AnzahlDerSamples DO BEGIN
Daten.Seek(SOfs[I-1]);
IF Daten.Read(EinSample,SIZEOF(TITSampleHeader))<>SIZEOF(TITSampleHeader) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF EinSample.Signatur<>'IMPS' THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
IF (Header.Flags AND $4)=0 THEN BEGIN
IF NOT ASSIGNED(Instrument[I]) THEN Instrument[I]:=TEngineInstrument.Create(I);
Instrument[I].SampleClear;
END;
IF NOT ASSIGNED(Sample[I]) THEN Sample[I]:=TEngineSample.Create(I);
SwapLittleEndianData32(EinSample.Laenge);
SwapLittleEndianData32(EinSample.SchleifeStart);
SwapLittleEndianData32(EinSample.SchleifeEnde);
SwapLittleEndianData32(EinSample.SustainSchleifeSt art);
SwapLittleEndianData32(EinSample.SustainSchleifeEn de);
SwapLittleEndianData32(EinSample.C5Speed);
SwapLittleEndianData32(EinSample.SampleDatenPositi on);
Sample[I].Clear;
Sample[I].Name:=TRIMRIGHT(EinSample.Name);
Sample[I].DateiName:=TRIMRIGHT(EinSample.DateiName);
Sample[I].Laenge:=0;
Sample[I].SchleifeStart:=EinSample.SchleifeStart;
Sample[I].SchleifeEnde:=EinSample.SchleifeEnde;
IF Sample[I].SchleifeEnde>EinSample.Laenge THEN Sample[I].SchleifeEnde:=EinSample.Laenge;
Sample[I].SustainSchleifeStart:=EinSample.SustainSchleifeSt art;
Sample[I].SustainSchleifeEnde:=EinSample.SustainSchleifeEnd e;
Sample[I].C4Speed:=EinSample.C5Speed;
IF Sample[I].C4Speed=0 THEN Sample[I].C4Speed:=8363;
IF EinSample.C5Speed<256 THEN Sample[I].C4Speed:=256;
Sample[I].Lautstaerke:=EinSample.Lautstaerke SHL 2;
IF Sample[I].Lautstaerke>256 THEN Sample[I].Lautstaerke:=256;
Sample[I].GlobalLautstaerke:=EinSample.GvL;
IF Sample[I].GlobalLautstaerke>64 THEN Sample[I].GlobalLautstaerke:=64;
Sample[I].Loop:=(EinSample.Flags AND $10)<>0;
Sample[I].SustainLoop:=(EinSample.Flags AND $20)<>0;
Sample[I].PingPongLoop:=(EinSample.Flags AND $40)<>0;
Sample[I].PingPongSustain:=(EinSample.Flags AND $80)<>0;
Sample[I].Pan:=(EinSample.Panning AND $7F) SHL 2;
IF Sample[I].Pan>256 THEN Sample[I].Pan:=256;
Sample[I].Panning:=(EinSample.Panning AND $80)<>0;
Sample[I].VibratoRate:=EinSample.VibratoRate;
Sample[I].VibratoDepth:=EinSample.VibratoDepth;
Sample[I].VibratoSpeed:=EinSample.VibratoSpeed;
Sample[I].VibratoWaveForm:=VibratoIT2XM[EinSample.VibratoWaveForm AND 7];
IF (EinSample.SampleDatenPosition<>0) AND (EinSample.SampleDatenPosition<LONGWORD(Daten.Size)) AND (EinSample.Laenge<>0) THEN BEGIN
Daten.Seek(EinSample.SampleDatenPosition);
Sample[I].Laenge:=EinSample.Laenge;
Sample[I].Vergroessern(0);
IF (EinSample.Flags AND 4)<>0 THEN BEGIN
Sample[I].Kaenale:=2;
END ELSE BEGIN
Sample[I].Kaenale:=1;
END;
IF (EinSample.Flags AND 2)<>0 THEN BEGIN
Sample[I].Bits:=16;
END ELSE BEGIN
Sample[I].Bits:=8;
END;
IF (EinSample.Flags AND 4)<>0 THEN BEGIN
IF (EinSample.Flags AND 2)<>0 THEN BEGIN
IF ((EinSample.Flags AND 8)<>0) AND (Header.TrackerVersion>=$214) THEN BEGIN // Gepackt
Sample[I].LeseITSample16BitPacked(Daten,Sample[I].Laenge*4,(Header.KompatibelMitVersion>=$215) AND ((EinSample.KonvertierFlag AND 4)<>0),TRUE);
END ELSE BEGIN // Nicht gepackt
Sample[I].LesePCMSample16Bit(Daten,Sample[I].Laenge*4,(EinSample.KonvertierFlag AND 1)<>0,TRUE);
END;
END ELSE BEGIN // 8 Bit
IF ((EinSample.Flags AND 8)<>0) AND (Header.TrackerVersion>=$214) THEN BEGIN // Gepackt
Sample[I].LeseITSample8BitPacked(Daten,Sample[I].Laenge*2,(Header.KompatibelMitVersion>=$215) AND ((EinSample.KonvertierFlag AND 4)<>0),TRUE);
END ELSE IF EinSample.KonvertierFlag=$FF THEN BEGIN // ADPCM4
Sample[I].LeseSample8BitADPCM4(Daten,Sample[I].Laenge,(EinSample.KonvertierFlag AND 1)<>0,FALSE);
END ELSE BEGIN // Nicht gepackt
Sample[I].LesePCMSample8Bit(Daten,Sample[I].Laenge*2,(EinSample.KonvertierFlag AND 1)<>0,TRUE);
END;
END;
END ELSE BEGIN // Mono
IF (EinSample.Flags AND 2)<>0 THEN BEGIN
IF ((EinSample.Flags AND 8)<>0) AND (Header.TrackerVersion>=$214) THEN BEGIN // Gepackt
Sample[I].LeseITSample16BitPacked(Daten,Sample[I].Laenge*2,(Header.KompatibelMitVersion>=$215) AND ((EinSample.KonvertierFlag AND 4)<>0),FALSE);
END ELSE BEGIN // Nicht gepackt
Sample[I].LesePCMSample16Bit(Daten,Sample[I].Laenge*2,(EinSample.KonvertierFlag AND 1)<>0,FALSE);
END;
END ELSE BEGIN // 8 Bit
IF ((EinSample.Flags AND 8)<>0) AND (Header.TrackerVersion>=$214) THEN BEGIN // Gepackt
Sample[I].LeseITSample8BitPacked(Daten,Sample[I].Laenge,(Header.KompatibelMitVersion>=$215) AND ((EinSample.KonvertierFlag AND 4)<>0),FALSE);
END ELSE IF EinSample.KonvertierFlag=$FF THEN BEGIN // ADPCM4
Sample[I].LeseSample8BitADPCM4(Daten,Sample[I].Laenge,(EinSample.KonvertierFlag AND 1)<>0,FALSE);
END ELSE BEGIN // Nicht gepackt
Sample[I].LesePCMSample8Bit(Daten,Sample[I].Laenge,(EinSample.KonvertierFlag AND 1)<>0,FALSE);
END;
END;
END;
END;
Sample[I].FrequenzZuTranspose;
END;

FOR I:=0 TO AnzahlDerPatterns-1 DO BEGIN
IF I<MaximumAnzahlDerDateiPatterns THEN BEGIN
IF NOT LesePattern(I) THEN BEGIN
RESULT:=FALSE;
EXIT;
END;
END;
END;

Daten.Seek(LP);
TRY
ReadExtendedData(Daten,FALSE);
EXCEPT
END;

IF (ExtendedDataOffset>0) AND (ExtendedDataOffset<LONGWORD(Daten.Size)) AND ((Header.Flags AND $8000)<>0) THEN BEGIN
Daten.Seek(ExtendedDataOffset);
END;
Daten.Seek(Daten.Size);

//DoImportMPFX:=TRUE;
MindestPeriodeWert:=8;
MaximumPeriodeWert:=$F000;
TrackType:=ettIT;
EffectNotation:=EFFECTNOTATION_S3MIT;
RESULT:=TRUE;
END;



{$IFDEF FPC}
{$IFDEF FPC_LITTLE_ENDIAN}
{$DEFINE LITTLE_ENDIAN}
{$ELSE}
{$IFDEF FPC_BIG_ENDIAN}
{$DEFINE BIG_ENDIAN}
{$ENDIF}
{$ENDIF}
{$ELSE}
{$DEFINE LITTLE_ENDIAN}
{$ENDIF}
FUNCTION SwapWordLittleEndian(Value:WORD):WORD; {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
BEGIN
{$IFDEF BIG_ENDIAN}
RESULT:=((Value AND $FF00) SHR 8) OR ((Value AND $FF) SHL 8);
{$ELSE}
RESULT:=Value;
{$ENDIF}
END;

FUNCTION SwapDWordLittleEndian(Value:LONGWORD):LONGWORD; {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
BEGIN
{$IFDEF BIG_ENDIAN}
RESULT:=((Value AND $FF000000) SHR 24) OR ((Value AND $00FF0000) SHR 8) OR
((Value AND $0000FF00) SHL 8) OR ((Value AND $000000FF) SHL 24);
{$ELSE}
RESULT:=Value;
{$ENDIF}
END;

FUNCTION SwapWordBigEndian(Value:WORD):WORD; {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
BEGIN
{$IFDEF LITTLE_ENDIAN}
RESULT:=((Value AND $FF00) SHR 8) OR ((Value AND $FF) SHL 8);
{$ELSE}
RESULT:=Value;
{$ENDIF}
END;

FUNCTION SwapDWordBigEndian(Value:LONGWORD):LONGWORD; {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
BEGIN
{$IFDEF LITTLE_ENDIAN}
RESULT:=((Value AND $FF000000) SHR 24) OR ((Value AND $00FF0000) SHR 8) OR
((Value AND $0000FF00) SHL 8) OR ((Value AND $000000FF) SHL 24);
{$ELSE}
RESULT:=Value;
{$ENDIF}
END;

PROCEDURE SwapLittleEndianData16(VAR Data); {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
{$IFDEF BIG_ENDIAN}
VAR Value:WORD ABSOLUTE Data;
BEGIN
Value:=((Value AND $FF00) SHR 8) OR ((Value AND $FF) SHL 8);
{$ELSE}
BEGIN
{$ENDIF}
END;

PROCEDURE SwapLittleEndianData32(VAR Data); {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
{$IFDEF BIG_ENDIAN}
VAR Value:LONGWORD ABSOLUTE Data;
BEGIN
Value:=((Value AND $FF000000) SHR 24) OR ((Value AND $00FF0000) SHR 8) OR
((Value AND $0000FF00) SHL 8) OR ((Value AND $000000FF) SHL 24);
{$ELSE}
BEGIN
{$ENDIF}
END;

PROCEDURE SwapBigEndianData16(VAR Data); {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
{$IFDEF LITTLE_ENDIAN}
VAR Value:WORD ABSOLUTE Data;
BEGIN
Value:=((Value AND $FF00) SHR 8) OR ((Value AND $FF) SHL 8);
{$ELSE}
BEGIN
RESULT:=Value;
{$ENDIF}
END;

PROCEDURE SwapBigEndianData32(VAR Data); {$IFDEF FPC}{INLINE;}{$ELSE}REGISTER;{$ENDIF}
{$IFDEF LITTLE_ENDIAN}
VAR Value:LONGWORD ABSOLUTE Data;
BEGIN
Value:=((Value AND $FF000000) SHR 24) OR ((Value AND $00FF0000) SHR 8) OR
((Value AND $0000FF00) SHL 8) OR ((Value AND $000000FF) SHL 24);
{$ELSE}
BEGIN
{$ENDIF}
END;

WILL
29-06-2006, 05:25 AM
Well I guess I asked for it. :)

Thanks for the code, it'll give me something to dig through while I'm trying to figure out how to handle the sample data given.

So in plain language, what ARE all the different types of sample compression/packing for IT (including both Impulse Tracker & MODPlug Tracker)? Just so I have a general run-down and know what to plan for when I rewrite my functions?

On a side-note, my plan is to merge all the seperate Mixer classes into one common object class and make alternate loading commands all accessable by a single one LoadFromFile() procedure that will do all the autodetection and loading of all major 4 supported MOD formats. (Should make adding other more obscure formats quite easy too!) I can include the BeRoTracker format too if you have a specification document?

bantri
07-08-2007, 08:24 PM
BINGO!

just spotted a bug in your code :)

IT_Pattern = class(TObject)
NumberOfRows : Word; {Ranges from 32->200}
NumberOfNotes : Integer;
Notes: Array of IT_Note;
end;

this definition is wrong
first field is length in bytes
second field number of rows
third is ok

bantri
08-08-2007, 03:02 PM
it´s not a packed record, it´s an object,
then i guess it´s ok...

>One thing I do remember quite specifically though was that when I took >a mod that was created (seemingly) with Impulse Tracker it's self and >then tried to load one saved (modified or created by) ModPlug Tracker it >would cease to load properly.

i think i can see some reasons for this :)

i am doing an x-ray in delphi for it files and it shows that files saved with modplug don´t have 214/215 saved for version.
instead they have 888 for version and the pattern size turns do $100 which is beyond the limit of the it default format.