Mercurial > hg > dmlib
diff tools/xm2jss.c @ 2278:40ccc09f09be
Implement empty channel removal in xm2jss and make JSSMOD format support
channel remapping for this.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 18 Jun 2019 12:12:51 +0300 |
parents | 026c3aa0e48f |
children | fc58f62f100c |
line wrap: on
line diff
--- a/tools/xm2jss.c Tue Jun 18 12:11:16 2019 +0300 +++ b/tools/xm2jss.c Tue Jun 18 12:12:51 2019 +0300 @@ -147,6 +147,15 @@ } +static inline const JSSNote * jssGetNotePtr(const JSSPattern *pattern, const int channel, const int row) +{ + if (!pattern->used[channel]) + return NULL; + else + return pattern->data + (pattern->nchannels * row) + channel; +} + + /* These functions and the macro mess are meant to make the * conversion routines themselves clearer and simpler. */ @@ -189,7 +198,7 @@ /* Convert a note */ -static int jssConvertNote( +static int jssDoConvertNote( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *pnote) { @@ -220,7 +229,7 @@ /* Compress a note */ -static int jssCompressNote( +static int jssDoCompressNote( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *pnote) { @@ -256,13 +265,33 @@ JSCOMPPUT(JM_COMP_VOLUME, pnote->volume, "Volume"); JSCOMPPUT(JM_COMP_EFFECT, pnote->effect, "Effect"); JSCOMPPUT(JM_COMP_PARAM, pnote->param, "Param"); + + return DMERR_OK; } else { // Was 4 bytes or more, just dump it all in .. - return jssConvertNote(patBuf, patBufSize, patSize, pnote); + return jssDoConvertNote(patBuf, patBufSize, patSize, pnote); } +} + +static int jssCompressNote( + Uint8 *patBuf, const size_t patBufSize, + size_t *patSize, const JSSPattern *pattern, + const int channel, const int row) +{ + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); + if (pnote != NULL) + { + int res = jssDoCompressNote(patBuf, patBufSize, patSize, pnote); + if (res != DMERR_OK) + { + JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", + patBuf, patBufSize, *patSize, row, channel); + return res; + } + } return DMERR_OK; } @@ -278,14 +307,9 @@ for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); + int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -301,14 +325,9 @@ for (int channel = 0; channel < pattern->nchannels; channel++) for (int row = 0; row < pattern->nrows; row++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); + int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -317,6 +336,26 @@ /* Convert a pattern */ +static int jssConvertNote( + Uint8 *patBuf, const size_t patBufSize, + size_t *patSize, const JSSPattern *pattern, + const int channel, const int row) +{ + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); + if (pnote != NULL) + { + int res = jssDoConvertNote(patBuf, patBufSize, patSize, pnote); + if (res != DMERR_OK) + { + JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", + patBuf, patBufSize, *patSize, row, channel); + return res; + } + } + return DMERR_OK; +} + + static int jssConvertPatternRawHoriz( Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) @@ -326,14 +365,9 @@ for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); + int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -349,14 +383,9 @@ for (int channel = 0; channel < pattern->nchannels; channel++) for (int row = 0; row < pattern->nrows; row++) { - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; - const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); + int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); if (res != DMERR_OK) - { - JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", - patBuf, patBufSize, *patSize, row, channel); return res; - } } return DMERR_OK; @@ -366,9 +395,11 @@ #define JSFOREACHNOTE1 \ for (channel = 0; channel < pattern->nchannels; channel++) \ for (row = 0; row < pattern->nrows; row++) { \ - const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; + const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); \ + if (pnote != NULL) { -#define JSFOREACHNOTE2 } +#define JSFOREACHNOTE2 } } + static int jssConvertPatternRawElem( Uint8 *patBuf, const size_t patBufSize, @@ -593,10 +624,28 @@ if (!dm_fwrite_le32(outFile, dataSize) || !dm_fwrite_le16(outFile, pattern->nrows) || - !dm_fwrite_str(outFile, patBuf, dataSize)) + !dm_fwrite_le16(outFile, pattern->nmap)) { JSSERROR(DMERR_FWRITE, DMERR_FWRITE, - "Error writing JSSMOD pattern #%d.\n", + "Error writing JSSMOD pattern header #%d.\n", + index); + } + + if (pattern->nmap != pattern->nchannels) + { + if (!dm_fwrite_str(outFile, pattern->map, + sizeof(pattern->map[0]) * pattern->nmap)) + { + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing JSSMOD channel map for pattern #%d.\n", + index); + } + } + + if (!dm_fwrite_str(outFile, patBuf, dataSize)) + { + JSSERROR(DMERR_FWRITE, DMERR_FWRITE, + "Error writing JSSMOD pattern data #%d.\n", index); } } @@ -744,18 +793,19 @@ /* Scan given pattern for used instruments and channels. * Also checks if the pattern is empty. */ -void scanPattern(const JSSModule *module, const JSSPattern *pattern, - const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels, BOOL *empty) +BOOL jssScanPattern(const JSSModule *module, const JSSPattern *pattern, + const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels) { JSSNote *n = pattern->data; - *empty = FALSE; + BOOL empty = TRUE; // Check all notes in this pattern for (int row = 0; row < pattern->nrows; row++) for (int channel = 0; channel < pattern->nchannels; channel++, n++) { // Is the instrument set? - if (n->instrument != jsetNotSet) + if (usedExtInstruments != NULL && + n->instrument != jsetNotSet) { // Is it valid? if (n->instrument >= 0 && n->instrument < module->nextInstruments) @@ -774,10 +824,14 @@ n->effect != jsetNotSet || n->param != jsetNotSet) { - usedChannels[channel] = TRUE; - *empty = FALSE; + if (usedChannels != NULL) + usedChannels[channel] = TRUE; + + empty = FALSE; } } + + return empty; } @@ -798,8 +852,7 @@ { BOOL usedPatterns[jsetMaxPatterns + 1], usedInstruments[jsetMaxInstruments + 1], - usedExtInstruments[jsetMaxInstruments + 1], - usedChannels[jsetMaxChannels]; + usedExtInstruments[jsetMaxInstruments + 1]; int mapExtInstruments[jsetMaxInstruments + 1], mapInstruments[jsetMaxInstruments + 1], mapPatterns[jsetMaxPatterns + 1], @@ -827,7 +880,6 @@ for (int i = 0; i < jsetNChannels; i++) { r->defPanning[i] = m->defPanning[i]; - usedChannels[i] = FALSE; } // Initialize values @@ -859,13 +911,8 @@ JSSPattern *pattern = m->patterns[npat]; if (pattern != NULL) { - BOOL empty; - - // Mark this pattern as used - usedPatterns[npat] = TRUE; - - // Scan for used instruments and channels - scanPattern(m, pattern, npat, usedExtInstruments, usedChannels, &empty); + // Scan for used instruments etc + BOOL empty = jssScanPattern(m, pattern, npat, usedExtInstruments, NULL); // Empty patterns with known number of rows are "removed" if (empty && pattern->nrows == jsetDefaultRows) @@ -873,6 +920,8 @@ m->orderList[norder] = jsetNotSet; usedPatterns[npat] = FALSE; } + else + usedPatterns[npat] = TRUE; } else { @@ -1077,19 +1126,6 @@ r->nextInstruments, nunused); // - // Check for actually used channels - // XXX TODO: Actually remove the unused channels. - // - nunused = 0; - for (int i = 0; i < r->nchannels; i++) - { - if(!usedChannels[i]) - nunused++; - } - dmMsg(1, "%d channels (%d unused).\n", - r->nchannels - nunused, nunused); - - // // Remap pattern data with remapped instrument data // for (int i = 0; i < r->npatterns; i++) @@ -1150,6 +1186,29 @@ if (nunused) dmPrint(2, "\n"); + // + // Do final pass on patterns to remove unused channels + // + for (int i = 0; i < r->npatterns; i++) + { + JSSPattern *p = r->patterns[i]; + + jssScanPattern(r, p, i, NULL, p->used); + + p->nmap = 0; + for (int i = 0; i < r->nchannels; i++) + { + if (p->used[i]) + p->map[p->nmap++] = i; + } + + if (p->nmap != p->nchannels) + { + dmMsg(2, "Pattern %d: %d/%d used channels (%d unused).\n", + i, p->nchannels - p->nmap, p->nchannels, p->nmap); + } + } + return r; }