#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-mod.h (OptInfo)*/ #include "play-mod.h" /*(playMod)*/ #include "mem.h" /*(memSong)*/ #define NOTE_MAX (12*5-1) /* 5 octave */ static i15 *note; #define shift(x,n) ((n) >= 0? (x) << (n) : (x) >> -(n)) /* x * 2**n */ static void makeNoteTable(void) { static i15x oct0[] = { 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907 }; i15x i; note = memSong((NOTE_MAX+1) * sizeof(*note)); for (i = 0; i <= NOTE_MAX; i++) { note[i] = shift(oct0[i%12], 0-i/12); /*printf(" %d", note[i]); if (i%12 == 11) printf("\n");*/ } } static i15x roundToNote(i15x p) { i15x i,s; if (p >= note[0]) return 0; if (p <= note[NOTE_MAX]) return NOTE_MAX; for (i=0, s=32; s > 0; s /= 2) /* 32: =2**n, and 32*2 > NOTE_MAX */ if (i+s < NOTE_MAX && note[i+s] > p) i += s; /* i < NOTE_MAX !! */ if (note[i] - p > p - note[i+1]) i++; /* choose nearest */ return i; } #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]) typedef u8 ModSample[30]; typedef u8 ModNote[4]; struct { const u8 *p0; const u8 *songName; i15x ordNum; i15x insNum; i15x patNum; i15x chNum; i15x rstOrd; const u8 *ord; const ModSample *smpInfop; const u8 *smp0p; const ModNote *pat; } 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 ModNote *np) { static u8 *note1 = "CCDDEFFGGAAB"; static u8 *note2 = "-#-#--#-#-#-"; i15x n; /*note*/ n = (*np)[0]%16 * 256 + (*np)[1]; /*period*/ #if 0 if (n) { i15x note; note = roundToNote(n); printf(" %4d(%c%c%d)", n, note1[note%12], note2[note%12], note / 12); } else printf(" (...)"); #else if (n) { i15x note; note = roundToNote(n); printf(" %c%c%d", note1[note%12], note2[note%12], note / 12); } else printf(" ..."); #endif /*ins*/ n = ((*np)[0] & 0xf0) | (*np)[2]/16; if (n == 0) printf(" .."); else printf(" %02d", n); /*eff*/ n = (*np)[2]%16 * 256 + (*np)[3]; printf(" %03X|", n); } #if 0 static void showPattern(i15x pnum) { i15x ch,row; const ModNote *np; np = &h.pat[pnum * h.chNum * 64]; for (row=0; row < 64; row++) { printf("|"); for (ch = 0; ch < h.chNum; ch++) displayNote(np + row + 64*ch); printf("\n"); } } #endif static void modToS3m(const ModNote *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)[3]/16) #define Y ((*mp)[3]%16) #define XY ((*mp)[3]) /*note*/ n = (*mp)[0]%16 * 256 + (*mp)[1]; /*period*/ if (n) { n = roundToNote(n); np->note = (n/12+2)*16 + n%12; } else np->note = 255; /*ins*/ np->ins = ((*mp)[0] & 0xf0) | (*mp)[2]/16; /*vol*/ np->vol = 255; /*eff*/ np->cmd = 255; np->info = 0; switch ((*mp)[2]%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)[2]%16] - '@'; np->info = (XY >= 0xE0)? 0xdf : XY; /*fearexam.mod*/ } break; case 0xA: if (!XY) break; default: np->cmd = transTab[(*mp)[2]%16] - '@'; np->info = XY; } #undef X #undef Y #undef XY } static i15x playAPattern(const OptInfo *oip, i15x pat) { const ModNote *np, *np0; Note note; i15x row,ch; i15x loopBeg, loopCnt; static i15x row0; i15x nextOrd; row = row0; row0 = nextOrd = 0; np0 = &h.pat[h.chNum * 64 * pat]; loopBeg = loopCnt = 0; if (oip->showEvents) printf("\n"); for (; row < 64; row++) { np = &np0[row * h.chNum]; if (oip->showEvents) printf("%02d|", row); for (ch=0; ch < h.chNum; ch++,np++) { modToS3m(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); switch (i % 4) { case 0: case 3: instPanPosition(3 * 64/15); break; /* left */ default: instPanPosition(12 * 64/15); /* right */ } } } 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, i15x smp15) { const ModSample *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 LOff LLen C2Hz V\n"); for (i = 0; i < h.insNum; i++, p++, msp++) { u16x len, lLen, lOff; /*p = &sip[i];*/ /*msp = &h.smpInfop[i];*/ p->beg = sp; len = u16BigEndian(*msp+22) * 2; /*len*/ lLen = u16BigEndian(*msp+28); /*loopLen*/ if (lLen > 1) { /*loopLen > 1*/ lLen *= smp15? 1:2; lOff = u16BigEndian(*msp+26)*(smp15? 1:2); /*loopOffset*/ p->loopBeg = sp + lOff; /*sp+loopOffset*/ p->end = p->loopBeg + lLen; } else { p->loopBeg = NULL; p->end = sp + len; /*sp+len*/ } p->xor = 0; p->mag = 1; p->c4spd = freq[(*msp)[24]%16]; p->vol = (*msp)[25] > 64? 64 : (*msp)[25]; if (showMsg) { if (lLen > 1) { printf("%2i: %4X %4X %4X %4d %2d ", i+1, len, lOff, lLen, p->c4spd, p->vol); } else { printf("%2i: %4X %4d %2d ", i+1, len, p->c4spd, p->vol); } msgOut(&(*msp)[0], 22); } 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; } } sp = sp + u16BigEndian(*msp+22) * 2; /*sp+len*/ } } #if 0 typedef struct { u8 name[20]; union { struct { ModSample smp[31]; u8 ordNum; u8 rstOrd; u8 ord[128]; u8 magic[4]; ModNote pat; } mod31; struct { ModSample smp[15]; u8 ordNum; u8 rstOrd; u8 ord[128]; ModNote pat; } mod15; } u; } ModHeader; #endif static void parseMod(const u8 *p0, i15x smp15) { i15x i,n; const u8 *p; p = p0; h.p0 = h.songName = p; p += 20; h.smpInfop = (const ModSample *)p; if (smp15) { p += 15*sizeof(ModSample); h.insNum = 15; } else { p += 31*sizeof(ModSample); h.insNum = 31; } h.ordNum = *p; p++; h.rstOrd = *p; p++; h.ord = p; /*for (i = 0, n = 0; i < h.ordNum; i++) mod.creamoftheearth incompatible*/ for (i = 0, n = 0; i < 128; i++) if (n < p[i]) n = p[i]; /*find max pat*/ h.patNum = n + 1; p += 128 + (smp15? 0 : 4); h.pat = (const ModNote *)p; h.smp0p = h.pat[h.chNum * 64 * h.patNum]; } 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 (;;) { 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++; } if (h.rstOrd >= h.ordNum) break; else ord0 = h.rstOrd; } ord0 = 0; } while (oip->forceRep); RET: printf("\n"); } void playMod(const u8 *p, i31x size, MagicType mt, i15x chNum, const OptInfo *oip) { fileEnd = p + size; h.chNum = chNum; repCounter = memSong(128 * sizeof(*repCounter)); parseMod(p, mt == MAGIC_MOD15); /*printf("rstOrd = %d\n", h.rstOrd);*/ msgOut(h.songName, 28); instSetPeriodAmigaLimit(1); tellChSettings(); makeSampleInfo(oip->showMsg, mt == MAGIC_MOD15); /*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); }