/* MikMod sound library (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS for complete list. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*============================================================================== $Id$ The Protracker Player Driver The protracker driver supports all base Protracker 3.x commands and features. ==============================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifdef SRANDOM_IN_MATH_H #include #else #include #endif #include #include "mikmod_internals.h" #ifdef SUNOS extern int fprintf(int, const char *, ...); extern long int random(void); #endif /* The currently playing module */ MODULE *pf = NULL; #define NUMVOICES(mod) (md_sngchn < (mod)->numvoices ? md_sngchn : (mod)->numvoices) #define HIGH_OCTAVE 2 /* number of above-range octaves */ static const UWORD oldperiods[OCTAVE*2]={ 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80, 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0, 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160, 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700 }; static const UBYTE VibratoTable[32]={ 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253, 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24 }; static const UBYTE avibtab[128]={ 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23, 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44, 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58, 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63, 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59, 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46, 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25, 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1 }; /* Triton's linear periods to frequency translation table (for XM modules) */ static const ULONG lintab[768]={ 535232,534749,534266,533784,533303,532822,532341,531861, 531381,530902,530423,529944,529466,528988,528511,528034, 527558,527082,526607,526131,525657,525183,524709,524236, 523763,523290,522818,522346,521875,521404,520934,520464, 519994,519525,519057,518588,518121,517653,517186,516720, 516253,515788,515322,514858,514393,513929,513465,513002, 512539,512077,511615,511154,510692,510232,509771,509312, 508852,508393,507934,507476,507018,506561,506104,505647, 505191,504735,504280,503825,503371,502917,502463,502010, 501557,501104,500652,500201,499749,499298,498848,498398, 497948,497499,497050,496602,496154,495706,495259,494812, 494366,493920,493474,493029,492585,492140,491696,491253, 490809,490367,489924,489482,489041,488600,488159,487718, 487278,486839,486400,485961,485522,485084,484647,484210, 483773,483336,482900,482465,482029,481595,481160,480726, 480292,479859,479426,478994,478562,478130,477699,477268, 476837,476407,475977,475548,475119,474690,474262,473834, 473407,472979,472553,472126,471701,471275,470850,470425, 470001,469577,469153,468730,468307,467884,467462,467041, 466619,466198,465778,465358,464938,464518,464099,463681, 463262,462844,462427,462010,461593,461177,460760,460345, 459930,459515,459100,458686,458272,457859,457446,457033, 456621,456209,455797,455386,454975,454565,454155,453745, 453336,452927,452518,452110,451702,451294,450887,450481, 450074,449668,449262,448857,448452,448048,447644,447240, 446836,446433,446030,445628,445226,444824,444423,444022, 443622,443221,442821,442422,442023,441624,441226,440828, 440430,440033,439636,439239,438843,438447,438051,437656, 437261,436867,436473,436079,435686,435293,434900,434508, 434116,433724,433333,432942,432551,432161,431771,431382, 430992,430604,430215,429827,429439,429052,428665,428278, 427892,427506,427120,426735,426350,425965,425581,425197, 424813,424430,424047,423665,423283,422901,422519,422138, 421757,421377,420997,420617,420237,419858,419479,419101, 418723,418345,417968,417591,417214,416838,416462,416086, 415711,415336,414961,414586,414212,413839,413465,413092, 412720,412347,411975,411604,411232,410862,410491,410121, 409751,409381,409012,408643,408274,407906,407538,407170, 406803,406436,406069,405703,405337,404971,404606,404241, 403876,403512,403148,402784,402421,402058,401695,401333, 400970,400609,400247,399886,399525,399165,398805,398445, 398086,397727,397368,397009,396651,396293,395936,395579, 395222,394865,394509,394153,393798,393442,393087,392733, 392378,392024,391671,391317,390964,390612,390259,389907, 389556,389204,388853,388502,388152,387802,387452,387102, 386753,386404,386056,385707,385359,385012,384664,384317, 383971,383624,383278,382932,382587,382242,381897,381552, 381208,380864,380521,380177,379834,379492,379149,378807, 378466,378124,377783,377442,377102,376762,376422,376082, 375743,375404,375065,374727,374389,374051,373714,373377, 373040,372703,372367,372031,371695,371360,371025,370690, 370356,370022,369688,369355,369021,368688,368356,368023, 367691,367360,367028,366697,366366,366036,365706,365376, 365046,364717,364388,364059,363731,363403,363075,362747, 362420,362093,361766,361440,361114,360788,360463,360137, 359813,359488,359164,358840,358516,358193,357869,357547, 357224,356902,356580,356258,355937,355616,355295,354974, 354654,354334,354014,353695,353376,353057,352739,352420, 352103,351785,351468,351150,350834,350517,350201,349885, 349569,349254,348939,348624,348310,347995,347682,347368, 347055,346741,346429,346116,345804,345492,345180,344869, 344558,344247,343936,343626,343316,343006,342697,342388, 342079,341770,341462,341154,340846,340539,340231,339924, 339618,339311,339005,338700,338394,338089,337784,337479, 337175,336870,336566,336263,335959,335656,335354,335051, 334749,334447,334145,333844,333542,333242,332941,332641, 332341,332041,331741,331442,331143,330844,330546,330247, 329950,329652,329355,329057,328761,328464,328168,327872, 327576,327280,326985,326690,326395,326101,325807,325513, 325219,324926,324633,324340,324047,323755,323463,323171, 322879,322588,322297,322006,321716,321426,321136,320846, 320557,320267,319978,319690,319401,319113,318825,318538, 318250,317963,317676,317390,317103,316817,316532,316246, 315961,315676,315391,315106,314822,314538,314254,313971, 313688,313405,313122,312839,312557,312275,311994,311712, 311431,311150,310869,310589,310309,310029,309749,309470, 309190,308911,308633,308354,308076,307798,307521,307243, 306966,306689,306412,306136,305860,305584,305308,305033, 304758,304483,304208,303934,303659,303385,303112,302838, 302565,302292,302019,301747,301475,301203,300931,300660, 300388,300117,299847,299576,299306,299036,298766,298497, 298227,297958,297689,297421,297153,296884,296617,296349, 296082,295815,295548,295281,295015,294749,294483,294217, 293952,293686,293421,293157,292892,292628,292364,292100, 291837,291574,291311,291048,290785,290523,290261,289999, 289737,289476,289215,288954,288693,288433,288173,287913, 287653,287393,287134,286875,286616,286358,286099,285841, 285583,285326,285068,284811,284554,284298,284041,283785, 283529,283273,283017,282762,282507,282252,281998,281743, 281489,281235,280981,280728,280475,280222,279969,279716, 279464,279212,278960,278708,278457,278206,277955,277704, 277453,277203,276953,276703,276453,276204,275955,275706, 275457,275209,274960,274712,274465,274217,273970,273722, 273476,273229,272982,272736,272490,272244,271999,271753, 271508,271263,271018,270774,270530,270286,270042,269798, 269555,269312,269069,268826,268583,268341,268099,267857 }; #define LOGFAC 2*16 static const UWORD logtab[104]={ LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887, LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862, LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838, LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814, LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791, LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768, LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746, LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725, LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704, LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684, LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665, LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646, LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628, LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610, LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592, LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575, LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559, LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543, LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528, LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513, LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498, LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484, LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470, LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457, LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443, LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431 }; static const SBYTE PanbrelloTable[256]={ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23, -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2 }; /* returns a random value between 0 and ceil-1, ceil must be a power of two */ static int getrandom(int ceilval) { #if defined(HAVE_SRANDOM) && !defined(_MIKMOD_AMIGA) return random()&(ceilval-1); #else return (rand()*ceilval)/(RAND_MAX+1.0); #endif } /* New Note Action Scoring System : -------------------------------- 1) total-volume (fadevol, chanvol, volume) is the main scorer. 2) a looping sample is a bonus x2 3) a foreground channel is a bonus x4 4) an active envelope with keyoff is a handicap -x2 */ static int MP_FindEmptyChannel(MODULE *mod) { MP_VOICE *a; ULONG t,k,tvol,pp; for (t=0;tvoice[t].main.kick==KICK_ABSENT)|| (mod->voice[t].main.kick==KICK_ENV))&& Voice_Stopped_internal(t)) return t; tvol=0xffffffUL;t=-1;a=mod->voice; for (k=0;kmain.s) return k; if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) { pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0); if ((a->master)&&(a==a->master->slave)) pp<<=2; if (pp8000*7) return -1; return t; } static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2) { if ((p1==p2)||(p==p1)) return v1; return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1)); } UWORD getlinearperiod(UWORD note,ULONG fine) { UWORD t; t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1); return t; } static UWORD getlogperiod(UWORD note,ULONG fine) { UWORD n,o; UWORD p1,p2; ULONG i; n=note%(2*OCTAVE); o=note/(2*OCTAVE); i=(n<<2)+(fine>>4); /* n*8 + fine/16 */ p1=logtab[i]; p2=logtab[i+1]; return (Interpolate(fine>>4,0,15,p1,p2)>>o); } static UWORD getoldperiod(UWORD note,ULONG speed) { UWORD n,o; /* This happens sometimes on badly converted AMF, and old MOD */ if (!speed) { #ifdef MIKMOD_DEBUG fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note); #endif return 4242; /* <- prevent divide overflow.. (42 hehe) */ } n=note%(2*OCTAVE); o=note/(2*OCTAVE); return ((8363L*(ULONG)oldperiods[n])>>o)/speed; } static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed) { if (flags & UF_XMPERIODS) { if (flags & UF_LINEAR) return getlinearperiod(note, speed); else return getlogperiod(note, speed); } else return getoldperiod(note, speed); } static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b) { return (Interpolate(p,a->pos,b->pos,a->val,b->val)); } static SWORD DoPan(SWORD envpan,SWORD pan) { int newpan; newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128); return (newpanPAN_RIGHT?PAN_RIGHT:newpan); } static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff) { t->flg=flg; t->pts=pts; t->susbeg=susbeg; t->susend=susend; t->beg=beg; t->end=end; t->env=p; t->p=0; t->a=0; t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1; if (!t->pts) { /* FIXME: bad/crafted file. better/more general solution? */ t->b=0; return t->env[0].val; } /* Imago Orpheus sometimes stores an extra initial point in the envelope */ if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) { t->a++; t->b++; } /* Fit in the envelope, still */ if (t->a >= t->pts) t->a = t->pts - 1; if (t->b >= t->pts) t->b = t->pts-1; return t->env[t->a].val; } /* This procedure processes all envelope types, include volume, pitch, and panning. Envelopes are defined by a set of points, each with a magnitude [relating either to volume, panning position, or pitch modifier] and a tick position. Envelopes work in the following manner: (a) Each tick the envelope is moved a point further in its progression. For an accurate progression, magnitudes between two envelope points are interpolated. (b) When progression reaches a defined point on the envelope, values are shifted to interpolate between this point and the next, and checks for loops or envelope end are done. Misc: Sustain loops are loops that are only active as long as the keyoff flag is clear. When a volume envelope terminates, so does the current fadeout. */ static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v) { if (t->flg & EF_ON) { UBYTE a, b; /* actual points in the envelope */ UWORD p; /* the 'tick counter' - real point being played */ a = t->a; b = t->b; p = t->p; /* * Sustain loop on one point (XM type). * Not processed if KEYOFF. * Don't move and don't interpolate when the point is reached */ if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend && (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) { v = t->env[t->susbeg].val; } else { /* * All following situations will require interpolation between * two envelope points. */ /* * Sustain loop between two points (IT type). * Not processed if KEYOFF. */ /* if we were on a loop point, loop now */ if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) && a >= t->susend) { a = t->susbeg; b = (t->susbeg==t->susend)?a:a+1; p = t->env[a].pos; v = t->env[a].val; } else /* * Regular loop. * Be sure to correctly handle single point loops. */ if ((t->flg & EF_LOOP) && a >= t->end) { a = t->beg; b = t->beg == t->end ? a : a + 1; p = t->env[a].pos; v = t->env[a].val; } else /* * Non looping situations. */ if (a != b) v = InterpolateEnv(p, &t->env[a], &t->env[b]); else v = t->env[a].val; /* * Start to fade if the volume envelope is finished. */ if (p >= t->env[t->pts - 1].pos) { if (t->flg & EF_VOLENV) { aout->main.keyoff |= KEY_FADE; if (!v) aout->main.fadevol = 0; } } else { p++; /* did pointer reach point b? */ if (p >= t->env[b].pos) a = b++; /* shift points a and b */ } t->a = a; t->b = b; t->p = p; } } return v; } /* XM linear period to frequency conversion */ ULONG getfrequency(UWORD flags,ULONG period) { if (flags & UF_LINEAR) { SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE; if (shift >= 0) return lintab[period % 768] >> shift; else return lintab[period % 768] << (-shift); } else return (8363L*1712L)/(period?period:1); } /*========== Protracker effects */ static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style) { UBYTE note=a->main.note; if (a->arpmem) { switch (style) { case 0: /* mod style: N, N+x, N+y */ switch (tick % 3) { /* case 0: unchanged */ case 1: note += (a->arpmem >> 4); break; case 2: note += (a->arpmem & 0xf); break; } break; case 3: /* okt arpeggio 3: N-x, N, N+y */ switch (tick % 3) { case 0: note -= (a->arpmem >> 4); break; /* case 1: unchanged */ case 2: note += (a->arpmem & 0xf); break; } break; case 4: /* okt arpeggio 4: N, N+y, N, N-x */ switch (tick % 4) { /* case 0, case 2: unchanged */ case 1: note += (a->arpmem & 0xf); break; case 3: note -= (a->arpmem >> 4); break; } break; case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */ if (!tick) break; switch (tick % 3) { /* case 0: unchanged */ case 1: note -= (a->arpmem >> 4); break; case 2: note += (a->arpmem & 0xf); break; } break; } a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed); a->ownper = 1; } } static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)mod; (void)channel; dat = UniGetByte(); if (!tick) { if (!dat && (flags & UF_ARPMEM)) dat=a->arpmem; else a->arpmem=dat; } if (a->main.period) DoArpeggio(tick, flags, a, 0); return 0; } static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (!tick && dat) a->slidespeed = (UWORD)dat << 2; if (a->main.period) if (tick) a->tmpperiod -= a->slidespeed; return 0; } static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (!tick && dat) a->slidespeed = (UWORD)dat << 2; if (a->main.period) if (tick) a->tmpperiod += a->slidespeed; return 0; } static void DoToneSlide(UWORD tick, MP_CONTROL *a) { if (!a->main.fadevol) a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF; else a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT; if (tick != 0) { int dist; /* We have to slide a->main.period towards a->wantedperiod, so compute the difference between those two values */ dist=a->main.period-a->wantedperiod; /* if they are equal or if portamentospeed is too big ...*/ if (dist == 0 || a->portspeed > abs(dist)) /* ...make tmpperiod equal tperiod */ a->tmpperiod=a->main.period=a->wantedperiod; else if (dist>0) { a->tmpperiod-=a->portspeed; a->main.period-=a->portspeed; /* dist>0, slide up */ } else { a->tmpperiod+=a->portspeed; a->main.period+=a->portspeed; /* dist<0, slide down */ } } else a->tmpperiod=a->main.period; a->ownper = 1; } static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2; if (a->main.period) DoToneSlide(tick, a); return 0; } static void DoVibrato(UWORD tick, MP_CONTROL *a) { UBYTE q; UWORD temp = 0; /* silence warning */ if (!tick) return; q=(a->vibpos>>2)&0x1f; switch (a->wavecontrol&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* ramp down */ q<<=3; if (a->vibpos<0) q=255-q; temp=q; break; case 2: /* square wave */ temp=255; break; case 3: /* random wave */ temp=getrandom(256); break; } temp*=a->vibdepth; temp>>=7;temp<<=2; if (a->vibpos>=0) a->main.period=a->tmpperiod+temp; else a->main.period=a->tmpperiod-temp; a->ownper = 1; if (tick != 0) a->vibpos+=a->vibspd; } static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) { if (dat&0x0f) a->vibdepth=dat&0xf; if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; } if (a->main.period) DoVibrato(tick, a); return 0; } static void DoVolSlide(MP_CONTROL *a, UBYTE dat) { if (dat&0xf) { a->tmpvolume-=(dat&0x0f); if (a->tmpvolume<0) a->tmpvolume=0; } else { a->tmpvolume+=(dat>>4); if (a->tmpvolume>64) a->tmpvolume=64; } } static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (a->main.period) DoToneSlide(tick, a); if (tick) DoVolSlide(a, dat); return 0; } /* DoPTEffect6 after DoPTEffectA */ static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; UBYTE q; UWORD temp = 0; /* silence warning */ (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) { if (dat&0x0f) a->trmdepth=dat&0xf; if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; } if (a->main.period) { q=(a->trmpos>>2)&0x1f; switch ((a->wavecontrol>>4)&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* ramp down */ q<<=3; if (a->trmpos<0) q=255-q; temp=q; break; case 2: /* square wave */ temp=255; break; case 3: /* random wave */ temp=getrandom(256); break; } temp*=a->trmdepth; temp>>=6; if (a->trmpos>=0) { a->volume=a->tmpvolume+temp; if (a->volume>64) a->volume=64; } else { a->volume=a->tmpvolume-temp; if (a->volume<0) a->volume=0; } a->ownvol = 1; if (tick) a->trmpos+=a->trmspd; } return 0; } static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)tick; (void)flags; dat = UniGetByte(); if (mod->panflag) a->main.panning = mod->panning[channel] = dat; return 0; } static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) { if (dat) a->soffset=(UWORD)dat<<8; a->main.start=a->hioffset|a->soffset; if ((a->main.s)&&(a->main.start>(SLONG)a->main.s->length)) a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? a->main.s->loopstart:a->main.s->length; } return 0; } static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (tick) DoVolSlide(a, dat); return 0; } static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { if (a->main.period) DoVibrato(tick, a); DoPTEffectA(tick, flags, a, mod, channel); return 0; } static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)a; (void)channel; dat=UniGetByte(); if (tick || mod->patdly2) return 0; if (dat >= mod->numpos) { /* crafted file? */ /* fprintf(stderr,"DoPTEffectB: numpos=%d, dat=%d -> %d\n",mod->numpos,dat,mod->numpos-1);*/ dat=mod->numpos-1; } /* Vincent Voois uses a nasty trick in "Universal Bolero" */ if (dat == mod->sngpos && mod->patbrk == mod->patpos) return 0; if (!mod->loop && !mod->patbrk && (dat < mod->sngpos || (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) || (dat == mod->sngpos && (flags & UF_NOWRAP)) ) ) { /* if we don't loop, better not to skip the end of the pattern, after all... so: mod->patbrk=0; */ mod->posjmp=3; } else { /* if we were fading, adjust... */ if (mod->sngpos == (mod->numpos-1)) mod->volume=mod->initvolume>128?128:mod->initvolume; mod->sngpos=dat; mod->posjmp=2; mod->patpos=0; /* cancel the FT2 pattern loop (E60) bug. * also see DoEEffects() below for it. */ if (flags & UF_FT2QUIRKS) mod->patbrk=0; } return 0; } static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (tick) return 0; if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */ else if (dat>64) dat=64; a->tmpvolume=dat; return 0; } static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)a; (void)channel; dat=UniGetByte(); if ((tick)||(mod->patdly2)) return 0; if (dat && dat >= mod->numrow) { /* crafted file? */ /* fprintf(stderr,"DoPTEffectD: numrow=%d, dat=%d -> 0\n",mod->numrow,dat);*/ dat=0; } if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&& (dat>mod->pattrows[mod->positions[mod->sngpos]])) { dat=mod->pattrows[mod->positions[mod->sngpos]]; } mod->patbrk=dat; if (!mod->posjmp) { /* don't ask me to explain this code - it makes backwards.s3m and children.xm (heretic's version) play correctly, among others. Take that for granted, or write the page of comments yourself... you might need some aspirin - Miod */ if ((mod->sngpos==mod->numpos-1)&&(dat)&& ((mod->loop) || (mod->positions[mod->sngpos]==(mod->numpat-1) && !(flags&UF_NOWRAP)))) { mod->sngpos=0; mod->posjmp=2; } else mod->posjmp=3; } return 0; } static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel, UBYTE dat) { UBYTE nib = dat & 0xf; switch (dat>>4) { case 0x0: /* hardware filter toggle, not supported */ break; case 0x1: /* fineslide up */ if (a->main.period) if (!tick) a->tmpperiod-=(nib<<2); break; case 0x2: /* fineslide dn */ if (a->main.period) if (!tick) a->tmpperiod+=(nib<<2); break; case 0x3: /* glissando ctrl */ a->glissando=nib; break; case 0x4: /* set vibrato waveform */ a->wavecontrol&=0xf0; a->wavecontrol|=nib; break; case 0x5: /* set finetune */ if (a->main.period) { if (flags&UF_XMPERIODS) a->speed=nib+128; else a->speed=finetune[nib]; a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed); } break; case 0x6: /* set patternloop */ if (tick) break; if (nib) { /* set reppos or repcnt ? */ /* set repcnt, so check if repcnt already is set, which means we are already looping */ if (a->pat_repcnt) a->pat_repcnt--; /* already looping, decrease counter */ else { #if 0 /* this would make walker.xm, shipped with Xsoundtracker, play correctly, but it's better to remain compatible with FT2 */ if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE)) #endif a->pat_repcnt=nib; /* not yet looping, so set repcnt */ } if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */ if (a->pat_reppos==POS_NONE) a->pat_reppos=mod->patpos-1; if (a->pat_reppos==-1) { mod->pat_repcrazy=1; mod->patpos=0; } else mod->patpos=a->pat_reppos; } else a->pat_reppos=POS_NONE; } else { a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */ /* emulate the FT2 pattern loop (E60) bug: * http://milkytracker.org/docs/MilkyTracker.html#fxE6x * roadblas.xm plays correctly with this. */ if (flags & UF_FT2QUIRKS) mod->patbrk=mod->patpos; } break; case 0x7: /* set tremolo waveform */ a->wavecontrol&=0x0f; a->wavecontrol|=nib<<4; break; case 0x8: /* set panning */ if (mod->panflag) { if (nib<=8) nib<<=4; else nib*=17; a->main.panning=mod->panning[channel]=nib; } break; case 0x9: /* retrig note */ /* do not retrigger on tick 0, until we are emulating FT2 and effect data is zero */ if (!tick && !((flags & UF_FT2QUIRKS) && (!nib))) break; /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */ if (nib || !tick) { if (!a->retrig) { /* when retrig counter reaches 0, reset counter and restart the sample */ if (a->main.period) a->main.kick=KICK_NOTE; a->retrig=nib; } a->retrig--; /* countdown */ } break; case 0xa: /* fine volume slide up */ if (tick) break; a->tmpvolume+=nib; if (a->tmpvolume>64) a->tmpvolume=64; break; case 0xb: /* fine volume slide dn */ if (tick) break; a->tmpvolume-=nib; if (a->tmpvolume<0)a->tmpvolume=0; break; case 0xc: /* cut note */ /* When tick reaches the cut-note value, turn the volume to zero (just like on the amiga) */ if (tick>=nib) a->tmpvolume=0; /* just turn the volume down */ break; case 0xd: /* note delay */ /* delay the start of the sample until tick==nib */ if (!tick) a->main.notedelay=nib; else if (a->main.notedelay) a->main.notedelay--; break; case 0xe: /* pattern delay */ if (!tick) if (!mod->patdly2) mod->patdly=nib+1; /* only once, when tick=0 */ break; case 0xf: /* invert loop, not supported */ break; } } static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { DoEEffects(tick, flags, a, mod, channel, UniGetByte()); return 0; } static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)a; (void)channel; dat=UniGetByte(); if (tick||mod->patdly2) return 0; if (mod->extspd&&(dat>=mod->bpmlimit)) mod->bpm=dat; else if (dat) { mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat; mod->vbtick=0; } return 0; } /*========== Scream Tracker effects */ static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE speed; (void)flags; (void)a; (void)channel; speed = UniGetByte(); if (tick || mod->patdly2) return 0; if (speed > 128) speed -= 128; if (speed) { mod->sngspd = speed; mod->vbtick = 0; } return 0; } static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf) { UBYTE lo, hi; if (inf) a->s3mvolslide=inf; else inf=a->s3mvolslide; lo=inf&0xf; hi=inf>>4; if (!lo) { if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi; } else if (!hi) { if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo; } else if (lo==0xf) { if (!tick) a->tmpvolume+=(hi?hi:0xf); } else if (hi==0xf) { if (!tick) a->tmpvolume-=(lo?lo:0xf); } else return; if (a->tmpvolume<0) a->tmpvolume=0; else if (a->tmpvolume>64) a->tmpvolume=64; } static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)mod; (void)channel; DoS3MVolSlide(tick, flags, a, UniGetByte()); return 1; } static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf) { UBYTE hi,lo; if (inf) a->slidespeed=inf; else inf=a->slidespeed; hi=inf>>4; lo=inf&0xf; if (hi==0xf) { if (!tick) a->tmpperiod+=(UWORD)lo<<2; } else if (hi==0xe) { if (!tick) a->tmpperiod+=lo; } else { if (tick) a->tmpperiod+=(UWORD)inf<<2; } } static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (a->main.period) DoS3MSlideDn(tick, a,dat); return 0; } static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf) { UBYTE hi,lo; if (inf) a->slidespeed=inf; else inf=a->slidespeed; hi=inf>>4; lo=inf&0xf; if (hi==0xf) { if (!tick) a->tmpperiod-=(UWORD)lo<<2; } else if (hi==0xe) { if (!tick) a->tmpperiod-=lo; } else { if (tick) a->tmpperiod-=(UWORD)inf<<2; } } static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (a->main.period) DoS3MSlideUp(tick, a,dat); return 0; } static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, on, off; (void)flags; (void)mod; (void)channel; inf = UniGetByte(); if (inf) a->s3mtronof = inf; else { inf = a->s3mtronof; if (!inf) return 0; } if (!tick) return 0; on=(inf>>4)+1; off=(inf&0xf)+1; a->s3mtremor%=(on+off); a->volume=(a->s3mtremortmpvolume:0; a->ownvol=1; a->s3mtremor++; return 0; } static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf; (void)mod; (void)channel; inf = UniGetByte(); if (a->main.period) { if (inf) { a->s3mrtgslide=inf>>4; a->s3mrtgspeed=inf&0xf; } /* only retrigger if low nibble > 0 */ if (a->s3mrtgspeed>0) { if (!a->retrig) { /* when retrig counter reaches 0, reset counter and restart the sample */ if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF; a->retrig=a->s3mrtgspeed; if ((tick)||(flags&UF_S3MSLIDES)) { switch (a->s3mrtgslide) { case 1: case 2: case 3: case 4: case 5: a->tmpvolume-=(1<<(a->s3mrtgslide-1)); break; case 6: a->tmpvolume=(2*a->tmpvolume)/3; break; case 7: a->tmpvolume>>=1; break; case 9: case 0xa: case 0xb: case 0xc: case 0xd: a->tmpvolume+=(1<<(a->s3mrtgslide-9)); break; case 0xe: a->tmpvolume=(3*a->tmpvolume)>>1; break; case 0xf: a->tmpvolume=a->tmpvolume<<1; break; } if (a->tmpvolume<0) a->tmpvolume=0; else if (a->tmpvolume>64) a->tmpvolume=64; } } a->retrig--; /* countdown */ } } return 0; } static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, q; UWORD temp=0; /* silence warning */ (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (!tick) { if (dat&0x0f) a->trmdepth=dat&0xf; if (dat&0xf0) a->trmspd=(dat&0xf0)>>2; } q=(a->trmpos>>2)&0x1f; switch ((a->wavecontrol>>4)&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* ramp down */ q<<=3; if (a->trmpos<0) q=255-q; temp=q; break; case 2: /* square wave */ temp=255; break; case 3: /* random */ temp=getrandom(256); break; } temp*=a->trmdepth; temp>>=7; if (a->trmpos>=0) { a->volume=a->tmpvolume+temp; if (a->volume>64) a->volume=64; } else { a->volume=a->tmpvolume-temp; if (a->volume<0) a->volume=0; } a->ownvol = 1; if (tick) a->trmpos+=a->trmspd; return 0; } static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE tempo; (void)flags; (void)a; (void)channel; tempo = UniGetByte(); if (tick || mod->patdly2) return 0; mod->bpm = (tempo < 32) ? 32 : tempo; return 0; } static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, q; UWORD temp = 0; /* silence warning */ (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (!tick) { if (dat&0x0f) a->vibdepth=dat&0xf; if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; } else if (a->main.period) { q=(a->vibpos>>2)&0x1f; switch (a->wavecontrol&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* ramp down */ q<<=3; if (a->vibpos<0) q=255-q; temp=q; break; case 2: /* square wave */ temp=255; break; case 3: /* random */ temp=getrandom(256); break; } temp*=a->vibdepth; temp>>=8; if (a->vibpos>=0) a->main.period=a->tmpperiod+temp; else a->main.period=a->tmpperiod-temp; a->ownper = 1; a->vibpos+=a->vibspd; } return 0; } /*========== Envelope helpers */ static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)tick; (void)flags; (void)mod; (void)channel; a->main.keyoff|=KEY_OFF; if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP)) a->main.keyoff=KEY_KILL; return 0; } static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)channel; dat=UniGetByte(); if ((tick>=dat)||(tick==mod->sngspd-1)) { a->main.keyoff=KEY_KILL; if (!(a->main.volflg&EF_ON)) a->main.fadevol=0; } return 0; } /*========== Fast Tracker effects */ /* DoXMEffect6 after DoXMEffectA */ static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, lo, hi; (void)flags; (void)mod; (void)channel; inf = UniGetByte(); if (inf) a->s3mvolslide = inf; else inf = a->s3mvolslide; if (tick) { lo=inf&0xf; hi=inf>>4; if (!hi) { a->tmpvolume-=lo; if (a->tmpvolume<0) a->tmpvolume=0; } else { a->tmpvolume+=hi; if (a->tmpvolume>64) a->tmpvolume=64; } } return 0; } static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)flags; (void)mod; (void)channel; if (a->main.period) DoVibrato(tick, a); return DoXMEffectA(tick, flags, a, mod, channel); } static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) { if (dat) a->fportupspd=dat; if (a->main.period) a->tmpperiod-=(a->fportupspd<<2); } return 0; } static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) { if (dat) a->fportdnspd=dat; if (a->main.period) a->tmpperiod+=(a->fportdnspd<<2); } return 0; } static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) if (dat) a->fslideupspd=dat; a->tmpvolume+=a->fslideupspd; if (a->tmpvolume>64) a->tmpvolume=64; return 0; } static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if (!tick) if (dat) a->fslidednspd=dat; a->tmpvolume-=a->fslidednspd; if (a->tmpvolume<0) a->tmpvolume=0; return 0; } static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)tick; (void)flags; (void)a; (void)channel; mod->volume=UniGetByte()<<1; if (mod->volume>128) mod->volume=128; return 0; } static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf; (void)flags; (void)a; (void)channel; inf = UniGetByte(); if (tick) { if (inf) mod->globalslide=inf; else inf=mod->globalslide; if (inf & 0xf0) inf&=0xf0; mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2; if (mod->volume<0) mod->volume=0; else if (mod->volume>128) mod->volume=128; } return 0; } static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat=UniGetByte(); if ((!tick)&&(a->main.i)) { UWORD points; INSTRUMENT *i=a->main.i; MP_VOICE *aout; if ((aout=a->slave) != NULL) { if (aout->venv.env) { points=i->volenv[i->volpts-1].pos; aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos; } if (aout->penv.env) { points=i->panenv[i->panpts-1].pos; aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos; } } } return 0; } static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, lo, hi; SWORD pan; (void)flags; (void)channel; inf = UniGetByte(); if (!mod->panflag) return 0; if (inf) a->pansspd = inf; else inf =a->pansspd; if (tick) { lo=inf&0xf; hi=inf>>4; /* slide right has absolute priority */ if (hi) lo = 0; pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo; a->main.panning=(panPAN_RIGHT?PAN_RIGHT:pan); } return 0; } static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (dat) a->ffportupspd = dat; else dat = a->ffportupspd; if (a->main.period) if (!tick) { a->main.period-=dat; a->tmpperiod-=dat; a->ownper = 1; } return 0; } static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat; (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (dat) a->ffportdnspd=dat; else dat = a->ffportdnspd; if (a->main.period) if (!tick) { a->main.period+=dat; a->tmpperiod+=dat; a->ownper = 1; } return 0; } /*========== Impulse Tracker effects */ static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat) { if (dat) a->portspeed = dat; /* if we don't come from another note, ignore the slide and play the note as is */ if (!a->oldnote || !a->main.period) return; if ((!tick)&&(a->newsamp)){ a->main.kick=KICK_NOTE; a->main.start=-1; } else a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT; if (tick) { int dist; /* We have to slide a->main.period towards a->wantedperiod, compute the difference between those two values */ dist=a->main.period-a->wantedperiod; /* if they are equal or if portamentospeed is too big... */ if ((!dist)||((a->portspeed<<2)>abs(dist))) /* ... make tmpperiod equal tperiod */ a->tmpperiod=a->main.period=a->wantedperiod; else if (dist>0) { a->tmpperiod-=a->portspeed<<2; a->main.period-=a->portspeed<<2; /* dist>0 slide up */ } else { a->tmpperiod+=a->portspeed<<2; a->main.period+=a->portspeed<<2; /* dist<0 slide down */ } } else a->tmpperiod=a->main.period; a->ownper=1; } static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)flags; (void)mod; (void)channel; DoITToneSlide(tick, a, UniGetByte()); return 0; } static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat) { UBYTE q; UWORD temp=0; if (!tick) { if (dat&0x0f) a->vibdepth=dat&0xf; if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; } if (!a->main.period) return; q=(a->vibpos>>2)&0x1f; switch (a->wavecontrol&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* square wave */ temp=255; break; case 2: /* ramp down */ q<<=3; if (a->vibpos<0) q=255-q; temp=q; break; case 3: /* random */ temp=getrandom(256); break; } temp*=a->vibdepth; temp>>=8; temp<<=2; if (a->vibpos>=0) a->main.period=a->tmpperiod+temp; else a->main.period=a->tmpperiod-temp; a->ownper=1; a->vibpos+=a->vibspd; } static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)flags; (void)mod; (void)channel; DoITVibrato(tick, a, UniGetByte()); return 0; } static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, on, off; (void)tick; (void)flags; (void)mod; (void)channel; inf = UniGetByte(); if (inf) a->s3mtronof = inf; else { inf = a->s3mtronof; if (!inf) return 0; } on=(inf>>4); off=(inf&0xf); a->s3mtremor%=(on+off); a->volume=(a->s3mtremortmpvolume:0; a->ownvol = 1; a->s3mtremor++; return 0; } static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)tick; (void)flags; (void)mod; (void)channel; a->main.chanvol=UniGetByte(); if (a->main.chanvol>64) a->main.chanvol=64; else if (a->main.chanvol<0) a->main.chanvol=0; return 0; } static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, lo, hi; (void)tick; (void)flags; (void)mod; (void)channel; inf = UniGetByte(); if (inf) a->chanvolslide = inf; else inf = a->chanvolslide; lo=inf&0xf; hi=inf>>4; if (!hi) a->main.chanvol-=lo; else if (!lo) { a->main.chanvol+=hi; } else if (hi==0xf) { if (!tick) a->main.chanvol-=lo; } else if (lo==0xf) { if (!tick) a->main.chanvol+=hi; } if (a->main.chanvol<0) a->main.chanvol=0; else if (a->main.chanvol>64) a->main.chanvol=64; return 0; } static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, lo, hi; SWORD pan; (void)flags; (void)mod; (void)channel; inf = UniGetByte(); if (inf) a->pansspd = inf; else inf = a->pansspd; if (!mod->panflag) return 0; lo=inf&0xf; hi=inf>>4; pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning; if (!hi) pan+=lo<<2; else if (!lo) { pan-=hi<<2; } else if (hi==0xf) { if (!tick) pan+=lo<<2; } else if (lo==0xf) { if (!tick) pan-=hi<<2; } a->main.panning= (panPAN_RIGHT?PAN_RIGHT:pan); return 0; } static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE tempo; SWORD temp; (void)tick; (void)flags; (void)a; (void)channel; tempo = UniGetByte(); if (mod->patdly2) return 0; temp = mod->bpm; if (tempo & 0x10) temp += (tempo & 0x0f); else temp -= tempo; mod->bpm=(temp>255)?255:(temp<1?1:temp); return 0; } static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, q; UWORD temp = 0; /* silence warning */ (void)flags; (void)mod; (void)channel; dat = UniGetByte(); if (!tick) { if (dat&0x0f) a->vibdepth=dat&0xf; if (dat&0xf0) a->vibspd=(dat&0xf0)>>2; } if (a->main.period) { q=(a->vibpos>>2)&0x1f; switch (a->wavecontrol&3) { case 0: /* sine */ temp=VibratoTable[q]; break; case 1: /* square wave */ temp=255; break; case 2: /* ramp down */ q<<=3; if (a->vibpos<0) q=255-q; temp=q; break; case 3: /* random */ temp=getrandom(256); break; } temp*=a->vibdepth; temp>>=8; if (a->vibpos>=0) a->main.period=a->tmpperiod+temp; else a->main.period=a->tmpperiod-temp; a->ownper = 1; a->vibpos+=a->vibspd; } return 0; } static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE inf, lo, hi; (void)flags; (void)a; (void)channel; inf = UniGetByte(); if (inf) mod->globalslide = inf; else inf = mod->globalslide; lo=inf&0xf; hi=inf>>4; if (!lo) { if (tick) mod->volume+=hi; } else if (!hi) { if (tick) mod->volume-=lo; } else if (lo==0xf) { if (!tick) mod->volume+=hi; } else if (hi==0xf) { if (!tick) mod->volume-=lo; } if (mod->volume<0) mod->volume=0; else if (mod->volume>128) mod->volume=128; return 0; } static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, q; SLONG temp = 0; /* silence warning */ (void)flags; dat=UniGetByte(); if (!tick) { if (dat&0x0f) a->panbdepth=(dat&0xf); if (dat&0xf0) a->panbspd=(dat&0xf0)>>4; } if (mod->panflag) { q=a->panbpos; switch (a->panbwave) { case 0: /* sine */ temp=PanbrelloTable[q]; break; case 1: /* square wave */ temp=(q<0x80)?64:0; break; case 2: /* ramp down */ q<<=3; temp=q; break; case 3: /* random */ temp=getrandom(256); break; } temp*=a->panbdepth; temp=(temp/8)+mod->panning[channel]; a->main.panning= (tempPAN_RIGHT?PAN_RIGHT:temp); a->panbpos+=a->panbspd; } return 0; } static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE); /* Impulse/Scream Tracker Sxx effects. All Sxx effects share the same memory space. */ static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, inf, c; dat = UniGetByte(); inf=dat&0xf; c=dat>>4; if (!dat) { c=a->sseffect; inf=a->ssdata; } else { a->sseffect=c; a->ssdata=inf; } switch (c) { case SS_GLISSANDO: /* S1x set glissando voice */ DoEEffects(tick, flags, a, mod, channel, 0x30|inf); break; case SS_FINETUNE: /* S2x set finetune */ DoEEffects(tick, flags, a, mod, channel, 0x50|inf); break; case SS_VIBWAVE: /* S3x set vibrato waveform */ DoEEffects(tick, flags, a, mod, channel, 0x40|inf); break; case SS_TREMWAVE: /* S4x set tremolo waveform */ DoEEffects(tick, flags, a, mod, channel, 0x70|inf); break; case SS_PANWAVE: /* S5x panbrello */ a->panbwave=inf; break; case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */ DoEEffects(tick, flags, a, mod, channel, 0xe0|inf); break; case SS_S7EFFECTS: /* S7x instrument / NNA commands */ DoNNAEffects(mod, a, inf); break; case SS_PANNING: /* S8x set panning position */ DoEEffects(tick, flags, a, mod, channel, 0x80 | inf); break; case SS_SURROUND: /* S9x set surround sound */ if (mod->panflag) a->main.panning = mod->panning[channel] = PAN_SURROUND; break; case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */ if (!tick) { a->hioffset=inf<<16; a->main.start=a->hioffset|a->soffset; if ((a->main.s)&&(a->main.start>(SLONG)a->main.s->length)) a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? a->main.s->loopstart:a->main.s->length; } break; case SS_PATLOOP: /* SBx pattern loop */ DoEEffects(tick, flags, a, mod, channel, 0x60|inf); break; case SS_NOTECUT: /* SCx notecut */ if (!inf) inf = 1; DoEEffects(tick, flags, a, mod, channel, 0xC0|inf); break; case SS_NOTEDELAY: /* SDx notedelay */ DoEEffects(tick, flags, a, mod, channel, 0xD0|inf); break; case SS_PATDELAY: /* SEx patterndelay */ DoEEffects(tick, flags, a, mod, channel, 0xE0|inf); break; } return 0; } /*========== Impulse Tracker Volume/Pan Column effects */ /* * All volume/pan column effects share the same memory space. */ static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE c, inf; (void)channel; c = UniGetByte(); inf = UniGetByte(); if ((!c)&&(!inf)) { c=a->voleffect; inf=a->voldata; } else { a->voleffect=c; a->voldata=inf; } if (c) switch (c) { case VOL_VOLUME: if (tick) break; if (inf>64) inf=64; a->tmpvolume=inf; break; case VOL_PANNING: if (mod->panflag) a->main.panning=inf; break; case VOL_VOLSLIDE: DoS3MVolSlide(tick, flags, a, inf); return 1; case VOL_PITCHSLIDEDN: if (a->main.period) DoS3MSlideDn(tick, a, inf); break; case VOL_PITCHSLIDEUP: if (a->main.period) DoS3MSlideUp(tick, a, inf); break; case VOL_PORTAMENTO: DoITToneSlide(tick, a, inf); break; case VOL_VIBRATO: DoITVibrato(tick, a, inf); break; } return 0; } /*========== UltraTracker effects */ static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UWORD offset=UniGetWord(); (void)tick; (void)flags; (void)mod; (void)channel; if (offset) a->ultoffset=offset; a->main.start=a->ultoffset<<2; if ((a->main.s)&&(a->main.start>(SLONG)a->main.s->length)) a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)? a->main.s->loopstart:a->main.s->length; return 0; } /*========== OctaMED effects */ static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UWORD speed=UniGetWord(); (void)tick; (void)flags; (void)a; (void)channel; mod->bpm=speed; return 0; } static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2)); return 0; } static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2)); return 0; } static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3)); return 0; } /*========== Oktalyzer effects */ static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { UBYTE dat, dat2; (void)mod; (void)channel; dat2 = UniGetByte(); /* arpeggio style */ dat = UniGetByte(); if (!tick) { if (!dat && (flags & UF_ARPMEM)) dat=a->arpmem; else a->arpmem=dat; } if (a->main.period) DoArpeggio(tick, flags, a, dat2); return 0; } /*========== General player functions */ static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel) { (void)tick; (void)flags; (void)a; (void)mod; (void)channel; UniSkipOpcode(); return 0; } typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD); static effect_func effects[UNI_LAST] = { DoNothing, /* 0 */ DoNothing, /* UNI_NOTE */ DoNothing, /* UNI_INSTRUMENT */ DoPTEffect0, /* UNI_PTEFFECT0 */ DoPTEffect1, /* UNI_PTEFFECT1 */ DoPTEffect2, /* UNI_PTEFFECT2 */ DoPTEffect3, /* UNI_PTEFFECT3 */ DoPTEffect4, /* UNI_PTEFFECT4 */ DoPTEffect5, /* UNI_PTEFFECT5 */ DoPTEffect6, /* UNI_PTEFFECT6 */ DoPTEffect7, /* UNI_PTEFFECT7 */ DoPTEffect8, /* UNI_PTEFFECT8 */ DoPTEffect9, /* UNI_PTEFFECT9 */ DoPTEffectA, /* UNI_PTEFFECTA */ DoPTEffectB, /* UNI_PTEFFECTB */ DoPTEffectC, /* UNI_PTEFFECTC */ DoPTEffectD, /* UNI_PTEFFECTD */ DoPTEffectE, /* UNI_PTEFFECTE */ DoPTEffectF, /* UNI_PTEFFECTF */ DoS3MEffectA, /* UNI_S3MEFFECTA */ DoS3MEffectD, /* UNI_S3MEFFECTD */ DoS3MEffectE, /* UNI_S3MEFFECTE */ DoS3MEffectF, /* UNI_S3MEFFECTF */ DoS3MEffectI, /* UNI_S3MEFFECTI */ DoS3MEffectQ, /* UNI_S3MEFFECTQ */ DoS3MEffectR, /* UNI_S3MEFFECTR */ DoS3MEffectT, /* UNI_S3MEFFECTT */ DoS3MEffectU, /* UNI_S3MEFFECTU */ DoKeyOff, /* UNI_KEYOFF */ DoKeyFade, /* UNI_KEYFADE */ DoVolEffects, /* UNI_VOLEFFECTS */ DoPTEffect4, /* UNI_XMEFFECT4 */ DoXMEffect6, /* UNI_XMEFFECT6 */ DoXMEffectA, /* UNI_XMEFFECTA */ DoXMEffectE1, /* UNI_XMEFFECTE1 */ DoXMEffectE2, /* UNI_XMEFFECTE2 */ DoXMEffectEA, /* UNI_XMEFFECTEA */ DoXMEffectEB, /* UNI_XMEFFECTEB */ DoXMEffectG, /* UNI_XMEFFECTG */ DoXMEffectH, /* UNI_XMEFFECTH */ DoXMEffectL, /* UNI_XMEFFECTL */ DoXMEffectP, /* UNI_XMEFFECTP */ DoXMEffectX1, /* UNI_XMEFFECTX1 */ DoXMEffectX2, /* UNI_XMEFFECTX2 */ DoITEffectG, /* UNI_ITEFFECTG */ DoITEffectH, /* UNI_ITEFFECTH */ DoITEffectI, /* UNI_ITEFFECTI */ DoITEffectM, /* UNI_ITEFFECTM */ DoITEffectN, /* UNI_ITEFFECTN */ DoITEffectP, /* UNI_ITEFFECTP */ DoITEffectT, /* UNI_ITEFFECTT */ DoITEffectU, /* UNI_ITEFFECTU */ DoITEffectW, /* UNI_ITEFFECTW */ DoITEffectY, /* UNI_ITEFFECTY */ DoNothing, /* UNI_ITEFFECTZ */ DoITEffectS0, /* UNI_ITEFFECTS0 */ DoULTEffect9, /* UNI_ULTEFFECT9 */ DoMEDSpeed, /* UNI_MEDSPEED */ DoMEDEffectF1, /* UNI_MEDEFFECTF1 */ DoMEDEffectF2, /* UNI_MEDEFFECTF2 */ DoMEDEffectF3, /* UNI_MEDEFFECTF3 */ DoOktArp, /* UNI_OKTARP */ }; static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a) { UWORD tick = mod->vbtick; UWORD flags = mod->flags; UBYTE c; int explicitslides = 0; effect_func f; while((c=UniGetByte()) != 0) { #if 0 /* this doesn't normally happen unless things go fubar elsewhere */ if (c >= UNI_LAST) fprintf(stderr,"fubar'ed opcode %u\n",c); #endif f = effects[c]; if (f != DoNothing) a->sliding = 0; explicitslides |= f(tick, flags, a, mod, channel); } return explicitslides; } static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat) { int t; MP_VOICE *aout; dat&=0xf; aout=(a->slave)?a->slave:NULL; switch (dat) { case 0x0: /* past note cut */ for (t=0;tvoice[t].master==a) mod->voice[t].main.fadevol=0; break; case 0x1: /* past note off */ for (t=0;tvoice[t].master==a) { mod->voice[t].main.keyoff|=KEY_OFF; if ((!(mod->voice[t].venv.flg & EF_ON))|| (mod->voice[t].venv.flg & EF_LOOP)) mod->voice[t].main.keyoff=KEY_KILL; } break; case 0x2: /* past note fade */ for (t=0;tvoice[t].master==a) mod->voice[t].main.keyoff|=KEY_FADE; break; case 0x3: /* set NNA note cut */ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT; break; case 0x4: /* set NNA note continue */ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE; break; case 0x5: /* set NNA note off */ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF; break; case 0x6: /* set NNA note fade */ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE; break; case 0x7: /* disable volume envelope */ if (aout) aout->main.volflg&=~EF_ON; break; case 0x8: /* enable volume envelope */ if (aout) aout->main.volflg|=EF_ON; break; case 0x9: /* disable panning envelope */ if (aout) aout->main.panflg&=~EF_ON; break; case 0xa: /* enable panning envelope */ if (aout) aout->main.panflg|=EF_ON; break; case 0xb: /* disable pitch envelope */ if (aout) aout->main.pitflg&=~EF_ON; break; case 0xc: /* enable pitch envelope */ if (aout) aout->main.pitflg|=EF_ON; break; } } static void pt_UpdateVoices(MODULE *mod, int max_volume) { SWORD envpan,envvol,envpit,channel; UWORD playperiod; SLONG vibval,vibdpt; ULONG tmpvol; MP_VOICE *aout; INSTRUMENT *i; SAMPLE *s; mod->totalchn=mod->realchn=0; for (channel=0;channelvoice[channel]; i=aout->main.i; s=aout->main.s; if (!s || !s->length) continue; if (aout->main.period<40) aout->main.period=40; else if (aout->main.period>50000) aout->main.period=50000; if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) { Voice_Play_internal(channel,s,(aout->main.start==-1)? ((s->flags&SF_UST_LOOP)?(SLONG)s->loopstart:0):aout->main.start); aout->main.fadevol=32768; aout->aswppos=0; } envvol = 256; envpan = PAN_CENTER; envpit = 32; if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) { if (aout->main.volflg & EF_ON) envvol = StartEnvelope(&aout->venv,aout->main.volflg, i->volpts,i->volsusbeg,i->volsusend, i->volbeg,i->volend,i->volenv,aout->main.keyoff); if (aout->main.panflg & EF_ON) envpan = StartEnvelope(&aout->penv,aout->main.panflg, i->panpts,i->pansusbeg,i->pansusend, i->panbeg,i->panend,i->panenv,aout->main.keyoff); if (aout->main.pitflg & EF_ON) envpit = StartEnvelope(&aout->cenv,aout->main.pitflg, i->pitpts,i->pitsusbeg,i->pitsusend, i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff); if (aout->cenv.flg & EF_ON) aout->masterperiod=GetPeriod(mod->flags, (UWORD)aout->main.note<<1, aout->master->speed); } else { if (aout->main.volflg & EF_ON) envvol = ProcessEnvelope(aout,&aout->venv,256); if (aout->main.panflg & EF_ON) envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER); if (aout->main.pitflg & EF_ON) envpit = ProcessEnvelope(aout,&aout->cenv,32); } if (aout->main.kick == KICK_NOTE) { aout->main.kick_flag = 1; } aout->main.kick=KICK_ABSENT; tmpvol = aout->main.fadevol; /* max 32768 */ tmpvol *= aout->main.chanvol; /* * max 64 */ tmpvol *= aout->main.outvolume; /* * max 256 */ tmpvol /= (256 * 64); /* tmpvol is max 32768 again */ aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */ tmpvol *= envvol; /* * max 256 */ tmpvol *= mod->volume; /* * max 128 */ tmpvol /= (128 * 256 * 128); /* fade out */ if (mod->sngpos>=mod->numpos) tmpvol=0; else tmpvol=(tmpvol*max_volume)/128; if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted) Voice_SetVolume_internal(channel,0); else { Voice_SetVolume_internal(channel,tmpvol); if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) mod->realchn++; mod->totalchn++; } if (aout->main.panning==PAN_SURROUND) Voice_SetPanning_internal(channel,PAN_SURROUND); else if ((mod->panflag)&&(aout->penv.flg & EF_ON)) Voice_SetPanning_internal(channel, DoPan(envpan,aout->main.panning)); else Voice_SetPanning_internal(channel,aout->main.panning); if (aout->main.period && s->vibdepth) switch (s->vibtype) { case 0: vibval=avibtab[s->avibpos&127]; if (aout->avibpos & 0x80) vibval=-vibval; break; case 1: vibval=64; if (aout->avibpos & 0x80) vibval=-vibval; break; case 2: vibval=63-(((aout->avibpos+128)&255)>>1); break; default: vibval=(((aout->avibpos+128)&255)>>1)-64; break; } else vibval=0; if (s->vibflags & AV_IT) { if ((aout->aswppos>>8)vibdepth) { aout->aswppos += s->vibsweep; vibdpt=aout->aswppos; } else vibdpt=s->vibdepth<<8; vibval=(vibval*vibdpt)>>16; if (aout->mflag) { if (!(mod->flags&UF_LINEAR)) vibval>>=1; aout->main.period-=vibval; } } else { /* do XM style auto-vibrato */ if (!(aout->main.keyoff & KEY_OFF)) { if (aout->aswpposvibsweep) { vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep; aout->aswppos++; } else vibdpt=s->vibdepth; } else { /* keyoff -> depth becomes 0 if final depth wasn't reached or stays at final level if depth WAS reached */ if (aout->aswppos>=s->vibsweep) vibdpt=s->vibdepth; else vibdpt=0; } vibval=(vibval*vibdpt)>>8; aout->main.period-=vibval; } /* update vibrato position */ aout->avibpos=(aout->avibpos+s->vibrate)&0xff; /* process pitch envelope */ playperiod=aout->main.period; if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) { long p1; envpit-=32; if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1); p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit, aout->master->speed)-aout->masterperiod; if (p1>0) { if ((UWORD)(playperiod+p1)<=playperiod) { p1=0; aout->main.keyoff|=KEY_OFF; } } else if (p1<0) { if ((UWORD)(playperiod+p1)>=playperiod) { p1=0; aout->main.keyoff|=KEY_OFF; } } playperiod+=p1; } if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */ Voice_Stop_internal(channel); mod->totalchn--; if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout)) mod->realchn--; } else { Voice_SetFrequency_internal(channel, getfrequency(mod->flags,playperiod)); /* if keyfade, start substracting fadeoutspeed from fadevol: */ if ((i)&&(aout->main.keyoff&KEY_FADE)) { if (aout->main.fadevol>=i->volfade) aout->main.fadevol-=i->volfade; else aout->main.fadevol=0; } } md_bpm=mod->bpm+mod->relspd; if (md_bpm<32) md_bpm=32; else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255) md_bpm=255; } } /* Handles new notes or instruments */ static void pt_Notes(MODULE *mod) { SWORD channel; MP_CONTROL *a; UBYTE c,inst; int tr,funky; /* funky is set to indicate note or instrument change */ for (channel=0;channelnumchn;channel++) { a=&mod->control[channel]; if (mod->sngpos>=mod->numpos) { tr=mod->numtrk; mod->numrow=0; } else { tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel]; mod->numrow=mod->pattrows[mod->positions[mod->sngpos]]; } a->row=(trnumtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL; a->newsamp=0; if (!mod->vbtick) a->main.notedelay=0; if (!a->row) continue; UniSetRow(a->row); funky=0; while((c=UniGetByte()) != 0) switch (c) { case UNI_NOTE: funky|=1; a->oldnote=a->anote,a->anote=UniGetByte(); a->main.kick =KICK_NOTE; a->main.start=-1; a->sliding=0; /* retrig tremolo and vibrato waves ? */ if (!(a->wavecontrol & 0x80)) a->trmpos=0; if (!(a->wavecontrol & 0x08)) a->vibpos=0; if (!a->panbwave) a->panbpos=0; break; case UNI_INSTRUMENT: inst=UniGetByte(); if (inst>=mod->numins) break; /* safety valve */ funky|=2; a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL; a->retrig=0; a->s3mtremor=0; a->ultoffset=0; a->main.sample=inst; break; default: UniSkipOpcode(); break; } if (funky) { INSTRUMENT *i; SAMPLE *s; if ((i=a->main.i) != NULL) { if (i->samplenumber[a->anote] >= mod->numsmp) continue; s=&mod->samples[i->samplenumber[a->anote]]; a->main.note=i->samplenote[a->anote]; } else { a->main.note=a->anote; s=&mod->samples[a->main.sample]; } if (a->main.s!=s) { a->main.s=s; a->newsamp=a->main.period; } /* channel or instrument determined panning ? */ a->main.panning=mod->panning[channel]; if (s->flags & SF_OWNPAN) a->main.panning=s->panning; else if ((i)&&(i->flags & IF_OWNPAN)) a->main.panning=i->panning; a->main.handle=s->handle; a->speed=s->speed; if (i) { if ((mod->panflag)&&(i->flags & IF_PITCHPAN) &&(a->main.panning!=PAN_SURROUND)){ a->main.panning+= ((a->anote-i->pitpancenter)*i->pitpansep)/8; if (a->main.panningmain.panning=PAN_LEFT; else if (a->main.panning>PAN_RIGHT) a->main.panning=PAN_RIGHT; } a->main.pitflg=i->pitflg; a->main.volflg=i->volflg; a->main.panflg=i->panflg; a->main.nna=i->nnatype; a->dca=i->dca; a->dct=i->dct; } else { a->main.pitflg=a->main.volflg=a->main.panflg=0; a->main.nna=a->dca=0; a->dct=DCT_OFF; } if (funky&2) /* instrument change */ { /* IT random volume variations: 0:8 bit fixed, and one bit for sign. */ a->volume=a->tmpvolume=s->volume; if ((s)&&(i)) { if (i->rvolvar) { a->volume=a->tmpvolume=s->volume+ ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512) ))/25600); if (a->volume<0) a->volume=a->tmpvolume=0; else if (a->volume>64) a->volume=a->tmpvolume=64; } if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) { a->main.panning+=((a->main.panning*((SLONG)i->rpanvar* (SLONG)getrandom(512)))/25600); if (a->main.panningmain.panning=PAN_LEFT; else if (a->main.panning>PAN_RIGHT) a->main.panning=PAN_RIGHT; } } } a->wantedperiod=a->tmpperiod= GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed); a->main.keyoff=KEY_KICK; } } } /* Handles effects */ static void pt_EffectsPass1(MODULE *mod) { SWORD channel; MP_CONTROL *a; MP_VOICE *aout; int explicitslides; for (channel=0;channelnumchn;channel++) { a=&mod->control[channel]; if ((aout=a->slave) != NULL) { a->main.fadevol=aout->main.fadevol; a->main.period=aout->main.period; if (a->main.kick==KICK_KEYOFF) a->main.keyoff=aout->main.keyoff; } if (!a->row) continue; UniSetRow(a->row); a->ownper=a->ownvol=0; explicitslides = pt_playeffects(mod, channel, a); /* continue volume slide if necessary for XM and IT */ if (mod->flags&UF_BGSLIDES) { if (!explicitslides && a->sliding) DoS3MVolSlide(mod->vbtick, mod->flags, a, 0); else if (a->tmpvolume) a->sliding = explicitslides; } if (!a->ownper) a->main.period=a->tmpperiod; if (!a->ownvol) a->volume=a->tmpvolume; if (a->main.s) { if (a->main.i) a->main.outvolume= (a->volume*a->main.s->globvol*a->main.i->globvol)>>10; else a->main.outvolume=(a->volume*a->main.s->globvol)>>4; if (a->main.outvolume>256) a->main.outvolume=256; else if (a->main.outvolume<0) a->main.outvolume=0; } } } /* NNA management */ static void pt_NNA(MODULE *mod) { SWORD channel; MP_CONTROL *a; for (channel=0;channelnumchn;channel++) { a=&mod->control[channel]; if (a->main.kick==KICK_NOTE) { int kill=0; if (a->slave) { MP_VOICE *aout; aout=a->slave; if (aout->main.nna & NNA_MASK) { /* Make sure the old MP_VOICE channel knows it has no master now ! */ a->slave=NULL; /* assume the channel is taken by NNA */ aout->mflag=0; switch (aout->main.nna) { case NNA_CONTINUE: /* continue note, do nothing */ break; case NNA_OFF: /* note off */ aout->main.keyoff|=KEY_OFF; if ((!(aout->main.volflg & EF_ON))|| (aout->main.volflg & EF_LOOP)) aout->main.keyoff=KEY_KILL; break; case NNA_FADE: aout->main.keyoff |= KEY_FADE; break; } } } if (a->dct!=DCT_OFF) { int t; for (t=0;tvoice[t].masterchn==channel)&& (a->main.sample==mod->voice[t].main.sample)) { kill=0; switch (a->dct) { case DCT_NOTE: if (a->main.note==mod->voice[t].main.note) kill=1; break; case DCT_SAMPLE: if (a->main.handle==mod->voice[t].main.handle) kill=1; break; case DCT_INST: kill=1; break; } if (kill) switch (a->dca) { case DCA_CUT: mod->voice[t].main.fadevol=0; break; case DCA_OFF: mod->voice[t].main.keyoff|=KEY_OFF; if ((!(mod->voice[t].main.volflg&EF_ON))|| (mod->voice[t].main.volflg&EF_LOOP)) mod->voice[t].main.keyoff=KEY_KILL; break; case DCA_FADE: mod->voice[t].main.keyoff|=KEY_FADE; break; } } } } /* if (a->main.kick==KICK_NOTE) */ } } /* Setup module and NNA voices */ static void pt_SetupVoices(MODULE *mod) { SWORD channel; MP_CONTROL *a; MP_VOICE *aout; for (channel=0;channelnumchn;channel++) { a=&mod->control[channel]; if (a->main.notedelay) continue; if (a->main.kick==KICK_NOTE) { /* if no channel was cut above, find an empty or quiet channel here */ if (mod->flags&UF_NNA) { if (!a->slave) { int newchn; if ((newchn=MP_FindEmptyChannel(mod))!=-1) a->slave=&mod->voice[a->slavechn=newchn]; } } else a->slave=&mod->voice[a->slavechn=channel]; /* assign parts of MP_VOICE only done for a KICK_NOTE */ if ((aout=a->slave) != NULL) { if (aout->mflag && aout->master) aout->master->slave=NULL; aout->master=a; a->slave=aout; aout->masterchn=channel; aout->mflag=1; } } else aout=a->slave; if (aout) aout->main=a->main; a->main.kick=KICK_ABSENT; } } /* second effect pass */ static void pt_EffectsPass2(MODULE *mod) { SWORD channel; MP_CONTROL *a; UBYTE c; for (channel=0;channelnumchn;channel++) { a=&mod->control[channel]; if (!a->row) continue; UniSetRow(a->row); while((c=UniGetByte()) != 0) if (c==UNI_ITEFFECTS0) { c=UniGetByte(); if ((c>>4)==SS_S7EFFECTS) DoNNAEffects(mod, a, c&0xf); } else UniSkipOpcode(); } } void Player_HandleTick(void) { SWORD channel; int max_volume; #if 0 /* don't handle the very first ticks, this allows the other hardware to settle down so we don't loose any starting notes */ if (isfirst) { isfirst--; return; } #endif if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return; /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */ pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */ pf->sngtime+=pf->sngremainder/pf->bpm; pf->sngremainder%=pf->bpm; if (++pf->vbtick>=pf->sngspd) { if (pf->pat_repcrazy) pf->pat_repcrazy=0; /* play 2 times row 0 */ else pf->patpos++; pf->vbtick=0; /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is the command memory. */ if (pf->patdly) pf->patdly2=pf->patdly,pf->patdly=0; if (pf->patdly2) { /* patterndelay active */ if (--pf->patdly2) /* so turn back pf->patpos by 1 */ if (pf->patpos) pf->patpos--; } /* do we have to get a new patternpointer ? (when pf->patpos reaches the pattern size, or when a patternbreak is active) */ if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp)) pf->posjmp=3; if (pf->posjmp) { pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0; pf->pat_repcrazy=0; pf->sngpos+=(pf->posjmp-2); for (channel=0;channelnumchn;channel++) pf->control[channel].pat_reppos=-1; pf->patbrk=pf->posjmp=0; if (pf->sngpos<0) pf->sngpos=(SWORD)(pf->numpos-1); /* handle the "---" (end of song) pattern since it can occur *inside* the module in some formats */ if ((pf->sngpos>=pf->numpos)|| (pf->positions[pf->sngpos]==LAST_PATTERN)) { if (!pf->wrap) return; if (!(pf->sngpos=pf->reppos)) { pf->volume=pf->initvolume>128?128:pf->initvolume; if(pf->initspeed!=0) pf->sngspd=pf->initspeed<32?pf->initspeed:32; else pf->sngspd=6; pf->bpm=pf->inittempo<32?32:pf->inittempo; } } } if (!pf->patdly2) pt_Notes(pf); } /* Fade global volume if enabled and we're playing the last pattern */ if (((pf->sngpos==pf->numpos-1)|| (pf->positions[pf->sngpos+1]==LAST_PATTERN))&& (pf->fadeout)) max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0; else max_volume=128; pt_EffectsPass1(pf); if (pf->flags&UF_NNA) pt_NNA(pf); pt_SetupVoices(pf); pt_EffectsPass2(pf); /* now set up the actual hardware channel playback information */ pt_UpdateVoices(pf, max_volume); } static void Player_Init_internal(MODULE* mod) { int t; for (t=0;tnumchn;t++) { mod->control[t].main.chanvol=mod->chanvol[t]; mod->control[t].main.panning=mod->panning[t]; } mod->sngtime=0; mod->sngremainder=0; mod->pat_repcrazy=0; mod->sngpos=0; if(mod->initspeed!=0) mod->sngspd=mod->initspeed<32?mod->initspeed:32; else mod->sngspd=6; mod->volume=mod->initvolume>128?128:mod->initvolume; mod->vbtick=mod->sngspd; mod->patdly=0; mod->patdly2=0; mod->bpm=mod->inittempo<32?32:mod->inittempo; mod->realchn=0; mod->patpos=0; mod->posjmp=2; /* make sure the player fetches the first note */ mod->numrow=-1; mod->patbrk=0; } int Player_Init(MODULE* mod) { mod->extspd=1; mod->panflag=1; mod->wrap=0; mod->loop=1; mod->fadeout=0; mod->relspd=0; /* make sure the player doesn't start with garbage */ if (!(mod->control=(MP_CONTROL*)MikMod_calloc(mod->numchn,sizeof(MP_CONTROL)))) return 1; if (!(mod->voice=(MP_VOICE*)MikMod_calloc(md_sngchn,sizeof(MP_VOICE)))) return 1; /* mod->numvoices was used during loading to clamp md_sngchn. After loading it's used to remember how big mod->voice is. */ mod->numvoices = md_sngchn; Player_Init_internal(mod); return 0; } void Player_Exit_internal(MODULE* mod) { if (!mod) return; /* Stop playback if necessary */ if (mod==pf) { Player_Stop_internal(); pf=NULL; } MikMod_free(mod->control); MikMod_free(mod->voice); mod->control=NULL; mod->voice=NULL; } void Player_Exit(MODULE* mod) { MUTEX_LOCK(vars); Player_Exit_internal(mod); MUTEX_UNLOCK(vars); } MIKMODAPI void Player_SetVolume(SWORD volume) { MUTEX_LOCK(vars); if (pf) { pf->volume=(volume<0)?0:(volume>128)?128:volume; pf->initvolume=pf->volume; } MUTEX_UNLOCK(vars); } MIKMODAPI MODULE* Player_GetModule(void) { MODULE* result; MUTEX_LOCK(vars); result=pf; MUTEX_UNLOCK(vars); return result; } MIKMODAPI void Player_Start(MODULE *mod) { int t; if (!mod) return; if (!MikMod_Active()) MikMod_EnableOutput(); mod->forbid=0; MUTEX_LOCK(vars); if (pf!=mod) { /* new song is being started, so completely stop out the old one. */ if (pf) pf->forbid=1; for (t=0;tforbid=1; pf=NULL; } MIKMODAPI void Player_Stop(void) { MUTEX_LOCK(vars); Player_Stop_internal(); MUTEX_UNLOCK(vars); } MIKMODAPI int Player_Active(void) { int result=0; MUTEX_LOCK(vars); if (pf) result=(!(pf->sngpos>=pf->numpos)); MUTEX_UNLOCK(vars); return result; } MIKMODAPI void Player_NextPosition(void) { MUTEX_LOCK(vars); if (pf) { int t; pf->forbid=1; pf->posjmp=3; pf->patbrk=0; pf->vbtick=pf->sngspd; for (t=0;tvoice[t].main.i=NULL; pf->voice[t].main.s=NULL; } for (t=0;tnumchn;t++) { pf->control[t].main.i=NULL; pf->control[t].main.s=NULL; } pf->forbid=0; } MUTEX_UNLOCK(vars); } MIKMODAPI void Player_PrevPosition(void) { MUTEX_LOCK(vars); if (pf) { int t; pf->forbid=1; pf->posjmp=1; pf->patbrk=0; pf->vbtick=pf->sngspd; for (t=0;tvoice[t].main.i=NULL; pf->voice[t].main.s=NULL; } for (t=0;tnumchn;t++) { pf->control[t].main.i=NULL; pf->control[t].main.s=NULL; } pf->forbid=0; } MUTEX_UNLOCK(vars); } MIKMODAPI void Player_SetPosition(UWORD pos) { MUTEX_LOCK(vars); if (pf) { int t; pf->forbid=1; if (pos>=pf->numpos) pos=pf->numpos; pf->posjmp=2; pf->patbrk=0; pf->sngpos=pos; pf->vbtick=pf->sngspd; for (t=0;tvoice[t].main.i=NULL; pf->voice[t].main.s=NULL; } for (t=0;tnumchn;t++) { pf->control[t].main.i=NULL; pf->control[t].main.s=NULL; } pf->forbid=0; if (!pos) Player_Init_internal(pf); } MUTEX_UNLOCK(vars); } static void Player_Unmute_internal(SLONG arg1,va_list ap) { SLONG t,arg2,arg3=0; if (pf) { switch (arg1) { case MUTE_INCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (;arg2numchn && arg2<=arg3;arg2++) pf->control[arg2].muted=0; break; case MUTE_EXCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (t=0;tnumchn;t++) { if ((t>=arg2) && (t<=arg3)) continue; pf->control[t].muted=0; } break; default: if (arg1numchn) pf->control[arg1].muted=0; break; } } } MIKMODAPI void Player_Unmute(SLONG arg1, ...) { va_list args; va_start(args,arg1); MUTEX_LOCK(vars); Player_Unmute_internal(arg1,args); MUTEX_UNLOCK(vars); va_end(args); } static void Player_Mute_internal(SLONG arg1,va_list ap) { SLONG t,arg2,arg3=0; if (pf) { switch (arg1) { case MUTE_INCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (;arg2numchn && arg2<=arg3;arg2++) pf->control[arg2].muted=1; break; case MUTE_EXCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (t=0;tnumchn;t++) { if ((t>=arg2) && (t<=arg3)) continue; pf->control[t].muted=1; } break; default: if (arg1numchn) pf->control[arg1].muted=1; break; } } } MIKMODAPI void Player_Mute(SLONG arg1,...) { va_list args; va_start(args,arg1); MUTEX_LOCK(vars); Player_Mute_internal(arg1,args); MUTEX_UNLOCK(vars); va_end(args); } static void Player_ToggleMute_internal(SLONG arg1,va_list ap) { SLONG arg2,arg3=0; SLONG t; if (pf) { switch (arg1) { case MUTE_INCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (;arg2numchn && arg2<=arg3;arg2++) pf->control[arg2].muted=1-pf->control[arg2].muted; break; case MUTE_EXCLUSIVE: if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))|| (arg2>arg3)||(arg3>=pf->numchn)) return; for (t=0;tnumchn;t++) { if ((t>=arg2) && (t<=arg3)) continue; pf->control[t].muted=1-pf->control[t].muted; } break; default: if (arg1numchn) pf->control[arg1].muted=1-pf->control[arg1].muted; break; } } } MIKMODAPI void Player_ToggleMute(SLONG arg1,...) { va_list args; va_start(args,arg1); MUTEX_LOCK(vars); Player_ToggleMute_internal(arg1,args); MUTEX_UNLOCK(vars); va_end(args); } MIKMODAPI int Player_Muted(UBYTE chan) { int result=1; MUTEX_LOCK(vars); if (pf) result=(channumchn)?pf->control[chan].muted:1; MUTEX_UNLOCK(vars); return result; } MIKMODAPI int Player_GetChannelVoice(UBYTE chan) { int result=0; MUTEX_LOCK(vars); if (pf) result=(channumchn)?pf->control[chan].slavechn:-1; MUTEX_UNLOCK(vars); return result; } MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan) { UWORD result=0; MUTEX_LOCK(vars); if (pf) result=(channumchn)?pf->control[chan].main.period:0; MUTEX_UNLOCK(vars); return result; } int Player_Paused_internal(void) { return pf?pf->forbid:1; } MIKMODAPI int Player_Paused(void) { int result; MUTEX_LOCK(vars); result=Player_Paused_internal(); MUTEX_UNLOCK(vars); return result; } MIKMODAPI void Player_TogglePause(void) { MUTEX_LOCK(vars); if (pf) pf->forbid=1-pf->forbid; MUTEX_UNLOCK(vars); } MIKMODAPI void Player_SetSpeed(UWORD speed) { MUTEX_LOCK(vars); if (pf) pf->sngspd=speed?(speed<32?speed:32):1; MUTEX_UNLOCK(vars); } MIKMODAPI void Player_SetTempo(UWORD tempo) { if (tempo<32) tempo=32; MUTEX_LOCK(vars); if (pf) { if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255; pf->bpm=tempo; } MUTEX_UNLOCK(vars); } MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo) { int i; if (numvoices > md_sngchn) numvoices = md_sngchn; MUTEX_LOCK(vars); if (pf) for (i = 0; i < md_sngchn; i++) { vinfo [i].i = pf->voice[i].main.i; vinfo [i].s = pf->voice[i].main.s; vinfo [i].panning = pf->voice [i].main.panning; vinfo [i].volume = pf->voice [i].main.chanvol; vinfo [i].period = pf->voice [i].main.period; vinfo [i].kick = pf->voice [i].main.kick_flag; pf->voice [i].main.kick_flag = 0; } MUTEX_UNLOCK(vars); return numvoices; } /* Get current module order */ MIKMODAPI int Player_GetOrder(void) { int ret; MUTEX_LOCK(vars); ret = pf ? pf->sngpos :0; /* pf->positions[pf->sngpos ? pf->sngpos-1 : 0]: 0; */ MUTEX_UNLOCK(vars); return ret; } /* Get current module row */ MIKMODAPI int Player_GetRow(void) { int ret; MUTEX_LOCK(vars); ret = pf ? pf->patpos : 0; MUTEX_UNLOCK(vars); return ret; } /* ex:set ts=4: */