Mercurial > ~darius > hgwebdir.cgi > mikmod
view playercode/mplayer.c @ 22:d9f79436e0af default tip
Makefile
Add ranlib for the library
author | darius |
---|---|
date | Fri, 24 Apr 1998 08:05:26 +0000 |
parents | 80fa6dd10e14 |
children |
line wrap: on
line source
/* * --> The Protracker Player Driver -> Part of the SPLAYER pack for MikMod * 3.0 * * The protracker driver supports all base Protracker 3.x commands and fea- * tures. */ #include <string.h> #include <stdarg.h> #include "mikmod.h" static void DoNNAEffects(UBYTE dat); /* * Set forbid to 1 when you want to modify any of the pf->sngpos, pf->patpos * etc. variables and clear it when you're done. This prevents getting * strange results due to intermediate interrupts. */ static UNIMOD *pf; /* <- this modfile is being played */ static SWORD mp_channel; /* channel it's working on */ static MP_CONTROL *a; /* current AUDTMP it's working on */ static int isfirst; static MP_VOICE aout_dummy; static UWORD mytab[12] = { 1712 * 16, 1616 * 16, 1524 * 16, 1440 * 16, 1356 * 16, 1280 * 16, 1208 * 16, 1140 * 16, 1076 * 16, 1016 * 16, 960 * 16, 907 * 16 }; static 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 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 * Fast * Tracker 2 [XM] modules): */ static 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 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 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 }; /* * 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 forground channel is a bonus x4 4) an active * envelope with keyoff is a handicap -x2 */ static int MP_FindEmptyChannel(int curchan) { /* returns mp_control index of free channel */ MP_VOICE *a; ULONG t, k, tvol, p, pp; /* * for(t=md_sngchn; t; t--, audpool++) { if(audpool == md_sngchn) * audpool = 0; if(!(pf->voice[audpool].kick) && Voice_Stopped(audpool)) * { audpool++; return audpool-1; } } */ for (t = 0; t < md_sngchn; t++) { if (!(pf->voice[t].kick) && Voice_Stopped(t)) { return t; } } tvol = 0xffffff; t = 0; p = 0; a = pf->voice; for (k = 0; k < md_sngchn; k++, a++) { if (!a->kick) { pp = a->totalvol << ((a->s->flags & SF_LOOP) ? 1 : 0); if ((a->master != NULL) && (a == a->master->slave)) pp <<= 2; /* * if(a->volflg & EF_ON) { if(a->volflg & (EF_SUSTAIN | * EF_LOOP)) { if(a->keyoff & KEY_OFF) { pp >>= 1; * if(a->venv.env[a->venv.end].val < 32) pp>>=1; } else pp <<= 1; * } else pp <<= 1; } */ if (pp < tvol) { tvol = pp; t = k; } } } if (tvol > (8000 * 7)) return -1; /* mp_channel; */ return t; } static SWORD Interpolate(SWORD p, SWORD p1, SWORD p2, SWORD v1, SWORD v2) { SWORD dp, dv, di; if (p1 == p2) return v1; dv = v2 - v1; dp = p2 - p1; di = p - p1; return v1 + ((SLONG) (di * dv) / dp); } UWORD getlinearperiod(UBYTE note, ULONG fine) { return ((10L * 12 * 16 * 4) - ((ULONG) note * 16 * 4) - (fine / 2) + 64); } static UWORD getlogperiod(UBYTE note, ULONG fine) { UBYTE n, o; UWORD p1, p2; ULONG i; n = note % 12; o = note / 12; i = (n << 3) + (fine >> 4); /* n*8 + fine/16 */ p1 = logtab[i]; p2 = logtab[i + 1]; return (Interpolate(fine / 16, 0, 15, p1, p2) >> o); } static UWORD getoldperiod(UBYTE note, ULONG speed) { UBYTE n, o; ULONG period; if (!speed) return 4242; /* <- prevent divide overflow.. (42 eheh) */ n = note % 12; o = note / 12; period = ((8363l * (ULONG) mytab[n]) >> o) / speed; return period; } static UWORD GetPeriod(UBYTE note, ULONG speed) { if (pf->flags & UF_XMPERIODS) { return (pf->flags & UF_LINEAR) ? getlinearperiod(note, speed) : getlogperiod(note, speed); } 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) { return (pan + (((envpan - 128) * (128 - abs(pan - 128))) / 128)); } static void 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; } /* * 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, panniong 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. 1. * 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(ENVPR * t, SWORD v, UBYTE keyoff) { 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; /* compute the current envelope value between points a and b */ if (a == b) v = t->env[a].val; else v = InterpolateEnv(p, &t->env[a], &t->env[b]); p++; /* pointer reached point b? */ if (p >= t->env[b].pos) { a = b++; /* shift points a and b */ /* Check for loops, sustain loops, or end of envelope. */ if ((t->flg & EF_SUSTAIN) && !(keyoff & KEY_OFF) && (b > t->susend)) { a = t->susbeg; if (t->susbeg == t->susend) b = a; else b = a + 1; p = t->env[a].pos; } else if ((t->flg & EF_LOOP) && (b > t->end)) { a = t->beg; if (t->beg == t->end) b = a; else b = a + 1; p = t->env[a].pos; } else { if (b >= t->pts) { if ((t->flg & EF_VOLENV) && (mp_channel != -1)) { pf->voice[mp_channel].keyoff |= KEY_FADE; if (v == 0) pf->voice[mp_channel].fadevol = 0; } b--; p--; } } } t->a = a; t->b = b; t->p = p; } return v; } /* XM linear period to frequency conversion */ ULONG getfrequency(UBYTE flags, ULONG period) { ULONG result; if (flags & UF_LINEAR) result = lintab[period % 768] >> (period / 768); else result = (8363L * 1712L) / period; return result; } static void DoEEffects(UBYTE dat) { UBYTE nib; nib = dat & 0xf; switch (dat >> 4) { case 0x0: /* filter toggle, not supported */ break; case 0x1: /* fineslide up */ if (!pf->vbtick) a->tmpperiod -= (nib << 2); break; case 0x2: /* fineslide dn */ if (!pf->vbtick) 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 */ /* a->speed=finetune[nib]; */ /* * * a->tmpperiod=GetPeriod(a->note,pf->samples[a->sample].transpose,a-> * speed); */ break; case 0x6: /* set patternloop */ if (pf->vbtick) break; /* * hmm.. this one is a real kludge. But now it works */ if (nib) { /* set reppos or repcnt ? set repcnt, so * check if repcnt already is set, which * means we are already looping */ if (pf->pat_repcnt > 0) pf->pat_repcnt--; /* already looping, decrease counter */ else pf->pat_repcnt = nib; /* not yet looping, so set repcnt */ if (pf->pat_repcnt) /* jump to reppos if repcnt>0 */ pf->patpos = pf->pat_reppos; } else { pf->pat_reppos = pf->patpos - 1; /* set reppos */ } break; case 0x7: /* set tremolo waveform */ a->wavecontrol &= 0x0f; a->wavecontrol |= nib << 4; break; case 0x8: /* set panning */ if (pf->panflag) { if (nib <= 8) nib *= 16; else nib *= 17; a->panning = nib; pf->panning[mp_channel] = nib; } break; case 0x9: /* retrig note */ /* * only retrigger if data nibble > 0 */ if (nib > 0) { if (a->retrig == 0) { /* when retrig counter reaches 0, * reset counter and restart the * sample */ a->kick = 1; a->retrig = nib; } a->retrig--; /* countdown */ } break; case 0xa: /* fine volume slide up */ if (pf->vbtick) break; a->tmpvolume += nib; if (a->tmpvolume > 64) a->tmpvolume = 64; break; case 0xb: /* fine volume slide dn */ if (pf->vbtick) break; a->tmpvolume -= nib; if (a->tmpvolume < 0) a->tmpvolume = 0; break; case 0xc: /* cut note */ /* * When pf->vbtick reaches the cut-note value, turn the volume to * zero ( Just like on the amiga) */ if (pf->vbtick >= nib) a->tmpvolume = 0; /* just turn the volume down */ break; case 0xd: /* note delay */ /* * delay the start of the sample until pf->vbtick==nib */ if (pf->vbtick == nib) {/* a->kick = 1; */ a->notedelay = 0; } else if (pf->vbtick == 0) { /* a->kick = 0; */ a->notedelay = 1; } break; case 0xe: /* pattern delay */ if (pf->vbtick) break; if (!pf->patdly2) pf->patdly = nib + 1; /* only once (when pf->vbtick = 0) */ break; case 0xf: /* invert loop, not supported */ break; } } static void DoVibrato(void) { UBYTE q; UWORD temp; 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: /* Evil random wave */ temp = rand() & 255; break; } temp *= a->vibdepth; temp >>= 7; temp <<= 2; if (a->vibpos >= 0) a->period = a->tmpperiod + temp; else a->period = a->tmpperiod - temp; if (pf->vbtick) a->vibpos += a->vibspd; /* do not update when pf->vbtick==0 */ } static void DoTremolo(void) { UBYTE q; UWORD temp; 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: /* Evil random wave */ temp = rand() & 255; 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; } if (pf->vbtick) a->trmpos += a->trmspd; /* do not update when pf->vbtick == 0 */ } static void DoVolSlide(UBYTE dat) { if (!pf->vbtick) return; /* do not update when pf->vbtick == 0 */ a->tmpvolume += dat >> 4; /* volume slide */ a->tmpvolume -= dat & 0xf; if (a->tmpvolume < 0) a->tmpvolume = 0; if (a->tmpvolume > 64) a->tmpvolume = 64; } static void DoToneSlide(void) { int dist; if (a->period == 0) return; if (!pf->vbtick) { a->tmpperiod = a->period; return; } /* * We have to slide a->period towards a->wantedperiod, so compute the * difference between those two values */ dist = a->period - a->wantedperiod; if (dist == 0 || a->portspeed > abs(dist)) /* if they are equal or if * portamentospeed is too big */ a->period = a->wantedperiod; /* make tmpperiod equal tperiod */ else if (dist > 0) /* dist > 0 ? */ a->period -= a->portspeed; /* then slide up */ else a->period += a->portspeed; /* dist < 0 -> slide down */ a->tmpperiod = a->period; } static void DoPTEffect0(UBYTE dat) { UBYTE note; note = a->note; if (dat != 0) { switch (pf->vbtick % 3) { case 1: note += (dat >> 4); break; case 2: note += (dat & 0xf); break; } a->period = GetPeriod(note, a->speed); a->ownper = 1; } } /* * ----------------------------------------- --> ScreamTreacker 3 Specific * Effects <-- ----------------------------------------- */ static void DoS3MVolSlide(UBYTE inf) { UBYTE lo, hi; if (inf) a->s3mvolslide = inf; inf = a->s3mvolslide; lo = inf & 0xf; hi = inf >> 4; if (hi == 0) a->tmpvolume -= lo; else if (lo == 0) a->tmpvolume += hi; else if (hi == 0xf) { if (!pf->vbtick) a->tmpvolume -= lo; } else if (lo == 0xf) { if (!pf->vbtick) a->tmpvolume += hi; } if (a->tmpvolume < 0) a->tmpvolume = 0; if (a->tmpvolume > 64) a->tmpvolume = 64; } static void DoS3MSlideDn(UBYTE inf) { UBYTE hi, lo; if (inf != 0) a->slidespeed = inf; else inf = a->slidespeed; hi = inf >> 4; lo = inf & 0xf; if (hi == 0xf) { if (!pf->vbtick) a->tmpperiod += (UWORD) lo << 2; } else if (hi == 0xe) { if (!pf->vbtick) a->tmpperiod += lo; } else { if (pf->vbtick) a->tmpperiod += (UWORD) inf << 2; } } static void DoS3MSlideUp(UBYTE inf) { UBYTE hi, lo; if (inf != 0) a->slidespeed = inf; else inf = a->slidespeed; hi = inf >> 4; lo = inf & 0xf; if (hi == 0xf) { if (!pf->vbtick) a->tmpperiod -= (UWORD) lo << 2; } else if (hi == 0xe) { if (!pf->vbtick) a->tmpperiod -= lo; } else { if (pf->vbtick) a->tmpperiod -= (UWORD) inf << 2; } } static void DoS3MTremor(UBYTE inf) { UBYTE on, off; if (inf != 0) a->s3mtronof = inf; else inf = a->s3mtronof; if (!pf->vbtick) return; on = (inf >> 4) + 1; off = (inf & 0xf) + 1; a->s3mtremor %= (on + off); a->volume = (a->s3mtremor < on) ? a->tmpvolume : 0; a->s3mtremor++; } static void DoS3MRetrig(UBYTE inf) { UBYTE hi, lo; hi = inf >> 4; lo = inf & 0xf; if (inf) { a->s3mrtgslide = hi; a->s3mrtgspeed = lo; } /* * only retrigger if lo nibble > 0 */ if (a->s3mrtgspeed > 0) { if (a->retrig == 0) { /* when retrig counter reaches 0, reset * counter and restart the sample */ if (!a->kick) a->kick = 2; a->retrig = a->s3mrtgspeed; if (pf->vbtick) { /* don't slide on first retrig */ 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 = 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) / 2; break; case 0xf: a->tmpvolume = a->tmpvolume << 1; break; } if (a->tmpvolume < 0) a->tmpvolume = 0; if (a->tmpvolume > 64) a->tmpvolume = 64; } } a->retrig--; /* countdown */ } } static void DoS3MSpeed(UBYTE speed) { if (pf->vbtick || pf->patdly2) return; if (speed) { pf->sngspd = speed; pf->vbtick = 0; } } static void DoS3MTempo(UBYTE tempo) { if (pf->vbtick || pf->patdly2) return; pf->bpm = tempo; } static void DoS3MFineVibrato(void) { UBYTE q; UWORD temp; 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: /* evil random */ temp = rand() & 255; /* (range 0 to 255) */ } temp *= a->vibdepth; temp >>= 8; if (a->vibpos >= 0) a->period = a->tmpperiod + temp; else a->period = a->tmpperiod - temp; a->vibpos += a->vibspd; } static void DoS3MTremolo(void) { UBYTE q; UWORD temp; 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: /* evil random */ temp = rand() & 255; /* (range 0 to 255) */ } 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; } if (pf->vbtick) a->trmpos += a->trmspd; /* do not update when pf->vbtick == 0 */ } /* * -------------------------------------- --> FastTracker 2 Specific Effects * <-- -------------------------------------- */ static void DoXMVolSlide(UBYTE inf) { UBYTE lo, hi; if (inf) a->s3mvolslide = inf; inf = a->s3mvolslide; if (!pf->vbtick) return; lo = inf & 0xf; hi = inf >> 4; if (hi == 0) a->tmpvolume -= lo; else a->tmpvolume += hi; if (a->tmpvolume < 0) a->tmpvolume = 0; else if (a->tmpvolume > 64) a->tmpvolume = 64; } static void DoXMGlobalSlide(UBYTE inf) { if (pf->vbtick) { if (inf) pf->globalslide = inf; else inf = pf->globalslide; if (inf & 0xf0) inf &= 0xf0; pf->volume = pf->volume + ((inf >> 4) - (inf & 0xf)) * 2; if (pf->volume < 0) pf->volume = 0; else if (pf->volume > 128) pf->volume = 128; } } static void DoXMPanSlide(UBYTE inf) { UBYTE lo, hi; SWORD pan; if (inf != 0) a->pansspd = inf; else inf = a->pansspd; if (!pf->vbtick) return; lo = inf & 0xf; hi = inf >> 4; /* slide right has absolute priority: */ if (hi) lo = 0; pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; pan -= lo; pan += hi; if (pan < 0) pan = 0; if (pan > 255) pan = 255; a->panning = pan; } static void DoXMExtraFineSlideUp(UBYTE inf) { if (!pf->vbtick) { if (inf) a->ffportupspd = inf; else inf = a->ffportupspd; a->period -= inf; } a->tmpperiod = a->period; } static void DoXMExtraFineSlideDown(UBYTE inf) { if (!pf->vbtick) { if (inf) a->ffportdnspd = inf; else inf = a->ffportdnspd; a->period += inf; } a->tmpperiod = a->period; } /* * --------------------------------------- --> ImpulseTracker Player * Functions <-- --------------------------------------- */ static void DoITChanVolSlide(UBYTE inf) { UBYTE lo, hi; if (inf) a->chanvolslide = inf; inf = a->chanvolslide; lo = inf & 0xf; hi = inf >> 4; if (hi == 0) { a->chanvol -= lo; } else if (lo == 0) { a->chanvol += hi; } else if (hi == 0xf) { if (!pf->vbtick) a->chanvol -= lo; } else if (lo == 0xf) { if (!pf->vbtick) a->chanvol += hi; } if (a->chanvol < 0) a->chanvol = 0; if (a->chanvol > 64) a->chanvol = 64; } static void DoITGlobalSlide(UBYTE inf) { UBYTE lo, hi; if (inf) pf->globalslide = inf; inf = pf->globalslide; lo = inf & 0xf; hi = inf >> 4; if (lo == 0) { pf->volume += hi; } else if (hi == 0) { pf->volume -= lo; } else if (lo == 0xf) { if (!pf->vbtick) pf->volume += hi; } else if (hi == 0xf) { if (!pf->vbtick) pf->volume -= lo; } if (pf->volume < 0) pf->volume = 0; if (pf->volume > 128) pf->volume = 128; } static void DoITPanSlide(UBYTE inf) { UBYTE lo, hi; SWORD pan; if (inf) a->pansspd = inf; inf = a->pansspd; lo = inf & 0xf; hi = inf >> 4; pan = (a->panning == PAN_SURROUND) ? 128 : a->panning; if (hi == 0) { pan += lo << 2; } else if (lo == 0) { pan -= hi << 2; } else if (hi == 0xf) { if (!pf->vbtick) pan += lo << 2; } else if (lo == 0xf) { if (!pf->vbtick) pan -= hi << 2; } if (pan > 255) pan = 255; if (pan < 0) pan = 0; a->panning = /* pf->panning[mp_channel] = */ pan; } static void DoITVibrato(void) { UBYTE q; UWORD temp; 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: /* evil random */ temp = rand() & 255; /* (range 0 to 255) */ break; } temp *= a->vibdepth; temp >>= 8; temp <<= 2; if (a->vibpos >= 0) a->period = a->tmpperiod + temp; else a->period = a->tmpperiod - temp; a->vibpos += a->vibspd; } static void DoITFineVibrato(void) { UBYTE q; UWORD temp; 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: /* evil random */ temp = rand() & 255; /* (range 0 to 255) */ break; } temp *= a->vibdepth; temp >>= 8; if (a->vibpos >= 0) a->period = a->tmpperiod + temp; else a->period = a->tmpperiod - temp; a->vibpos += a->vibspd; } static void DoITTremor(UBYTE inf) { UBYTE on, off; if (inf != 0) a->s3mtronof = inf; else inf = a->s3mtronof; if (!pf->vbtick) return; on = (inf >> 4); off = (inf & 0xf); a->s3mtremor %= (on + off); a->volume = (a->s3mtremor < on) ? a->tmpvolume : 0; a->s3mtremor++; } static void DoITPanbrello(void) { UBYTE q; static SLONG temp; q = a->panbpos; switch (a->panbwave) { case 0: /* sine */ temp = PanbrelloTable[q]; break; /* only sinewave is correctly supported right now */ case 1: /* ramp down */ q <<= 3; temp = q; break; case 2: /* square wave */ temp = 64; break; case 3: /* evil random */ if (a->panbpos >= a->panbspd) { a->panbpos = 0; temp = rand() & 255; } } temp *= a->panbdepth; temp /= 8; a->panning = pf->panning[mp_channel] + temp; a->panbpos += a->panbspd; } static void DoITToneSlide(void) { int dist; if (a->period == 0) return; if (!pf->vbtick) { a->tmpperiod = a->period; return; } /* * We have to slide a->period towards a->wantedperiod, compute the * difference between those two values */ dist = a->period - a->wantedperiod; if ((dist == 0) || /* if they are equal */ ((a->slidespeed << 2) > abs(dist))) { /* or if portamentospeed is * too big */ a->period = a->wantedperiod; /* make tmpperiod equal tperiod */ } else if (dist > 0) { /* dist > 0 ? */ a->period -= a->slidespeed << 2; /* then slide up */ } else { a->period += a->slidespeed << 2; /* dist<0 -> slide down */ } a->tmpperiod = a->period; } /* * Impulse/Scream Tracker Sxx effects. All Sxx effects share the same memory * space. */ static void DoSSEffects(UBYTE dat) { UBYTE inf, c; inf = dat & 0xf; c = dat >> 4; if (dat == 0) { c = a->sseffect; inf = a->ssdata; } else { a->sseffect = c; a->ssdata = inf; } switch (c) { case SS_GLISSANDO: /* S1x set glissando voice */ DoEEffects(0x30 | inf); break; case SS_FINETUNE: /* S2x set finetune */ DoEEffects(0x50 | inf); break; case SS_VIBWAVE: /* S3x set vibrato waveform */ DoEEffects(0x40 | inf); break; case SS_TREMWAVE: /* S4x set tremolo waveform */ DoEEffects(0x70 | inf); break; case SS_PANWAVE: /* The Satanic Panbrello waveform */ a->panbwave = (UniGetByte()); break; case SS_FRAMEDELAY: /* S6x Delay x number of frames (patdly) */ DoEEffects(0xe0 | inf); break; case SS_S7EFFECTS: /* S7x Instrument / NNA commands */ DoNNAEffects(UniGetByte()); break; case SS_PANNING: /* S8x set panning position */ DoEEffects(0x80 | inf); break; case SS_SURROUND: /* S9x Set Surround Sound */ a->panning = pf->panning[mp_channel] = PAN_SURROUND; break; case SS_HIOFFSET: /* SAy Set high order sample offset yxx00h */ a->hioffset |= UniGetByte() << 16; break; case SS_PATLOOP: /* SBx pattern loop */ DoEEffects(0x60 | inf); break; case SS_NOTECUT: /* SCx notecut */ DoEEffects(0xC0 | inf); break; case SS_NOTEDELAY: /* SDx notedelay */ DoEEffects(0xD0 | inf); break; case SS_PATDELAY: /* SEx patterndelay */ DoEEffects(0xE0 | inf); break; } } /* * Impulse Tracker Volume/Pan Column effects. All volume/pan column effects * share the same memory space. */ static void DoVolEffects(UBYTE c) { UBYTE inf; inf = UniGetByte(); if (c == 0 && inf == 0) { c = a->voleffect; inf = a->voldata; } else { a->voleffect = c; a->voldata = inf; } switch (c) { case 0: break; /* do nothing */ case VOL_VOLUME: if (pf->vbtick) break; if (inf > 64) inf = 64; a->tmpvolume = inf; break; case VOL_PANNING: if (pf->panflag) { a->panning = inf; pf->panning[mp_channel] = inf; } break; case VOL_VOLSLIDE: DoS3MVolSlide(inf); break; case VOL_PITCHSLIDEDN: DoS3MSlideDn(UniGetByte()); break; case VOL_PITCHSLIDEUP: DoS3MSlideUp(UniGetByte()); break; case VOL_PORTAMENTO: if (inf != 0) a->slidespeed = inf; if (a->period != 0) { if (!(pf->vbtick == pf->sngspd - 1) && (a->newsamp)) { a->kick = 1; a->start = -1; /* a->period *= a->speed * a->newsamp; */ } else a->kick = 0; DoITToneSlide(); a->ownper = 1; } break; case VOL_VIBRATO: if (inf & 0x0f) a->vibdepth = inf & 0xf; if (inf & 0xf0) a->vibspd = (inf & 0xf0) >> 2; DoITVibrato(); a->ownper = 1; break; } } /* * -------------------------------- --> General Player Functions <-- * -------------------------------- */ static void pt_playeffects(void) { UBYTE dat, c; while (c = UniGetByte()) switch (c) { case UNI_NOTE: case UNI_INSTRUMENT: UniSkipOpcode(c); break; case UNI_PTEFFECT0: DoPTEffect0(UniGetByte()); break; case UNI_PTEFFECT1: dat = UniGetByte(); if (dat != 0) a->slidespeed = (UWORD) dat << 2; if (pf->vbtick) a->tmpperiod -= a->slidespeed; break; case UNI_PTEFFECT2: dat = UniGetByte(); if (dat != 0) a->slidespeed = (UWORD) dat << 2; if (pf->vbtick) a->tmpperiod += a->slidespeed; break; case UNI_PTEFFECT3: dat = UniGetByte(); if (dat != 0) { a->portspeed = dat; a->portspeed <<= 2; } if (a->period != 0) { a->kick = 0; /* temp XM fix */ DoToneSlide(); a->ownper = 1; } break; case UNI_PTEFFECT4: dat = UniGetByte(); if (dat & 0x0f) a->vibdepth = dat & 0xf; if (dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; DoVibrato(); a->ownper = 1; break; case UNI_PTEFFECT5: dat = UniGetByte(); a->kick = 0; DoToneSlide(); DoVolSlide(dat); a->ownper = 1; break; case UNI_PTEFFECT6: dat = UniGetByte(); DoVibrato(); DoVolSlide(dat); a->ownper = 1; break; case UNI_PTEFFECT7: dat = UniGetByte(); if (dat & 0x0f) a->trmdepth = dat & 0xf; if (dat & 0xf0) a->trmspd = (dat & 0xf0) >> 2; DoTremolo(); a->ownvol = 1; break; case UNI_PTEFFECT8: dat = UniGetByte(); if (pf->panflag) { a->panning = dat; pf->panning[mp_channel] = dat; } break; case UNI_PTEFFECT9: dat = UniGetByte(); if (dat) a->soffset = (UWORD) dat << 8; a->start = a->hioffset | a->soffset; if ((a->s != NULL) && (a->start > a->s->length)) a->start = a->s->loopstart; break; case UNI_PTEFFECTA: DoVolSlide(UniGetByte()); break; case UNI_PTEFFECTB: dat = UniGetByte(); if (pf->patdly2) break; pf->patbrk = 0; pf->sngpos = dat - 1; pf->posjmp = 3; break; case UNI_PTEFFECTC: dat = UniGetByte(); if (pf->vbtick) break; if (dat > 64) dat = 64; a->tmpvolume = dat; break; case UNI_PTEFFECTD: dat = UniGetByte(); if (pf->patdly2) break; pf->patbrk = dat; if (pf->patbrk > pf->pattrows[mp_channel]) pf->patbrk = pf->pattrows[mp_channel]; pf->posjmp = 3; break; case UNI_PTEFFECTE: DoEEffects(UniGetByte()); break; case UNI_PTEFFECTF: dat = UniGetByte(); if (pf->vbtick || pf->patdly2) break; if (pf->extspd && (dat >= 0x20)) pf->bpm = dat; else { if (dat) { pf->sngspd = dat; pf->vbtick = 0; } } break; case UNI_S3MEFFECTA: DoS3MSpeed(UniGetByte()); break; case UNI_S3MEFFECTD: DoS3MVolSlide(UniGetByte()); break; case UNI_S3MEFFECTE: DoS3MSlideDn(UniGetByte()); break; case UNI_S3MEFFECTF: DoS3MSlideUp(UniGetByte()); break; case UNI_S3MEFFECTI: DoS3MTremor(UniGetByte()); a->ownvol = 1; break; case UNI_S3MEFFECTQ: DoS3MRetrig(UniGetByte()); break; case UNI_S3MEFFECTR: dat = UniGetByte(); if (dat & 0x0f) a->trmdepth = dat & 0xf; if (dat & 0xf0) a->trmspd = (dat & 0xf0) >> 2; DoS3MTremolo(); a->ownvol = 1; break; case UNI_S3MEFFECTT: DoS3MTempo(UniGetByte()); break; case UNI_S3MEFFECTU: dat = UniGetByte(); if (dat & 0x0f) a->vibdepth = dat & 0xf; if (dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; DoS3MFineVibrato(); a->ownper = 1; break; case UNI_KEYOFF: a->keyoff |= KEY_OFF; if (a->i != NULL) { if (!(a->i->volflg & EF_ON) || (a->i->volflg & EF_LOOP)) a->keyoff = KEY_KILL; } break; case UNI_KEYFADE: if (pf->vbtick >= UniGetByte()) { a->keyoff = KEY_KILL; if ((a->i != NULL) && !(a->i->volflg & EF_ON)) a->fadevol = 0; } break; case UNI_VOLEFFECTS: DoVolEffects(UniGetByte()); break; case UNI_XMEFFECT4: dat = UniGetByte(); if (pf->vbtick) { if (dat & 0x0f) a->vibdepth = dat & 0xf; if (dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; } DoVibrato(); a->ownper = 1; break; case UNI_XMEFFECTA: DoXMVolSlide(UniGetByte()); break; case UNI_XMEFFECTE1: /* xm fineslide up */ dat = UniGetByte(); if (!pf->vbtick) { if (dat) a->fportupspd = dat; else dat = a->fportupspd; a->tmpperiod -= (dat << 2); } break; case UNI_XMEFFECTE2: /* xm fineslide dn */ dat = UniGetByte(); if (!pf->vbtick) { if (dat) a->fportdnspd = dat; else dat = a->fportdnspd; a->tmpperiod += (dat << 2); } break; case UNI_XMEFFECTEA: /* fine volume slide up */ dat = UniGetByte(); if (pf->vbtick) break; if (dat) a->fslideupspd = dat; else dat = a->fslideupspd; a->tmpvolume += dat; if (a->tmpvolume > 64) a->tmpvolume = 64; break; case UNI_XMEFFECTEB: /* fine volume slide dn */ dat = UniGetByte(); if (pf->vbtick) break; if (dat) a->fslidednspd = dat; else dat = a->fslidednspd; a->tmpvolume -= dat; if (a->tmpvolume < 0) a->tmpvolume = 0; break; case UNI_XMEFFECTG: pf->volume = UniGetByte(); break; case UNI_XMEFFECTH: DoXMGlobalSlide(UniGetByte()); break; case UNI_XMEFFECTL: dat = UniGetByte(); if (!pf->vbtick && a->i != NULL) { UWORD points; INSTRUMENT *i = a->i; MP_VOICE *aout; if ((aout = a->slave) != NULL) { points = i->volenv[i->volpts - 1].pos; aout->venv.p = aout->venv.env[(dat > points) ? points : dat].pos; points = i->panenv[i->panpts - 1].pos; aout->penv.p = aout->penv.env[(dat > points) ? points : dat].pos; } } break; case UNI_XMEFFECTP: DoXMPanSlide(UniGetByte()); break; case UNI_XMEFFECTX1: DoXMExtraFineSlideUp(UniGetByte()); a->ownper = 1; break; case UNI_XMEFFECTX2: DoXMExtraFineSlideDown(UniGetByte()); a->ownper = 1; break; case UNI_ITEFFECTG: dat = UniGetByte(); if (dat != 0) a->slidespeed = dat; if (a->period != 0) { if ((pf->vbtick < 1) && (a->newsamp)) { a->kick = 1; a->start = -1; /* a->period *= a->speed * a->newsamp; */ } else a->kick = 0; DoITToneSlide(); a->ownper = 1; } break; case UNI_ITEFFECTH: /* it vibrato */ dat = UniGetByte(); if (dat & 0x0f) a->vibdepth = dat & 0xf; if (dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; DoITVibrato(); a->ownper = 1; break; case UNI_ITEFFECTI: /* it tremor */ DoITTremor(UniGetByte()); a->ownvol = 1; break; case UNI_ITEFFECTM: a->chanvol = UniGetByte(); if (a->chanvol > 64) a->chanvol = 64; else if (a->chanvol < 0) a->chanvol = 0; break; case UNI_ITEFFECTN: /* Slide / Fineslide Channel Volume */ DoITChanVolSlide(UniGetByte()); break; case UNI_ITEFFECTP: /* slide / fineslide channel panning */ DoITPanSlide(UniGetByte()); break; case UNI_ITEFFECTU: /* fine vibrato */ dat = UniGetByte(); if (dat & 0x0f) a->vibdepth = dat & 0xf; if (dat & 0xf0) a->vibspd = (dat & 0xf0) >> 2; DoITFineVibrato(); a->ownper = 1; break; case UNI_ITEFFECTW: /* Slide / Fineslide Global volume */ DoITGlobalSlide(UniGetByte()); break; case UNI_ITEFFECTY: /* The Satanic Panbrello */ dat = UniGetByte(); if (dat & 0x0f) a->panbdepth = (dat & 0xf); if (dat & 0xf0) a->panbspd = (dat & 0xf0) >> 4; DoITPanbrello(); break; case UNI_ITEFFECTS0: DoSSEffects(UniGetByte()); break; default: UniSkipOpcode(c); break; } } static void DoNNAEffects(UBYTE dat) { int t; MP_VOICE *aout; dat &= 0xf; aout = (a->slave == NULL) ? &aout_dummy : a->slave; switch (dat) { case 0x0: /* Past note cut */ for (t = 0; t < md_sngchn; t++) if (pf->voice[t].master == a) pf->voice[t].fadevol = 0; break; case 0x1: /* Past note off */ for (t = 0; t < md_sngchn; t++) if (pf->voice[t].master == a) { pf->voice[t].keyoff |= KEY_OFF; if (!(pf->voice[t].venv.flg & EF_ON)) pf->voice[t].keyoff = KEY_KILL; } break; case 0x2: /* Past note fade */ for (t = 0; t < md_sngchn; t++) if (pf->voice[t].master == a) pf->voice[t].keyoff |= KEY_FADE; break; case 0x3: /* set NNA note cut */ a->nna = (a->nna & ~0x3f) | NNA_CUT; break; case 0x4: /* set NNA note continue */ a->nna = (a->nna & ~0x3f) | NNA_CONTINUE; break; case 0x5: /* set NNA note off */ a->nna = (a->nna & ~0x3f) | NNA_OFF; break; case 0x6: /* set NNA note fade */ a->nna = (a->nna & ~0x3f) | NNA_FADE; break; case 0x7: /* disable volume envelope */ aout->volflg &= ~EF_ON; break; case 0x8: /* enable volume envelope */ aout->volflg |= EF_ON; break; case 0x9: /* disable panning envelope */ aout->panflg &= ~EF_ON; break; case 0xa: /* enable panning envelope */ aout->panflg |= EF_ON; break; case 0xb: /* disable pitch envelope */ aout->pitflg &= ~EF_ON; break; case 0xc: /* enable pitch envelope */ aout->pitflg |= EF_ON; break; } } void Player_HandleTick(void) { MP_VOICE *aout; /* current audout (slave of audtmp) it's * working on */ int t, tr, t2, k; ULONG tmpvol, period; UBYTE c; BOOL funky; SAMPLE *s; INSTRUMENT *i; if (isfirst) { /* don't handle the very first ticks, this * allows the other hardware to settle down * so we don't loose any starting notes */ isfirst--; return; } if (pf == NULL || pf->forbid) return; if (++pf->vbtick >= pf->sngspd) { 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) pf->patpos--; /* so turn back pf->patpos by 1 */ } /* * Do we have to get a new patternpointer ? (when pf->patpos reaches * 64 or when a patternbreak is active) */ if (pf->patpos == pf->numrow) pf->posjmp = 3; if (pf->posjmp) { pf->patpos = pf->patbrk; pf->sngpos += (pf->posjmp - 2); pf->patbrk = pf->posjmp = 0; if (pf->sngpos >= pf->numpos) { if (!pf->loop) return; if ((pf->sngpos = pf->reppos) == 0) { pf->volume = pf->initvolume; pf->sngspd = pf->initspeed; pf->bpm = pf->inittempo; } } if (pf->sngpos < 0) pf->sngpos = pf->numpos - 1; } if (!pf->patdly2) { for (t = 0; t < pf->numchn; t++) { UBYTE inst; tr = pf->patterns[(pf->positions[pf->sngpos] * pf->numchn) + t]; pf->numrow = pf->pattrows[pf->positions[pf->sngpos]]; mp_channel = t; a = &pf->control[t]; a->row = (tr < pf->numtrk) ? UniFindRow(pf->tracks[tr], pf->patpos) : NULL; a->newsamp = 0; if (a->row == NULL) continue; UniSetRow(a->row); funky = 0; /* Funky is set to indicate note or inst * change */ while (c = UniGetByte()) { switch (c) { case UNI_NOTE: funky |= 1; a->anote = UniGetByte(); a->kick = 1; a->start = -1; /* 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: funky |= 2; inst = UniGetByte(); if (inst >= pf->numins) break; /* <- safety valve */ a->i = (pf->flags & UF_INST) ? &pf->instruments[inst] : NULL; a->retrig = 0; a->s3mtremor = 0; a->sample = inst; break; default: UniSkipOpcode(c); break; } } if (funky) { i = a->i; if (i != NULL) { if (i->samplenumber[a->anote] >= pf->numsmp) continue; s = &pf->samples[i->samplenumber[a->anote]]; a->note = i->samplenote[a->anote]; } else { a->note = a->anote; s = &pf->samples[a->sample]; } if (a->s != s) { a->s = s; a->newsamp = a->period; } /* channel or instrument determined panning ? */ a->panning = pf->panning[t]; if (s->flags & SF_OWNPAN) a->panning = s->panning; else if ((i != NULL) && (i->flags & IF_OWNPAN)) a->panning = i->panning; a->handle = s->handle; a->speed = s->speed; if (i != NULL) { if (i->flags & IF_PITCHPAN) a->panning += ((a->anote - i->pitpancenter) * i->pitpansep) / 8; a->pitflg = i->pitflg; a->volflg = i->volflg; a->panflg = i->panflg; a->nna = i->nnatype; a->dca = i->dca; a->dct = i->dct; } else { a->pitflg = 0; a->volflg = 0; a->panflg = 0; a->nna = 0; a->dca = 0; a->dct = 0; } if (funky & 2) { /* IT's random volume variations: 0:8 * bit fixed, and one bit for sign. */ a->volume = s->volume; a->tmpvolume = s->volume; if ((s != NULL) && (i != NULL)) { a->volume = a->tmpvolume = s->volume + ((s->volume * ((SLONG) i->rvolvar * (SLONG) ((rand() & 511) - 255))) / 25600); if (a->panning != PAN_SURROUND) a->panning += ((a->panning * ((SLONG) i->rpanvar * (SLONG) ((rand() & 511) - 255))) / 25600); } } period = GetPeriod(a->note, a->speed); a->wantedperiod = period; a->tmpperiod = period; a->keyoff = KEY_KICK; } } } } /* Update effects */ for (t = 0; t < pf->numchn; t++) { mp_channel = t; a = &pf->control[t]; if ((aout = a->slave) != NULL) { a->fadevol = aout->fadevol; a->period = aout->period; if (a->kick != 1) a->keyoff = aout->keyoff; } if (a->row == NULL) continue; UniSetRow(a->row); a->ownper = a->ownvol = 0; pt_playeffects(); if (!a->ownper) a->period = a->tmpperiod; if (!a->ownvol) a->volume = a->tmpvolume; if (a->s != NULL) { if (a->i != NULL) a->outvolume = (a->volume * a->s->globvol * a->i->globvol) / 1024; /* max val: 256 */ else a->outvolume = (a->volume * a->s->globvol) / 16; /* max val: 256 */ if (a->outvolume > 256) a->volume = 256; } } a = pf->control; if (pf->flags & UF_NNA) { for (t = 0; t < pf->numchn; t++, a++) { if (a->kick == 1) { if (a->slave != NULL) { aout = a->slave; if (aout->nna & 0x3f) { /* oh boy, we have to do an * NNA 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->nna) { case NNA_CONTINUE: break; /* continue note, do nothing */ case NNA_OFF: /* note off */ aout->keyoff |= KEY_OFF; if (!(aout->volflg & EF_ON) || (aout->volflg & EF_LOOP)) aout->keyoff = KEY_KILL; break; case NNA_FADE: aout->keyoff |= KEY_FADE; break; } } } k = 0; if (a->dct != DCT_OFF) { for (t2 = 0; t2 < md_sngchn; t2++) { if (!(Voice_Stopped(t2)) && (pf->voice[t2].masterchn == t) && (a->sample == pf->voice[t2].sample)) { switch (a->dct) { case DCT_NOTE: if (a->note == pf->voice[t2].note) k = 1; break; case DCT_SAMPLE: if (a->handle == pf->voice[t2].handle) k = 1; break; case DCT_INST: k = 1; break; } if (k == 1) { k = 0; switch (a->dca) { case DCA_CUT: pf->voice[t2].fadevol = 0; a->slave = &pf->voice[a->slavechn = t2]; break; case DCA_OFF: /* a->slave = &pf->voice[newchn]; */ pf->voice[t2].keyoff |= KEY_OFF; if (!(pf->voice[t2].volflg & EF_ON) || (pf->voice[t2].volflg & EF_LOOP)) pf->voice[t2].keyoff = KEY_KILL; break; case DCA_FADE: /* a->slave = &pf->voice[newchn]; */ pf->voice[t2].keyoff |= KEY_FADE; break; } } } } } /* DCT checking */ } /* if a->kick */ } /* for loop */ } a = pf->control; for (t = 0; t < pf->numchn; t++, a++) { int newchn; if (a->notedelay) continue; if (a->kick == 1) { /* If no channel was cut above, find an empty * or quiet channel here */ if (pf->flags & UF_NNA) { if (a->slave == NULL) { if ((newchn = MP_FindEmptyChannel(t)) != -1) a->slave = &pf->voice[a->slavechn = newchn]; } } else a->slave = &pf->voice[a->slavechn = t]; /* Assign parts of MP_VOICE only done for a KICK! */ if ((aout = a->slave) != NULL) { if (aout->mflag && (aout->master != NULL)) aout->master->slave = NULL; a->slave = aout; aout->master = a; aout->masterchn = t; aout->mflag = 1; } } else { aout = a->slave; } if (aout != NULL) { aout->kick = a->kick; aout->i = a->i; aout->s = a->s; aout->sample = a->sample; aout->handle = a->handle; aout->period = a->period; aout->panning = a->panning; aout->chanvol = a->chanvol; aout->fadevol = a->fadevol; aout->start = a->start; aout->volflg = a->volflg; aout->panflg = a->panflg; aout->pitflg = a->pitflg; aout->volume = a->outvolume; aout->keyoff = a->keyoff; aout->note = a->note; aout->nna = a->nna; } a->kick = 0; } /* Now set up the actual hardware channel playback information */ for (t = 0; t < md_sngchn; t++) { SWORD envpan, envvol = 256, envpit = 0; SLONG vibval, vibdpt; aout = &pf->voice[mp_channel = t]; i = aout->i; s = aout->s; if (s == NULL) continue; if (aout->period < 40) aout->period = 40; if (aout->period > 50000) aout->period = 50000; if (aout->kick) { Voice_Play(t, s, (aout->start == -1) ? ((s->flags & SF_UST_LOOP) ? s->loopstart : 0) : aout->start); /* aout->keyoff = KEY_KICK; */ aout->fadevol = 32768; aout->aswppos = 0; if ((i != NULL) && (aout->kick != 2)) { StartEnvelope(&aout->venv, aout->volflg, i->volpts, i->volsusbeg, i->volsusend, i->volbeg, i->volend, i->volenv, aout->keyoff); StartEnvelope(&aout->penv, aout->panflg, i->panpts, i->pansusbeg, i->pansusend, i->panbeg, i->panend, i->panenv, aout->keyoff); StartEnvelope(&aout->cenv, aout->pitflg, i->pitpts, i->pitsusbeg, i->pitsusend, i->pitbeg, i->pitend, i->pitenv, aout->keyoff); } aout->kick = 0; } if (i != NULL) { envvol = ProcessEnvelope(&aout->venv, 256, aout->keyoff); envpan = ProcessEnvelope(&aout->penv, 128, aout->keyoff); envpit = ProcessEnvelope(&aout->cenv, 32, aout->keyoff); } tmpvol = aout->fadevol; /* max 32768 */ tmpvol *= aout->chanvol;/* * max 64 */ tmpvol *= aout->volume; /* * max 256 */ tmpvol /= 16384L; /* tmpvol is max 32768 */ aout->totalvol = tmpvol >> 2; /* totalvolume used to determine * samplevolume */ tmpvol *= envvol; /* * max 256 */ tmpvol *= pf->volume; /* * max 128 */ tmpvol /= 4194304; if ((aout->masterchn != -1) && pf->control[aout->masterchn].muted) /* Channel Muting Line */ Voice_SetVolume(t, 0); else Voice_SetVolume(t, tmpvol); if (aout->panning == PAN_SURROUND) Voice_SetPanning(t, PAN_SURROUND); else { if (aout->penv.flg & EF_ON) Voice_SetPanning(t, DoPan(envpan, aout->panning)); else Voice_SetPanning(t, aout->panning); } if (aout->period && s->vibdepth) { switch (s->vibtype) { case 0: vibval = avibtab[s->avibpos & 127]; if (s->avibpos & 0x80) vibval = -vibval; break; case 1: vibval = 64; if (s->avibpos & 0x80) vibval = -vibval; break; case 2: vibval = 63 - (((s->avibpos + 128) & 255) >> 1); break; case 3: vibval = (((s->avibpos + 128) & 255) >> 1) - 64; break; } } if (s->vibflags & AV_IT) { if ((aout->aswppos >> 8) < s->vibdepth) { aout->aswppos += s->vibsweep; vibdpt = aout->aswppos; } else vibdpt = s->vibdepth << 8; vibval = (vibval * vibdpt) >> 16; if (aout->mflag) { if (!(pf->flags & UF_LINEAR)) vibval >>= 1; aout->period -= vibval; } } else { /* do XM style auto-vibrato */ if (!(aout->keyoff & KEY_OFF)) { if (aout->aswppos < s->vibsweep) { vibdpt = (aout->aswppos * s->vibdepth) / s->vibsweep; aout->aswppos++; } else vibdpt = s->vibdepth; } else { /* * key-off -> 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->period -= vibval; } /* update vibrato position */ s->avibpos = (s->avibpos + s->vibrate) & 0xff; if (aout->cenv.flg & EF_ON) { envpit = envpit - 32; aout->period -= envpit; } Voice_SetFrequency(t, getfrequency(pf->flags, aout->period)); if (aout->fadevol == 0) /* check for a dead note (fadevol = 0) */ Voice_Stop(t); else { /* if keyfade, start substracting * fadeoutspeed from fadevol: */ if ((i != NULL) && (aout->keyoff & KEY_FADE)) { if (aout->fadevol >= i->volfade) aout->fadevol -= i->volfade; else aout->fadevol = 0; } } MD_SetBPM(pf->bpm); } } BOOL Player_Init(UNIMOD * mf) { int t; mf->extspd = 1; mf->panflag = 1; mf->loop = 0; mf->pat_reppos = 0; mf->pat_repcnt = 0; mf->sngpos = 0; mf->sngspd = mf->initspeed; mf->volume = mf->initvolume; mf->vbtick = mf->sngspd; mf->patdly = 0; mf->patdly2 = 0; mf->bpm = mf->inittempo; mf->patpos = 0; mf->posjmp = 2; /* <- make sure the player fetches the first * note */ mf->patbrk = 0; /* Make sure the player doesn't start with garbage: */ if ((mf->control = (MP_CONTROL *) _mm_calloc(mf->numchn, sizeof(MP_CONTROL))) == NULL) return 1; if ((mf->voice = (MP_VOICE *) _mm_calloc(md_sngchn, sizeof(MP_VOICE))) == NULL) return 1; for (t = 0; t < mf->numchn; t++) { mf->control[t].chanvol = mf->chanvol[t]; mf->control[t].panning = mf->panning[t]; } return 0; } void Player_Exit(UNIMOD * mf) { if (mf == NULL) return; if (mf == pf) { Player_Stop(); pf = NULL; } if (mf->control != NULL) free(mf->control); if (mf->voice != NULL) free(mf->voice); mf->control = NULL; mf->voice = NULL; } void Player_SetVolume(int volume) { if (pf == NULL) return; if (volume > 128) volume = 128; if (volume < 0) volume = 0; pf->volume = volume; } UNIMOD * Player_GetUnimod(void) { return pf; } void Player_Start(UNIMOD * mf) { int t; if (!MikMod_Active()) { isfirst = 2; MikMod_EnableOutput(); } if (mf == NULL) return; mf->forbid = 0; if (pf != mf) { /* new song is being started, so completely * stop out the old one. */ if (pf != NULL) pf->forbid = 1; for (t = 0; t < md_sngchn; t++) Voice_Stop(t); } pf = mf; } void Player_Stop(void) { if (md_sfxchn == 0) MikMod_DisableOutput(); if (pf != NULL) pf->forbid = 1; pf = NULL; } BOOL MP_Playing(UNIMOD * mf) { if ((mf == NULL) || (mf != pf)) return 0; return (!(mf->sngpos >= mf->numpos)); } BOOL Player_Active(void) { if (pf == NULL) return 0; return (!(pf->sngpos >= pf->numpos)); } void MP_NextPosition(UNIMOD * mf) { int t; if (mf == NULL) return; mf->forbid = 1; mf->posjmp = 3; mf->patbrk = 0; mf->vbtick = mf->sngspd; for (t = 0; t < md_sngchn; t++) { Voice_Stop(t); mf->voice[t].i = NULL; mf->voice[t].s = NULL; } for (t = 0; t < mf->numchn; t++) { mf->control[t].i = NULL; mf->control[t].s = NULL; } mf->forbid = 0; } void Player_NextPosition(void) { MP_NextPosition(pf); } void MP_PrevPosition(UNIMOD * mf) { int t; if (mf == NULL) return; mf->forbid = 1; mf->posjmp = 1; mf->patbrk = 0; mf->vbtick = mf->sngspd; for (t = 0; t < md_sngchn; t++) { Voice_Stop(t); mf->voice[t].i = NULL; mf->voice[t].s = NULL; } for (t = 0; t < mf->numchn; t++) { mf->control[t].i = NULL; mf->control[t].s = NULL; } mf->forbid = 0; } void Player_PrevPosition(void) { MP_PrevPosition(pf); } void MP_SetPosition(UNIMOD * mf, UWORD pos) { int t; if (mf == NULL) return; mf->forbid = 1; if (pos >= mf->numpos) pos = mf->numpos; mf->posjmp = 2; mf->patbrk = 0; mf->sngpos = pos; mf->vbtick = mf->sngspd; for (t = 0; t < md_sngchn; t++) { Voice_Stop(t); mf->voice[t].i = NULL; mf->voice[t].s = NULL; } for (t = 0; t < mf->numchn; t++) { mf->control[t].i = NULL; mf->control[t].s = NULL; } mf->forbid = 0; } void Player_SetPosition(UWORD pos) { MP_SetPosition(pf, pos); } void MP_Unmute(UNIMOD * mf, SLONG arg1,...) { va_list ap; SLONG t, arg2, arg3; va_start(ap, arg1); if (mf != NULL) { switch (arg1) { case MUTE_INCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) mf->control[arg2].muted = 0; break; case MUTE_EXCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (t = 0; t < mf->numchn; t++) { if ((t >= arg2) && (t <= arg3)) continue; mf->control[t].muted = 0; } break; default: if (arg1 < mf->numchn) mf->control[arg1].muted = 0; break; } } va_end(ap); return; } void Player_Unmute(SLONG arg1,...) { va_list argptr; MP_Unmute(pf, arg1, argptr); va_end(argptr); } void MP_Mute(UNIMOD * mf, SLONG arg1,...) { va_list ap; SLONG t, arg2, arg3; va_start(ap, arg1); if (mf != NULL) { switch (arg1) { case MUTE_INCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) mf->control[arg2].muted = 1; break; case MUTE_EXCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (t = 0; t < mf->numchn; t++) { if ((t >= arg2) && (t <= arg3)) continue; mf->control[t].muted = 1; } break; default: if (arg1 < mf->numchn) mf->control[arg1].muted = 1; break; } } va_end(ap); return; } void Player_Mute(SLONG arg1,...) { va_list argptr; MP_Mute(pf, arg1, argptr); va_end(argptr); } void MP_ToggleMute(UNIMOD * mf, SLONG arg1,...) { va_list ap; SLONG arg2, arg3; ULONG t; va_start(ap, arg1); if (mf != NULL) { switch (arg1) { case MUTE_INCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (; arg2 < mf->numchn && arg2 <= arg3; arg2++) mf->control[arg2].muted = (mf->control[arg2].muted) ? 0 : 1; break; case MUTE_EXCLUSIVE: if ((!(arg2 = va_arg(ap, SLONG))) && (!(arg3 = va_arg(ap, SLONG))) || (arg2 > arg3) || (arg3 >= mf->numchn)) { va_end(ap); return; } for (t = 0; t < mf->numchn; t++) { if ((t >= arg2) && (t <= arg3)) continue; mf->control[t].muted = (mf->control[t].muted) ? 0 : 1; } break; default: if (arg1 < mf->numchn) mf->control[arg1].muted = (mf->control[arg1].muted) ? 0 : 1; break; } } va_end(ap); return; } void Player_ToggleMute(SLONG arg1,...) { va_list argptr; MP_ToggleMute(pf, arg1, argptr); va_end(argptr); } BOOL MP_Muted(UNIMOD * mf, int chan) { if (mf == NULL) return 1; return (chan < mf->numchn) ? mf->control[chan].muted : 1; } BOOL Player_Muted(int chan) { if (pf == NULL) return 1; return (chan < pf->numchn) ? pf->control[chan].muted : 1; } int MP_GetChannelVoice(UNIMOD * mf, int chan) { if (mf == NULL) return 0; return mf->control[chan].slavechn; } int Player_GetChannelVoice(int chan) { if (pf == NULL) return 0; return pf->control[chan].slavechn; } void Player_TogglePause(void) { if (pf == NULL) return; if (pf->forbid == 1) pf->forbid = 0; else pf->forbid = 1; } /* * --> The following procedures were taken from UNITRK because they -> are * ProTracker format specific. */ /* Appends UNI_INSTRUMENT opcode to the unitrk stream. */ void UniInstrument(UBYTE ins) { UniWrite(UNI_INSTRUMENT); UniWrite(ins); } /* Appends UNI_NOTE opcode to the unitrk stream. */ void UniNote(UBYTE note) { UniWrite(UNI_NOTE); UniWrite(note); } /* Appends UNI_PTEFFECTX opcode to the unitrk stream. */ void UniPTEffect(UBYTE eff, UBYTE dat) { if (eff != 0 || dat != 0) { /* don't write empty effect */ UniWrite(UNI_PTEFFECT0 + eff); UniWrite(dat); } } /* Appends UNI_VOLEFFECT + effect/dat to unistream. */ void UniVolEffect(UWORD eff, UBYTE dat) { if (eff != 0 || dat != 0) { /* don't write empty effect */ UniWrite(UNI_VOLEFFECTS); UniWrite(eff); UniWrite(dat); } }