/* instrument layer */ #include /*(fprintf)*/ #include /*(rand)*/ #include "defs.h" /*->inst.h (u8,u16x,...)*/ #include "inst.h" /*SampleInfo*/ #include "hirev.h" /*InstHirevInfo*/ #include "mem.h" /*(memPerm)*/ /* note-period table */ #define NOTE_MAX (12*8-1) /* 8octave */ static i15 *note; #define shift(x,n) ((n) >= 0? (x) << (n) : (x) >> -(n)) /* x * 2**n */ static void makeNoteTable(void) { static i15x oct4[] = { 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 907 }; i15x i; note = memPerm((NOTE_MAX+1) * sizeof(*note)); for (i = 0; i <= NOTE_MAX; i++) { note[i] = shift(oct4[i%12], 4-i/12); /*printf(" %d", note[i]); if (i%12 == 11) printf("\n");*/ } } /* period -> nearest note */ static i15x normalizePeriod(i15x *pp) { i15x p,i,s; p = *pp; if (p >= note[0]) { *pp = note[0]; return 0; } if (p <= note[NOTE_MAX]) { *pp = note[NOTE_MAX]; return NOTE_MAX; } for (i=0, s=64; s > 0; s /= 2) /* 64: =2**n, and 64*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 */ *pp = note[i]; return i; } typedef struct { i15x type;/* sine,ramp,square,... */ i15x noRetrig; i15x phase; i15x d; /* phase accumlates this for each frame */ #if 0 i15x depth0;/* org * table[phase] * depth/MAX_IN_TABLE = cur */ i15x depth1;/* 0: phase<32, 1: phase>=32 */ #else i15x depth; /* org * table[phase] * depth/MAX_IN_TABLE = cur */ #endif } ModulateInfo; /*static i15x frameLen;*/ /* output / frame */ /* output rate */ static u16x outRate; /* output clock rate in Hz */ static u32x mclk; #define MCLK0 ((u32x)3579545*4) void instOutRate(u16x or) { outRate = or; /* MCLK0 * 65536 / outRate.. */ mclk = MCLK0 / or * 65536 + MCLK0 % or * 65536 / or; instHirevSetOutRate(or); } typedef struct { InstHirevInfo hirev; /* for hirev loop */ struct { i15x cur; /* current period */ i15x org; /* original period */ i15x note; /* note */ i15x notePer; /* note period */ i31x mclk; /* master clock in Hz (c4spd * 1712) */ /*u16x hz;*/ /* current hz = mclk/period */ /*i31x hzAcc;*/ /* hz accumlator */ struct { i15x speed; /* added/sub'ed for each frame */ i15x fine; /* fine slide */ } slide; struct { i15x speed; i15x glissando; i15x nextNote; /* (glissando) next note */ } port; /* portamento */ ModulateInfo mod; struct { i15x baseNote; i15x plus[2];/* baseNote + plus[n] halfnote is played */ } rpgo; /* arpeggio */ struct { i15x n; /*i15x new;*/ i15x newNote; } delay; } per; /* period */ struct { i15x cur; /* current volume */ i15x org; i15x pan; /* 0(left)..64(right) */ struct { i15x d; /* added for each frame */ i15x mul; /* multiply for each frame */ i15x div; /* divide for each frame */ i15x nthFrame; /* slide every nth frame */ i15x fine; /* fine slide */ i15x count; /* frame counter */ } slide; ModulateInfo mod; struct { i15x onOff; i15x count; i15x onTime; i15x offTime; } tremor; struct { i15x n; i15x new; } delay; } vol; struct { i15x cur; /* current frame */ i15x count; /* decrement counter, 0 starts next frame */ } frame; struct { /*const u8 *cur;*/ struct { i15x nthFrame; /* retrig every nth frame */ i15x count; /* frame counter */ } retrig; /*SampleInfo si;*/ const SampleInfo *sip; const SampleInfo *newSip; i15x c4spd; struct { i15x n; const SampleInfo *sip; } delay; i15x cutFrame; } smp; struct { #define PFW_MAX 3 /* note delay = note delay + sample delay + vol delay */ void (*func[PFW_MAX])(void); i15x n; } pfw; /* per frame works */ } InstInfo; static InstInfo *instBank; static InstInfo *instp; /* select channel */ void instSelectCh(i15x ch) { instp = &instBank[ch]; } /* per frame work */ void instClearPFW(void) { instp->pfw.n = 0; } void instDoPerFrameWorks(i15x frame) { i15x i; instp->frame.cur = frame; for (i = 0; i < instp->pfw.n; i++) (*instp->pfw.func[i])(); } static void addPerFrameWork(void (*f)(void)) { if (instp->pfw.n >= PFW_MAX) { fprintf(stderr, "Too many PFWs\n"); exit(1); } instp->pfw.func[instp->pfw.n++] = f; } /* hirev loop */ void instLoop(void) { instHirevLoop(&instp->hirev); } /* initialize */ void instInit(void) { i15x i; static SampleInfo si0; instHirevInit(); makeNoteTable(); instBank = memPerm(32 * sizeof(InstInfo)); si0.beg = si0.end = NULL; si0.c4spd = 8363; si0.mag = 1; for (i = 0; i < 32; i++) { /* prepare for "note w/o inst and volume" */ instBank[i].smp.sip = instBank[i].smp.newSip = &si0; instBank[i].smp.c4spd = 8363; /* dope.mod ch14 begins with GF0 */ instBank[i].hirev.end = instBank[i].hirev.ptr = NULL; instBank[i].vol.slide.div = 1; } } /* frame */ #if 0 void instFrameLen(i15x len) { /*frameLen = len;*/ instHirevSetFrameLen(len); } #endif #if 0 void instStartRow(void) { #if 0 instp->frame.count = frameLen + 1; /* longer by one */ instp->frame.cur = 0; #endif } #endif /* set period */ /*#define MCLK (3579545*4)*/ static void setW(void) { instp->hirev.w = (mclk * instp->smp.sip->mag) / (instp->per.cur < 16? 16 : instp->per.cur); } /*#define noteToPeriod(n) ((i31x)note[(n)] * 8363 / instp->smp.sip->c4spd)*/ #define noteToPeriod(n) ((i31x)note[(n)] * 8363 / instp->smp.c4spd) static void setPeriod(void) { if (instp->smp.sip != instp->smp.newSip) { /* actural sample switching is carried out here */ instp->smp.sip = instp->smp.newSip; instp->hirev.end = instp->smp.sip->end; instp->hirev.loopBeg = instp->smp.sip->loopBeg; instp->hirev.xor = instp->smp.sip->xor; } instp->per.note = instp->per.delay.newNote; instp->per.cur = instp->per.org = instp->per.notePer = noteToPeriod(instp->per.note); instp->hirev.ptr = instp->smp.sip->beg; /* key-on */ instp->hirev.wAcc = 0; instp->hirev.fadeout = 0; if (!instp->per.mod.noRetrig) instp->per.mod.phase = 0; if (!instp->vol.mod.noRetrig) instp->vol.mod.phase = 0; setW(); } static void setPeriodPFW(void) { if (instp->per.delay.n == instp->frame.cur) setPeriod(); } #if 0 void instPeriod(i15x p, i15x delay) { instp->per.delay.new = p; if (delay == 0) setPeriod(); else { instp->per.delay.n = delay; addPerFrameWork(setPeriodPFW); } } #endif void instNote(i15x n, i15x delay) { instp->per.delay.newNote = n; if (delay == 0) setPeriod(); else { instp->per.delay.n = delay; addPerFrameWork(setPeriodPFW); } } /* set volume */ static i15x mono; void instMono(i15x n) { mono = n; } static void setHirevVol(void) { if (mono) { instp->hirev.volL = instp->vol.cur; return; } /* currently linear, which makes front sounds rather weaker */ if (instp->vol.pan >= 0) { instp->hirev.volL = instp->vol.cur * (64 - instp->vol.pan)/64; instp->hirev.volR = instp->vol.cur * instp->vol.pan/64; } else { /* surround!! */ instp->hirev.volL = instp->vol.cur / 2; instp->hirev.volR = -instp->vol.cur / 2; } } static void setVol(void) { instp->vol.cur = instp->vol.org = instp->vol.delay.new; setHirevVol(); } static void setVolPFW(void) { if (instp->vol.delay.n == instp->frame.cur) setVol(); } void instVol(i15x v, i15x delay) { instp->vol.delay.new = v > 64? 64: v; if (delay == 0) setVol(); else { instp->vol.delay.n = delay; addPerFrameWork(setVolPFW); } } /* tuning */ void instTuning(i15x c4spd) { instp->smp.c4spd = c4spd; } /* set sample */ static void setSample() { /* actual sample switching is not done here.. */ instp->smp.newSip = instp->smp.delay.sip; /* set smp's default vol.. */ instp->vol.cur = instp->vol.org = instp->smp.newSip->vol; /* set smp's c4spd.. */ instp->smp.c4spd = instp->smp.newSip->c4spd; setHirevVol(); } static void setSamplePFW(void) { if (instp->smp.delay.n == instp->frame.cur) setSample(); } void instSample(const SampleInfo *sip, i15x delay) { instp->smp.delay.sip = sip; if (delay) { instp->smp.delay.n = delay; addPerFrameWork(setSamplePFW); } else setSample(); } /* volume slide */ #define limitVol(v) \ {\ if ((v) > 64) (v) = 64; \ else if ((v) < 0) (v) = 0; \ } static i15x fastVolSlide; static void volSlidePFW(void) { if (!fastVolSlide && !instp->frame.cur) return; /* skip frame 0 */ if (--instp->vol.slide.count <= 0) { instp->vol.slide.count = instp->vol.slide.nthFrame; instp->vol.cur = instp->vol.cur * instp->vol.slide.mul / instp->vol.slide.div + instp->vol.slide.d; limitVol(instp->vol.cur); setHirevVol(); } } void instVolSlide(void) { if (instp->vol.slide.fine) { instp->vol.cur = instp->vol.cur * instp->vol.slide.mul / instp->vol.slide.div + instp->vol.slide.d; limitVol(instp->vol.cur); setHirevVol(); } else addPerFrameWork(volSlidePFW); } void instSetVolSlideParams(i15x d, i15x mul, i15x div, i15x nthFrame, i15x fine) { instp->vol.slide.d = d; instp->vol.slide.mul = mul; instp->vol.slide.div = div; instp->vol.slide.nthFrame = instp->vol.slide.count = nthFrame; instp->vol.slide.fine = fine; } void instSetVolSlideFast(i15x onOff) { fastVolSlide = onOff; } /* period slide */ /*#define noteOff() { instp->hirev.ptr = NULL; }*/ #define noteOff() { instp->hirev.fadeout = 256; } /* eventually note-off */ static i15x amigaLimit; static void limitPeriod(void) { #define p instp->per.cur if (amigaLimit) { if ((p) > note[3*12]) (p) = note[3*12]; else if ((p) < note[5*12+11]) (p) = note[5*12+11]; } else { if ((p) > 32000) (p) = 32000; else if ((p) < 0) { (p) = 0; noteOff(); /*panic.s3m needs this*/ } } #undef p } static void periodSlideUpPFW(void) { if (!instp->frame.cur) return; /* skip frame 0 */ instp->per.cur -= instp->per.slide.speed; /* ignore per.org */ limitPeriod(/*instp->per.cur*/); instp->per.org = instp->per.cur; setW(); } static void periodSlideDownPFW(void) { if (!instp->frame.cur) return; /* skip frame 0 */ instp->per.cur += instp->per.slide.speed; limitPeriod(/*instp->per.cur*/); instp->per.org = instp->per.cur; setW(); } void instPeriodSlideUp(void) { if (instp->per.slide.fine) { instp->per.cur -= instp->per.slide.speed; limitPeriod(/*instp->per.cur*/); instp->per.org = instp->per.cur; setW(); } else addPerFrameWork(periodSlideUpPFW); } void instPeriodSlideDown(void) { if (instp->per.slide.fine) { instp->per.cur += instp->per.slide.speed; limitPeriod(/*instp->per.cur*/); instp->per.org = instp->per.cur; setW(); } else addPerFrameWork(periodSlideDownPFW); } void instSetPeriodSlideParams(i15x speed, i15x fine) { instp->per.slide.speed = speed; instp->per.slide.fine = fine; } void instSetPeriodAmigaLimit(i15x onOff) { amigaLimit = onOff; } /* portamento */ static void portamentoPFW(void) { if (!instp->frame.cur) return; /* which S3M slides at frame 0? */ if (instp->per.org > instp->per.notePer) { /* port up now */ instp->per.org -= instp->per.port.speed; if (instp->per.org < instp->per.notePer) instp->per.cur = instp->per.org = instp->per.notePer; else { instp->per.cur = instp->per.org; if (instp->per.port.glissando) normalizePeriod(&instp->per.cur); } } else { /* port down now */ instp->per.org += instp->per.port.speed; if (instp->per.org > instp->per.notePer) instp->per.cur = instp->per.org = instp->per.notePer; else { instp->per.cur = instp->per.org; if (instp->per.port.glissando) normalizePeriod(&instp->per.cur); } } setW(); } void instPortamento(void) { addPerFrameWork(portamentoPFW); } void instSetPortamentoTo(i15x to) { instp->per.note = to; instp->per.notePer = noteToPeriod(to); } void instSetPortamentoSpeed(i15x speed) { instp->per.port.speed = speed; } void instSetPortamentoDefaultVol(void) { instp->vol.cur = instp->vol.org = instp->smp.sip->vol; setHirevVol(); } void instSetPortamentoGlissando(i15x onOff) { instp->per.port.glissando = onOff; } /* arpeggio */ static void arpeggioPFW(void) { if (instp->frame.cur % 3) { instp->per.cur = note[instp->per.note + instp->per.rpgo.plus[instp->frame.cur%3 - 1]]; } else instp->per.cur = instp->per.notePer; setW(); } void instArpeggio(void) { addPerFrameWork(arpeggioPFW); } void instSetArpeggioParams(i15x plus1, i15x plus2) { instp->per.rpgo.plus[0] = plus1; instp->per.rpgo.plus[1] = plus2; } /* retrig */ static void retrigPFW(void) { if (--instp->smp.retrig.count <= 0) { instp->smp.retrig.count = instp->smp.retrig.nthFrame; instp->hirev.ptr = instp->smp.sip->beg; setW(); } } void instRetrig(void) { addPerFrameWork(retrigPFW); } void instSetRetrigParam(i15x nthFrame) { instp->smp.retrig.nthFrame = nthFrame; instp->smp.retrig.count = 0; } /* sample offset */ void instSampleOffset(i31x offset) { instp->hirev.ptr = instp->smp.sip->beg + offset * instp->smp.sip->mag; if (instp->hirev.ptr >= instp->hirev.end) { if (instp->hirev.loopBeg) { instp->hirev.ptr = instp->hirev.loopBeg + (instp->hirev.ptr - instp->hirev.end) % (instp->hirev.end - instp->hirev.loopBeg); } else { noteOff(); } } } /* modulation */ static u8 sine[] = { 000, 25, 50, 74, 98, 120, 142, 162, 180, 197, 212, 225, 236, 244, 250, 254, 255 }; /* sin(x), 0 <= x < pi/4, max = 255 */ static i15x wave(const ModulateInfo *mip) { i15x i; switch (mip->type) { case 1: /* ramp up (period: down) */ i = (255 * 2 * mip->phase)/63 - 255; break; case 2: /* square */ i = (mip->phase < 32)? 255 : 0; /* yes, not 255/-255 */ break; default: /* sine */ if (mip->phase < 16) i = sine[mip->phase]; else if (mip->phase < 32) i = sine[32 - mip->phase]; else if (mip->phase < 48) i = -sine[mip->phase - 32]; else i = -sine[64 - mip->phase]; } return mip->depth * i / 255; } static void vibratoPFW(void) { if (!instp->frame.cur) return; /* skip frame 0 */ instp->per.mod.phase += instp->per.mod.d; instp->per.mod.phase %= 64; /* per.org: no change.. */ instp->per.cur = instp->per.org + wave(&instp->per.mod); limitPeriod(/*instp->per.cur*/); setW(); } void instVibrato(void) { addPerFrameWork(vibratoPFW); } /* depth = period */ void instSetVibratoParams(i15x d, i15x depth) { if (d) instp->per.mod.d = d; instp->per.mod.depth = depth; /*if (!instp->per.mod.noRetrig) instp->per.mod.phase = 0;*/ } void instSetVibratoWave(i15x type, i15x noRetrig) { if (type == 3) type = rand() % 3; instp->per.mod.type = type; instp->per.mod.noRetrig = noRetrig; } static void tremoloPFW(void) { if (!instp->frame.cur) return; /* skip frame 0 */ instp->vol.mod.phase += instp->vol.mod.d; instp->vol.mod.phase %= 64; instp->vol.cur = instp->vol.org + wave(&instp->vol.mod); limitVol(instp->vol.cur); setHirevVol(); } void instTremolo(void) { addPerFrameWork(tremoloPFW); } void instSetTremoloParams(i15x d, i15x depth) { if (d) instp->vol.mod.d = d; instp->vol.mod.depth = depth; /*if (!instp->vol.mod.noRetrig) instp->vol.mod.phase = 0;*/ } void instSetTremoloWave(i15x type, i15x noRetrig) { if (type == 3) type = rand() % 3; instp->vol.mod.type = type; instp->vol.mod.noRetrig = noRetrig; } /* note cut */ static void noteCutPFW(void) { /* said to be vol := 0, but ST3 seems to key-off */ if (instp->smp.cutFrame == instp->frame.cur) noteOff(); } void instNoteCut(i15x frame) { if (frame) { instp->smp.cutFrame = frame; addPerFrameWork(noteCutPFW); } else noteOff(); } /* tremor */ static void tremorPFW(void) { if (--instp->vol.tremor.count <= 0) { if (instp->vol.tremor.onOff) { instp->vol.cur = 0; setHirevVol(); instp->vol.tremor.onOff = 0; instp->vol.tremor.count = instp->vol.tremor.offTime; } else { instp->vol.cur = instp->vol.org; setHirevVol(); instp->vol.tremor.onOff = 1; instp->vol.tremor.count = instp->vol.tremor.onTime; } } } void instTremor(void) { addPerFrameWork(tremorPFW); } void instSetTremorParams(i15x onTime, i15x offTime) { instp->vol.tremor.onTime = onTime; instp->vol.tremor.offTime = offTime; instp->vol.tremor.count = 0; instp->vol.tremor.onOff = 0; } /* note off */ static void noteOffPFW(void) { if (instp->per.delay.n == instp->frame.cur) noteOff(); } void instNoteOff(i15x delay) { if (delay) { instp->per.delay.n = delay; addPerFrameWork(noteOffPFW); } else { noteOff(); } } i15x instIsNoteOff(void) { return instp->hirev.ptr == NULL; } /* pan position */ /* pos = 0(left)..64(right), -1(surround) */ void instPanPosition(i15x pos) { instp->vol.pan = pos; setHirevVol(); } /* empty command */ void instEmptyCmd(void) { instp->per.cur = instp->per.org; setW(); /* empty command has some special meanings... (when after glissando portamento or vibrato) */ }