Results 1 to 8 of 8

Thread: Sample Compression Format for IT (Impulse Tracker) Modules

  1. #1
    Co-Founder / PGD Elder WILL's Avatar
    Join Date
    Apr 2003
    Location
    Canada
    Posts
    6,107
    Blog Entries
    25

    Sample Compression Format for IT (Impulse Tracker) Modules

    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.
    Jason McMillen
    Pascal Game Development
    Co-Founder





  2. #2

    Sample Compression Format for IT (Impulse Tracker) Modules

    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 >_<

  3. #3
    Co-Founder / PGD Elder WILL's Avatar
    Join Date
    Apr 2003
    Location
    Canada
    Posts
    6,107
    Blog Entries
    25

    Sample Compression Format for IT (Impulse Tracker) Modules

    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.

    [pascal]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, );
    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], );
    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_MONO;

    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.[/pascal]

    [pascal]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 12 > 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 ;

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

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

    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 ;

    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.[/pascal]

    [pascal]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 < 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_IT214 or (flags = RS_IT215 then
    begin
    //ITUnpack8Bit(Samples[i].SampleData, Samples[i].Length, Samples[i].SamplePointer,
    // Len, (flags = RS_IT215)
    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.[/pascal]
    Jason McMillen
    Pascal Game Development
    Co-Founder





  4. #4
    Co-Founder / PGD Elder WILL's Avatar
    Join Date
    Apr 2003
    Location
    Canada
    Posts
    6,107
    Blog Entries
    25

    Sample Compression Format for IT (Impulse Tracker) Modules

    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.
    Jason McMillen
    Pascal Game Development
    Co-Founder





  5. #5

    Sample Compression Format for IT (Impulse Tracker) Modules

    Directly from BeRoTracker src (sorry for the german variable names etc. from my old coding style, the code base is very old)

    [pascal]
    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;
    [/pascal]

    [pascal]
    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 DestPosSHORTINT;
    BlockLength:WORD;
    BlockPosition:WORD;
    Width:BYTE;
    Value:WORD;
    D1HORTINT;
    D2HORTINT;
    VHORTINT;
    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+) 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 DestPosSMALLINT;
    BlockLength:WORD;
    BlockPosition:WORD;
    Width:BYTE;
    Value:LONGWORD;
    D1MALLINT;
    D2MALLINT;
    VMALLINT;
    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 BSHORTINT;
    RB:BYTE;
    DHORTINT;
    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;
    SampleBufferOINTER;
    PQB,PZBBYTE;
    PQW,PZWWORD;
    BEGIN
    TRY
    IF (Bits IN [8,16]) AND (Kaenale=2) THEN BEGIN
    SampleSize:=RealSize;
    SampleLength:=SampleSize DIV (2*(Bits DIV );
    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;
    [/pascal]

    [pascal]
    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;
    PDatenBufferOINTER;
    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(T ITPattern) 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>=12 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 <>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(TITHe ader) 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>MaximumAnzahlDerDatei Instrumente) OR (Header.AnzahlDerSamples>MaximumAnzahlDerDateiSamp les) OR (Header.AnzahlDerPatterns>MaximumAnzahlDerDateiPat terns) 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 $<>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))<>He ader.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.Trac kLaenge 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*Hea der.AnzahlDerSamples) THEN BEGIN
    RESULT:=FALSE;
    EXIT;
    END;
    IF Daten.Read(POfs,4*Header.AnzahlDerPatterns)<>(4*He ader.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 <>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 <>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 <>0;
    Instrument[I].PitchEnvAlsFilterEnv:=(NeuesInstrument.PitchEnv.F lags AND 12<>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))<>SIZ EOF(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 <>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 <>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 <>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 <>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;
    [/pascal]

    [pascal]
    {$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 OR ((Value AND $FF) SHL ;
    {$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 OR
    ((Value AND $0000FF00) SHL 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 OR ((Value AND $FF) SHL ;
    {$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 OR
    ((Value AND $0000FF00) SHL 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 OR ((Value AND $FF) SHL ;
    {$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 OR
    ((Value AND $0000FF00) SHL 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 OR ((Value AND $FF) SHL ;
    {$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 OR
    ((Value AND $0000FF00) SHL OR ((Value AND $000000FF) SHL 24);
    {$ELSE}
    BEGIN
    {$ENDIF}
    END;
    [/pascal]

  6. #6
    Co-Founder / PGD Elder WILL's Avatar
    Join Date
    Apr 2003
    Location
    Canada
    Posts
    6,107
    Blog Entries
    25

    Sample Compression Format for IT (Impulse Tracker) Modules

    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. [size=9px](Should make adding other more obscure formats quite easy too!)[/size] I can include the BeRoTracker format too if you have a specification document?
    Jason McMillen
    Pascal Game Development
    Co-Founder





  7. #7

    bug

    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

  8. #8

    ooops...

    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.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •