Mercurial > audlegacy-plugins
view src/modplug/load_psm.cxx @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | 6907fc39b53f |
children | 107c1fed3d92 |
line wrap: on
line source
/* * This source code is public domain. * * Authors: Olivier Lapicque <olivierl@jps.net> */ /////////////////////////////////////////////////// // // PSM module loader // /////////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" //#define PSM_LOG #define PSM_ID_NEW 0x204d5350 #define PSM_ID_OLD 0xfe4d5350 #define IFFID_FILE 0x454c4946 #define IFFID_TITL 0x4c544954 #define IFFID_SDFT 0x54464453 #define IFFID_PBOD 0x444f4250 #define IFFID_SONG 0x474e4f53 #define IFFID_PATT 0x54544150 #define IFFID_DSMP 0x504d5344 #define IFFID_OPLH 0x484c504f #pragma pack(1) typedef struct _PSMCHUNK { DWORD id; DWORD len; DWORD listid; } PSMCHUNK; typedef struct _PSMSONGHDR { CHAR songname[8]; // "MAINSONG" BYTE reserved1; BYTE reserved2; BYTE channels; } PSMSONGHDR; typedef struct _PSMPATTERN { DWORD size; DWORD name; WORD rows; WORD reserved1; BYTE data[4]; } PSMPATTERN; typedef struct _PSMSAMPLE { BYTE flags; CHAR songname[8]; DWORD smpid; CHAR samplename[34]; DWORD reserved1; BYTE reserved2; BYTE insno; BYTE reserved3; DWORD length; DWORD loopstart; DWORD loopend; WORD reserved4; BYTE defvol; DWORD reserved5; DWORD samplerate; BYTE reserved6[19]; } PSMSAMPLE; #pragma pack() BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) //----------------------------------------------------------- { PSMCHUNK *pfh = (PSMCHUNK *)lpStream; PSMCHUNK pfh_swap; DWORD dwMemPos, dwSongPos; DWORD smpnames[MAX_SAMPLES]; DWORD patptrs[MAX_PATTERNS]; BYTE samplemap[MAX_SAMPLES]; UINT nPatterns; pfh_swap.id = bswapLE32(pfh->id); pfh_swap.len = bswapLE32(pfh->len); pfh_swap.listid = bswapLE32(pfh->listid); // Chunk0: "PSM ",filesize,"FILE" if (dwMemLength < 256) return FALSE; if (pfh_swap.id == PSM_ID_OLD) { #ifdef PSM_LOG Log("Old PSM format not supported\n"); #endif return FALSE; } if ((pfh_swap.id != PSM_ID_NEW) || (pfh_swap.len+12 > dwMemLength) || (pfh_swap.listid != IFFID_FILE)) return FALSE; m_nType = MOD_TYPE_PSM; m_nChannels = 16; m_nSamples = 0; nPatterns = 0; dwMemPos = 12; dwSongPos = 0; for (UINT iChPan=0; iChPan<16; iChPan++) { UINT pan = (((iChPan & 3) == 1) || ((iChPan&3)==2)) ? 0xC0 : 0x40; ChnSettings[iChPan].nPan = pan; } while (dwMemPos+8 < dwMemLength) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); pchunk->id = bswapLE32(pchunk->id); pchunk->len = bswapLE32(pchunk->len); pchunk->listid = bswapLE32(pchunk->listid); if ((pchunk->len >= dwMemLength - 8) || (dwMemPos + pchunk->len + 8 > dwMemLength)) break; dwMemPos += 8; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; if (len) switch(pchunk->id) { // "TITL": Song title case IFFID_TITL: if (!pdata[0]) { pdata++; len--; } memcpy(m_szNames[0], pdata, (len>31) ? 31 : len); m_szNames[0][31] = 0; break; // "PBOD": Pattern case IFFID_PBOD: if ((len >= 12) && (nPatterns < MAX_PATTERNS)) { patptrs[nPatterns++] = dwMemPos-8; } break; // "SONG": Song description case IFFID_SONG: if ((len >= sizeof(PSMSONGHDR)+8) && (!dwSongPos)) { dwSongPos = dwMemPos - 8; } break; // "DSMP": Sample Data case IFFID_DSMP: if ((len >= sizeof(PSMSAMPLE)) && (m_nSamples+1 < MAX_SAMPLES)) { m_nSamples++; MODINSTRUMENT *pins = &Ins[m_nSamples]; PSMSAMPLE *psmp = (PSMSAMPLE *)pdata; psmp->smpid = bswapLE32(psmp->smpid); psmp->length = bswapLE32(psmp->length); psmp->loopstart = bswapLE32(psmp->loopstart); psmp->loopend = bswapLE32(psmp->loopend); psmp->samplerate = bswapLE32(psmp->samplerate); smpnames[m_nSamples] = psmp->smpid; memcpy(m_szNames[m_nSamples], psmp->samplename, 31); m_szNames[m_nSamples][31] = 0; samplemap[m_nSamples-1] = (BYTE)m_nSamples; // Init sample pins->nGlobalVol = 0x40; pins->nC4Speed = psmp->samplerate; pins->nLength = psmp->length; pins->nLoopStart = psmp->loopstart; pins->nLoopEnd = psmp->loopend; pins->nPan = 128; pins->nVolume = (psmp->defvol+1) * 2; pins->uFlags = (psmp->flags & 0x80) ? CHN_LOOP : 0; if (pins->nLoopStart > 0) pins->nLoopStart--; // Point to sample data pdata += 0x60; len -= 0x60; // Load sample data if ((pins->nLength > 3) && (len > 3)) { ReadSample(pins, RS_PCM8D, (LPCSTR)pdata, len); } else { pins->nLength = 0; } } break; #if 0 default: { CHAR s[8], s2[64]; *(DWORD *)s = pchunk->id; s[4] = 0; wsprintf(s2, "%s: %4d bytes @ %4d\n", s, pchunk->len, dwMemPos); OutputDebugString(s2); } #endif } dwMemPos += pchunk->len; } // Step #1: convert song structure PSMSONGHDR *pSong = (PSMSONGHDR *)(lpStream+dwSongPos+8); if ((!dwSongPos) || (pSong->channels < 2) || (pSong->channels > 32)) return TRUE; m_nChannels = pSong->channels; // Valid song header -> convert attached chunks { DWORD dwSongEnd = dwSongPos + 8 + *(DWORD *)(lpStream+dwSongPos+4); dwMemPos = dwSongPos + 8 + 11; // sizeof(PSMCHUNK)+sizeof(PSMSONGHDR) while (dwMemPos + 8 < dwSongEnd) { PSMCHUNK *pchunk = (PSMCHUNK *)(lpStream+dwMemPos); pchunk->id = bswapLE32(pchunk->id); pchunk->len = bswapLE32(pchunk->len); pchunk->listid = bswapLE32(pchunk->listid); dwMemPos += 8; if ((pchunk->len > dwSongEnd) || (dwMemPos + pchunk->len > dwSongEnd)) break; PUCHAR pdata = (PUCHAR)(lpStream+dwMemPos); ULONG len = pchunk->len; switch(pchunk->id) { case IFFID_OPLH: if (len >= 0x20) { UINT pos = len - 3; while (pos > 5) { BOOL bFound = FALSE; pos -= 5; DWORD dwName = *(DWORD *)(pdata+pos); for (UINT i=0; i<nPatterns; i++) { DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; if (dwName == dwPatName) { bFound = TRUE; break; } } if ((!bFound) && (pdata[pos+1] > 0) && (pdata[pos+1] <= 0x10) && (pdata[pos+3] > 0x40) && (pdata[pos+3] < 0xC0)) { m_nDefaultSpeed = pdata[pos+1]; m_nDefaultTempo = pdata[pos+3]; break; } } UINT iOrd = 0; while ((pos+5<len) && (iOrd < MAX_ORDERS)) { DWORD dwName = *(DWORD *)(pdata+pos); for (UINT i=0; i<nPatterns; i++) { DWORD dwPatName = ((PSMPATTERN *)(lpStream+patptrs[i]+8))->name; if (dwName == dwPatName) { Order[iOrd++] = i; break; } } pos += 5; } } break; } dwMemPos += pchunk->len; } } // Step #2: convert patterns for (UINT nPat=0; nPat<nPatterns; nPat++) { PSMPATTERN *pPsmPat = (PSMPATTERN *)(lpStream+patptrs[nPat]+8); pPsmPat->size = bswapLE32(pPsmPat->size); pPsmPat->name = bswapLE32(pPsmPat->name); pPsmPat->rows = bswapLE16(pPsmPat->rows); ULONG len = *(DWORD *)(lpStream+patptrs[nPat]+4) - 12; UINT nRows = pPsmPat->rows; if (len > pPsmPat->size) len = pPsmPat->size; if ((nRows < 64) || (nRows > 256)) nRows = 64; PatternSize[nPat] = nRows; if ((Patterns[nPat] = AllocatePattern(nRows, m_nChannels)) == NULL) break; MODCOMMAND *m = Patterns[nPat]; BYTE *p = pPsmPat->data; UINT pos = 0; UINT row = 0; UINT oldch = 0; BOOL bNewRow = FALSE; #ifdef PSM_LOG Log("Pattern %d at offset 0x%04X\n", nPat, (DWORD)(p - (BYTE *)lpStream)); #endif while ((row < nRows) && (pos+1 < len)) { UINT flags = p[pos++]; UINT ch = p[pos++]; #ifdef PSM_LOG //Log("flags+ch: %02X.%02X\n", flags, ch); #endif if (((flags & 0xf0) == 0x10) && (ch <= oldch) /*&& (!bNewRow)*/) { if ((pos+1<len) && (!(p[pos] & 0x0f)) && (p[pos+1] < m_nChannels)) { #ifdef PSM_LOG //if (!nPat) Log("Continuing on new row\n"); #endif row++; m += m_nChannels; oldch = ch; continue; } } if ((pos >= len) || (row >= nRows)) break; if (!(flags & 0xf0)) { #ifdef PSM_LOG //if (!nPat) Log("EOR(%d): %02X.%02X\n", row, p[pos], p[pos+1]); #endif row++; m += m_nChannels; bNewRow = TRUE; oldch = ch; continue; } bNewRow = FALSE; if (ch >= m_nChannels) { #ifdef PSM_LOG if (!nPat) Log("Invalid channel row=%d (0x%02X.0x%02X)\n", row, flags, ch); #endif ch = 0; } // Note + Instr if ((flags & 0x40) && (pos+1 < len)) { UINT note = p[pos++]; UINT nins = p[pos++]; #ifdef PSM_LOG //if (!nPat) Log("note+ins: %02X.%02X\n", note, nins); if ((!nPat) && (nins >= m_nSamples)) Log("WARNING: invalid instrument number (%d)\n", nins); #endif if ((note) && (note < 0x80)) note = (note>>4)*12+(note&0x0f)+12+1; m[ch].instr = samplemap[nins]; m[ch].note = note; } // Volume if ((flags & 0x20) && (pos < len)) { m[ch].volcmd = VOLCMD_VOLUME; m[ch].vol = p[pos++] / 2; } // Effect if ((flags & 0x10) && (pos+1 < len)) { UINT command = p[pos++]; UINT param = p[pos++]; // Convert effects switch(command) { // 01: fine volslide up case 0x01: command = CMD_VOLUMESLIDE; param |= 0x0f; break; // 04: fine volslide down case 0x04: command = CMD_VOLUMESLIDE; param>>=4; param |= 0xf0; break; // 0C: portamento up case 0x0C: command = CMD_PORTAMENTOUP; param = (param+1)/2; break; // 0E: portamento down case 0x0E: command = CMD_PORTAMENTODOWN; param = (param+1)/2; break; // 33: Position Jump case 0x33: command = CMD_POSITIONJUMP; break; // 34: Pattern break case 0x34: command = CMD_PATTERNBREAK; break; // 3D: speed case 0x3D: command = CMD_SPEED; break; // 3E: tempo case 0x3E: command = CMD_TEMPO; break; // Unknown default: #ifdef PSM_LOG Log("Unknown PSM effect pat=%d row=%d ch=%d: %02X.%02X\n", nPat, row, ch, command, param); #endif command = param = 0; } m[ch].command = (BYTE)command; m[ch].param = (BYTE)param; } oldch = ch; } #ifdef PSM_LOG if (pos < len) { Log("Pattern %d: %d/%d[%d] rows (%d bytes) -> %d bytes left\n", nPat, row, nRows, pPsmPat->rows, pPsmPat->size, len-pos); } #endif } // Done (finally!) return TRUE; } ////////////////////////////////////////////////////////////// // // PSM Old Format // /* CONST c_PSM_MaxOrder = $FF; c_PSM_MaxSample = $FF; c_PSM_MaxChannel = $0F; TYPE PPSM_Header = ^TPSM_Header; TPSM_Header = RECORD PSM_Sign : ARRAY[01..04] OF CHAR; { PSM + #254 } PSM_SongName : ARRAY[01..58] OF CHAR; PSM_Byte00 : BYTE; PSM_Byte1A : BYTE; PSM_Unknown00 : BYTE; PSM_Unknown01 : BYTE; PSM_Unknown02 : BYTE; PSM_Speed : BYTE; PSM_Tempo : BYTE; PSM_Unknown03 : BYTE; PSM_Unknown04 : WORD; PSM_OrderLength : WORD; PSM_PatternNumber : WORD; PSM_SampleNumber : WORD; PSM_ChannelNumber : WORD; PSM_ChannelUsed : WORD; PSM_OrderPosition : LONGINT; PSM_ChannelSettingPosition : LONGINT; PSM_PatternPosition : LONGINT; PSM_SamplePosition : LONGINT; { *** perhaps there are some more infos in a larger header, but i have not decoded it and so it apears here NOT } END; PPSM_Sample = ^TPSM_Sample; TPSM_Sample = RECORD PSM_SampleFileName : ARRAY[01..12] OF CHAR; PSM_SampleByte00 : BYTE; PSM_SampleName : ARRAY[01..22] OF CHAR; PSM_SampleUnknown00 : ARRAY[01..02] OF BYTE; PSM_SamplePosition : LONGINT; PSM_SampleUnknown01 : ARRAY[01..04] OF BYTE; PSM_SampleNumber : BYTE; PSM_SampleFlags : WORD; PSM_SampleLength : LONGINT; PSM_SampleLoopBegin : LONGINT; PSM_SampleLoopEnd : LONGINT; PSM_Unknown03 : BYTE; PSM_SampleVolume : BYTE; PSM_SampleC5Speed : WORD; END; PPSM_SampleList = ^TPSM_SampleList; TPSM_SampleList = ARRAY[01..c_PSM_MaxSample] OF TPSM_Sample; PPSM_Order = ^TPSM_Order; TPSM_Order = ARRAY[00..c_PSM_MaxOrder] OF BYTE; PPSM_ChannelSettings = ^TPSM_ChannelSettings; TPSM_ChannelSettings = ARRAY[00..c_PSM_MaxChannel] OF BYTE; CONST PSM_NotesInPattern : BYTE = $00; PSM_ChannelInPattern : BYTE = $00; CONST c_PSM_SetSpeed = 60; FUNCTION PSM_Size(FileName : STRING;FilePosition : LONGINT) : LONGINT; BEGIN END; PROCEDURE PSM_UnpackPattern(VAR Source,Destination;PatternLength : WORD); VAR Witz : ARRAY[00..04] OF WORD; I1,I2 : WORD; I3,I4 : WORD; TopicalByte : ^BYTE; Pattern : PUnpackedPattern; ChannelP : BYTE; NoteP : BYTE; InfoByte : BYTE; CodeByte : BYTE; InfoWord : WORD; Effect : BYTE; Opperand : BYTE; Panning : BYTE; Volume : BYTE; PrevInfo : BYTE; InfoIndex : BYTE; BEGIN Pattern := @Destination; TopicalByte := @Source; { *** Initialize patttern } FOR I2 := 0 TO c_Maximum_NoteIndex DO FOR I3 := 0 TO c_Maximum_ChannelIndex DO BEGIN Pattern^[I2,I3,c_Pattern_NoteIndex] := $FF; Pattern^[I2,I3,c_Pattern_SampleIndex] := $00; Pattern^[I2,I3,c_Pattern_VolumeIndex] := $FF; Pattern^[I2,I3,c_Pattern_PanningIndex] := $FF; Pattern^[I2,I3,c_Pattern_EffectIndex] := $00; Pattern^[I2,I3,c_Pattern_OpperandIndex] := $00; END; { *** Byte-pointer on first pattern-entry } ChannelP := $00; NoteP := $00; InfoByte := $00; PrevInfo := $00; InfoIndex := $02; { *** read notes in pattern } PSM_NotesInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); PSM_ChannelInPattern := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); { *** unpack pattern } WHILE (INTEGER(PatternLength) > 0) AND (NoteP < c_Maximum_NoteIndex) DO BEGIN { *** Read info-byte } InfoByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); INC(InfoIndex); IF InfoByte <> $00 THEN BEGIN ChannelP := InfoByte AND $0F; IF InfoByte AND 128 = 128 THEN { note and sample } BEGIN { *** read note } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); DEC(CodeByte); CodeByte := CodeByte MOD 12 * 16 + CodeByte DIV 12 + 2; Pattern^[NoteP,ChannelP,c_Pattern_NoteIndex] := CodeByte; { *** read sample } CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_SampleIndex] := CodeByte; END; IF InfoByte AND 64 = 64 THEN { Volume } BEGIN CodeByte := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Pattern^[NoteP,ChannelP,c_Pattern_VolumeIndex] := CodeByte; END; IF InfoByte AND 32 = 32 THEN { effect AND opperand } BEGIN Effect := TopicalByte^; INC(TopicalByte); DEC(PatternLength); Opperand := TopicalByte^; INC(TopicalByte); DEC(PatternLength); CASE Effect OF c_PSM_SetSpeed: BEGIN Effect := c_I_Set_Speed; END; ELSE BEGIN Effect := c_I_NoEffect; Opperand := $00; END; END; Pattern^[NoteP,ChannelP,c_Pattern_EffectIndex] := Effect; Pattern^[NoteP,ChannelP,c_Pattern_OpperandIndex] := Opperand; END; END ELSE INC(NoteP); END; END; PROCEDURE PSM_Load(FileName : STRING;FilePosition : LONGINT;VAR Module : PModule;VAR ErrorCode : WORD); { *** caution : Module has to be inited before!!!! } VAR Header : PPSM_Header; Sample : PPSM_SampleList; Order : PPSM_Order; ChannelSettings : PPSM_ChannelSettings; MultiPurposeBuffer : PByteArray; PatternBuffer : PUnpackedPattern; TopicalParaPointer : WORD; InFile : FILE; I1,I2 : WORD; I3,I4 : WORD; TempW : WORD; TempB : BYTE; TempP : PByteArray; TempI : INTEGER; { *** copy-vars for loop-extension } CopySource : LONGINT; CopyDestination : LONGINT; CopyLength : LONGINT; BEGIN { *** try to open file } ASSIGN(InFile,FileName); {$I-} RESET(InFile,1); {$I+} IF IORESULT <> $00 THEN BEGIN EXIT; END; {$I-} { *** seek start of module } IF FILESIZE(InFile) < FilePosition THEN BEGIN EXIT; END; SEEK(InFile,FilePosition); { *** look for enough memory for temporary variables } IF MEMAVAIL < SIZEOF(TPSM_Header) + SIZEOF(TPSM_SampleList) + SIZEOF(TPSM_Order) + SIZEOF(TPSM_ChannelSettings) + SIZEOF(TByteArray) + SIZEOF(TUnpackedPattern) THEN BEGIN EXIT; END; { *** init dynamic variables } NEW(Header); NEW(Sample); NEW(Order); NEW(ChannelSettings); NEW(MultiPurposeBuffer); NEW(PatternBuffer); { *** read header } BLOCKREAD(InFile,Header^,SIZEOF(TPSM_Header)); { *** test if this is a DSM-file } IF NOT ((Header^.PSM_Sign[1] = 'P') AND (Header^.PSM_Sign[2] = 'S') AND (Header^.PSM_Sign[3] = 'M') AND (Header^.PSM_Sign[4] = #254)) THEN BEGIN ErrorCode := c_NoValidFileFormat; CLOSE(InFile); EXIT; END; { *** read order } SEEK(InFile,FilePosition + Header^.PSM_OrderPosition); BLOCKREAD(InFile,Order^,Header^.PSM_OrderLength); { *** read channelsettings } SEEK(InFile,FilePosition + Header^.PSM_ChannelSettingPosition); BLOCKREAD(InFile,ChannelSettings^,SIZEOF(TPSM_ChannelSettings)); { *** read samplelist } SEEK(InFile,FilePosition + Header^.PSM_SamplePosition); BLOCKREAD(InFile,Sample^,Header^.PSM_SampleNumber * SIZEOF(TPSM_Sample)); { *** copy header to intern NTMIK-structure } Module^.Module_Sign := 'MF'; Module^.Module_FileFormatVersion := $0100; Module^.Module_SampleNumber := Header^.PSM_SampleNumber; Module^.Module_PatternNumber := Header^.PSM_PatternNumber; Module^.Module_OrderLength := Header^.PSM_OrderLength; Module^.Module_ChannelNumber := Header^.PSM_ChannelNumber+1; Module^.Module_Initial_GlobalVolume := 64; Module^.Module_Initial_MasterVolume := $C0; Module^.Module_Initial_Speed := Header^.PSM_Speed; Module^.Module_Initial_Tempo := Header^.PSM_Tempo; { *** paragraph 01 start } Module^.Module_Flags := c_Module_Flags_ZeroVolume * BYTE(1) + c_Module_Flags_Stereo * BYTE(1) + c_Module_Flags_ForceAmigaLimits * BYTE(0) + c_Module_Flags_Panning * BYTE(1) + c_Module_Flags_Surround * BYTE(1) + c_Module_Flags_QualityMixing * BYTE(1) + c_Module_Flags_FastVolumeSlides * BYTE(0) + c_Module_Flags_SpecialCustomData * BYTE(0) + c_Module_Flags_SongName * BYTE(1); I1 := $01; WHILE (Header^.PSM_SongName[I1] > #00) AND (I1 < c_Module_SongNameLength) DO BEGIN Module^.Module_Name[I1] := Header^.PSM_SongName[I1]; INC(I1); END; Module^.Module_Name[c_Module_SongNameLength] := #00; { *** Init channelsettings } FOR I1 := 0 TO c_Maximum_ChannelIndex DO BEGIN IF I1 < Header^.PSM_ChannelUsed THEN BEGIN { *** channel enabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := 64; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := (ChannelSettings^[I1]) * $08; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := I1 + $10 * BYTE(ChannelSettings^[I1] > $08) + c_ChannelSettings_Code_ChannelEnabled * BYTE(1) + c_ChannelSettings_Code_ChannelDigital * BYTE(1); Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := c_ChannelSettings_Controls_EnhancedMode * BYTE(1) + c_ChannelSettings_Controls_SurroundMode * BYTE(0); END ELSE BEGIN { *** channel disabled } Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_GlobalVolume := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Panning := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Code := $00; Module^.Module_ChannelSettingPointer^[I1].ChannelSettings_Controls := $00; END; END; { *** init and copy order } FILLCHAR(Module^.Module_OrderPointer^,c_Maximum_OrderIndex+1,$FF); MOVE(Order^,Module^.Module_OrderPointer^,Header^.PSM_OrderLength); { *** read pattern } SEEK(InFile,FilePosition + Header^.PSM_PatternPosition); NTMIK_LoaderPatternNumber := Header^.PSM_PatternNumber-1; FOR I1 := 0 TO Header^.PSM_PatternNumber-1 DO BEGIN NTMIK_LoadPatternProcedure; { *** read length } BLOCKREAD(InFile,TempW,2); { *** read pattern } BLOCKREAD(InFile,MultiPurposeBuffer^,TempW-2); { *** unpack pattern and set notes per channel to 64 } PSM_UnpackPattern(MultiPurposeBuffer^,PatternBuffer^,TempW); NTMIK_PackPattern(MultiPurposeBuffer^,PatternBuffer^,PSM_NotesInPattern); TempW := WORD(256) * MultiPurposeBuffer^[01] + MultiPurposeBuffer^[00]; GETMEM(Module^.Module_PatternPointer^[I1],TempW); MOVE(MultiPurposeBuffer^,Module^.Module_PatternPointer^[I1]^,TempW); { *** next pattern } END; { *** read samples } NTMIK_LoaderSampleNumber := Header^.PSM_SampleNumber; FOR I1 := 1 TO Header^.PSM_SampleNumber DO BEGIN NTMIK_LoadSampleProcedure; { *** get index for sample } I3 := Sample^[I1].PSM_SampleNumber; { *** clip PSM-sample } IF Sample^[I1].PSM_SampleLoopEnd > Sample^[I1].PSM_SampleLength THEN Sample^[I1].PSM_SampleLoopEnd := Sample^[I1].PSM_SampleLength; { *** init intern sample } NEW(Module^.Module_SamplePointer^[I3]); FILLCHAR(Module^.Module_SamplePointer^[I3]^,SIZEOF(TSample),$00); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_SampleName,c_Sample_SampleNameLength,#32); FILLCHAR(Module^.Module_SamplePointer^[I3]^.Sample_FileName,c_Sample_FileNameLength,#32); { *** copy informations to intern sample } I2 := $01; WHILE (Sample^[I1].PSM_SampleName[I2] > #00) AND (I2 < c_Sample_SampleNameLength) DO BEGIN Module^.Module_SamplePointer^[I3]^.Sample_SampleName[I2] := Sample^[I1].PSM_SampleName[I2]; INC(I2); END; Module^.Module_SamplePointer^[I3]^.Sample_Sign := 'DF'; Module^.Module_SamplePointer^[I3]^.Sample_FileFormatVersion := $00100; Module^.Module_SamplePointer^[I3]^.Sample_Position := $00000000; Module^.Module_SamplePointer^[I3]^.Sample_Selector := $0000; Module^.Module_SamplePointer^[I3]^.Sample_Volume := Sample^[I1].PSM_SampleVolume; Module^.Module_SamplePointer^[I3]^.Sample_LoopCounter := $00; Module^.Module_SamplePointer^[I3]^.Sample_C5Speed := Sample^[I1].PSM_SampleC5Speed; Module^.Module_SamplePointer^[I3]^.Sample_Length := Sample^[I1].PSM_SampleLength; Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin := Sample^[I1].PSM_SampleLoopBegin; Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd := Sample^[I1].PSM_SampleLoopEnd; { *** now it's time for the flags } Module^.Module_SamplePointer^[I3]^.Sample_Flags := c_Sample_Flags_DigitalSample * BYTE(1) + c_Sample_Flags_8BitSample * BYTE(1) + c_Sample_Flags_UnsignedSampleData * BYTE(1) + c_Sample_Flags_Packed * BYTE(0) + c_Sample_Flags_LoopCounter * BYTE(0) + c_Sample_Flags_SampleName * BYTE(1) + c_Sample_Flags_LoopActive * BYTE(Sample^[I1].PSM_SampleFlags AND (LONGINT(1) SHL 15) = (LONGINT(1) SHL 15)); { *** alloc memory for sample-data } E_Getmem(Module^.Module_SamplePointer^[I3]^.Sample_Selector, Module^.Module_SamplePointer^[I3]^.Sample_Position, Module^.Module_SamplePointer^[I3]^.Sample_Length + c_LoopExtensionSize); { *** read out data } EPT(TempP).p_Selector := Module^.Module_SamplePointer^[I3]^.Sample_Selector; EPT(TempP).p_Offset := $0000; SEEK(InFile,Sample^[I1].PSM_SamplePosition); E_BLOCKREAD(InFile,TempP^,Module^.Module_SamplePointer^[I3]^.Sample_Length); { *** 'coz the samples are signed in a DSM-file -> PC-fy them } IF Module^.Module_SamplePointer^[I3]^.Sample_Length > 4 THEN BEGIN CopyLength := Module^.Module_SamplePointer^[I3]^.Sample_Length; { *** decode sample } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI XOR AH,AH { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB ADD AL,AH MOV AH,AL DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** make samples unsigned } ASM DB 066h; MOV CX,WORD PTR CopyLength { *** load sample selector } MOV ES,WORD PTR TempP[00002h] DB 066h; XOR SI,SI DB 066h; XOR DI,DI { *** conert all bytes } @@MainLoop: DB 026h; DB 067h; LODSB SUB AL,080h DB 067h; STOSB DB 066h; LOOP @@MainLoop END; { *** Create Loop-Extension } IF Module^.Module_SamplePointer^[I3]^.Sample_Flags AND c_Sample_Flags_LoopActive = c_Sample_Flags_LoopActive THEN BEGIN CopySource := Module^.Module_SamplePointer^[I3]^.Sample_LoopBegin; CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_LoopEnd; CopyLength := CopyDestination - CopySource; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** calculate number of full sample-loops to copy } XOR DX,DX MOV AX,c_LoopExtensionSize MOV BX,WORD PTR CopyLength DIV BX OR AX,AX JE @@NoFullLoop { *** copy some full-loops (size=bx) } MOV CX,AX @@InnerLoop: PUSH CX DB 066h; MOV SI,WORD PTR CopySource MOV CX,BX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } POP CX LOOP @@InnerLoop @@NoFullLoop: { *** calculate number of rest-bytes to copy } DB 066h; MOV SI,WORD PTR CopySource MOV CX,DX DB 0F3h; DB 026h,067h,0A4h { REP MOVS BYTE PTR ES:[EDI],ES:[ESI] } END; END ELSE BEGIN CopyDestination := Module^.Module_SamplePointer^[I3]^.Sample_Length; ASM { *** load sample-selector } MOV ES,WORD PTR TempP[00002h] DB 066h; MOV DI,WORD PTR CopyDestination { *** clear extension } MOV CX,c_LoopExtensionSize MOV AL,080h DB 0F3h; DB 067h,0AAh { REP STOS BYTE PTR ES:[EDI] } END; END; END; { *** next sample } END; { *** init period-ranges } NTMIK_MaximumPeriod := $0000D600 SHR 1; NTMIK_MinimumPeriod := $0000D600 SHR 8; { *** close file } CLOSE(InFile); { *** dispose all dynamic variables } DISPOSE(Header); DISPOSE(Sample); DISPOSE(Order); DISPOSE(ChannelSettings); DISPOSE(MultiPurposeBuffer); DISPOSE(PatternBuffer); { *** set errorcode to noerror } ErrorCode := c_NoError; END; */