Mercurial > audlegacy-plugins
view src/modplug/load_mt2.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
#include "stdafx.h" #include "sndfile.h" //#define MT2DEBUG #pragma pack(1) typedef struct _MT2FILEHEADER { DWORD dwMT20; // 0x3032544D "MT20" DWORD dwSpecial; WORD wVersion; CHAR szTrackerName[32]; // "MadTracker 2.0" CHAR szSongName[64]; WORD nOrders; WORD wRestart; WORD wPatterns; WORD wChannels; WORD wSamplesPerTick; BYTE bTicksPerLine; BYTE bLinesPerBeat; DWORD fulFlags; // b0=packed patterns WORD wInstruments; WORD wSamples; BYTE Orders[256]; } MT2FILEHEADER; typedef struct _MT2PATTERN { WORD wLines; DWORD wDataLen; } MT2PATTERN; typedef struct _MT2COMMAND { BYTE note; // 0=nothing, 97=note off BYTE instr; BYTE vol; BYTE pan; BYTE fxcmd; BYTE fxparam1; BYTE fxparam2; } MT2COMMAND; typedef struct _MT2DRUMSDATA { WORD wDrumPatterns; WORD wDrumSamples[8]; BYTE DrumPatternOrder[256]; } MT2DRUMSDATA; typedef struct _MT2AUTOMATION { DWORD dwFlags; DWORD dwEffectId; DWORD nEnvPoints; } MT2AUTOMATION; typedef struct _MT2INSTRUMENT { CHAR szName[32]; DWORD dwDataLen; WORD wSamples; BYTE GroupsMapping[96]; BYTE bVibType; BYTE bVibSweep; BYTE bVibDepth; BYTE bVibRate; WORD wFadeOut; WORD wNNA; WORD wInstrFlags; WORD wEnvFlags1; WORD wEnvFlags2; } MT2INSTRUMENT; typedef struct _MT2ENVELOPE { BYTE nFlags; BYTE nPoints; BYTE nSustainPos; BYTE nLoopStart; BYTE nLoopEnd; BYTE bReserved[3]; BYTE EnvData[64]; } MT2ENVELOPE; typedef struct _MT2SYNTH { BYTE nSynthId; BYTE nFxId; WORD wCutOff; BYTE nResonance; BYTE nAttack; BYTE nDecay; BYTE bReserved[25]; } MT2SYNTH; typedef struct _MT2SAMPLE { CHAR szName[32]; DWORD dwDataLen; DWORD dwLength; DWORD dwFrequency; BYTE nQuality; BYTE nChannels; BYTE nFlags; BYTE nLoop; DWORD dwLoopStart; DWORD dwLoopEnd; WORD wVolume; BYTE nPan; BYTE nBaseNote; WORD wSamplesPerBeat; } MT2SAMPLE; typedef struct _MT2GROUP { BYTE nSmpNo; BYTE nVolume; // 0-128 BYTE nFinePitch; BYTE Reserved[5]; } MT2GROUP; #pragma pack() static VOID ConvertMT2Command(CSoundFile *that, MODCOMMAND *m, MT2COMMAND *p) //--------------------------------------------------------------------------- { // Note m->note = 0; if (p->note) m->note = (p->note > 96) ? 0xFF : p->note+12; // Instrument m->instr = p->instr; // Volume Column if ((p->vol >= 0x10) && (p->vol <= 0x90)) { m->volcmd = VOLCMD_VOLUME; m->vol = (p->vol - 0x10) >> 1; } else if ((p->vol >= 0xA0) && (p->vol <= 0xAF)) { m->volcmd = VOLCMD_VOLSLIDEDOWN; m->vol = (p->vol & 0x0f); } else if ((p->vol >= 0xB0) && (p->vol <= 0xBF)) { m->volcmd = VOLCMD_VOLSLIDEUP; m->vol = (p->vol & 0x0f); } else if ((p->vol >= 0xC0) && (p->vol <= 0xCF)) { m->volcmd = VOLCMD_FINEVOLDOWN; m->vol = (p->vol & 0x0f); } else if ((p->vol >= 0xD0) && (p->vol <= 0xDF)) { m->volcmd = VOLCMD_FINEVOLUP; m->vol = (p->vol & 0x0f); } else { m->volcmd = 0; m->vol = 0; } // Effects m->command = 0; m->param = 0; if ((p->fxcmd) || (p->fxparam1) || (p->fxparam2)) { if (!p->fxcmd) { m->command = p->fxparam2; m->param = p->fxparam1; that->ConvertModCommand(m); } else { // TODO: MT2 Effects } } } BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) //----------------------------------------------------------- { MT2FILEHEADER *pfh = (MT2FILEHEADER *)lpStream; DWORD dwMemPos, dwDrumDataPos, dwExtraDataPos; UINT nDrumDataLen, nExtraDataLen; MT2DRUMSDATA *pdd; MT2INSTRUMENT *InstrMap[255]; MT2SAMPLE *SampleMap[256]; if ((!lpStream) || (dwMemLength < sizeof(MT2FILEHEADER)) || (pfh->dwMT20 != 0x3032544D) || (pfh->wVersion < 0x0200) || (pfh->wVersion >= 0x0300) || (pfh->wChannels < 4) || (pfh->wChannels > 64)) return FALSE; pdd = NULL; m_nType = MOD_TYPE_MT2; m_nChannels = pfh->wChannels; m_nRestartPos = pfh->wRestart; m_nDefaultSpeed = pfh->bTicksPerLine; m_nDefaultTempo = 125; if ((pfh->wSamplesPerTick > 100) && (pfh->wSamplesPerTick < 5000)) { m_nDefaultTempo = 110250 / pfh->wSamplesPerTick; } for (UINT iOrd=0; iOrd<MAX_ORDERS; iOrd++) { Order[iOrd] = (BYTE)((iOrd < pfh->nOrders) ? pfh->Orders[iOrd] : 0xFF); } memcpy(m_szNames[0], pfh->szSongName, 32); m_szNames[0][31] = 0; dwMemPos = sizeof(MT2FILEHEADER); nDrumDataLen = *(WORD *)(lpStream + dwMemPos); dwDrumDataPos = dwMemPos + 2; if (nDrumDataLen >= 2) pdd = (MT2DRUMSDATA *)(lpStream+dwDrumDataPos); dwMemPos += 2 + nDrumDataLen; #ifdef MT2DEBUG Log("MT2 v%03X: \"%s\" (flags=%04X)\n", pfh->wVersion, m_szNames[0], pfh->fulFlags); Log("%d Channels, %d Patterns, %d Instruments, %d Samples\n", pfh->wChannels, pfh->wPatterns, pfh->wInstruments, pfh->wSamples); Log("Drum Data: %d bytes @%04X\n", nDrumDataLen, dwDrumDataPos); #endif if (dwMemPos >= dwMemLength-12) return TRUE; if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; if (!*(DWORD *)(lpStream+dwMemPos)) dwMemPos += 4; nExtraDataLen = *(DWORD *)(lpStream+dwMemPos); dwExtraDataPos = dwMemPos + 4; dwMemPos += 4; #ifdef MT2DEBUG Log("Extra Data: %d bytes @%04X\n", nExtraDataLen, dwExtraDataPos); #endif if (dwMemPos + nExtraDataLen >= dwMemLength) return TRUE; while (dwMemPos+8 < dwExtraDataPos + nExtraDataLen) { DWORD dwId = *(DWORD *)(lpStream+dwMemPos); DWORD dwLen = *(DWORD *)(lpStream+dwMemPos+4); dwMemPos += 8; if (dwMemPos + dwLen > dwMemLength) return TRUE; #ifdef MT2DEBUG CHAR s[5]; memcpy(s, &dwId, 4); s[4] = 0; Log("pos=0x%04X: %s: %d bytes\n", dwMemPos-8, s, dwLen); #endif switch(dwId) { // MSG case 0x0047534D: if ((dwLen > 3) && (!m_lpszSongComments)) { DWORD nTxtLen = dwLen; if (nTxtLen > 32000) nTxtLen = 32000; m_lpszSongComments = new char[nTxtLen]; // changed from CHAR if (m_lpszSongComments) { memcpy(m_lpszSongComments, lpStream+dwMemPos+1, nTxtLen-1); m_lpszSongComments[nTxtLen-1] = 0; } } break; // SUM -> author name (or "Unregistered") // TMAP // TRKS case 0x534b5254: break; } dwMemPos += dwLen; } // Load Patterns dwMemPos = dwExtraDataPos + nExtraDataLen; for (UINT iPat=0; iPat<pfh->wPatterns; iPat++) if (dwMemPos < dwMemLength-6) { MT2PATTERN *pmp = (MT2PATTERN *)(lpStream+dwMemPos); UINT wDataLen = (pmp->wDataLen + 1) & ~1; dwMemPos += 6; if (dwMemPos + wDataLen > dwMemLength) break; UINT nLines = pmp->wLines; if ((iPat < MAX_PATTERNS) && (nLines > 0) && (nLines <= 256)) { #ifdef MT2DEBUG Log("Pattern #%d @%04X: %d lines, %d bytes\n", iPat, dwMemPos-6, nLines, pmp->wDataLen); #endif PatternSize[iPat] = nLines; Patterns[iPat] = AllocatePattern(nLines, m_nChannels); if (!Patterns[iPat]) return TRUE; MODCOMMAND *m = Patterns[iPat]; UINT len = wDataLen; if (pfh->fulFlags & 1) // Packed Patterns { BYTE *p = (BYTE *)(lpStream+dwMemPos); UINT pos = 0, row=0, ch=0; while (pos < len) { MT2COMMAND cmd; UINT infobyte = p[pos++]; UINT rptcount = 0; if (infobyte == 0xff) { rptcount = p[pos++]; infobyte = p[pos++]; #if 0 Log("(%d.%d) FF(%02X).%02X\n", row, ch, rptcount, infobyte); } else { Log("(%d.%d) %02X\n", row, ch, infobyte); #endif } if (infobyte & 0x7f) { UINT patpos = row*m_nChannels+ch; cmd.note = cmd.instr = cmd.vol = cmd.pan = cmd.fxcmd = cmd.fxparam1 = cmd.fxparam2 = 0; if (infobyte & 1) cmd.note = p[pos++]; if (infobyte & 2) cmd.instr = p[pos++]; if (infobyte & 4) cmd.vol = p[pos++]; if (infobyte & 8) cmd.pan = p[pos++]; if (infobyte & 16) cmd.fxcmd = p[pos++]; if (infobyte & 32) cmd.fxparam1 = p[pos++]; if (infobyte & 64) cmd.fxparam2 = p[pos++]; #ifdef MT2DEBUG if (cmd.fxcmd) { Log("(%d.%d) MT2 FX=%02X.%02X.%02X\n", row, ch, cmd.fxcmd, cmd.fxparam1, cmd.fxparam2); } #endif ConvertMT2Command(this, &m[patpos], &cmd); } row += rptcount+1; while (row >= nLines) { row-=nLines; ch++; } if (ch >= m_nChannels) break; } } else { MT2COMMAND *p = (MT2COMMAND *)(lpStream+dwMemPos); UINT n = 0; while ((len > sizeof(MT2COMMAND)) && (n < m_nChannels*nLines)) { ConvertMT2Command(this, m, p); len -= sizeof(MT2COMMAND); n++; p++; m++; } } } dwMemPos += wDataLen; } // Skip Drum Patterns if (pdd) { #ifdef MT2DEBUG Log("%d Drum Patterns at offset 0x%08X\n", pdd->wDrumPatterns, dwMemPos); #endif for (UINT iDrm=0; iDrm<pdd->wDrumPatterns; iDrm++) { if (dwMemPos > dwMemLength-2) return TRUE; UINT nLines = *(WORD *)(lpStream+dwMemPos); #ifdef MT2DEBUG if (nLines != 64) Log("Drum Pattern %d: %d Lines @%04X\n", iDrm, nLines, dwMemPos); #endif dwMemPos += 2 + nLines * 32; } } // Automation if (pfh->fulFlags & 2) { #ifdef MT2DEBUG Log("Automation at offset 0x%08X\n", dwMemPos); #endif UINT nAutoCount = m_nChannels; if (pfh->fulFlags & 0x10) nAutoCount++; // Master Automation if ((pfh->fulFlags & 0x08) && (pdd)) nAutoCount += 8; // Drums Automation nAutoCount *= pfh->wPatterns; for (UINT iAuto=0; iAuto<nAutoCount; iAuto++) { if (dwMemPos+12 >= dwMemLength) return TRUE; MT2AUTOMATION *pma = (MT2AUTOMATION *)(lpStream+dwMemPos); dwMemPos += (pfh->wVersion <= 0x201) ? 4 : 8; for (UINT iEnv=0; iEnv<14; iEnv++) { if (pma->dwFlags & (1 << iEnv)) { #ifdef MT2DEBUG UINT nPoints = *(DWORD *)(lpStream+dwMemPos); Log(" Env[%d/%d] %04X @%04X: %d points\n", iAuto, nAutoCount, 1 << iEnv, dwMemPos-8, nPoints); #endif dwMemPos += 260; } } } } // Load Instruments #ifdef MT2DEBUG Log("Loading instruments at offset 0x%08X\n", dwMemPos); #endif memset(InstrMap, 0, sizeof(InstrMap)); m_nInstruments = (pfh->wInstruments < MAX_INSTRUMENTS) ? pfh->wInstruments : MAX_INSTRUMENTS-1; for (UINT iIns=1; iIns<=255; iIns++) { if (dwMemPos+36 > dwMemLength) return TRUE; MT2INSTRUMENT *pmi = (MT2INSTRUMENT *)(lpStream+dwMemPos); INSTRUMENTHEADER *penv = NULL; if (iIns <= m_nInstruments) { penv = new INSTRUMENTHEADER; Headers[iIns] = penv; if (penv) { memset(penv, 0, sizeof(INSTRUMENTHEADER)); memcpy(penv->name, pmi->szName, 32); penv->nGlobalVol = 64; penv->nPan = 128; for (UINT i=0; i<120; i++) { penv->NoteMap[i] = i+1; } } } #ifdef MT2DEBUG if (iIns <= pfh->wInstruments) Log(" Instrument #%d at offset %04X: %d bytes\n", iIns, dwMemPos, pmi->dwDataLen); #endif if (((LONG)pmi->dwDataLen > 0) && (dwMemPos + pmi->dwDataLen + 40 <= dwMemLength)) { InstrMap[iIns-1] = pmi; if (penv) { penv->nFadeOut = pmi->wFadeOut; penv->nNNA = pmi->wNNA & 3; penv->nDCT = (pmi->wNNA>>8) & 3; penv->nDNA = (pmi->wNNA>>12) & 3; MT2ENVELOPE *pehdr[4]; WORD *pedata[4]; if (pfh->wVersion <= 0x201) { DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT) - 4; pehdr[0] = (MT2ENVELOPE *)(lpStream+dwEnvPos); pehdr[1] = (MT2ENVELOPE *)(lpStream+dwEnvPos+8); pehdr[2] = pehdr[3] = NULL; pedata[0] = (WORD *)(lpStream+dwEnvPos+16); pedata[1] = (WORD *)(lpStream+dwEnvPos+16+64); pedata[2] = pedata[3] = NULL; } else { DWORD dwEnvPos = dwMemPos + sizeof(MT2INSTRUMENT); for (UINT i=0; i<4; i++) { if (pmi->wEnvFlags1 & (1<<i)) { pehdr[i] = (MT2ENVELOPE *)(lpStream+dwEnvPos); pedata[i] = (WORD *)pehdr[i]->EnvData; dwEnvPos += sizeof(MT2ENVELOPE); } else { pehdr[i] = NULL; pedata[i] = NULL; } } } // Load envelopes for (UINT iEnv=0; iEnv<4; iEnv++) if (pehdr[iEnv]) { MT2ENVELOPE *pme = pehdr[iEnv]; WORD *pEnvPoints = NULL; BYTE *pEnvData = NULL; #ifdef MT2DEBUG Log(" Env %d.%d @%04X: %d points\n", iIns, iEnv, (UINT)(((BYTE *)pme)-lpStream), pme->nPoints); #endif switch(iEnv) { // Volume Envelope case 0: if (pme->nFlags & 1) penv->dwFlags |= ENV_VOLUME; if (pme->nFlags & 2) penv->dwFlags |= ENV_VOLSUSTAIN; if (pme->nFlags & 4) penv->dwFlags |= ENV_VOLLOOP; penv->nVolEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; penv->nVolSustainBegin = penv->nVolSustainEnd = pme->nSustainPos; penv->nVolLoopStart = pme->nLoopStart; penv->nVolLoopEnd = pme->nLoopEnd; pEnvPoints = penv->VolPoints; pEnvData = penv->VolEnv; break; // Panning Envelope case 1: if (pme->nFlags & 1) penv->dwFlags |= ENV_PANNING; if (pme->nFlags & 2) penv->dwFlags |= ENV_PANSUSTAIN; if (pme->nFlags & 4) penv->dwFlags |= ENV_PANLOOP; penv->nPanEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; penv->nPanSustainBegin = penv->nPanSustainEnd = pme->nSustainPos; penv->nPanLoopStart = pme->nLoopStart; penv->nPanLoopEnd = pme->nLoopEnd; pEnvPoints = penv->PanPoints; pEnvData = penv->PanEnv; break; // Pitch/Filter envelope default: if (pme->nFlags & 1) penv->dwFlags |= (iEnv==3) ? (ENV_PITCH|ENV_FILTER) : ENV_PITCH; if (pme->nFlags & 2) penv->dwFlags |= ENV_PITCHSUSTAIN; if (pme->nFlags & 4) penv->dwFlags |= ENV_PITCHLOOP; penv->nPitchEnv = (pme->nPoints > 16) ? 16 : pme->nPoints; penv->nPitchSustainBegin = penv->nPitchSustainEnd = pme->nSustainPos; penv->nPitchLoopStart = pme->nLoopStart; penv->nPitchLoopEnd = pme->nLoopEnd; pEnvPoints = penv->PitchPoints; pEnvData = penv->PitchEnv; } // Envelope data if ((pEnvPoints) && (pEnvData) && (pedata[iEnv])) { WORD *psrc = pedata[iEnv]; for (UINT i=0; i<16; i++) { pEnvPoints[i] = psrc[i*2]; pEnvData[i] = (BYTE)psrc[i*2+1]; } } } } dwMemPos += pmi->dwDataLen + 36; if (pfh->wVersion > 0x201) dwMemPos += 4; // ? } else { dwMemPos += 36; } } #ifdef MT2DEBUG Log("Loading samples at offset 0x%08X\n", dwMemPos); #endif memset(SampleMap, 0, sizeof(SampleMap)); m_nSamples = (pfh->wSamples < MAX_SAMPLES) ? pfh->wSamples : MAX_SAMPLES-1; for (UINT iSmp=1; iSmp<=256; iSmp++) { if (dwMemPos+36 > dwMemLength) return TRUE; MT2SAMPLE *pms = (MT2SAMPLE *)(lpStream+dwMemPos); #ifdef MT2DEBUG if (iSmp <= m_nSamples) Log(" Sample #%d at offset %04X: %d bytes\n", iSmp, dwMemPos, pms->dwDataLen); #endif if (iSmp < MAX_SAMPLES) { memcpy(m_szNames[iSmp], pms->szName, 32); } if (pms->dwDataLen > 0) { SampleMap[iSmp-1] = pms; if (iSmp < MAX_SAMPLES) { MODINSTRUMENT *psmp = &Ins[iSmp]; psmp->nGlobalVol = 64; psmp->nVolume = (pms->wVolume >> 7); psmp->nPan = (pms->nPan == 0x80) ? 128 : (pms->nPan^0x80); psmp->nLength = pms->dwLength; psmp->nC4Speed = pms->dwFrequency; psmp->nLoopStart = pms->dwLoopStart; psmp->nLoopEnd = pms->dwLoopEnd; FrequencyToTranspose(psmp); psmp->RelativeTone -= pms->nBaseNote - 49; psmp->nC4Speed = TransposeToFrequency(psmp->RelativeTone, psmp->nFineTune); if (pms->nQuality == 2) { psmp->uFlags |= CHN_16BIT; psmp->nLength >>= 1; } if (pms->nChannels == 2) { psmp->nLength >>= 1; } if (pms->nLoop == 1) psmp->uFlags |= CHN_LOOP; if (pms->nLoop == 2) psmp->uFlags |= CHN_LOOP|CHN_PINGPONGLOOP; } dwMemPos += pms->dwDataLen + 36; } else { dwMemPos += 36; } } #ifdef MT2DEBUG Log("Loading groups at offset 0x%08X\n", dwMemPos); #endif for (UINT iMap=0; iMap<255; iMap++) if (InstrMap[iMap]) { if (dwMemPos+8 > dwMemLength) return TRUE; MT2INSTRUMENT *pmi = InstrMap[iMap]; INSTRUMENTHEADER *penv = NULL; if (iMap<m_nInstruments) penv = Headers[iMap+1]; for (UINT iGrp=0; iGrp<pmi->wSamples; iGrp++) { if (penv) { MT2GROUP *pmg = (MT2GROUP *)(lpStream+dwMemPos); for (UINT i=0; i<96; i++) { if (pmi->GroupsMapping[i] == iGrp) { UINT nSmp = pmg->nSmpNo+1; penv->Keyboard[i+12] = (BYTE)nSmp; if (nSmp <= m_nSamples) { Ins[nSmp].nVibType = pmi->bVibType; Ins[nSmp].nVibSweep = pmi->bVibSweep; Ins[nSmp].nVibDepth = pmi->bVibDepth; Ins[nSmp].nVibRate = pmi->bVibRate; } } } } dwMemPos += 8; } } #ifdef MT2DEBUG Log("Loading sample data at offset 0x%08X\n", dwMemPos); #endif for (UINT iData=0; iData<256; iData++) if ((iData < m_nSamples) && (SampleMap[iData])) { MT2SAMPLE *pms = SampleMap[iData]; MODINSTRUMENT *psmp = &Ins[iData+1]; if (!(pms->nFlags & 5)) { if (psmp->nLength > 0) { #ifdef MT2DEBUG Log(" Reading sample #%d at offset 0x%04X (len=%d)\n", iData+1, dwMemPos, psmp->nLength); #endif UINT rsflags; if (pms->nChannels == 2) rsflags = (psmp->uFlags & CHN_16BIT) ? RS_STPCM16D : RS_STPCM8D; else rsflags = (psmp->uFlags & CHN_16BIT) ? RS_PCM16D : RS_PCM8D; dwMemPos += ReadSample(psmp, rsflags, (LPCSTR)(lpStream+dwMemPos), dwMemLength-dwMemPos); } } else if (dwMemPos+4 < dwMemLength) { UINT nNameLen = *(DWORD *)(lpStream+dwMemPos); dwMemPos += nNameLen + 16; } if (dwMemPos+4 >= dwMemLength) break; } return TRUE; }