ref: 0b7b60511d71e3c80deb8d1900e1ec778d964c0c
dir: /omidi.c/
#include <u.h> #include <libc.h> #include "dat.h" #include "fns.h" enum{ NCHIPS = 1, RATE = 44100 }; /* FIXME: 8192 bytes or shorts? */ static short abuf[8192]; static short *ap; static int afd = -1; /* FIXME: using multiple chips */ /* FIXME: the only reason this exists and FM_OPL is defined in dat.h instead of being * static in fmopl.c is to have multiple cards... and we don't support that; either * implement it and it's cool and it should be that way, or nuke this */ static FM_OPL *chip[NCHIPS]; static int oplretval; static int oplregno; static double otm; enum{ MINDELAY = 1, OPLBASE = 0x388 }; static int verbose; static struct MusData mdat; int ColorNums = -1; const long Period[12] = { 907,960,1016,1076, 1140,1208,1280,1356, 1440,1524,1616,1712 }; static const char Portit[9] = {0,1,2, 8,9,10, 16,17,18}; static signed char Pans[18] = {0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0}; signed char Trig[18] = {0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0}; //#define SetBase(a) ((a)==2 ? (a) : 9) #define SetBase(a) (a) static uchar FMVol[65] = { /* Generated similarly as LogTable */ 0,32,48,58,64,70,74,77,80,83,86, 88,90,92,93,95,96,98,99,100,102, 103,104,105,106,107,108,108,109, 110,111,112,112,113,114,114,115, 116,116,117,118,118,119,119,120, 120,121,121,122,122,123,123,124, 124,124,125,125,126,126,126,127, 127,127,128,128 }; static uchar adl[] = { 14,0,0,1,143,242,244,0,8,1,6,242,247,0,14,0,0,1,75,242,244,0,8,1,0,242,247,0,14,0,0,1,73,242,244,0,8,1,0,242,246,0,14,0,0,129,18,242,247,0,6,65,0,242,247,0,14,0,0,1,87,241,247,0,0,1,0,242,247,0,14,0,0,1,147,241,247,0,0,1,0,242,247,0,14,0,0,1,128,161, 242,0,8,22,14,242,245,0,14,0,0,1,146,194,248,0,10,1,0,194,248,0,14,0,0,12,92,246,244,0,0,129,0,243,245,0,14,0,0,7,151,243,242,0,2,17,128,242,241,0,14,0,0,23,33,84,244,0,2,1,0,244,244,0,14,0,0,152,98,243,246,0,0,129,0,242,246,0,14,0,0,24,35,246,246, 0,0,1,0,231,247,0,14,0,0,21,145,246,246,0,4,1,0,246,246,0,14,0,0,69,89,211,243,0,12,129,128,163,243,0,14,0,0,3,73,117,245,1,4,129,128,181,245,0,14,0,0,113,146,246,20,0,2,49,0,241,7,0,14,0,0,114,20,199,88,0,2,48,0,199,8,0,14,0,0,112,68,170,24,0,4,177, 0,138,8,0,14,0,0,35,147,151,35,1,4,177,0,85,20,0,14,0,0,97,19,151,4,1,0,177,128,85,4,0,14,0,0,36,72,152,42,1,12,177,0,70,26,0,14,0,0,97,19,145,6,1,10,33,0,97,7,0,14,0,0,33,19,113,6,0,6,161,137,97,7,0,14,0,0,2,156,243,148,1,12,65,128,243,200,0,14,0, 0,3,84,243,154,1,12,17,0,241,231,0,14,0,0,35,95,241,58,0,0,33,0,242,248,0,14,0,0,3,135,246,34,1,6,33,128,243,248,0,14,0,0,3,71,249,84,0,0,33,0,246,58,0,14,0,0,35,74,145,65,1,8,33,5,132,25,0,14,0,0,35,74,149,25,1,8,33,0,148,25,0,14,0,0,9,161,32,79, 0,8,132,128,209,248,0,14,0,0,33,30,148,6,0,2,162,0,195,166,0,14,0,0,49,18,241,40,0,10,49,0,241,24,0,14,0,0,49,141,241,232,0,10,49,0,241,120,0,14,0,0,49,91,81,40,0,12,50,0,113,72,0,14,0,0,1,139,161,154,0,8,33,64,242,223,0,14,0,0,33,139,162,22,0,8,33, 8,161,223,0,14,0,0,49,139,244,232,0,10,49,0,241,120,0,14,0,0,49,18,241,40,0,10,49,0,241,24,0,14,0,0,49,21,221,19,1,8,33,0,86,38,0,14,0,0,49,22,221,19,1,8,33,0,102,6,0,14,0,0,113,73,209,28,1,8,49,0,97,12,0,14,0,0,33,77,113,18,1,2,35,128,114,6,0,14, 0,0,241,64,241,33,1,2,225,0,111,22,0,14,0,0,2,26,245,117,1,0,1,128,133,53,0,14,0,0,2,29,245,117,1,0,1,128,243,244,0,14,0,0,16,65,245,5,1,2,17,0,242,195,0,14,0,0,33,155,177,37,1,14,162,1,114,8,0,14,0,0,161,152,127,3,1,0,33,0,63,7,1,14,0,0,161,147,193, 18,0,10,97,0,79,5,0,14,0,0,33,24,193,34,0,12,97,0,79,5,0,14,0,0,49,91,244,21,0,0,114,131,138,5,0,14,0,0,161,144,116,57,0,0,97,0,113,103,0,14,0,0,113,87,84,5,0,12,114,0,122,5,0,14,0,0,144,0,84,99,0,8,65,0,165,69,0,14,0,0,33,146,133,23,0,12,33,1,143, 9,0,14,0,0,33,148,117,23,0,12,33,5,143,9,0,14,0,0,33,148,118,21,0,12,97,0,130,55,0,14,0,0,49,67,158,23,1,2,33,0,98,44,1,14,0,0,33,155,97,106,0,2,33,0,127,10,0,14,0,0,97,138,117,31,0,8,34,6,116,15,0,14,0,0,161,134,114,85,1,0,33,131,113,24,0,14,0,0, 33,77,84,60,0,8,33,0,166,28,0,14,0,0,49,143,147,2,1,8,97,0,114,11,0,14,0,0,49,142,147,3,1,8,97,0,114,9,0,14,0,0,49,145,147,3,1,10,97,0,130,9,0,14,0,0,49,142,147,15,1,10,97,0,114,15,0,14,0,0,33,75,170,22,1,8,33,0,143,10,0,14,0,0,49,144,126,23,1,6,33, 0,139,12,1,14,0,0,49,129,117,25,1,0,50,0,97,25,0,14,0,0,50,144,155,33,0,4,33,0,114,23,0,14,0,0,225,31,133,95,0,0,225,0,101,26,0,14,0,0,225,70,136,95,0,0,225,0,101,26,0,14,0,0,161,156,117,31,0,2,33,0,117,10,0,14,0,0,49,139,132,88,0,0,33,0,101,26,0, 14,0,0,225,76,102,86,0,0,161,0,101,38,0,14,0,0,98,203,118,70,0,0,161,0,85,54,0,14,0,0,98,153,87,7,0,11,161,0,86,7,0,14,0,0,98,147,119,7,0,11,161,0,118,7,0,14,0,0,34,89,255,3,2,0,33,0,255,15,0,14,0,0,33,14,255,15,1,0,33,0,255,15,1,14,0,0,34,70,134, 85,0,0,33,128,100,24,0,14,0,0,33,69,102,18,0,0,161,0,150,10,0,14,0,0,33,139,146,42,1,0,34,0,145,42,0,14,0,0,162,158,223,5,0,2,97,64,111,7,0,14,0,0,32,26,239,1,0,0,96,0,143,6,2,14,0,0,33,143,241,41,0,10,33,128,244,9,0,14,0,0,119,165,83,148,0,2,161, 0,160,5,0,14,0,0,97,31,168,17,0,10,177,128,37,3,0,14,0,0,97,23,145,52,0,12,97,0,85,22,0,14,0,0,113,93,84,1,0,0,114,0,106,3,0,14,0,0,33,151,33,67,0,8,162,0,66,53,0,14,0,0,161,28,161,119,1,0,33,0,49,71,1,14,0,0,33,137,17,51,0,10,97,3,66,37,0,14,0,0, 161,21,17,71,1,0,33,0,207,7,0,14,0,0,58,206,248,246,0,2,81,0,134,2,0,14,0,0,33,21,33,35,1,0,33,0,65,19,0,14,0,0,6,91,116,149,0,0,1,0,165,114,0,14,0,0,34,146,177,129,0,12,97,131,242,38,0,14,0,0,65,77,241,81,1,0,66,0,242,245,0,14,0,0,97,148,17,81,1, 6,163,128,17,19,0,14,0,0,97,140,17,49,0,6,161,128,29,3,0,14,0,0,164,76,243,115,1,4,97,0,129,35,0,14,0,0,2,133,210,83,0,0,7,3,242,246,1,14,0,0,17,12,163,17,1,0,19,128,162,229,0,14,0,0,17,6,246,65,1,4,17,0,242,230,2,14,0,0,147,145,212,50,0,8,145,0,235, 17,1,14,0,0,4,79,250,86,0,12,1,0,194,5,0,14,0,0,33,73,124,32,0,6,34,0,111,12,1,14,0,0,49,133,221,51,1,10,33,0,86,22,0,14,0,0,32,4,218,5,2,6,33,129,143,11,0,14,0,0,5,106,241,229,0,6,3,128,195,229,0,14,0,0,7,21,236,38,0,10,2,0,248,22,0,14,0,0,5,157, 103,53,0,8,1,0,223,5,0,14,0,0,24,150,250,40,0,10,18,0,248,229,0,14,0,0,16,134,168,7,0,6,0,3,250,3,0,14,0,0,17,65,248,71,2,4,16,3,243,3,0,14,0,0,1,142,241,6,2,14,16,0,243,2,0,14,0,0,14,0,31,0,0,14,192,0,31,255,3,14,0,0,6,128,248,36,0,14,3,136,86,132, 2,14,0,0,14,0,248,0,0,14,208,5,52,4,3,14,0,0,14,0,246,0,0,14,192,0,31,2,3,14,0,0,213,149,55,163,0,0,218,64,86,55,0,14,0,0,53,92,178,97,2,10,20,8,244,21,0,14,0,0,14,0,246,0,0,14,208,0,79,245,3,14,0,0,38,0,255,1,0,14,228,0,18,22,1,14,0,0,0,0,243,240, 0,14,0,0,246,201,2,14,0,35,16,68,248,119,2,8,17,0,243,6,0,14,0,35,16,68,248,119,2,8,17,0,243,6,0,14,0,52,2,7,249,255,0,8,17,0,248,255,0,14,0,48,0,0,252,5,2,14,0,0,250,23,0,14,0,58,0,2,255,7,0,0,1,0,255,8,0,14,0,60,0,0,252,5,2,14,0,0,250,23,0,14,0, 47,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,8,0,10,18,0,251,71,2,14,0,49,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,8,0,10,18,5,123,71,2,14,0,51,0,0,246,12,0,4,0,0,246,6,0,14,0,43,12,0,246,2,0,10,18,0,203,67,2,14,0,54,0,0,246,12,0,4,0,0,246,6, 0,14,0,57,0,0,246,12,0,4,0,0,246,6,0,14,0,72,14,0,246,0,0,14,208,0,159,2,3,14,0,60,0,0,246,12,0,4,0,0,246,6,0,14,0,76,14,8,248,66,0,14,7,74,244,228,3,14,0,84,14,0,245,48,0,14,208,10,159,2,0,14,0,36,14,10,228,228,3,6,7,93,245,229,1,14,0,65,2,3,180, 4,0,14,5,10,151,247,0,14,0,84,78,0,246,0,0,14,158,0,159,2,3,14,0,83,17,69,248,55,2,8,16,8,243,5,0,14,0,84,14,0,246,0,0,14,208,0,159,2,3,14,0,24,128,0,255,3,3,12,16,13,255,20,0,14,0,77,14,8,248,66,0,14,7,74,244,228,3,14,0,60,6,11,245,12,0,6,2,0,245, 8,0,14,0,65,1,0,250,191,0,7,2,0,200,151,0,14,0,59,1,81,250,135,0,6,1,0,250,183,0,14,0,51,1,84,250,141,0,6,2,0,248,184,0,14,0,45,1,89,250,136,0,6,2,0,248,182,0,14,0,71,1,0,249,10,3,14,0,0,250,6,0,14,0,60,0,128,249,137,3,14,0,0,246,108,0,14,0,58,3,128, 248,136,3,15,12,8,246,182,0,14,0,53,3,133,248,136,3,15,12,0,246,182,0,14,0,64,14,64,118,79,0,14,0,8,119,24,2,14,0,71,14,64,200,73,0,14,3,0,155,105,2,14,0,61,215,220,173,5,3,14,199,0,141,5,0,14,0,61,215,220,168,4,3,14,199,0,136,4,0,14,0,44,128,0,246, 6,3,14,17,0,103,23,3,14,0,40,128,0,245,5,2,14,17,9,70,22,3,14,0,69,6,63,0,244,0,1,21,0,247,245,0,14,0,68,6,63,0,244,3,0,18,0,247,245,0,14,0,63,6,63,0,244,0,1,18,0,247,245,0,14,0,74,1,88,103,231,0,0,2,0,117,7,0,14,0,60,65,69,248,72,0,0,66,8,117,5,0, 14,0,80,10,64,224,240,3,8,30,78,255,5,0,14,0,64,10,124,224,240,3,8,30,82,255,2,0,14,0,72,14,64,122,74,0,14,0,8,123,27,2,14,0,73,14,10,228,228,3,6,7,64,85,57,1,14,0,70,5,5,249,50,3,14,4,64,214,165,0,14,0,68,2,63,0,243,3,8,21,0,247,245,0,14,0,48,1,79, 250,141,0,7,2,0,248,181,0,14,0,53,0,0,246,12,0,4,0,0,246,6,0 }; static void pcmout(int v) { if(v < -32768) v = -32768; if(v > 32767) v = 32767; /* FIXME: portability (endianness) */ *ap++ = v; /* FIXME: stereo shit */ *ap++ = v; /* FIXME: write last samples before end */ if(ap >= abuf+nelem(abuf)){ write(afd, abuf, sizeof abuf); ap = abuf; } } static uint tadv(double length, double add) { add += otm; otm = fmod(add, length); return add / length; } static void mix(uint usecs) { int i; uint n; s16int *buf; n = tadv((double)1.0 / RATE, (double)usecs / 1E6); if((buf = mallocz(n * sizeof *buf, 1)) == nil) sysfatal("mallocz: %r"); ym3812_update_one(chip[0], buf, n); for(i = 0; i < n; i++) //pcmout((double)buf[i] / 32768.0 * 10000.0); /* FIXME: why? */ pcmout(buf[i]); free(buf); } static uchar inb(uint port) { if(port >= 0x388 && port <= 0x38b) return oplretval; return 0; } static void outb(uint port, uchar v) { uint ind; if(port >= 0x388 && port <= 0x38b){ ind = port - 0x388; ym3812_write(chip[0], ind, v); if(ind & 1){ if(oplregno == 4){ if(v == 0x80) oplretval = 0x02; else if(v == 0x21) oplretval = 0xc0; } }else oplregno = v; } } static void OPL_Byte(uchar Index, uchar Data) { int a; outb(OPLBASE, Index); for(a=0; a<6; a++) inb(OPLBASE); outb(OPLBASE+1, Data); for(a=0; a<35; a++) inb(OPLBASE); } static void OPL_NoteOff(int c) { Trig[c] = 0; c = SetBase(c); if(c<9) { int Ope = Portit[c]; /* KEYON_BLOCK+c seems to not work alone?? */ OPL_Byte(KEYON_BLOCK+c, 0); OPL_Byte(KSL_LEVEL+ Ope, 0xFF); OPL_Byte(KSL_LEVEL+3+Ope, 0xFF); } } /* OPL_NoteOn changes the frequency on specified channel and guarantees the key is on. (Doesn't retrig, just turns the note on and sets freq.) */ /* Could be used for pitch bending also. */ static void OPL_NoteOn(int c, unsigned long Herz) { int Oct; Trig[c] = 127; c = SetBase(c); if(c >= 9)return; for(Oct=0; Herz>0x1FF; Oct++)Herz >>= 1; /* Bytes A0-B8 - Octave / F-Number / Key-On 7 6 5 4 3 2 1 0 +-----+-----+-----+-----+-----+-----+-----+-----+ | F-Number (least significant byte) | (A0-A8) +-----+-----+-----+-----+-----+-----+-----+-----+ | Unused | Key | Octave | F-Number | (B0-B8) | | On | | most sig. | +-----+-----+-----+-----+-----+-----+-----+-----+ */ OPL_Byte(0xA0+c, Herz&255); //F-Number low 8 bits OPL_Byte(0xB0+c, 0x20 //Key on | ((Herz>>8)&3) //F-number high 2 bits | ((Oct&7)<<2) ); } static void OPL_Touch(int c, int Instru, ushort Vol) { int Ope; //int level; c = SetBase(c); if(c >= 9)return; Ope = Portit[c]; /* Bytes 40-55 - Level Key Scaling / Total Level 7 6 5 4 3 2 1 0 +-----+-----+-----+-----+-----+-----+-----+-----+ | Scaling | Total Level | | Level | 24 12 6 3 1.5 .75 | <-- dB +-----+-----+-----+-----+-----+-----+-----+-----+ bits 7-6 - causes output levels to decrease as the frequency rises: 00 - no change 10 - 1.5 dB/8ve 01 - 3 dB/8ve 11 - 6 dB/8ve bits 5-0 - controls the total output level of the operator. all bits CLEAR is loudest; all bits SET is the softest. Don't ask me why. */ /* if 1 */ OPL_Byte(KSL_LEVEL+ Ope, (mdat.Instr[Instru]->D[2]&KSL_MASK) | (63 + (mdat.Instr[Instru]->D[2]&63) * Vol / 63 - Vol)); OPL_Byte(KSL_LEVEL+3+Ope, (mdat.Instr[Instru]->D[3]&KSL_MASK) | (63 + (mdat.Instr[Instru]->D[3]&63) * Vol / 63 - Vol)); /* else level = (mdat.Instr[Instru]->D[2]&63) - (Vol*72-8); if(level<0)level=0; if(level>63)level=63; OPL_Byte(KSL_LEVEL+ Ope, (mdat.Instr[Instru]->D[2]&KSL_MASK) | level); level = (mdat.Instr[Instru]->D[3]&63) - (Vol*72-8); if(level<0)level=0; if(level>63)level=63; OPL_Byte(KSL_LEVEL+3+Ope, (mdat.Instr[Instru]->D[3]&KSL_MASK) | level); endif */ } static void OPL_Pan(int c, uchar val) { Pans[c] = val - 128; } static void OPL_Patch(int c, int Instru) { int Ope; c = SetBase(c); if(c >= 9)return; Ope = Portit[c]; if(Instru > sizeof(mdat.Instr)-1) sysfatal("invalid instrument patch %ud", Instru); /* FIXME: see dat.h comments */ OPL_Byte(AM_VIB+ Ope, mdat.Instr[Instru]->D[0]); OPL_Byte(ATTACK_DECAY+ Ope, mdat.Instr[Instru]->D[4]); OPL_Byte(SUSTAIN_RELEASE+ Ope, mdat.Instr[Instru]->D[6]); OPL_Byte(WAVE_SELECT+ Ope, mdat.Instr[Instru]->D[8]&3);// 6 high bits used elsewhere OPL_Byte(AM_VIB+ 3+Ope, mdat.Instr[Instru]->D[1]); OPL_Byte(ATTACK_DECAY+ 3+Ope, mdat.Instr[Instru]->D[5]); OPL_Byte(SUSTAIN_RELEASE+3+Ope, mdat.Instr[Instru]->D[7]); OPL_Byte(WAVE_SELECT+ 3+Ope, mdat.Instr[Instru]->D[9]&3);// 6 high bits used elsewhere /* Panning... */ OPL_Byte(FEEDBACK_CONNECTION+c, (mdat.Instr[Instru]->D[10] & ~STEREO_BITS) | (Pans[c]<-32 ? VOICE_TO_LEFT : Pans[c]>32 ? VOICE_TO_RIGHT : (VOICE_TO_LEFT | VOICE_TO_RIGHT) ) ); } /* u32int, word and byte have been defined in adlib.h */ static u32int ConvL(u8int *s) { return (((u32int)s[0] << 24) | ((u32int)s[1] << 16) | ((u16int)s[2] << 8) | s[3]); } static u16int ConvI(u8int *s) { return (s[0] << 8) | s[1]; } static struct Midi { /* Fixed by ReadMIDI() */ int Fmt; int TrackCount; int DeltaTicks; ulong *TrackLen; u8int **Tracks; /* Used by play() */ u32int *Waiting, *sWaiting, *SWaiting; u8int *Running, *sRunning, *SRunning; ulong *Posi, *sPosi, *SPosi; u32int Tempo; u32int oldtempo; u32int initempo; /* Per channel */ u8int Pan[16]; u8int Patch[16]; u8int MainVol[16]; u8int PitchSense[16]; int Bend[16]; int OldBend[16]; int Used[16][127]; /* contains references to adlib channels per note */ } MIDI; #define snNoteOff 0x7fb1 #define snNoteOn 0x7fb2 #define snNoteModify 0x7fb3 #define snNoteUpdate 0x7fb4 enum{ MAXS3MCHAN = 9 }; typedef struct { /* Important - at offset 0 to be saved to file */ long Age; u8int Note; u8int Instru; u8int Volume; u8int cmd; u8int info; u8int KeyFlag; /* Required to diff aftertouch and noteon */ /* Byte to save space */ /* Real volume(0..127) and main volume(0..127) */ int RealVol; int MainVol; int exp; /* vol = MainVol * RealVol/127 * exp/127 */ /* RealVol must be first non-saved */ /* Less important */ int LastInstru; /* Needed by SuggestNewChan() */ int BendFlag; u8int LastPan; /* To fasten forcement */ int uChn, uNote; } S3MChan; static S3MChan Chan[MAXCHN]; static S3MChan PrevChan[MAXCHN]; #define chansavesize ((int)((long)&Chan[0].RealVol - (long)&Chan[0].Age)) static int InstruUsed[256]; static int Forced=0; static const int tempochanged = -5; static void AnalyzeRow(void) { int a; for(a=0; a<MAXS3MCHAN; a++) if(!Chan[a].Age)break; if(a==MAXS3MCHAN) return; memcpy(PrevChan, Chan, sizeof PrevChan); Forced=0; for(a=0; a<MAXS3MCHAN; a++) Chan[a].KeyFlag=0; } static void FixSpeed(float *Speed, int *Tempo) { int a; float tmp = 1.0; *Speed = MIDI.Tempo * 4E-7 / MIDI.DeltaTicks; *Tempo = 125; for(a=0x40; a<=0xFF; a++) { double Tmp; float n = a * *Speed; n = modf(1.0/n, &Tmp); if(n < tmp) { *Tempo = a; tmp = n; } } *Speed *= *Tempo; } static int MakeVolume(int vol) { return FMVol[vol*64/127]*63/128; } static void Bendi(int chn, int a) { int bc, nt; long HZ1, HZ2, Herz; bc = MIDI.Bend[chn]; nt = Chan[a].Note; if(bc > MIDI.OldBend[chn]) Chan[a].BendFlag |= 1; else if(bc < MIDI.OldBend[chn]) Chan[a].BendFlag |= 2; else Chan[a].BendFlag = 0; MIDI.OldBend[chn] = bc; for(; bc < 0; bc += 0x1000) nt--; for(; bc >= 0x1000; bc -= 0x1000) nt++; HZ1 = Period[(nt+1)%12] * (8363L << (nt+1)/12) / 44100U; HZ2 = Period[(nt )%12] * (8363L << (nt )/12) / 44100U; Herz = HZ2 + bc * (HZ1 - HZ2) / 0x1000; OPL_NoteOn(a, Herz); } /* vole = RealVol*MainVol/127 */ static int SuggestNewChan(int instru, int /*vole*/) { int a, c=MAXS3MCHAN, f; long b; for(a=f=0; a<MAXS3MCHAN; a++) if(Chan[a].LastInstru==instru) f=1; /* Arvostellaan channels */ for(b=a=0; a<MAXS3MCHAN; a++) { /* empty if channel is silent */ if(!Chan[a].Volume) { long d; /* Pohjapisteet... * Jos instru oli uusi, mieluiten sijoitetaan * sellaiselle kanavalle, joka on pitk��n ollut hiljaa. * Muuten sille, mill� se juuri �skenkin soi. */ d = f?1:Chan[a].Age; /* Jos kanavan edellinen instru oli joku * soinniltaan hyvin lyhyt, pisteit� annetaan lis�� */ if(strchr("\x81\x82\x83\x84\x85\x86\x87\x88\x89" "\x8B\x8D\x8E\x90\x94\x96\x9A\x9B\x9C" "\x9D\xA4\xAA\xAB", Chan[a].LastInstru))d += 2; else { /* Jos oli pitk�sointinen percussion, * * annetaan pisteit� i�n mukaan. */ if(Chan[a].LastInstru > 0x80) d += Chan[a].Age*2; } /* Jos oli samaa instrua, pisteit� tulee paljon lis�� */ if(Chan[a].LastInstru == instru)d += 3; //d = (d-1)*Chan[a].Age+1; if(d > b) { b = d; c = a; } } } return c; } /* vole = RealVol*MainVol/127 */ static int ForceNewChan(int instru, int vole) { int a, c; long b=0; vole *= 127; Forced=1; for(a=c=0; c<MAXS3MCHAN; c++) if(Chan[c].Age > b + (((instru<128 && Chan[c].Instru>128) || (vole > Chan[c].RealVol*Chan[c].MainVol) ) ? 1:0 ) ) { a=c; b=Chan[c].Age; } return a; } /* Used twice by SetNote. This should be considered as a macro. */ static void SubNoteOff(int a, int chn, int note) { Chan[a].RealVol = 0; Chan[a].MainVol = 0; Chan[a].exp = 0x7f; Chan[a].Age = 0; Chan[a].Volume = 0; Chan[a].BendFlag= 0; MIDI.Used[chn][note] = 0; if(Chan[a].Instru < 0x80)OPL_NoteOff(a); } static void SetNote(int chn, int note, int RealVol,int MainVol, int bend, int e) { int a, vole; //vole = RealVol*(MainVol*e)/127; vole = RealVol*(MainVol)/127; if(!vole && (bend==snNoteOn || bend==snNoteModify))bend=snNoteOff; if(bend==snNoteOn && MIDI.Used[chn][note])bend=snNoteModify; switch(bend) { /* snNoteOn:ssa note ei koskaan ole -1 */ case snNoteOn: { int p; /* FIXME */ p = chn==9 ? 128+note-35 : MIDI.Patch[chn]; a = SuggestNewChan(p, vole); if(a==MAXS3MCHAN) { a = ForceNewChan(p, vole); MIDI.Used[Chan[a].uChn][Chan[a].uNote] = 0; } if(a < MAXS3MCHAN) { Chan[a].exp = e; Chan[a].RealVol= RealVol; Chan[a].MainVol= MainVol; Chan[a].Note = chn==9 ? 60 : note; Chan[a].Volume = MakeVolume(vole); Chan[a].Age = 0; Chan[a].Instru = p; Chan[a].LastInstru = p; Chan[a].KeyFlag= 1; Chan[a].uChn = chn; Chan[a].uNote = note; /* if(MIDI.Bend[chn] && Chan[a].Volume) { int adlbend = MIDI.Bend[chn] * 127L / 8000; //TODO: Fix this. It doesn't work at all. Chan[a].cmd = 'N'-64; Chan[a].info= adlbend+0x80; // To make it unsigned } else */ if(MIDI.Pan[chn] != Chan[a].LastPan && Chan[a].Volume) { Chan[a].cmd = 'X'-64; Chan[a].info= MIDI.Pan[chn]*2; Chan[a].LastPan = MIDI.Pan[chn]; } else { Chan[a].cmd = Chan[a].info = 0; } OPL_NoteOff(a); OPL_Patch(a, Chan[a].Instru); if(MIDI.Pan[chn] != 64)OPL_Pan(a, Chan[a].info); OPL_Touch(a, Chan[a].Instru, Chan[a].Volume); Bendi(chn, a); MIDI.Used[chn][note] = a+1; } break; } /* snNoteOff:ssa note voi olla -1 */ case snNoteOff: { int b=note, c=note; if(note < 0)b=0, c=127; MIDI.Bend[chn] = 0; /* N�in vaikuttaisi olevan hyv� */ for(note=b; note<=c; note++) { a = MIDI.Used[chn][note]; if(a > 0) SubNoteOff(a-1, chn, note); } break; } /* snNoteModify:ssa note ei koskaan ole -1 */ case snNoteModify: a = MIDI.Used[chn][note]-1; if(a != -1) { Chan[a].exp = e; Chan[a].RealVol= RealVol; Chan[a].MainVol= MainVol; Chan[a].Volume = MakeVolume(vole); Chan[a].Age = 0; OPL_Touch(a, Chan[a].Instru, Chan[a].Volume); } break; /* snNoteUpdate:ssa note on aina -1 */ case snNoteUpdate: /* snNoteUpdatessa RealVol ei muutu, vain MainVol */ for(note=0; note<=127; note++) { a = MIDI.Used[chn][note]-1; //vole = MainVol*(Chan[a].RealVol*Chan[a].exp)/127; vole = MainVol*(Chan[a].RealVol)/127; if(a >= 0 && Chan[a].Volume != MakeVolume(vole)) { Chan[a].MainVol= MainVol; if(!vole) SubNoteOff(a, chn, note); else { Chan[a].Volume = MakeVolume(vole); Chan[a].Age = 0; OPL_Touch(a, Chan[a].Instru, Chan[a].Volume); } } } break; /* Bendiss� note on aina -1 */ default: MIDI.Bend[chn] = bend; for(note=0; note<=127; note++) { a = MIDI.Used[chn][note]-1; if(a >= 0) Bendi(chn, a); } } } static u32int GetVarLen(int Track, ulong *Posi) { u32int d = 0; for(;;) { u8int b = MIDI.Tracks[Track][(*Posi)++]; d = (d<<7) + (b&127); if(b < 128)break; } return d; } /* NOTICE: This copies len-2 bytes */ static char *Sana(int len, const unsigned char *buf) { static char Buf[128]; char *s = Buf; len -= 2; #define add(c) if(s<&Buf[sizeof(Buf)-1])*s++=(c) while(len>0) { if(*buf<32||(*buf>=127&&*buf<160)){add('^');add(*buf + 64);} else add(*buf); buf++; len--; } #undef add *s=0; return Buf; } static void MPU_Byte(uchar) { } static void readmeta(int t, u8int *p, ulong n) { u8int b; b = *p; p += 2; //fprint(2, "meta %ux\n", b); switch(b){ case 1: case 2: case 3: case 4: case 5: case 6: if(verbose) fprint(2, "%s\n", Sana(n, p)); break; case 0x2f: MIDI.Posi[t] = MIDI.TrackLen[t]; if(verbose) fprint(2, "end\n"); break; case 0x51: MIDI.Tempo = p[0]<<16 | p[1]<<8 | p[2]; if(verbose) fprint(2, "tempo %ud\n", MIDI.Tempo); break; case 0: case 0x20: case 0x21: case 0x58: case 0x59: case 0x7f: break; default: fprint(2, "unknown metaevent %ux\n", b); } } static void Tavuja(int Track, u8int First, ulong posi, ulong Len) { ulong a; if(verbose){ fprint(2, "[%d]: %02ux ", Track, First); for(a=0; a<Len; a++) fprint(2, "%02ux", MIDI.Tracks[Track][a+posi]); fprint(2, "\n"); } /* FIXME: ignore sysex completely, do it like games/midi */ if(First == 0xff) readmeta(Track, MIDI.Tracks[Track]+posi, Len); else if(First < 0xf0){ //ulong a; // ← what the fuck a=0; if((First>>4) == 8) /* Note off, modify it a bit */ { /* FIXME: */ MPU_Byte(First); MPU_Byte(MIDI.Tracks[Track][posi]); MPU_Byte(0); Len -= 2; } else MPU_Byte(First); for(; a<Len; a++)MPU_Byte(MIDI.Tracks[Track][a+posi]); //fprint(2, "ev %ux\n", First >> 4); switch(First >> 4) { case 0x8: /* Note off */ SetNote( First&15, /* chn */ MIDI.Tracks[Track][posi+0], /* note */ MIDI.Tracks[Track][posi+1], /* volume */ MIDI.MainVol[First&15], /* mainvol */ snNoteOff, 127); break; case 0x9: /* Note on */ SetNote( First&15, /* chn */ MIDI.Tracks[Track][posi+0], /* note */ MIDI.Tracks[Track][posi+1], /* volume */ MIDI.MainVol[First&15], /* mainvol */ snNoteOn, Chan[First&15].exp); break; case 0xA: /* Key after-touch */ SetNote( First&15, /* chn */ MIDI.Tracks[Track][posi+0], /* note */ MIDI.Tracks[Track][posi+1], /* volume */ MIDI.MainVol[First&15], /* mainvol */ snNoteModify, Chan[First&15].exp); break; case 0xC: /* Patch change */ MIDI.Patch[First&15] = MIDI.Tracks[Track][posi]; break; case 0xD: /* Channel after-touch */ break; case 0xE: /* Wheel - 0x2000 = no change */ a = MIDI.Tracks[Track][posi+0] | MIDI.Tracks[Track][posi+1] << 7; SetNote(First&15, -1, 0,0, /* FIXME: these are fucked up, see below */ //(int)((long)a*MIDI.PitchSense[First&15]/2 - 0x2000L), Chan[First&15].exp); //(short)(((long)a - 0x2000L) *(double)MIDI.PitchSense[First&15]/8192.0), Chan[First&15].exp); (short)((long)a - 0x2000L), Chan[First&15].exp); break; case 0xB: /* Controller change */ switch(MIDI.Tracks[Track][posi+0]) { case 123: /* All notes off on channel */ SetNote(First&15, -1,0,0, snNoteOff, 127); break; case 121: /* Reset vibrato and bending */ MIDI.PitchSense[First&15] = 2; SetNote(First&15, -1,0,0, 0, Chan[First&15].exp); /* last 0=bend 0 */ break; case 7: MIDI.MainVol[First&15] = MIDI.Tracks[Track][posi+1]; SetNote(First&15, -1, 0, MIDI.MainVol[First&15], snNoteUpdate, Chan[First&15].exp); /* FIXME: why doesn't this work? */ //for(a = 0; a < 16; a++) // SetNote(a, -1, 0, MIDI.MainVol[First&15], snNoteUpdate, Chan[First&15].exp); break; case 64: if(verbose) fprint(2, "ctl 64: sustain unsupported\n"); break; case 1: if(verbose) fprint(2, "ctl 1: vibrato unsupported\n"); break; case 91: if(verbose) fprint(2, "ctl 91 %ud: reverb depth unsupported\n", MIDI.Tracks[Track][posi+1]); break; case 93: if(verbose) fprint(2, "ctl 93 %ud: chorus depth unsupported\n", MIDI.Tracks[Track][posi+1]); break; case 0x06: /* Pitch bender sensitivity */ /* FIXME: the fuck is this? whatever it is, * it's broken */ MIDI.PitchSense[First&15] = MIDI.Tracks[Track][posi+1]; break; case 0x0a: /* Pan */ MIDI.Pan[First&15] = MIDI.Tracks[Track][posi+1]; break; case 0: /* FIXME: unimplemented: select bank */ if(verbose) fprint(2, "ctl 0 %ud: bank select unsupported\n", MIDI.Tracks[Track][posi+1]); break; case 0x0b: /* FIXME: probably bullshizzles */ SetNote(First&15, -1,0,MIDI.MainVol[First&15], snNoteUpdate, MIDI.Tracks[Track][posi+1]); break; default: if(verbose) fprint(2, "unknown ctl %ud: %ux\n", MIDI.Tracks[Track][posi+0], MIDI.Tracks[Track][posi+1]); } break; } } } /* Return value: 0=ok, -1=user break */ static int play(void) { int a, NotFirst, Userbreak=0; long Viivetta; if((MIDI.Waiting = mallocz(MIDI.TrackCount * sizeof *MIDI.Waiting, 1)) == nil || (MIDI.sWaiting = mallocz(MIDI.TrackCount * sizeof *MIDI.sWaiting, 1)) == nil || (MIDI.SWaiting = mallocz(MIDI.TrackCount * sizeof *MIDI.SWaiting, 1)) == nil || (MIDI.Posi = mallocz(MIDI.TrackCount * sizeof *MIDI.Posi, 1)) == nil || (MIDI.sPosi = mallocz(MIDI.TrackCount * sizeof *MIDI.sPosi, 1)) == nil || (MIDI.SPosi = mallocz(MIDI.TrackCount * sizeof *MIDI.SPosi, 1)) == nil || (MIDI.Running = mallocz(MIDI.TrackCount * sizeof *MIDI.Running, 1)) == nil || (MIDI.sRunning = mallocz(MIDI.TrackCount * sizeof *MIDI.sRunning, 1)) == nil || (MIDI.SRunning = mallocz(MIDI.TrackCount * sizeof *MIDI.SRunning, 1)) == nil) sysfatal("mallocz: %r"); for(a=0; a<MIDI.TrackCount; a++) { ulong c = 0; MIDI.sWaiting[a]= GetVarLen(a, &c); MIDI.sRunning[a]= 0; MIDI.sPosi[a] = c; } for(a=0; a<16; a++) { MIDI.Pan[a] = 64; /* Middle */ MIDI.Patch[a] = 1; /* Piano */ MIDI.PitchSense[a] = 2; /* � seminotes */ MIDI.MainVol[a] = 127; MIDI.Bend[a] = 0; } NotFirst = 0; //ReLoop: for(a=0; a<MIDI.TrackCount; a++) { MIDI.Posi[a] = MIDI.sPosi[a]; MIDI.Waiting[a] = MIDI.sWaiting[a]; MIDI.Running[a] = MIDI.sRunning[a]; } MIDI.Tempo = MIDI.initempo; memset(Chan, 0, sizeof Chan); for(a = 0; a < nelem(Chan); a++){ Chan[a].exp = 0x7f; } memset(MIDI.Used, 0, sizeof MIDI.Used); Viivetta = 0; for(;;) { int Fin, Act; if(NotFirst) { long Lisa = MIDI.Tempo/MIDI.DeltaTicks; /* tempo = microseconds per quarter note * deltaticks = number of delta-time ticks per quarter note * * So, when tempo = 200000 * and deltaticks = 10, * then 10 ticks have 200000 microseconds. * 20 ticks have 400000 microseconds. * When deltaticks = 5, * then 10 ticks have 40000 microseconds. */ Viivetta += Lisa; if(Viivetta >= MINDELAY) { mix(Viivetta); Viivetta = 0; } AnalyzeRow(); } else { u32int b = 0xFFFFFFFFUL; /* Find the smallest delay */ for(a=0; a<MIDI.TrackCount; a++) if(MIDI.Waiting[a] < b) b = MIDI.Waiting[a]; /* Elapse that delay from all tracks */ for(a=0; a<MIDI.TrackCount; a++) MIDI.Waiting[a] -= b; } /* Let the notes on channels become older (Age++) only if * * something happens. This way, we don't overflow the ages * * too easily. */ for(Act=a=0; a<MAXS3MCHAN; a++) if(MIDI.Waiting[a]<=1 && MIDI.Posi[a]<MIDI.TrackLen[a]) Act++; for(a=0; a<MAXS3MCHAN; a++) if(!Chan[a].Age||Act!=0) Chan[a].Age++; for(a=0; a<MIDI.TrackCount; ++a) { MIDI.SPosi[a] = MIDI.Posi[a]; MIDI.SWaiting[a] = MIDI.Waiting[a]; MIDI.SRunning[a] = MIDI.Running[a]; } for(Fin=1, a=0; a<MIDI.TrackCount; a++) { if(MIDI.Waiting[a] > 0)MIDI.Waiting[a]--; if(MIDI.Posi[a] < MIDI.TrackLen[a])Fin=0; /* While this track has events that we should have handled */ while(MIDI.Waiting[a]<=0 && MIDI.Posi[a]<MIDI.TrackLen[a]) { ulong pos; u8int b = MIDI.Tracks[a][MIDI.Posi[a]]; if(b < 128) b = MIDI.Running[a]; else { MIDI.Posi[a]++; if(b < 0xF0)MIDI.Running[a] = b; } pos = MIDI.Posi[a]; //fprint(2, "b %ux\n", b); if(b == 0xFF) { int ls=0; ulong len; u8int typi = MIDI.Tracks[a][MIDI.Posi[a]++]; len = (ulong)GetVarLen(a, &MIDI.Posi[a]); if(typi == 6) /* marker */ if(!strncmp((char *)(MIDI.Tracks[a]+MIDI.Posi[a]), "loopStart", len)) { if(verbose) fprint(2, "Found loopStart\n"); ls=1; } MIDI.Posi[a] += len; if(ls)goto SaveLoopStart; } else if(b==0xF7 || b==0xF0) { MIDI.Posi[a] += (ulong)GetVarLen(a, &MIDI.Posi[a]); } else if(b == 0xF3)MIDI.Posi[a]++; else if(b == 0xF2)MIDI.Posi[a]+=2; else if(b>=0xC0 && b<=0xDF)MIDI.Posi[a]++; else if(b>=0x80 && b<=0xEF) { MIDI.Posi[a]+=2; if(b>=0x90 && b<=0x9F && !NotFirst) { int c; SaveLoopStart: NotFirst=1; /* Save the starting position for looping */ for(c=0; c<MIDI.TrackCount; c++) { MIDI.sPosi[c] = MIDI.SPosi[c]; MIDI.sWaiting[c] = MIDI.SWaiting[c]; MIDI.sRunning[c] = MIDI.SRunning[c]; } MIDI.initempo = MIDI.Tempo; } } Tavuja(a, b, pos, MIDI.Posi[a]-pos); if(MIDI.Posi[a] < MIDI.TrackLen[a]) MIDI.Waiting[a] += GetVarLen(a, &MIDI.Posi[a]); } } if(Fin) break; } write(afd, abuf, ap-abuf); return Userbreak; } static void initinst(void) { int m, i; InternalSample *t, *p; m = sizeof(adl) / 14; if((t = calloc(m, sizeof *t)) == nil) sysfatal("initinst: %r"); p = t; /* FIXME: .D needs to be uchar(?) */ /* FIXME: use char adl[][14] rather than this */ /* FIXME: find out where the values are from */ for(i=0; i<m; i++){ p->D[0] = adl[i * 14 + 3]; p->D[1] = adl[i * 14 + 9]; p->D[2] = adl[i * 14 + 4]; p->D[3] = adl[i * 14 + 10]; p->D[4] = adl[i * 14 + 5]; p->D[5] = adl[i * 14 + 11]; p->D[6] = adl[i * 14 + 6]; p->D[7] = adl[i * 14 + 12]; p->D[8] = adl[i * 14 + 7] & 3; p->D[9] = adl[i * 14 + 13] & 3; p->D[10]= adl[i * 14 + 8]; mdat.Instr[i] = p++; } } /* FIXME: get8/16/32 approach is better */ /* FIXME: use bio.h */ static void eread(int fd, void *buf, int n) { if(readn(fd, buf, n) < 0) sysfatal("read: %r"); } static void readmid(char *mid) { int i, fd; uint n; uchar id[4]; fd = 0; if(mid != nil && (fd = open(mid, OREAD)) < 0) sysfatal("open: %r"); /* FIXME: get8/16/32 better; Conv shit is also stupid */ /* FIXME: also, don't read file into memory but use bio? */ eread(fd, id, 4); if(memcmp(id, "MThd", 4) != 0) sysfatal("invalid header"); eread(fd, id, 4); if(ConvL(id) != 6) sysfatal("invalid midi file"); eread(fd, id, 2); MIDI.Fmt = ConvI(id); eread(fd, id, 2); MIDI.TrackCount = ConvI(id); eread(fd, id, 2); MIDI.DeltaTicks = ConvI(id); MIDI.TrackLen = calloc(MIDI.TrackCount, sizeof *MIDI.TrackLen); if(MIDI.TrackLen == nil) sysfatal("calloc len %ux %ux: %r", MIDI.TrackCount, sizeof *MIDI.TrackLen); MIDI.Tracks = calloc(MIDI.TrackCount, sizeof *MIDI.Tracks); if(MIDI.Tracks == nil) sysfatal("calloc trs %ux %ux: %r", MIDI.TrackCount, sizeof *MIDI.Tracks); for(i = 0; i < MIDI.TrackCount; i++){ eread(fd, id, 4); if(memcmp(id, "MTrk", 4) != 0) sysfatal("invalid track"); eread(fd, id, 4); n = ConvL(id); MIDI.TrackLen[i] = n; if((MIDI.Tracks[i] = mallocz(n, 1)) == nil) sysfatal("mallocz %ux: %r", n); eread(fd, MIDI.Tracks[i], n); } close(fd); MIDI.initempo = 150000; } static void usage(void) { print("%s [-cd] [midfile]\n", argv0); exits("usage"); } void main(int argc, char **argv) { int i; ARGBEGIN{ case 'c': afd = 1; break; case 'd': verbose++; break; default: usage(); }ARGEND readmid(*argv); initinst(); for(i = 0; i < NCHIPS; i++) chip[i] = ym3812_init(1789772*2, RATE); if(afd < 0 && (afd = open("/dev/audio", OWRITE)) < 0) sysfatal("open: %r"); ap = abuf; play(); for(i = 0; i < NCHIPS; i++) ym3812_shutdown(chip[i]); exits(nil); }