#include /*stderr*/ #include "defs.h" /*->inst.h (u8,u16x,...)*/ #include "inst.h" /*->playnote.h (SampleInfo)*/ #include "playnote.h" /*Note*/ #include "magic.h" /*MagicType*/ /*#include "dacio.h"*/ /*DacioConfInfo*/ #include "ovs.h" /*(ovs)*/ #include "nspmod.h" /*->play-mtm.h (OptInfo)*/ #include "play-mtm.h" /*(playMtm)*/ #include "mem.h" /*(memSong)*/ #define u16LittleEndian(x) \ (((const u8 *)(x))[0] + ((const u8 *)(x))[1]*(u16)256) #define u16BigEndian(x) \ (((const u8 *)(x))[0]*(u16)256 + ((const u8 *)(x))[1]) #define u32LittleEndian(x) \ (((const u8 *)(x))[0] \ + ((const u8 *)(x))[1]*((u32x)(1<<8)) \ + ((const u8 *)(x))[2]*((u32x)(1<<16)) \ + ((const u8 *)(x))[3]*((u32x)(1<<24))) typedef u8 MtmSample[37]; typedef u8 MtmNote[3]; typedef i15 MtmPat[32]; struct { const u8 *p0; const u8 *songName; i15x ordNum; i15x insNum; i15x patNum; i15x trkNum; i15x chNum; /*i15x restOrd;*/ u16x commentLen; const u8 *ord; const MtmSample *smpInfop; const u8 *smp0p; const MtmNote *trk; const MtmPat *pat; const u8 *pan; const u8 *comment; } h; static void displayS3mNote(Note *np) { static u8 *note1 = "CCDDEFFGGAAB"; static u8 *note2 = "-#-#--#-#-#-"; i15x n; n = np->note; switch (n) { case 255: printf(" ..."); break; case 254: printf(" ^^."); break; default: printf(" %c%c%d", note1[n&15], note2[n&15], n / 16); } n = np->ins; if (n == 0) printf(" .."); else printf(" %02d", n); n = np->vol; if (n == 255) printf(" .."); else printf(" %02d", n); n = np->cmd; if (n == 255) printf(" ."); else printf(" %c", n + '@'); printf("%02X||", np->info); } static void displayNote(const MtmNote *np) { static u8 *note1 = "CCDDEFFGGAAB"; static u8 *note2 = "-#-#--#-#-#-"; i15x n; /*note*/ n = (*np)[0] >> 2; if (n) printf(" %c%c%d", note1[n%12], note2[n%12], n / 12); else printf(" ..."); /*ins*/ n = (((*np)[0] & 3) << 4) | ((*np)[1] >> 4); if (n == 0) printf(" .."); else printf(" %02d", n); /*eff*/ n = (*np)[1]%16 * 256 + (*np)[2]; printf(" %03X|", n); } static void mtmToS3m(const MtmNote *mp, Note *np) { i15x n; /* 0123456789abcd */ static u8 *transTab = "JFEGHLKRXODB.C"; /* 0123456789abcdef */ static u8 *transTabE = "SFESSSSSSQ..SSSS"; /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ static u8 transTabEX[] = {0,0xF,0xF,1,3,2,0xB,4,8,0,0,0,0xC,0xD,0xE,0xF}; #define X ((*mp)[2]/16) #define Y ((*mp)[2]%16) #define XY ((*mp)[2]) /*note*/ n = (*mp)[0] >> 2; np->note = n? (n/12+2) * 16 + n%12 : 255; /*ins*/ np->ins = (((*mp)[0] & 3) << 4) | ((*mp)[1] >> 4); /*vol*/ np->vol = 255; /*eff*/ np->cmd = 255; np->info = 0; switch ((*mp)[1]%16) { case 0: /* arpeggio or nothing */ if (X || Y) { np->cmd = 'J' - '@'; np->info = XY; } break; #if 0 case 8: /* 256-position pan */ np->cmd = 'X' - '@'; np->info = XY * 0x80 / 255; break; #endif case 0xC: np->vol = XY>64? 64 : XY; np->cmd = 255; np->info = 0; break; case 0xE: switch (X) { case 0xA: if (Y) { np->cmd = 'D' - '@'; np->info = Y*16 + 15; } break; case 0xB: if (Y) { np->cmd = 'D' - '@'; np->info = 0xf0 + Y; } break; default: np->cmd = transTabE[X] - '@'; np->info = transTabEX[X]*16 + Y; } break; case 0xF: if (XY) { /* ignore speed 0 */ np->cmd = XY <= 32? 'A' - '@' : 'T' - '@'; np->info = XY? XY : 1; } break; case 1: case 2: if (XY) { np->cmd = transTab[(*mp)[1]%16] - '@'; np->info = (XY >= 0xE0)? 0xdf : XY; /*fearexam.mod*/ } break; case 0xA: if (!XY) break; default: np->cmd = transTab[(*mp)[1]%16] - '@'; np->info = XY; } #undef X #undef Y #undef XY } static i15x playAPattern(const OptInfo *oip, i15x pat) { const MtmPat *mp; const MtmNote *np, *np0; Note note; i15x row,ch; i15x loopBeg, loopCnt; static i15x row0; i15x nextOrd; static MtmNote emptyNote; row = row0; row0 = nextOrd = 0; mp = &h.pat[pat]; loopBeg = loopCnt = 0; if (oip->showEvents) printf("\n"); for (; row < 64; row++) { if (oip->showEvents) printf("%02d|", row); np0 = h.trk - 64 + row; for (ch=0; ch < h.chNum; ch++) { /*np = (*mp)[ch]? h.trk + ((*mp)[ch]-1)*64 + row : &emptyNote;*/ np = (*mp)[ch]? np0 + (*mp)[ch]*64 : &emptyNote; mtmToS3m(np, ¬e); if (!oip->onlyCh || ch == (oip->onlyCh & 0x7f)) { if (oip->showEvents) { displayNote(np); if (oip->showEvents > 1) displayS3mNote(¬e); /* -ee */ /*fflush(stdout);*/ } playNoteSetNote(ch, ¬e); } switch (note.cmd) { case 'A' - '@': /* set speed */ playNoteSetSpeed(note.info); break; case 'B' - '@': nextOrd = note.info | 0x100; row = 64; break; case 'C' - '@': row0 = note.info/16*10 + note.info%16; row = 64; break; case 'S' - '@': switch (note.info/16) { case 0xB: /* pattern loop */ if (note.info%16) { /* jump to mark */ if (loopCnt < note.info%16) { row = loopBeg-1; loopCnt++; } else { loopCnt = 0; } } else { /* set mark */ loopBeg = row; } break; case 0xE: /* pattern delay */ playNoteSetPatRepeat(note.info%16); break; } break; case 'T' - '@': /* set tempo */ playNoteSetTempo(note.info); break; } } playNote(); if (oip->showEvents) printf("\n"); } return nextOrd; } static void tellChSettings(void) { i15x i; for (i = 0; i < h.chNum; i++) { instSelectCh(i); instPanPosition(h.pan[i] * 64/15); } } static void msgOut(const u8 *p, i15x len) { for (; len > 0; len--,p++) { if (!*p) break; if (*p < 0x20 || 0x7e < *p) printf("<%X>", *p); else putchar(*p); } putchar('\n'); } /*static const u8 *fileEnd;*/ static SampleInfo *sip; static void makeSampleInfo(i15x showMsg) { const MtmSample *msp; SampleInfo *p; i15x i; const u8 *sp; static i15x freq[16] = { 8363,8413,8463,8529,8581,8651,8723,8757, 7895,7941,7985,8046,8107,8169,8232,8280 }; /*static u8 emptySample;*/ /*printf("fileEnd = 0x%p\n", fileEnd);*/ p = sip = memSong(sizeof(SampleInfo) * h.insNum); sp = h.smp0p; msp = &h.smpInfop[0]; if (showMsg) printf(" # Len LBeg LEnd C2Hz V\n"); for (i = 0; i < h.insNum; i++, p++, msp++) { u32x len, lBeg, lEnd; p->beg = sp; len = u32LittleEndian(*msp+22); /*len*/ lBeg = u32LittleEndian(*msp+26); /*loopBeg*/ lEnd = u32LittleEndian(*msp+30); /*loopEnd*/ if (lEnd > 2) { /*some mtm's (ex. 1omncrn.mtm) use lEnd = 2*/ p->loopBeg = sp + lBeg; p->end = sp + lEnd; } else { p->loopBeg = NULL; p->end = sp + len; /*sp+len*/ } if ((*msp)[36] & 1) { fprintf(stderr, "16bit sample not supported.\n"); exit(1); } p->xor = 0x80; p->mag = 1; p->c4spd = freq[(*msp)[34]%16]; p->vol = (*msp)[35] > 64? 64 : (*msp)[35]; if (showMsg) { if (lEnd > 2) { printf("%2i: %5X %5X %5X %4d %2d ", i+1, len, lBeg, lEnd, p->c4spd, p->vol); } else { printf("%2i: %5X %4d %2d ", i+1, len, p->c4spd, p->vol); } msgOut(&(*msp)[0], 22); } #if 0 if (p->end > fileEnd) { if (p->beg >= fileEnd || p->loopBeg >= fileEnd) { fprintf(stderr, "Error: short file " "(assigned an empty sample for #%d)\n", i+1); p->beg = &emptySample; p->end = &emptySample+1; p->loopBeg = NULL; p->vol = 0; } else { fprintf(stderr, "Warning: short file " "(sample #%d truncated)\n", i+1); p->end = fileEnd; } } #endif sp = sp + len; /*sp+len*/ } } static void parseMtm(const u8 *p0) { const u8 *p; p = p0; h.p0 = p; p += 4; h.songName = p; p += 20; h.trkNum = u16LittleEndian(p); p += 2; h.patNum = *p + 1; p++; h.ordNum = *p + 1; p++; h.commentLen = u16LittleEndian(p); p += 2; h.insNum = *p; p += 2; if (*p != 64) { fprintf(stderr, "Warning: beats per track=%d (!=64). What should I do?\n", *p); } p++; h.chNum = *p; p++; h.pan = p; p += 32; h.smpInfop = (const MtmSample *)p; p += sizeof(MtmSample) * h.insNum; h.ord = p; p += 128; h.trk = (const MtmNote *)p; p += sizeof(MtmNote) * 64 * h.trkNum; h.pat = (const MtmPat *)p; p += sizeof(MtmPat) * h.patNum; h.comment = p; p += h.commentLen; h.smp0p = p; #if 0 { int i; for (i=0; i<32; i++) { printf("%d : %x\n", i, h.pan[i]); } } #endif } static u8 *repCounter; static void playAlongOrder(const OptInfo *oip) { i15x ord; i15x ord0; i15x nextOrd; ord0 = oip->startOrd; for (ord = 0; ord < h.ordNum; ord++) repCounter[ord] = 0; do { for (ord = ord0; ord < h.ordNum; ) { if (oip->repLimit && ++repCounter[ord] > oip->repLimit) goto RET; printf("ord %d/%d, pat %d/%d \r", ord, h.ordNum, h.ord[ord], h.patNum); fflush(stdout); nextOrd = playAPattern(oip, h.ord[ord]); if (nextOrd) ord = nextOrd & 0xff; else ord++; } ord0 = 0; } while (oip->forceRep); RET: printf("\n"); } static void commentOut(const u8 *p, i15x len) { i15x zero; zero = 0; for (; len > 0; len--,p++) { if (!*p) { if (!zero) { putchar('\n'); zero = 1; } } else if (*p < 0x20 || 0x7e < *p) printf("<%X>", *p); else { putchar(*p); zero = 0; } } /*putchar('\n');*/ } void playMtm(const u8 *p, i31x size, const OptInfo *oip) { /*fileEnd = p + size;*/ repCounter = memSong(128 * sizeof(*repCounter)); parseMtm(p); /*printf("restOrd = %d\n", h.restOrd);*/ msgOut(h.songName, 20); if (h.commentLen) { /*printf("Comment(%d):\n", h.commentLen);*/ commentOut(h.comment, h.commentLen); } instSetPeriodAmigaLimit(1); tellChSettings(); makeSampleInfo(oip->showMsg); /*exit(1);*/ /*showSampleInfo();*/ ovs(h.insNum, sip, (u16x)1000 * oip->ovsFreq); playNoteSetSample(sip); playNoteSetTempo(125); playNoteSetSpeed(6); if (oip->masterVol) playNoteSetMasterVol((oip->masterVol < 0)? -oip->masterVol : oip->masterVol); else playNoteSetMasterVol(0x30); playNoteSetGlobalVol(0x40); /*makeNoteTable();*/ /*ord0 = oip->startOrd;*/ /*onlyCh = oip->onlyCh;*/ playAlongOrder(oip); }