Mercurial > audlegacy-plugins
view src/modplug/load_dmf.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> */ /////////////////////////////////////////////////////// // DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) // /////////////////////////////////////////////////////// #include "stdafx.h" #include "sndfile.h" //#define DMFLOG //#pragma warning(disable:4244) #pragma pack(1) typedef struct DMFHEADER { DWORD id; // "DDMF" = 0x464d4444 BYTE version; // 4 CHAR trackername[8]; // "XTRACKER" CHAR songname[30]; CHAR composer[20]; BYTE date[3]; } DMFHEADER; typedef struct DMFINFO { DWORD id; // "INFO" DWORD infosize; } DMFINFO; typedef struct DMFSEQU { DWORD id; // "SEQU" DWORD seqsize; WORD loopstart; WORD loopend; WORD sequ[2]; } DMFSEQU; typedef struct DMFPATT { DWORD id; // "PATT" DWORD patsize; WORD numpat; // 1-1024 BYTE tracks; BYTE firstpatinfo; } DMFPATT; typedef struct DMFTRACK { BYTE tracks; BYTE beat; // [hi|lo] -> hi=ticks per beat, lo=beats per measure WORD ticks; // max 512 DWORD jmpsize; } DMFTRACK; typedef struct DMFSMPI { DWORD id; DWORD size; BYTE samples; } DMFSMPI; typedef struct DMFSAMPLE { DWORD len; DWORD loopstart; DWORD loopend; WORD c3speed; BYTE volume; BYTE flags; } DMFSAMPLE; #pragma pack() #ifdef DMFLOG extern void Log(LPCSTR s, ...); #endif BOOL CSoundFile::ReadDMF(const BYTE *lpStream, DWORD dwMemLength) //--------------------------------------------------------------- { DMFHEADER *pfh = (DMFHEADER *)lpStream; DMFINFO *psi; DMFSEQU *sequ; DWORD dwMemPos; BYTE infobyte[32]; BYTE smplflags[MAX_SAMPLES]; if ((!lpStream) || (dwMemLength < 1024)) return FALSE; if ((pfh->id != 0x464d4444) || (!pfh->version) || (pfh->version & 0xF0)) return FALSE; dwMemPos = 66; memcpy(m_szNames[0], pfh->songname, 30); m_szNames[0][30] = 0; m_nType = MOD_TYPE_DMF; m_nChannels = 0; #ifdef DMFLOG Log("DMF version %d: \"%s\": %d bytes (0x%04X)\n", pfh->version, m_szNames[0], dwMemLength, dwMemLength); #endif while (dwMemPos + 7 < dwMemLength) { DWORD id = *((LPDWORD)(lpStream+dwMemPos)); switch(id) { // "INFO" case 0x4f464e49: // "CMSG" case 0x47534d43: psi = (DMFINFO *)(lpStream+dwMemPos); if (id == 0x47534d43) dwMemPos++; if ((psi->infosize > dwMemLength) || (psi->infosize + dwMemPos + 8 > dwMemLength)) goto dmfexit; if ((psi->infosize >= 8) && (!m_lpszSongComments)) { m_lpszSongComments = new char[psi->infosize]; // changed from CHAR if (m_lpszSongComments) { for (UINT i=0; i<psi->infosize-1; i++) { CHAR c = lpStream[dwMemPos+8+i]; if ((i % 40) == 39) m_lpszSongComments[i] = 0x0d; else m_lpszSongComments[i] = (c < ' ') ? ' ' : c; } m_lpszSongComments[psi->infosize-1] = 0; } } dwMemPos += psi->infosize + 8 - 1; break; // "SEQU" case 0x55514553: sequ = (DMFSEQU *)(lpStream+dwMemPos); if ((sequ->seqsize >= dwMemLength) || (dwMemPos + sequ->seqsize + 12 > dwMemLength)) goto dmfexit; { UINT nseq = sequ->seqsize >> 1; if (nseq >= MAX_ORDERS-1) nseq = MAX_ORDERS-1; if (sequ->loopstart < nseq) m_nRestartPos = sequ->loopstart; for (UINT i=0; i<nseq; i++) Order[i] = (BYTE)sequ->sequ[i]; } dwMemPos += sequ->seqsize + 8; break; // "PATT" case 0x54544150: if (!m_nChannels) { DMFPATT *patt = (DMFPATT *)(lpStream+dwMemPos); UINT numpat; DWORD dwPos = dwMemPos + 11; if ((patt->patsize >= dwMemLength) || (dwMemPos + patt->patsize + 8 > dwMemLength)) goto dmfexit; numpat = patt->numpat; if (numpat > MAX_PATTERNS) numpat = MAX_PATTERNS; m_nChannels = patt->tracks; if (m_nChannels < patt->firstpatinfo) m_nChannels = patt->firstpatinfo; if (m_nChannels > 32) m_nChannels = 32; if (m_nChannels < 4) m_nChannels = 4; for (UINT npat=0; npat<numpat; npat++) { DMFTRACK *pt = (DMFTRACK *)(lpStream+dwPos); #ifdef DMFLOG Log("Pattern #%d: %d tracks, %d rows\n", npat, pt->tracks, pt->ticks); #endif UINT tracks = pt->tracks; if (tracks > 32) tracks = 32; UINT ticks = pt->ticks; if (ticks > 256) ticks = 256; if (ticks < 16) ticks = 16; dwPos += 8; if ((pt->jmpsize >= dwMemLength) || (dwPos + pt->jmpsize + 4 >= dwMemLength)) break; PatternSize[npat] = (WORD)ticks; MODCOMMAND *m = AllocatePattern(PatternSize[npat], m_nChannels); if (!m) goto dmfexit; Patterns[npat] = m; DWORD d = dwPos; dwPos += pt->jmpsize; UINT ttype = 1; UINT tempo = 125; UINT glbinfobyte = 0; UINT pbeat = (pt->beat & 0xf0) ? pt->beat>>4 : 8; BOOL tempochange = (pt->beat & 0xf0) ? TRUE : FALSE; memset(infobyte, 0, sizeof(infobyte)); for (UINT row=0; row<ticks; row++) { MODCOMMAND *p = &m[row*m_nChannels]; // Parse track global effects if (!glbinfobyte) { BYTE info = lpStream[d++]; BYTE infoval = 0; if ((info & 0x80) && (d < dwPos)) glbinfobyte = lpStream[d++]; info &= 0x7f; if ((info) && (d < dwPos)) infoval = lpStream[d++]; switch(info) { case 1: ttype = 0; tempo = infoval; tempochange = TRUE; break; case 2: ttype = 1; tempo = infoval; tempochange = TRUE; break; case 3: pbeat = infoval>>4; tempochange = ttype; break; #ifdef DMFLOG default: if (info) Log("GLB: %02X.%02X\n", info, infoval); #endif } } else { glbinfobyte--; } // Parse channels for (UINT i=0; i<tracks; i++) if (!infobyte[i]) { MODCOMMAND cmd = {0,0,0,0,0,0}; BYTE info = lpStream[d++]; if (info & 0x80) infobyte[i] = lpStream[d++]; // Instrument if (info & 0x40) { cmd.instr = lpStream[d++]; } // Note if (info & 0x20) { cmd.note = lpStream[d++]; if ((cmd.note) && (cmd.note < 0xfe)) cmd.note &= 0x7f; if ((cmd.note) && (cmd.note < 128)) cmd.note += 24; } // Volume if (info & 0x10) { cmd.volcmd = VOLCMD_VOLUME; cmd.vol = (lpStream[d++]+3)>>2; } // Effect 1 if (info & 0x08) { BYTE efx = lpStream[d++]; BYTE eval = lpStream[d++]; switch(efx) { // 1: Key Off case 1: if (!cmd.note) cmd.note = 0xFE; break; // 2: Set Loop // 4: Sample Delay case 4: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; // 5: Retrig case 5: if (eval&0xe0) { cmd.command = CMD_RETRIG; cmd.param = (eval>>5); } break; // 6: Offset case 6: cmd.command = CMD_OFFSET; cmd.param = eval; break; #ifdef DMFLOG default: Log("FX1: %02X.%02X\n", efx, eval); #endif } } // Effect 2 if (info & 0x04) { BYTE efx = lpStream[d++]; BYTE eval = lpStream[d++]; switch(efx) { // 1: Finetune case 1: if (eval&0xf0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>4)|0x20; } break; // 2: Note Delay case 2: if (eval&0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xD0; } break; // 3: Arpeggio case 3: if (eval) { cmd.command = CMD_ARPEGGIO; cmd.param = eval; } break; // 4: Portamento Up case 4: cmd.command = CMD_PORTAMENTOUP; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; // 5: Portamento Down case 5: cmd.command = CMD_PORTAMENTODOWN; cmd.param = (eval >= 0xe0) ? 0xdf : eval; break; // 6: Tone Portamento case 6: cmd.command = CMD_TONEPORTAMENTO; cmd.param = eval; break; // 8: Vibrato case 8: cmd.command = CMD_VIBRATO; cmd.param = eval; break; // 12: Note cut case 12: if (eval & 0xe0) { cmd.command = CMD_S3MCMDEX; cmd.param = (eval>>5)|0xc0; } else if (!cmd.note) { cmd.note = 0xfe; } break; #ifdef DMFLOG default: Log("FX2: %02X.%02X\n", efx, eval); #endif } } // Effect 3 if (info & 0x02) { BYTE efx = lpStream[d++]; BYTE eval = lpStream[d++]; switch(efx) { // 1: Vol Slide Up case 1: if (eval == 0xff) break; eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; cmd.command = CMD_VOLUMESLIDE; cmd.param = eval<<4; break; // 2: Vol Slide Down case 2: if (eval == 0xff) break; eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; cmd.command = CMD_VOLUMESLIDE; cmd.param = eval; break; // 7: Set Pan case 7: if (!cmd.volcmd) { cmd.volcmd = VOLCMD_PANNING; cmd.vol = (eval+3)>>2; } else { cmd.command = CMD_PANNING8; cmd.param = eval; } break; // 8: Pan Slide Left case 8: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; cmd.command = CMD_PANNINGSLIDE; cmd.param = eval<<4; break; // 9: Pan Slide Right case 9: eval = (eval+3)>>2; if (eval > 0x0f) eval = 0x0f; cmd.command = CMD_PANNINGSLIDE; cmd.param = eval; break; #ifdef DMFLOG default: Log("FX3: %02X.%02X\n", efx, eval); #endif } } // Store effect if (i < m_nChannels) p[i] = cmd; if (d > dwPos) { #ifdef DMFLOG Log("Unexpected EOP: row=%d\n", row); #endif break; } } else { infobyte[i]--; } // Find free channel for tempo change if (tempochange) { tempochange = FALSE; UINT speed=6, modtempo=tempo; UINT rpm = ((ttype) && (pbeat)) ? tempo*pbeat : (tempo+1)*15; for (speed=30; speed>1; speed--) { modtempo = rpm*speed/24; if (modtempo <= 200) break; if ((speed < 6) && (modtempo < 256)) break; } #ifdef DMFLOG Log("Tempo change: ttype=%d pbeat=%d tempo=%3d -> speed=%d tempo=%d\n", ttype, pbeat, tempo, speed, modtempo); #endif for (UINT ich=0; ich<m_nChannels; ich++) if (!p[ich].command) { if (speed) { p[ich].command = CMD_SPEED; p[ich].param = (BYTE)speed; speed = 0; } else if ((modtempo >= 32) && (modtempo < 256)) { p[ich].command = CMD_TEMPO; p[ich].param = (BYTE)modtempo; modtempo = 0; } else { break; } } } if (d >= dwPos) break; } #ifdef DMFLOG Log(" %d/%d bytes remaining\n", dwPos-d, pt->jmpsize); #endif if (dwPos + 8 >= dwMemLength) break; } dwMemPos += patt->patsize + 8; } break; // "SMPI": Sample Info case 0x49504d53: { DMFSMPI *pds = (DMFSMPI *)(lpStream+dwMemPos); if (pds->size <= dwMemLength - dwMemPos) { DWORD dwPos = dwMemPos + 9; m_nSamples = pds->samples; if (m_nSamples >= MAX_SAMPLES) m_nSamples = MAX_SAMPLES-1; for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) { UINT namelen = lpStream[dwPos]; smplflags[iSmp] = 0; if (dwPos+namelen+1+sizeof(DMFSAMPLE) > dwMemPos+pds->size+8) break; if (namelen) { UINT rlen = (namelen < 32) ? namelen : 31; memcpy(m_szNames[iSmp], lpStream+dwPos+1, rlen); m_szNames[iSmp][rlen] = 0; } dwPos += namelen + 1; DMFSAMPLE *psh = (DMFSAMPLE *)(lpStream+dwPos); MODINSTRUMENT *psmp = &Ins[iSmp]; psmp->nLength = psh->len; psmp->nLoopStart = psh->loopstart; psmp->nLoopEnd = psh->loopend; psmp->nC4Speed = psh->c3speed; psmp->nGlobalVol = 64; psmp->nVolume = (psh->volume) ? ((WORD)psh->volume)+1 : (WORD)256; psmp->uFlags = (psh->flags & 2) ? CHN_16BIT : 0; if (psmp->uFlags & CHN_16BIT) psmp->nLength >>= 1; if (psh->flags & 1) psmp->uFlags |= CHN_LOOP; smplflags[iSmp] = psh->flags; dwPos += (pfh->version < 8) ? 22 : 30; #ifdef DMFLOG Log("SMPI %d/%d: len=%d flags=0x%02X\n", iSmp, m_nSamples, psmp->nLength, psh->flags); #endif } } dwMemPos += pds->size + 8; } break; // "SMPD": Sample Data case 0x44504d53: { DWORD dwPos = dwMemPos + 8; UINT ismpd = 0; for (UINT iSmp=1; iSmp<=m_nSamples; iSmp++) { ismpd++; DWORD pksize; if (dwPos + 4 >= dwMemLength) { #ifdef DMFLOG Log("Unexpected EOF at sample %d/%d! (pos=%d)\n", iSmp, m_nSamples, dwPos); #endif break; } pksize = *((LPDWORD)(lpStream+dwPos)); #ifdef DMFLOG Log("sample %d: pos=0x%X pksize=%d ", iSmp, dwPos, pksize); Log("len=%d flags=0x%X [%08X]\n", Ins[iSmp].nLength, smplflags[ismpd], *((LPDWORD)(lpStream+dwPos+4))); #endif dwPos += 4; if (pksize > dwMemLength - dwPos) { #ifdef DMFLOG Log("WARNING: pksize=%d, but only %d bytes left\n", pksize, dwMemLength-dwPos); #endif pksize = dwMemLength - dwPos; } if ((pksize) && (iSmp <= m_nSamples)) { UINT flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_PCM16S : RS_PCM8S; if (smplflags[ismpd] & 4) flags = (Ins[iSmp].uFlags & CHN_16BIT) ? RS_DMF16 : RS_DMF8; ReadSample(&Ins[iSmp], flags, (LPSTR)(lpStream+dwPos), pksize); } dwPos += pksize; } dwMemPos = dwPos; } break; // "ENDE": end of file case 0x45444e45: goto dmfexit; // Unrecognized id, or "ENDE" field default: dwMemPos += 4; break; } } dmfexit: if (!m_nChannels) { if (!m_nSamples) { m_nType = MOD_TYPE_NONE; return FALSE; } m_nChannels = 4; } return TRUE; } /////////////////////////////////////////////////////////////////////// // DMF Compression #pragma pack(1) typedef struct DMF_HNODE { short int left, right; BYTE value; } DMF_HNODE; typedef struct DMF_HTREE { LPBYTE ibuf, ibufmax; DWORD bitbuf; UINT bitnum; UINT lastnode, nodecount; DMF_HNODE nodes[256]; } DMF_HTREE; #pragma pack() // DMF Huffman ReadBits BYTE DMFReadBits(DMF_HTREE *tree, UINT nbits) //------------------------------------------- { BYTE x = 0, bitv = 1; while (nbits--) { if (tree->bitnum) { tree->bitnum--; } else { tree->bitbuf = (tree->ibuf < tree->ibufmax) ? *(tree->ibuf++) : 0; tree->bitnum = 7; } if (tree->bitbuf & 1) x |= bitv; bitv <<= 1; tree->bitbuf >>= 1; } return x; } // // tree: [8-bit value][12-bit index][12-bit index] = 32-bit // void DMFNewNode(DMF_HTREE *tree) //------------------------------ { BYTE isleft, isright; UINT actnode; actnode = tree->nodecount; if (actnode > 255) return; tree->nodes[actnode].value = DMFReadBits(tree, 7); isleft = DMFReadBits(tree, 1); isright = DMFReadBits(tree, 1); actnode = tree->lastnode; if (actnode > 255) return; tree->nodecount++; tree->lastnode = tree->nodecount; if (isleft) { tree->nodes[actnode].left = tree->lastnode; DMFNewNode(tree); } else { tree->nodes[actnode].left = -1; } tree->lastnode = tree->nodecount; if (isright) { tree->nodes[actnode].right = tree->lastnode; DMFNewNode(tree); } else { tree->nodes[actnode].right = -1; } } int DMFUnpack(LPBYTE psample, LPBYTE ibuf, LPBYTE ibufmax, UINT maxlen) //---------------------------------------------------------------------- { DMF_HTREE tree; UINT actnode; BYTE value, sign, delta = 0; memset(&tree, 0, sizeof(tree)); tree.ibuf = ibuf; tree.ibufmax = ibufmax; DMFNewNode(&tree); value = 0; for (UINT i=0; i<maxlen; i++) { actnode = 0; sign = DMFReadBits(&tree, 1); do { if (DMFReadBits(&tree, 1)) actnode = tree.nodes[actnode].right; else actnode = tree.nodes[actnode].left; if (actnode > 255) break; delta = tree.nodes[actnode].value; if ((tree.ibuf >= tree.ibufmax) && (!tree.bitnum)) break; } while ((tree.nodes[actnode].left >= 0) && (tree.nodes[actnode].right >= 0)); if (sign) delta ^= 0xFF; value += delta; psample[i] = (i) ? value : 0; } #ifdef DMFLOG // Log("DMFUnpack: %d remaining bytes\n", tree.ibufmax-tree.ibuf); #endif return tree.ibuf - ibuf; }