Mercurial > hg > dmlib
comparison 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 |
comparison
equal
deleted
inserted
replaced
2277:026c3aa0e48f | 2278:40ccc09f09be |
---|---|
145 | 145 |
146 return TRUE; | 146 return TRUE; |
147 } | 147 } |
148 | 148 |
149 | 149 |
150 static inline const JSSNote * jssGetNotePtr(const JSSPattern *pattern, const int channel, const int row) | |
151 { | |
152 if (!pattern->used[channel]) | |
153 return NULL; | |
154 else | |
155 return pattern->data + (pattern->nchannels * row) + channel; | |
156 } | |
157 | |
158 | |
150 /* These functions and the macro mess are meant to make the | 159 /* These functions and the macro mess are meant to make the |
151 * conversion routines themselves clearer and simpler. | 160 * conversion routines themselves clearer and simpler. |
152 */ | 161 */ |
153 BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val) | 162 BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val) |
154 { | 163 { |
187 } while (0) | 196 } while (0) |
188 | 197 |
189 | 198 |
190 /* Convert a note | 199 /* Convert a note |
191 */ | 200 */ |
192 static int jssConvertNote( | 201 static int jssDoConvertNote( |
193 Uint8 *patBuf, const size_t patBufSize, | 202 Uint8 *patBuf, const size_t patBufSize, |
194 size_t *patSize, const JSSNote *pnote) | 203 size_t *patSize, const JSSNote *pnote) |
195 { | 204 { |
196 Uint8 tmp; | 205 Uint8 tmp; |
197 if (pnote->note == jsetNotSet) | 206 if (pnote->note == jsetNotSet) |
218 } | 227 } |
219 | 228 |
220 | 229 |
221 /* Compress a note | 230 /* Compress a note |
222 */ | 231 */ |
223 static int jssCompressNote( | 232 static int jssDoCompressNote( |
224 Uint8 *patBuf, const size_t patBufSize, | 233 Uint8 *patBuf, const size_t patBufSize, |
225 size_t *patSize, const JSSNote *pnote) | 234 size_t *patSize, const JSSNote *pnote) |
226 { | 235 { |
227 Uint8 qflags = 0; | 236 Uint8 qflags = 0; |
228 int qcomp = 0; | 237 int qcomp = 0; |
254 | 263 |
255 JSCOMPPUT(JM_COMP_INSTRUMENT, pnote->instrument, "Instrument"); | 264 JSCOMPPUT(JM_COMP_INSTRUMENT, pnote->instrument, "Instrument"); |
256 JSCOMPPUT(JM_COMP_VOLUME, pnote->volume, "Volume"); | 265 JSCOMPPUT(JM_COMP_VOLUME, pnote->volume, "Volume"); |
257 JSCOMPPUT(JM_COMP_EFFECT, pnote->effect, "Effect"); | 266 JSCOMPPUT(JM_COMP_EFFECT, pnote->effect, "Effect"); |
258 JSCOMPPUT(JM_COMP_PARAM, pnote->param, "Param"); | 267 JSCOMPPUT(JM_COMP_PARAM, pnote->param, "Param"); |
268 | |
269 return DMERR_OK; | |
259 } | 270 } |
260 else | 271 else |
261 { | 272 { |
262 // Was 4 bytes or more, just dump it all in .. | 273 // Was 4 bytes or more, just dump it all in .. |
263 return jssConvertNote(patBuf, patBufSize, patSize, pnote); | 274 return jssDoConvertNote(patBuf, patBufSize, patSize, pnote); |
264 } | 275 } |
265 | 276 } |
277 | |
278 | |
279 static int jssCompressNote( | |
280 Uint8 *patBuf, const size_t patBufSize, | |
281 size_t *patSize, const JSSPattern *pattern, | |
282 const int channel, const int row) | |
283 { | |
284 const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); | |
285 if (pnote != NULL) | |
286 { | |
287 int res = jssDoCompressNote(patBuf, patBufSize, patSize, pnote); | |
288 if (res != DMERR_OK) | |
289 { | |
290 JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
291 patBuf, patBufSize, *patSize, row, channel); | |
292 return res; | |
293 } | |
294 } | |
266 return DMERR_OK; | 295 return DMERR_OK; |
267 } | 296 } |
268 | 297 |
269 | 298 |
270 /* Compress pattern | 299 /* Compress pattern |
276 *patSize = 0; | 305 *patSize = 0; |
277 | 306 |
278 for (int row = 0; row < pattern->nrows; row++) | 307 for (int row = 0; row < pattern->nrows; row++) |
279 for (int channel = 0; channel < pattern->nchannels; channel++) | 308 for (int channel = 0; channel < pattern->nchannels; channel++) |
280 { | 309 { |
281 const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; | 310 int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); |
282 const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); | |
283 if (res != DMERR_OK) | 311 if (res != DMERR_OK) |
284 { | |
285 JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
286 patBuf, patBufSize, *patSize, row, channel); | |
287 return res; | 312 return res; |
288 } | |
289 } | 313 } |
290 | 314 |
291 return DMERR_OK; | 315 return DMERR_OK; |
292 } | 316 } |
293 | 317 |
299 *patSize = 0; | 323 *patSize = 0; |
300 | 324 |
301 for (int channel = 0; channel < pattern->nchannels; channel++) | 325 for (int channel = 0; channel < pattern->nchannels; channel++) |
302 for (int row = 0; row < pattern->nrows; row++) | 326 for (int row = 0; row < pattern->nrows; row++) |
303 { | 327 { |
304 const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; | 328 int res = jssCompressNote(patBuf, patBufSize, patSize, pattern, channel, row); |
305 const int res = jssCompressNote(patBuf, patBufSize, patSize, pnote); | |
306 if (res != DMERR_OK) | 329 if (res != DMERR_OK) |
307 { | 330 return res; |
308 JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | 331 } |
332 | |
333 return DMERR_OK; | |
334 } | |
335 | |
336 | |
337 /* Convert a pattern | |
338 */ | |
339 static int jssConvertNote( | |
340 Uint8 *patBuf, const size_t patBufSize, | |
341 size_t *patSize, const JSSPattern *pattern, | |
342 const int channel, const int row) | |
343 { | |
344 const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); | |
345 if (pnote != NULL) | |
346 { | |
347 int res = jssDoConvertNote(patBuf, patBufSize, patSize, pnote); | |
348 if (res != DMERR_OK) | |
349 { | |
350 JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
309 patBuf, patBufSize, *patSize, row, channel); | 351 patBuf, patBufSize, *patSize, row, channel); |
310 return res; | 352 return res; |
311 } | 353 } |
312 } | 354 } |
313 | |
314 return DMERR_OK; | 355 return DMERR_OK; |
315 } | 356 } |
316 | 357 |
317 | 358 |
318 /* Convert a pattern | |
319 */ | |
320 static int jssConvertPatternRawHoriz( | 359 static int jssConvertPatternRawHoriz( |
321 Uint8 *patBuf, const size_t patBufSize, | 360 Uint8 *patBuf, const size_t patBufSize, |
322 size_t *patSize, const JSSPattern *pattern) | 361 size_t *patSize, const JSSPattern *pattern) |
323 { | 362 { |
324 *patSize = 0; | 363 *patSize = 0; |
325 | 364 |
326 for (int row = 0; row < pattern->nrows; row++) | 365 for (int row = 0; row < pattern->nrows; row++) |
327 for (int channel = 0; channel < pattern->nchannels; channel++) | 366 for (int channel = 0; channel < pattern->nchannels; channel++) |
328 { | 367 { |
329 const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; | 368 int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); |
330 const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); | |
331 if (res != DMERR_OK) | 369 if (res != DMERR_OK) |
332 { | |
333 JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
334 patBuf, patBufSize, *patSize, row, channel); | |
335 return res; | 370 return res; |
336 } | |
337 } | 371 } |
338 | 372 |
339 return DMERR_OK; | 373 return DMERR_OK; |
340 } | 374 } |
341 | 375 |
347 *patSize = 0; | 381 *patSize = 0; |
348 | 382 |
349 for (int channel = 0; channel < pattern->nchannels; channel++) | 383 for (int channel = 0; channel < pattern->nchannels; channel++) |
350 for (int row = 0; row < pattern->nrows; row++) | 384 for (int row = 0; row < pattern->nrows; row++) |
351 { | 385 { |
352 const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; | 386 int res = jssConvertNote(patBuf, patBufSize, patSize, pattern, channel, row); |
353 const int res = jssConvertNote(patBuf, patBufSize, patSize, pnote); | |
354 if (res != DMERR_OK) | 387 if (res != DMERR_OK) |
355 { | |
356 JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
357 patBuf, patBufSize, *patSize, row, channel); | |
358 return res; | 388 return res; |
359 } | |
360 } | 389 } |
361 | 390 |
362 return DMERR_OK; | 391 return DMERR_OK; |
363 } | 392 } |
364 | 393 |
365 | 394 |
366 #define JSFOREACHNOTE1 \ | 395 #define JSFOREACHNOTE1 \ |
367 for (channel = 0; channel < pattern->nchannels; channel++) \ | 396 for (channel = 0; channel < pattern->nchannels; channel++) \ |
368 for (row = 0; row < pattern->nrows; row++) { \ | 397 for (row = 0; row < pattern->nrows; row++) { \ |
369 const JSSNote *pnote = &pattern->data[(pattern->nchannels * row) + channel]; | 398 const JSSNote *pnote = jssGetNotePtr(pattern, channel, row); \ |
370 | 399 if (pnote != NULL) { |
371 #define JSFOREACHNOTE2 } | 400 |
401 #define JSFOREACHNOTE2 } } | |
402 | |
372 | 403 |
373 static int jssConvertPatternRawElem( | 404 static int jssConvertPatternRawElem( |
374 Uint8 *patBuf, const size_t patBufSize, | 405 Uint8 *patBuf, const size_t patBufSize, |
375 size_t *patSize, const JSSPattern *pattern) | 406 size_t *patSize, const JSSPattern *pattern) |
376 { | 407 { |
591 dmMsg(3, " - Pattern %d size %d bytes\n", index, dataSize); | 622 dmMsg(3, " - Pattern %d size %d bytes\n", index, dataSize); |
592 totalSize += dataSize + sizeof(JSSMODPattern); | 623 totalSize += dataSize + sizeof(JSSMODPattern); |
593 | 624 |
594 if (!dm_fwrite_le32(outFile, dataSize) || | 625 if (!dm_fwrite_le32(outFile, dataSize) || |
595 !dm_fwrite_le16(outFile, pattern->nrows) || | 626 !dm_fwrite_le16(outFile, pattern->nrows) || |
596 !dm_fwrite_str(outFile, patBuf, dataSize)) | 627 !dm_fwrite_le16(outFile, pattern->nmap)) |
597 { | 628 { |
598 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | 629 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, |
599 "Error writing JSSMOD pattern #%d.\n", | 630 "Error writing JSSMOD pattern header #%d.\n", |
631 index); | |
632 } | |
633 | |
634 if (pattern->nmap != pattern->nchannels) | |
635 { | |
636 if (!dm_fwrite_str(outFile, pattern->map, | |
637 sizeof(pattern->map[0]) * pattern->nmap)) | |
638 { | |
639 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
640 "Error writing JSSMOD channel map for pattern #%d.\n", | |
641 index); | |
642 } | |
643 } | |
644 | |
645 if (!dm_fwrite_str(outFile, patBuf, dataSize)) | |
646 { | |
647 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
648 "Error writing JSSMOD pattern data #%d.\n", | |
600 index); | 649 index); |
601 } | 650 } |
602 } | 651 } |
603 else | 652 else |
604 { | 653 { |
742 | 791 |
743 | 792 |
744 /* Scan given pattern for used instruments and channels. | 793 /* Scan given pattern for used instruments and channels. |
745 * Also checks if the pattern is empty. | 794 * Also checks if the pattern is empty. |
746 */ | 795 */ |
747 void scanPattern(const JSSModule *module, const JSSPattern *pattern, | 796 BOOL jssScanPattern(const JSSModule *module, const JSSPattern *pattern, |
748 const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels, BOOL *empty) | 797 const int npattern, BOOL *usedExtInstruments, BOOL *usedChannels) |
749 { | 798 { |
750 JSSNote *n = pattern->data; | 799 JSSNote *n = pattern->data; |
751 *empty = FALSE; | 800 BOOL empty = TRUE; |
752 | 801 |
753 // Check all notes in this pattern | 802 // Check all notes in this pattern |
754 for (int row = 0; row < pattern->nrows; row++) | 803 for (int row = 0; row < pattern->nrows; row++) |
755 for (int channel = 0; channel < pattern->nchannels; channel++, n++) | 804 for (int channel = 0; channel < pattern->nchannels; channel++, n++) |
756 { | 805 { |
757 // Is the instrument set? | 806 // Is the instrument set? |
758 if (n->instrument != jsetNotSet) | 807 if (usedExtInstruments != NULL && |
808 n->instrument != jsetNotSet) | |
759 { | 809 { |
760 // Is it valid? | 810 // Is it valid? |
761 if (n->instrument >= 0 && n->instrument < module->nextInstruments) | 811 if (n->instrument >= 0 && n->instrument < module->nextInstruments) |
762 usedExtInstruments[n->instrument] = TRUE; | 812 usedExtInstruments[n->instrument] = TRUE; |
763 else | 813 else |
772 n->instrument != jsetNotSet || | 822 n->instrument != jsetNotSet || |
773 n->volume != jsetNotSet || | 823 n->volume != jsetNotSet || |
774 n->effect != jsetNotSet || | 824 n->effect != jsetNotSet || |
775 n->param != jsetNotSet) | 825 n->param != jsetNotSet) |
776 { | 826 { |
777 usedChannels[channel] = TRUE; | 827 if (usedChannels != NULL) |
778 *empty = FALSE; | 828 usedChannels[channel] = TRUE; |
779 } | 829 |
780 } | 830 empty = FALSE; |
831 } | |
832 } | |
833 | |
834 return empty; | |
781 } | 835 } |
782 | 836 |
783 | 837 |
784 /* Check if two given patterns are dupes | 838 /* Check if two given patterns are dupes |
785 */ | 839 */ |
796 */ | 850 */ |
797 JSSModule *jssOptimizeModule(JSSModule *m) | 851 JSSModule *jssOptimizeModule(JSSModule *m) |
798 { | 852 { |
799 BOOL usedPatterns[jsetMaxPatterns + 1], | 853 BOOL usedPatterns[jsetMaxPatterns + 1], |
800 usedInstruments[jsetMaxInstruments + 1], | 854 usedInstruments[jsetMaxInstruments + 1], |
801 usedExtInstruments[jsetMaxInstruments + 1], | 855 usedExtInstruments[jsetMaxInstruments + 1]; |
802 usedChannels[jsetMaxChannels]; | |
803 int mapExtInstruments[jsetMaxInstruments + 1], | 856 int mapExtInstruments[jsetMaxInstruments + 1], |
804 mapInstruments[jsetMaxInstruments + 1], | 857 mapInstruments[jsetMaxInstruments + 1], |
805 mapPatterns[jsetMaxPatterns + 1], | 858 mapPatterns[jsetMaxPatterns + 1], |
806 dupPatterns[jsetMaxPatterns + 1]; | 859 dupPatterns[jsetMaxPatterns + 1]; |
807 | 860 |
825 r->norders = m->norders; | 878 r->norders = m->norders; |
826 | 879 |
827 for (int i = 0; i < jsetNChannels; i++) | 880 for (int i = 0; i < jsetNChannels; i++) |
828 { | 881 { |
829 r->defPanning[i] = m->defPanning[i]; | 882 r->defPanning[i] = m->defPanning[i]; |
830 usedChannels[i] = FALSE; | |
831 } | 883 } |
832 | 884 |
833 // Initialize values | 885 // Initialize values |
834 for (int i = 0; i <= jsetMaxInstruments; i++) | 886 for (int i = 0; i <= jsetMaxInstruments; i++) |
835 { | 887 { |
857 if (npat >= 0 && npat < m->npatterns) | 909 if (npat >= 0 && npat < m->npatterns) |
858 { | 910 { |
859 JSSPattern *pattern = m->patterns[npat]; | 911 JSSPattern *pattern = m->patterns[npat]; |
860 if (pattern != NULL) | 912 if (pattern != NULL) |
861 { | 913 { |
862 BOOL empty; | 914 // Scan for used instruments etc |
863 | 915 BOOL empty = jssScanPattern(m, pattern, npat, usedExtInstruments, NULL); |
864 // Mark this pattern as used | |
865 usedPatterns[npat] = TRUE; | |
866 | |
867 // Scan for used instruments and channels | |
868 scanPattern(m, pattern, npat, usedExtInstruments, usedChannels, &empty); | |
869 | 916 |
870 // Empty patterns with known number of rows are "removed" | 917 // Empty patterns with known number of rows are "removed" |
871 if (empty && pattern->nrows == jsetDefaultRows) | 918 if (empty && pattern->nrows == jsetDefaultRows) |
872 { | 919 { |
873 m->orderList[norder] = jsetNotSet; | 920 m->orderList[norder] = jsetNotSet; |
874 usedPatterns[npat] = FALSE; | 921 usedPatterns[npat] = FALSE; |
875 } | 922 } |
923 else | |
924 usedPatterns[npat] = TRUE; | |
876 } | 925 } |
877 else | 926 else |
878 { | 927 { |
879 dmErrorMsg("Pattern 0x%x is used on order 0x%x, but has no data!\n", | 928 dmErrorMsg("Pattern 0x%x is used on order 0x%x, but has no data!\n", |
880 npat, norder); | 929 npat, norder); |
1075 dmPrint(2, "\n"); | 1124 dmPrint(2, "\n"); |
1076 dmMsg(1, "%d extended instruments (%d unused).\n", | 1125 dmMsg(1, "%d extended instruments (%d unused).\n", |
1077 r->nextInstruments, nunused); | 1126 r->nextInstruments, nunused); |
1078 | 1127 |
1079 // | 1128 // |
1080 // Check for actually used channels | |
1081 // XXX TODO: Actually remove the unused channels. | |
1082 // | |
1083 nunused = 0; | |
1084 for (int i = 0; i < r->nchannels; i++) | |
1085 { | |
1086 if(!usedChannels[i]) | |
1087 nunused++; | |
1088 } | |
1089 dmMsg(1, "%d channels (%d unused).\n", | |
1090 r->nchannels - nunused, nunused); | |
1091 | |
1092 // | |
1093 // Remap pattern data with remapped instrument data | 1129 // Remap pattern data with remapped instrument data |
1094 // | 1130 // |
1095 for (int i = 0; i < r->npatterns; i++) | 1131 for (int i = 0; i < r->npatterns; i++) |
1096 { | 1132 { |
1097 JSSPattern *p = r->patterns[i]; | 1133 JSSPattern *p = r->patterns[i]; |
1148 r->orderList[i] = map; | 1184 r->orderList[i] = map; |
1149 } | 1185 } |
1150 if (nunused) | 1186 if (nunused) |
1151 dmPrint(2, "\n"); | 1187 dmPrint(2, "\n"); |
1152 | 1188 |
1189 // | |
1190 // Do final pass on patterns to remove unused channels | |
1191 // | |
1192 for (int i = 0; i < r->npatterns; i++) | |
1193 { | |
1194 JSSPattern *p = r->patterns[i]; | |
1195 | |
1196 jssScanPattern(r, p, i, NULL, p->used); | |
1197 | |
1198 p->nmap = 0; | |
1199 for (int i = 0; i < r->nchannels; i++) | |
1200 { | |
1201 if (p->used[i]) | |
1202 p->map[p->nmap++] = i; | |
1203 } | |
1204 | |
1205 if (p->nmap != p->nchannels) | |
1206 { | |
1207 dmMsg(2, "Pattern %d: %d/%d used channels (%d unused).\n", | |
1208 i, p->nchannels - p->nmap, p->nchannels, p->nmap); | |
1209 } | |
1210 } | |
1211 | |
1153 return r; | 1212 return r; |
1154 } | 1213 } |
1155 | 1214 |
1156 | 1215 |
1157 int main(int argc, char *argv[]) | 1216 int main(int argc, char *argv[]) |