1 /* MikMod sound library
\r
2 (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
\r
5 This library is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Library General Public License as
\r
7 published by the Free Software Foundation; either version 2 of
\r
8 the License, or (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU Library General Public License for more details.
\r
15 You should have received a copy of the GNU Library General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
\r
21 /*==============================================================================
\r
23 $Id: mplayer.c,v 1.5 2004/01/31 22:40:22 raph Exp $
\r
25 The Protracker Player Driver
\r
27 The protracker driver supports all base Protracker 3.x commands and features.
\r
29 ==============================================================================*/
\r
31 #ifdef HAVE_CONFIG_H
\r
37 #ifdef SRANDOM_IN_MATH_H
\r
43 #include "mikmod_internals.h"
\r
46 extern int fprintf(FILE *, const char *, ...);
\r
47 extern long int random(void);
\r
50 /* The currently playing module */
\r
51 /* This variable should better be static, but it would break the ABI, so this
\r
53 /*static*/ MODULE *pf = NULL;
\r
55 #define HIGH_OCTAVE 2 /* number of above-range octaves */
\r
57 static UWORD oldperiods[OCTAVE*2]={
\r
58 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
\r
59 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
\r
60 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160,
\r
61 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
\r
64 static UBYTE VibratoTable[32]={
\r
65 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
\r
66 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
\r
69 static UBYTE avibtab[128]={
\r
70 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
\r
71 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
\r
72 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
\r
73 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
\r
74 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
\r
75 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
\r
76 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
\r
77 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
\r
80 /* Triton's linear periods to frequency translation table (for XM modules) */
\r
81 static ULONG lintab[768]={
\r
82 535232,534749,534266,533784,533303,532822,532341,531861,
\r
83 531381,530902,530423,529944,529466,528988,528511,528034,
\r
84 527558,527082,526607,526131,525657,525183,524709,524236,
\r
85 523763,523290,522818,522346,521875,521404,520934,520464,
\r
86 519994,519525,519057,518588,518121,517653,517186,516720,
\r
87 516253,515788,515322,514858,514393,513929,513465,513002,
\r
88 512539,512077,511615,511154,510692,510232,509771,509312,
\r
89 508852,508393,507934,507476,507018,506561,506104,505647,
\r
90 505191,504735,504280,503825,503371,502917,502463,502010,
\r
91 501557,501104,500652,500201,499749,499298,498848,498398,
\r
92 497948,497499,497050,496602,496154,495706,495259,494812,
\r
93 494366,493920,493474,493029,492585,492140,491696,491253,
\r
94 490809,490367,489924,489482,489041,488600,488159,487718,
\r
95 487278,486839,486400,485961,485522,485084,484647,484210,
\r
96 483773,483336,482900,482465,482029,481595,481160,480726,
\r
97 480292,479859,479426,478994,478562,478130,477699,477268,
\r
98 476837,476407,475977,475548,475119,474690,474262,473834,
\r
99 473407,472979,472553,472126,471701,471275,470850,470425,
\r
100 470001,469577,469153,468730,468307,467884,467462,467041,
\r
101 466619,466198,465778,465358,464938,464518,464099,463681,
\r
102 463262,462844,462427,462010,461593,461177,460760,460345,
\r
103 459930,459515,459100,458686,458272,457859,457446,457033,
\r
104 456621,456209,455797,455386,454975,454565,454155,453745,
\r
105 453336,452927,452518,452110,451702,451294,450887,450481,
\r
106 450074,449668,449262,448857,448452,448048,447644,447240,
\r
107 446836,446433,446030,445628,445226,444824,444423,444022,
\r
108 443622,443221,442821,442422,442023,441624,441226,440828,
\r
109 440430,440033,439636,439239,438843,438447,438051,437656,
\r
110 437261,436867,436473,436079,435686,435293,434900,434508,
\r
111 434116,433724,433333,432942,432551,432161,431771,431382,
\r
112 430992,430604,430215,429827,429439,429052,428665,428278,
\r
113 427892,427506,427120,426735,426350,425965,425581,425197,
\r
114 424813,424430,424047,423665,423283,422901,422519,422138,
\r
115 421757,421377,420997,420617,420237,419858,419479,419101,
\r
116 418723,418345,417968,417591,417214,416838,416462,416086,
\r
117 415711,415336,414961,414586,414212,413839,413465,413092,
\r
118 412720,412347,411975,411604,411232,410862,410491,410121,
\r
119 409751,409381,409012,408643,408274,407906,407538,407170,
\r
120 406803,406436,406069,405703,405337,404971,404606,404241,
\r
121 403876,403512,403148,402784,402421,402058,401695,401333,
\r
122 400970,400609,400247,399886,399525,399165,398805,398445,
\r
123 398086,397727,397368,397009,396651,396293,395936,395579,
\r
124 395222,394865,394509,394153,393798,393442,393087,392733,
\r
125 392378,392024,391671,391317,390964,390612,390259,389907,
\r
126 389556,389204,388853,388502,388152,387802,387452,387102,
\r
127 386753,386404,386056,385707,385359,385012,384664,384317,
\r
128 383971,383624,383278,382932,382587,382242,381897,381552,
\r
129 381208,380864,380521,380177,379834,379492,379149,378807,
\r
130 378466,378124,377783,377442,377102,376762,376422,376082,
\r
131 375743,375404,375065,374727,374389,374051,373714,373377,
\r
132 373040,372703,372367,372031,371695,371360,371025,370690,
\r
133 370356,370022,369688,369355,369021,368688,368356,368023,
\r
134 367691,367360,367028,366697,366366,366036,365706,365376,
\r
135 365046,364717,364388,364059,363731,363403,363075,362747,
\r
136 362420,362093,361766,361440,361114,360788,360463,360137,
\r
137 359813,359488,359164,358840,358516,358193,357869,357547,
\r
138 357224,356902,356580,356258,355937,355616,355295,354974,
\r
139 354654,354334,354014,353695,353376,353057,352739,352420,
\r
140 352103,351785,351468,351150,350834,350517,350201,349885,
\r
141 349569,349254,348939,348624,348310,347995,347682,347368,
\r
142 347055,346741,346429,346116,345804,345492,345180,344869,
\r
143 344558,344247,343936,343626,343316,343006,342697,342388,
\r
144 342079,341770,341462,341154,340846,340539,340231,339924,
\r
145 339618,339311,339005,338700,338394,338089,337784,337479,
\r
146 337175,336870,336566,336263,335959,335656,335354,335051,
\r
147 334749,334447,334145,333844,333542,333242,332941,332641,
\r
148 332341,332041,331741,331442,331143,330844,330546,330247,
\r
149 329950,329652,329355,329057,328761,328464,328168,327872,
\r
150 327576,327280,326985,326690,326395,326101,325807,325513,
\r
151 325219,324926,324633,324340,324047,323755,323463,323171,
\r
152 322879,322588,322297,322006,321716,321426,321136,320846,
\r
153 320557,320267,319978,319690,319401,319113,318825,318538,
\r
154 318250,317963,317676,317390,317103,316817,316532,316246,
\r
155 315961,315676,315391,315106,314822,314538,314254,313971,
\r
156 313688,313405,313122,312839,312557,312275,311994,311712,
\r
157 311431,311150,310869,310589,310309,310029,309749,309470,
\r
158 309190,308911,308633,308354,308076,307798,307521,307243,
\r
159 306966,306689,306412,306136,305860,305584,305308,305033,
\r
160 304758,304483,304208,303934,303659,303385,303112,302838,
\r
161 302565,302292,302019,301747,301475,301203,300931,300660,
\r
162 300388,300117,299847,299576,299306,299036,298766,298497,
\r
163 298227,297958,297689,297421,297153,296884,296617,296349,
\r
164 296082,295815,295548,295281,295015,294749,294483,294217,
\r
165 293952,293686,293421,293157,292892,292628,292364,292100,
\r
166 291837,291574,291311,291048,290785,290523,290261,289999,
\r
167 289737,289476,289215,288954,288693,288433,288173,287913,
\r
168 287653,287393,287134,286875,286616,286358,286099,285841,
\r
169 285583,285326,285068,284811,284554,284298,284041,283785,
\r
170 283529,283273,283017,282762,282507,282252,281998,281743,
\r
171 281489,281235,280981,280728,280475,280222,279969,279716,
\r
172 279464,279212,278960,278708,278457,278206,277955,277704,
\r
173 277453,277203,276953,276703,276453,276204,275955,275706,
\r
174 275457,275209,274960,274712,274465,274217,273970,273722,
\r
175 273476,273229,272982,272736,272490,272244,271999,271753,
\r
176 271508,271263,271018,270774,270530,270286,270042,269798,
\r
177 269555,269312,269069,268826,268583,268341,268099,267857
\r
180 #define LOGFAC 2*16
\r
181 static UWORD logtab[104]={
\r
182 LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,
\r
183 LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
\r
184 LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,
\r
185 LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
\r
186 LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,
\r
187 LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
\r
188 LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,
\r
189 LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
\r
190 LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,
\r
191 LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
\r
192 LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,
\r
193 LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
\r
194 LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,
\r
195 LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
\r
196 LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,
\r
197 LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
\r
198 LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,
\r
199 LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
\r
200 LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,
\r
201 LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
\r
202 LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,
\r
203 LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
\r
204 LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,
\r
205 LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
\r
206 LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,
\r
207 LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
\r
210 static SBYTE PanbrelloTable[256]={
\r
211 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
\r
212 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
\r
213 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
\r
214 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
\r
215 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
\r
216 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
\r
217 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
\r
218 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
\r
219 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
\r
220 -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
\r
221 -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
\r
222 -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
\r
223 -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
\r
224 -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
\r
225 -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
\r
226 -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
\r
229 /* returns a random value between 0 and ceil-1, ceil must be a power of two */
\r
230 static int getrandom(int ceil)
\r
232 #ifdef HAVE_SRANDOM
\r
233 return random()&(ceil-1);
\r
235 return (rand()*ceil)/(RAND_MAX+1.0);
\r
240 /* New Note Action Scoring System :
\r
241 --------------------------------
\r
242 1) total-volume (fadevol, chanvol, volume) is the main scorer.
\r
243 2) a looping sample is a bonus x2
\r
244 3) a foreground channel is a bonus x4
\r
245 4) an active envelope with keyoff is a handicap -x2
\r
247 static int MP_FindEmptyChannel(MODULE *mod)
\r
252 for (t=0;t<md_sngchn;t++)
\r
253 if (((mod->voice[t].main.kick==KICK_ABSENT)||
\r
254 (mod->voice[t].main.kick==KICK_ENV))&&
\r
255 Voice_Stopped_internal(t))
\r
258 tvol=0xffffffUL;t=-1;a=mod->voice;
\r
259 for (k=0;k<md_sngchn;k++,a++) {
\r
260 /* allow us to take over a nonexisting sample */
\r
264 if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) {
\r
265 pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0);
\r
266 if ((a->master)&&(a==a->master->slave))
\r
276 if (tvol>8000*7) return -1;
\r
280 static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2)
\r
282 if ((p1==p2)||(p==p1)) return v1;
\r
283 return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1));
\r
286 UWORD getlinearperiod(UWORD note,ULONG fine)
\r
290 t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1);
\r
294 static UWORD getlogperiod(UWORD note,ULONG fine)
\r
302 i=(n<<2)+(fine>>4); /* n*8 + fine/16 */
\r
307 return (Interpolate(fine>>4,0,15,p1,p2)>>o);
\r
310 static UWORD getoldperiod(UWORD note,ULONG speed)
\r
314 /* This happens sometimes on badly converted AMF, and old MOD */
\r
316 #ifdef MIKMOD_DEBUG
\r
317 fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note);
\r
319 return 4242; /* <- prevent divide overflow.. (42 hehe) */
\r
324 return ((8363L*(ULONG)oldperiods[n])>>o)/speed;
\r
327 static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed)
\r
329 if (flags & UF_XMPERIODS) {
\r
330 if (flags & UF_LINEAR)
\r
331 return getlinearperiod(note, speed);
\r
333 return getlogperiod(note, speed);
\r
335 return getoldperiod(note, speed);
\r
338 static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b)
\r
340 return (Interpolate(p,a->pos,b->pos,a->val,b->val));
\r
343 static SWORD DoPan(SWORD envpan,SWORD pan)
\r
347 newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128);
\r
349 return (newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT?PAN_RIGHT:newpan);
\r
352 static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff)
\r
363 t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1;
\r
365 /* Imago Orpheus sometimes stores an extra initial point in the envelope */
\r
366 if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) {
\r
370 /* Fit in the envelope, still */
\r
371 if (t->a >= t->pts)
\r
373 if (t->b >= t->pts)
\r
376 return t->env[t->a].val;
\r
379 /* This procedure processes all envelope types, include volume, pitch, and
\r
380 panning. Envelopes are defined by a set of points, each with a magnitude
\r
381 [relating either to volume, panning position, or pitch modifier] and a tick
\r
384 Envelopes work in the following manner:
\r
386 (a) Each tick the envelope is moved a point further in its progression. For
\r
387 an accurate progression, magnitudes between two envelope points are
\r
390 (b) When progression reaches a defined point on the envelope, values are
\r
391 shifted to interpolate between this point and the next, and checks for
\r
392 loops or envelope end are done.
\r
395 Sustain loops are loops that are only active as long as the keyoff flag is
\r
396 clear. When a volume envelope terminates, so does the current fadeout.
\r
398 static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)
\r
400 if (t->flg & EF_ON) {
\r
401 UBYTE a, b; /* actual points in the envelope */
\r
402 UWORD p; /* the 'tick counter' - real point being played */
\r
409 * Sustain loop on one point (XM type).
\r
410 * Not processed if KEYOFF.
\r
411 * Don't move and don't interpolate when the point is reached
\r
413 if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend &&
\r
414 (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) {
\r
415 v = t->env[t->susbeg].val;
\r
418 * All following situations will require interpolation between
\r
419 * two envelope points.
\r
423 * Sustain loop between two points (IT type).
\r
424 * Not processed if KEYOFF.
\r
426 /* if we were on a loop point, loop now */
\r
427 if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) &&
\r
430 b = (t->susbeg==t->susend)?a:a+1;
\r
436 * Be sure to correctly handle single point loops.
\r
438 if ((t->flg & EF_LOOP) && a >= t->end) {
\r
440 b = t->beg == t->end ? a : a + 1;
\r
445 * Non looping situations.
\r
448 v = InterpolateEnv(p, &t->env[a], &t->env[b]);
\r
453 * Start to fade if the volume envelope is finished.
\r
455 if (p >= t->env[t->pts - 1].pos) {
\r
456 if (t->flg & EF_VOLENV) {
\r
457 aout->main.keyoff |= KEY_FADE;
\r
459 aout->main.fadevol = 0;
\r
463 /* did pointer reach point b? */
\r
464 if (p >= t->env[b].pos)
\r
465 a = b++; /* shift points a and b */
\r
475 /* XM linear period to frequency conversion */
\r
476 ULONG getfrequency(UWORD flags,ULONG period)
\r
478 if (flags & UF_LINEAR) {
\r
479 SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE;
\r
482 return lintab[period % 768] >> shift;
\r
484 return lintab[period % 768] << (-shift);
\r
486 return (8363L*1712L)/(period?period:1);
\r
489 /*========== Protracker effects */
\r
491 static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style)
\r
493 UBYTE note=a->main.note;
\r
497 case 0: /* mod style: N, N+x, N+y */
\r
498 switch (tick % 3) {
\r
499 /* case 0: unchanged */
\r
501 note += (a->arpmem >> 4);
\r
504 note += (a->arpmem & 0xf);
\r
508 case 3: /* okt arpeggio 3: N-x, N, N+y */
\r
509 switch (tick % 3) {
\r
511 note -= (a->arpmem >> 4);
\r
513 /* case 1: unchanged */
\r
515 note += (a->arpmem & 0xf);
\r
519 case 4: /* okt arpeggio 4: N, N+y, N, N-x */
\r
520 switch (tick % 4) {
\r
521 /* case 0, case 2: unchanged */
\r
523 note += (a->arpmem & 0xf);
\r
526 note -= (a->arpmem >> 4);
\r
530 case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */
\r
533 switch (tick % 3) {
\r
534 /* case 0: unchanged */
\r
536 note -= (a->arpmem >> 4);
\r
539 note += (a->arpmem & 0xf);
\r
544 a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed);
\r
549 static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
555 dat = UniGetByte();
\r
557 if (!dat && (flags & UF_ARPMEM))
\r
562 if (a->main.period)
\r
563 DoArpeggio(tick, flags, a, 0);
\r
568 static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
575 dat = UniGetByte();
\r
577 a->slidespeed = (UWORD)dat << 2;
\r
578 if (a->main.period)
\r
580 a->tmpperiod -= a->slidespeed;
\r
585 static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
592 dat = UniGetByte();
\r
594 a->slidespeed = (UWORD)dat << 2;
\r
595 if (a->main.period)
\r
597 a->tmpperiod += a->slidespeed;
\r
602 static void DoToneSlide(UWORD tick, MP_CONTROL *a)
\r
604 if (!a->main.fadevol)
\r
605 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF;
\r
607 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT;
\r
612 /* We have to slide a->main.period towards a->wantedperiod, so compute
\r
613 the difference between those two values */
\r
614 dist=a->main.period-a->wantedperiod;
\r
616 /* if they are equal or if portamentospeed is too big ...*/
\r
617 if (dist == 0 || a->portspeed > abs(dist))
\r
618 /* ...make tmpperiod equal tperiod */
\r
619 a->tmpperiod=a->main.period=a->wantedperiod;
\r
621 a->tmpperiod-=a->portspeed;
\r
622 a->main.period-=a->portspeed; /* dist>0, slide up */
\r
624 a->tmpperiod+=a->portspeed;
\r
625 a->main.period+=a->portspeed; /* dist<0, slide down */
\r
628 a->tmpperiod=a->main.period;
\r
632 static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
640 if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2;
\r
641 if (a->main.period)
\r
642 DoToneSlide(tick, a);
\r
647 static void DoVibrato(UWORD tick, MP_CONTROL *a)
\r
650 UWORD temp = 0; /* silence warning */
\r
655 q=(a->vibpos>>2)&0x1f;
\r
657 switch (a->wavecontrol&3) {
\r
659 temp=VibratoTable[q];
\r
661 case 1: /* ramp down */
\r
663 if (a->vibpos<0) q=255-q;
\r
666 case 2: /* square wave */
\r
669 case 3: /* random wave */
\r
670 temp=getrandom(256);
\r
678 a->main.period=a->tmpperiod+temp;
\r
680 a->main.period=a->tmpperiod-temp;
\r
684 a->vibpos+=a->vibspd;
\r
687 static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
696 if (dat&0x0f) a->vibdepth=dat&0xf;
\r
697 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
\r
699 if (a->main.period)
\r
700 DoVibrato(tick, a);
\r
705 static void DoVolSlide(MP_CONTROL *a, UBYTE dat)
\r
708 a->tmpvolume-=(dat&0x0f);
\r
709 if (a->tmpvolume<0)
\r
712 a->tmpvolume+=(dat>>4);
\r
713 if (a->tmpvolume>64)
\r
718 static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
726 if (a->main.period)
\r
727 DoToneSlide(tick, a);
\r
730 DoVolSlide(a, dat);
\r
735 /* DoPTEffect6 after DoPTEffectA */
\r
737 static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
741 UWORD temp = 0; /* silence warning */
\r
748 if (dat&0x0f) a->trmdepth=dat&0xf;
\r
749 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
\r
751 if (a->main.period) {
\r
752 q=(a->trmpos>>2)&0x1f;
\r
754 switch ((a->wavecontrol>>4)&3) {
\r
756 temp=VibratoTable[q];
\r
758 case 1: /* ramp down */
\r
760 if (a->trmpos<0) q=255-q;
\r
763 case 2: /* square wave */
\r
766 case 3: /* random wave */
\r
767 temp=getrandom(256);
\r
773 if (a->trmpos>=0) {
\r
774 a->volume=a->tmpvolume+temp;
\r
775 if (a->volume>64) a->volume=64;
\r
777 a->volume=a->tmpvolume-temp;
\r
778 if (a->volume<0) a->volume=0;
\r
783 a->trmpos+=a->trmspd;
\r
789 static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
795 dat = UniGetByte();
\r
797 a->main.panning = mod->panning[channel] = dat;
\r
802 static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
811 if (dat) a->soffset=(UWORD)dat<<8;
\r
812 a->main.start=a->hioffset|a->soffset;
\r
814 if ((a->main.s)&&(a->main.start>a->main.s->length))
\r
815 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
\r
816 a->main.s->loopstart:a->main.s->length;
\r
822 static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
831 DoVolSlide(a, dat);
\r
836 static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
838 if (a->main.period)
\r
839 DoVibrato(tick, a);
\r
840 DoPTEffectA(tick, flags, a, mod, channel);
\r
845 static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
853 if (tick || mod->patdly2)
\r
856 /* Vincent Voois uses a nasty trick in "Universal Bolero" */
\r
857 if (dat == mod->sngpos && mod->patbrk == mod->patpos)
\r
860 if (!mod->loop && !mod->patbrk &&
\r
861 (dat < mod->sngpos ||
\r
862 (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) ||
\r
863 (dat == mod->sngpos && (flags & UF_NOWRAP))
\r
865 /* if we don't loop, better not to skip the end of the
\r
866 pattern, after all... so:
\r
870 /* if we were fading, adjust... */
\r
871 if (mod->sngpos == (mod->numpos-1))
\r
872 mod->volume=mod->initvolume>128?128:mod->initvolume;
\r
881 static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
889 if (tick) return 0;
\r
890 if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */
\r
891 else if (dat>64) dat=64;
\r
897 static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
904 if ((tick)||(mod->patdly2)) return 0;
\r
905 if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&&
\r
906 (dat>mod->pattrows[mod->positions[mod->sngpos]]))
\r
907 dat=mod->pattrows[mod->positions[mod->sngpos]];
\r
909 if (!mod->posjmp) {
\r
910 /* don't ask me to explain this code - it makes
\r
911 backwards.s3m and children.xm (heretic's version) play
\r
912 correctly, among others. Take that for granted, or write
\r
913 the page of comments yourself... you might need some
\r
915 if ((mod->sngpos==mod->numpos-1)&&(dat)&&((mod->loop)||
\r
916 (mod->positions[mod->sngpos]==(mod->numpat-1)
\r
917 && !(flags&UF_NOWRAP)))) {
\r
927 static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
\r
928 SWORD channel, UBYTE dat)
\r
930 UBYTE nib = dat & 0xf;
\r
933 case 0x0: /* hardware filter toggle, not supported */
\r
935 case 0x1: /* fineslide up */
\r
936 if (a->main.period)
\r
938 a->tmpperiod-=(nib<<2);
\r
940 case 0x2: /* fineslide dn */
\r
941 if (a->main.period)
\r
943 a->tmpperiod+=(nib<<2);
\r
945 case 0x3: /* glissando ctrl */
\r
948 case 0x4: /* set vibrato waveform */
\r
949 a->wavecontrol&=0xf0;
\r
950 a->wavecontrol|=nib;
\r
952 case 0x5: /* set finetune */
\r
953 if (a->main.period) {
\r
954 if (flags&UF_XMPERIODS)
\r
957 a->speed=finetune[nib];
\r
958 a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed);
\r
961 case 0x6: /* set patternloop */
\r
964 if (nib) { /* set reppos or repcnt ? */
\r
965 /* set repcnt, so check if repcnt already is set, which means we
\r
966 are already looping */
\r
968 a->pat_repcnt--; /* already looping, decrease counter */
\r
971 /* this would make walker.xm, shipped with Xsoundtracker,
\r
972 play correctly, but it's better to remain compatible
\r
974 if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))
\r
976 a->pat_repcnt=nib; /* not yet looping, so set repcnt */
\r
979 if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */
\r
980 if (a->pat_reppos==POS_NONE)
\r
981 a->pat_reppos=mod->patpos-1;
\r
982 if (a->pat_reppos==-1) {
\r
983 mod->pat_repcrazy=1;
\r
986 mod->patpos=a->pat_reppos;
\r
987 } else a->pat_reppos=POS_NONE;
\r
989 a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */
\r
991 case 0x7: /* set tremolo waveform */
\r
992 a->wavecontrol&=0x0f;
\r
993 a->wavecontrol|=nib<<4;
\r
995 case 0x8: /* set panning */
\r
996 if (mod->panflag) {
\r
997 if (nib<=8) nib<<=4;
\r
999 a->main.panning=mod->panning[channel]=nib;
\r
1002 case 0x9: /* retrig note */
\r
1003 /* do not retrigger on tick 0, until we are emulating FT2 and effect
\r
1005 if (!tick && !((flags & UF_FT2QUIRKS) && (!nib)))
\r
1007 /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */
\r
1008 if (nib || !tick) {
\r
1010 /* when retrig counter reaches 0, reset counter and restart
\r
1012 if (a->main.period) a->main.kick=KICK_NOTE;
\r
1015 a->retrig--; /* countdown */
\r
1018 case 0xa: /* fine volume slide up */
\r
1021 a->tmpvolume+=nib;
\r
1022 if (a->tmpvolume>64) a->tmpvolume=64;
\r
1024 case 0xb: /* fine volume slide dn */
\r
1027 a->tmpvolume-=nib;
\r
1028 if (a->tmpvolume<0)a->tmpvolume=0;
\r
1030 case 0xc: /* cut note */
\r
1031 /* When tick reaches the cut-note value, turn the volume to
\r
1032 zero (just like on the amiga) */
\r
1034 a->tmpvolume=0; /* just turn the volume down */
\r
1036 case 0xd: /* note delay */
\r
1037 /* delay the start of the sample until tick==nib */
\r
1039 a->main.notedelay=nib;
\r
1040 else if (a->main.notedelay)
\r
1041 a->main.notedelay--;
\r
1043 case 0xe: /* pattern delay */
\r
1045 if (!mod->patdly2)
\r
1046 mod->patdly=nib+1; /* only once, when tick=0 */
\r
1048 case 0xf: /* invert loop, not supported */
\r
1053 static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1055 DoEEffects(tick, flags, a, mod, channel, UniGetByte());
\r
1060 static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1068 if (tick||mod->patdly2) return 0;
\r
1069 if (mod->extspd&&(dat>=mod->bpmlimit))
\r
1073 mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat;
\r
1080 /*========== Scream Tracker effects */
\r
1082 static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1089 speed = UniGetByte();
\r
1091 if (tick || mod->patdly2)
\r
1097 mod->sngspd = speed;
\r
1104 static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf)
\r
1109 a->s3mvolslide=inf;
\r
1111 inf=a->s3mvolslide;
\r
1117 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi;
\r
1120 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo;
\r
1123 if (!tick) a->tmpvolume+=(hi?hi:0xf);
\r
1126 if (!tick) a->tmpvolume-=(lo?lo:0xf);
\r
1130 if (a->tmpvolume<0)
\r
1132 else if (a->tmpvolume>64)
\r
1136 static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1141 DoS3MVolSlide(tick, flags, a, UniGetByte());
\r
1146 static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf)
\r
1151 a->slidespeed=inf;
\r
1153 inf=a->slidespeed;
\r
1159 if (!tick) a->tmpperiod+=(UWORD)lo<<2;
\r
1162 if (!tick) a->tmpperiod+=lo;
\r
1164 if (tick) a->tmpperiod+=(UWORD)inf<<2;
\r
1168 static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1176 if (a->main.period)
\r
1177 DoS3MSlideDn(tick, a,dat);
\r
1182 static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf)
\r
1186 if (inf) a->slidespeed=inf;
\r
1187 else inf=a->slidespeed;
\r
1193 if (!tick) a->tmpperiod-=(UWORD)lo<<2;
\r
1196 if (!tick) a->tmpperiod-=lo;
\r
1198 if (tick) a->tmpperiod-=(UWORD)inf<<2;
\r
1202 static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1210 if (a->main.period)
\r
1211 DoS3MSlideUp(tick, a,dat);
\r
1216 static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1218 UBYTE inf, on, off;
\r
1223 inf = UniGetByte();
\r
1225 a->s3mtronof = inf;
\r
1227 inf = a->s3mtronof;
\r
1237 a->s3mtremor%=(on+off);
\r
1238 a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
\r
1245 static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1251 inf = UniGetByte();
\r
1252 if (a->main.period) {
\r
1254 a->s3mrtgslide=inf>>4;
\r
1255 a->s3mrtgspeed=inf&0xf;
\r
1258 /* only retrigger if low nibble > 0 */
\r
1259 if (a->s3mrtgspeed>0) {
\r
1261 /* when retrig counter reaches 0, reset counter and restart the
\r
1263 if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF;
\r
1264 a->retrig=a->s3mrtgspeed;
\r
1266 if ((tick)||(flags&UF_S3MSLIDES)) {
\r
1267 switch (a->s3mrtgslide) {
\r
1273 a->tmpvolume-=(1<<(a->s3mrtgslide-1));
\r
1276 a->tmpvolume=(2*a->tmpvolume)/3;
\r
1286 a->tmpvolume+=(1<<(a->s3mrtgslide-9));
\r
1289 a->tmpvolume=(3*a->tmpvolume)>>1;
\r
1292 a->tmpvolume=a->tmpvolume<<1;
\r
1295 if (a->tmpvolume<0)
\r
1297 else if (a->tmpvolume>64)
\r
1301 a->retrig--; /* countdown */
\r
1308 static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1311 UWORD temp=0; /* silence warning */
\r
1316 dat = UniGetByte();
\r
1318 if (dat&0x0f) a->trmdepth=dat&0xf;
\r
1319 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
\r
1322 q=(a->trmpos>>2)&0x1f;
\r
1324 switch ((a->wavecontrol>>4)&3) {
\r
1325 case 0: /* sine */
\r
1326 temp=VibratoTable[q];
\r
1328 case 1: /* ramp down */
\r
1330 if (a->trmpos<0) q=255-q;
\r
1333 case 2: /* square wave */
\r
1336 case 3: /* random */
\r
1337 temp=getrandom(256);
\r
1341 temp*=a->trmdepth;
\r
1344 if (a->trmpos>=0) {
\r
1345 a->volume=a->tmpvolume+temp;
\r
1346 if (a->volume>64) a->volume=64;
\r
1348 a->volume=a->tmpvolume-temp;
\r
1349 if (a->volume<0) a->volume=0;
\r
1354 a->trmpos+=a->trmspd;
\r
1359 static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1366 tempo = UniGetByte();
\r
1368 if (tick || mod->patdly2)
\r
1371 mod->bpm = (tempo < 32) ? 32 : tempo;
\r
1376 static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1379 UWORD temp = 0; /* silence warning */
\r
1384 dat = UniGetByte();
\r
1386 if (dat&0x0f) a->vibdepth=dat&0xf;
\r
1387 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
\r
1389 if (a->main.period) {
\r
1390 q=(a->vibpos>>2)&0x1f;
\r
1392 switch (a->wavecontrol&3) {
\r
1393 case 0: /* sine */
\r
1394 temp=VibratoTable[q];
\r
1396 case 1: /* ramp down */
\r
1398 if (a->vibpos<0) q=255-q;
\r
1401 case 2: /* square wave */
\r
1404 case 3: /* random */
\r
1405 temp=getrandom(256);
\r
1409 temp*=a->vibdepth;
\r
1413 a->main.period=a->tmpperiod+temp;
\r
1415 a->main.period=a->tmpperiod-temp;
\r
1418 a->vibpos+=a->vibspd;
\r
1424 /*========== Envelope helpers */
\r
1426 static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1433 a->main.keyoff|=KEY_OFF;
\r
1434 if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP))
\r
1435 a->main.keyoff=KEY_KILL;
\r
1440 static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1447 if ((tick>=dat)||(tick==mod->sngspd-1)) {
\r
1448 a->main.keyoff=KEY_KILL;
\r
1449 if (!(a->main.volflg&EF_ON))
\r
1450 a->main.fadevol=0;
\r
1456 /*========== Fast Tracker effects */
\r
1458 /* DoXMEffect6 after DoXMEffectA */
\r
1460 static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1462 UBYTE inf, lo, hi;
\r
1467 inf = UniGetByte();
\r
1469 a->s3mvolslide = inf;
\r
1471 inf = a->s3mvolslide;
\r
1479 if (a->tmpvolume<0) a->tmpvolume=0;
\r
1482 if (a->tmpvolume>64) a->tmpvolume=64;
\r
1489 static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1491 if (a->main.period)
\r
1492 DoVibrato(tick, a);
\r
1494 return DoXMEffectA(tick, flags, a, mod, channel);
\r
1497 static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1506 if (dat) a->fportupspd=dat;
\r
1507 if (a->main.period)
\r
1508 a->tmpperiod-=(a->fportupspd<<2);
\r
1514 static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1523 if (dat) a->fportdnspd=dat;
\r
1524 if (a->main.period)
\r
1525 a->tmpperiod+=(a->fportdnspd<<2);
\r
1531 static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1540 if (dat) a->fslideupspd=dat;
\r
1541 a->tmpvolume+=a->fslideupspd;
\r
1542 if (a->tmpvolume>64) a->tmpvolume=64;
\r
1547 static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1556 if (dat) a->fslidednspd=dat;
\r
1557 a->tmpvolume-=a->fslidednspd;
\r
1558 if (a->tmpvolume<0) a->tmpvolume=0;
\r
1563 static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1570 mod->volume=UniGetByte()<<1;
\r
1571 if (mod->volume>128) mod->volume=128;
\r
1576 static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1583 inf = UniGetByte();
\r
1586 if (inf) mod->globalslide=inf;
\r
1587 else inf=mod->globalslide;
\r
1588 if (inf & 0xf0) inf&=0xf0;
\r
1589 mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2;
\r
1591 if (mod->volume<0)
\r
1593 else if (mod->volume>128)
\r
1600 static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1608 if ((!tick)&&(a->main.i)) {
\r
1610 INSTRUMENT *i=a->main.i;
\r
1613 if ((aout=a->slave)) {
\r
1614 if (aout->venv.env) {
\r
1615 points=i->volenv[i->volpts-1].pos;
\r
1616 aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos;
\r
1618 if (aout->penv.env) {
\r
1619 points=i->panenv[i->panpts-1].pos;
\r
1620 aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos;
\r
1628 static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1630 UBYTE inf, lo, hi;
\r
1635 inf = UniGetByte();
\r
1636 if (!mod->panflag)
\r
1648 /* slide right has absolute priority */
\r
1652 pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo;
\r
1653 a->main.panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
\r
1659 static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1666 dat = UniGetByte();
\r
1668 a->ffportupspd = dat;
\r
1670 dat = a->ffportupspd;
\r
1672 if (a->main.period)
\r
1674 a->main.period-=dat;
\r
1675 a->tmpperiod-=dat;
\r
1682 static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1689 dat = UniGetByte();
\r
1691 a->ffportdnspd=dat;
\r
1693 dat = a->ffportdnspd;
\r
1695 if (a->main.period)
\r
1697 a->main.period+=dat;
\r
1698 a->tmpperiod+=dat;
\r
1705 /*========== Impulse Tracker effects */
\r
1707 static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat)
\r
1710 a->portspeed = dat;
\r
1712 /* if we don't come from another note, ignore the slide and play the note
\r
1714 if (!a->oldnote || !a->main.period)
\r
1717 if ((!tick)&&(a->newsamp)){
\r
1718 a->main.kick=KICK_NOTE;
\r
1721 a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT;
\r
1726 /* We have to slide a->main.period towards a->wantedperiod, compute the
\r
1727 difference between those two values */
\r
1728 dist=a->main.period-a->wantedperiod;
\r
1730 /* if they are equal or if portamentospeed is too big... */
\r
1731 if ((!dist)||((a->portspeed<<2)>abs(dist)))
\r
1732 /* ... make tmpperiod equal tperiod */
\r
1733 a->tmpperiod=a->main.period=a->wantedperiod;
\r
1736 a->tmpperiod-=a->portspeed<<2;
\r
1737 a->main.period-=a->portspeed<<2; /* dist>0 slide up */
\r
1739 a->tmpperiod+=a->portspeed<<2;
\r
1740 a->main.period+=a->portspeed<<2; /* dist<0 slide down */
\r
1743 a->tmpperiod=a->main.period;
\r
1747 static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1753 DoITToneSlide(tick, a, UniGetByte());
\r
1758 static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)
\r
1764 if (dat&0x0f) a->vibdepth=dat&0xf;
\r
1765 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
\r
1767 if (!a->main.period)
\r
1770 q=(a->vibpos>>2)&0x1f;
\r
1772 switch (a->wavecontrol&3) {
\r
1773 case 0: /* sine */
\r
1774 temp=VibratoTable[q];
\r
1776 case 1: /* square wave */
\r
1779 case 2: /* ramp down */
\r
1781 if (a->vibpos<0) q=255-q;
\r
1784 case 3: /* random */
\r
1785 temp=getrandom(256);
\r
1789 temp*=a->vibdepth;
\r
1794 a->main.period=a->tmpperiod+temp;
\r
1796 a->main.period=a->tmpperiod-temp;
\r
1799 a->vibpos+=a->vibspd;
\r
1802 static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1808 DoITVibrato(tick, a, UniGetByte());
\r
1813 static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1815 UBYTE inf, on, off;
\r
1821 inf = UniGetByte();
\r
1823 a->s3mtronof = inf;
\r
1825 inf = a->s3mtronof;
\r
1833 a->s3mtremor%=(on+off);
\r
1834 a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
\r
1841 static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1848 a->main.chanvol=UniGetByte();
\r
1849 if (a->main.chanvol>64)
\r
1850 a->main.chanvol=64;
\r
1851 else if (a->main.chanvol<0)
\r
1852 a->main.chanvol=0;
\r
1857 static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1859 UBYTE inf, lo, hi;
\r
1864 inf = UniGetByte();
\r
1867 a->chanvolslide = inf;
\r
1869 inf = a->chanvolslide;
\r
1875 a->main.chanvol-=lo;
\r
1878 a->main.chanvol+=hi;
\r
1881 if (!tick) a->main.chanvol-=lo;
\r
1884 if (!tick) a->main.chanvol+=hi;
\r
1887 if (a->main.chanvol<0)
\r
1888 a->main.chanvol=0;
\r
1889 else if (a->main.chanvol>64)
\r
1890 a->main.chanvol=64;
\r
1895 static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1897 UBYTE inf, lo, hi;
\r
1902 inf = UniGetByte();
\r
1908 if (!mod->panflag)
\r
1914 pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning;
\r
1923 if (!tick) pan+=lo<<2;
\r
1926 if (!tick) pan-=hi<<2;
\r
1929 (pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
\r
1934 static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1943 tempo = UniGetByte();
\r
1950 temp += (tempo & 0x0f);
\r
1954 mod->bpm=(temp>255)?255:(temp<1?1:temp);
\r
1959 static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
1962 UWORD temp = 0; /* silence warning */
\r
1967 dat = UniGetByte();
\r
1969 if (dat&0x0f) a->vibdepth=dat&0xf;
\r
1970 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
\r
1972 if (a->main.period) {
\r
1973 q=(a->vibpos>>2)&0x1f;
\r
1975 switch (a->wavecontrol&3) {
\r
1976 case 0: /* sine */
\r
1977 temp=VibratoTable[q];
\r
1979 case 1: /* square wave */
\r
1982 case 2: /* ramp down */
\r
1984 if (a->vibpos<0) q=255-q;
\r
1987 case 3: /* random */
\r
1988 temp=getrandom(256);
\r
1992 temp*=a->vibdepth;
\r
1996 a->main.period=a->tmpperiod+temp;
\r
1998 a->main.period=a->tmpperiod-temp;
\r
2001 a->vibpos+=a->vibspd;
\r
2007 static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2009 UBYTE inf, lo, hi;
\r
2014 inf = UniGetByte();
\r
2017 mod->globalslide = inf;
\r
2019 inf = mod->globalslide;
\r
2025 if (tick) mod->volume+=hi;
\r
2028 if (tick) mod->volume-=lo;
\r
2031 if (!tick) mod->volume+=hi;
\r
2034 if (!tick) mod->volume-=lo;
\r
2037 if (mod->volume<0)
\r
2039 else if (mod->volume>128)
\r
2045 static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2048 SLONG temp = 0; /* silence warning */
\r
2054 if (dat&0x0f) a->panbdepth=(dat&0xf);
\r
2055 if (dat&0xf0) a->panbspd=(dat&0xf0)>>4;
\r
2057 if (mod->panflag) {
\r
2060 switch (a->panbwave) {
\r
2061 case 0: /* sine */
\r
2062 temp=PanbrelloTable[q];
\r
2064 case 1: /* square wave */
\r
2065 temp=(q<0x80)?64:0;
\r
2067 case 2: /* ramp down */
\r
2071 case 3: /* random */
\r
2072 temp=getrandom(256);
\r
2076 temp*=a->panbdepth;
\r
2077 temp=(temp/8)+mod->panning[channel];
\r
2080 (temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
\r
2081 a->panbpos+=a->panbspd;
\r
2088 static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE);
\r
2090 /* Impulse/Scream Tracker Sxx effects.
\r
2091 All Sxx effects share the same memory space. */
\r
2092 static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2094 UBYTE dat, inf, c;
\r
2096 dat = UniGetByte();
\r
2109 case SS_GLISSANDO: /* S1x set glissando voice */
\r
2110 DoEEffects(tick, flags, a, mod, channel, 0x30|inf);
\r
2112 case SS_FINETUNE: /* S2x set finetune */
\r
2113 DoEEffects(tick, flags, a, mod, channel, 0x50|inf);
\r
2115 case SS_VIBWAVE: /* S3x set vibrato waveform */
\r
2116 DoEEffects(tick, flags, a, mod, channel, 0x40|inf);
\r
2118 case SS_TREMWAVE: /* S4x set tremolo waveform */
\r
2119 DoEEffects(tick, flags, a, mod, channel, 0x70|inf);
\r
2121 case SS_PANWAVE: /* S5x panbrello */
\r
2124 case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */
\r
2125 DoEEffects(tick, flags, a, mod, channel, 0xe0|inf);
\r
2127 case SS_S7EFFECTS: /* S7x instrument / NNA commands */
\r
2128 DoNNAEffects(mod, a, inf);
\r
2130 case SS_PANNING: /* S8x set panning position */
\r
2131 DoEEffects(tick, flags, a, mod, channel, 0x80 | inf);
\r
2133 case SS_SURROUND: /* S9x set surround sound */
\r
2135 a->main.panning = mod->panning[channel] = PAN_SURROUND;
\r
2137 case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */
\r
2139 a->hioffset=inf<<16;
\r
2140 a->main.start=a->hioffset|a->soffset;
\r
2142 if ((a->main.s)&&(a->main.start>a->main.s->length))
\r
2143 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
\r
2144 a->main.s->loopstart:a->main.s->length;
\r
2147 case SS_PATLOOP: /* SBx pattern loop */
\r
2148 DoEEffects(tick, flags, a, mod, channel, 0x60|inf);
\r
2150 case SS_NOTECUT: /* SCx notecut */
\r
2151 if (!inf) inf = 1;
\r
2152 DoEEffects(tick, flags, a, mod, channel, 0xC0|inf);
\r
2154 case SS_NOTEDELAY: /* SDx notedelay */
\r
2155 DoEEffects(tick, flags, a, mod, channel, 0xD0|inf);
\r
2157 case SS_PATDELAY: /* SEx patterndelay */
\r
2158 DoEEffects(tick, flags, a, mod, channel, 0xE0|inf);
\r
2165 /*========== Impulse Tracker Volume/Pan Column effects */
\r
2168 * All volume/pan column effects share the same memory space.
\r
2171 static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2176 c = UniGetByte();
\r
2177 inf = UniGetByte();
\r
2179 if ((!c)&&(!inf)) {
\r
2191 if (inf>64) inf=64;
\r
2196 a->main.panning=inf;
\r
2198 case VOL_VOLSLIDE:
\r
2199 DoS3MVolSlide(tick, flags, a, inf);
\r
2201 case VOL_PITCHSLIDEDN:
\r
2202 if (a->main.period)
\r
2203 DoS3MSlideDn(tick, a, inf);
\r
2205 case VOL_PITCHSLIDEUP:
\r
2206 if (a->main.period)
\r
2207 DoS3MSlideUp(tick, a, inf);
\r
2209 case VOL_PORTAMENTO:
\r
2210 DoITToneSlide(tick, a, inf);
\r
2213 DoITVibrato(tick, a, inf);
\r
2220 /*========== UltraTracker effects */
\r
2222 static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2224 UWORD offset=UniGetWord();
\r
2231 a->ultoffset=offset;
\r
2233 a->main.start=a->ultoffset<<2;
\r
2234 if ((a->main.s)&&(a->main.start>a->main.s->length))
\r
2235 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
\r
2236 a->main.s->loopstart:a->main.s->length;
\r
2241 /*========== OctaMED effects */
\r
2243 static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2245 UWORD speed=UniGetWord();
\r
2256 static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2258 DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2));
\r
2263 static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2265 DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2));
\r
2270 static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2272 DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3));
\r
2277 /*========== Oktalyzer effects */
\r
2279 static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2285 dat2 = UniGetByte(); /* arpeggio style */
\r
2286 dat = UniGetByte();
\r
2288 if (!dat && (flags & UF_ARPMEM))
\r
2293 if (a->main.period)
\r
2294 DoArpeggio(tick, flags, a, dat2);
\r
2299 /*========== General player functions */
\r
2301 static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
\r
2313 typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD);
\r
2315 static effect_func effects[UNI_LAST] = {
\r
2316 DoNothing, /* 0 */
\r
2317 DoNothing, /* UNI_NOTE */
\r
2318 DoNothing, /* UNI_INSTRUMENT */
\r
2319 DoPTEffect0, /* UNI_PTEFFECT0 */
\r
2320 DoPTEffect1, /* UNI_PTEFFECT1 */
\r
2321 DoPTEffect2, /* UNI_PTEFFECT2 */
\r
2322 DoPTEffect3, /* UNI_PTEFFECT3 */
\r
2323 DoPTEffect4, /* UNI_PTEFFECT4 */
\r
2324 DoPTEffect5, /* UNI_PTEFFECT5 */
\r
2325 DoPTEffect6, /* UNI_PTEFFECT6 */
\r
2326 DoPTEffect7, /* UNI_PTEFFECT7 */
\r
2327 DoPTEffect8, /* UNI_PTEFFECT8 */
\r
2328 DoPTEffect9, /* UNI_PTEFFECT9 */
\r
2329 DoPTEffectA, /* UNI_PTEFFECTA */
\r
2330 DoPTEffectB, /* UNI_PTEFFECTB */
\r
2331 DoPTEffectC, /* UNI_PTEFFECTC */
\r
2332 DoPTEffectD, /* UNI_PTEFFECTD */
\r
2333 DoPTEffectE, /* UNI_PTEFFECTE */
\r
2334 DoPTEffectF, /* UNI_PTEFFECTF */
\r
2335 DoS3MEffectA, /* UNI_S3MEFFECTA */
\r
2336 DoS3MEffectD, /* UNI_S3MEFFECTD */
\r
2337 DoS3MEffectE, /* UNI_S3MEFFECTE */
\r
2338 DoS3MEffectF, /* UNI_S3MEFFECTF */
\r
2339 DoS3MEffectI, /* UNI_S3MEFFECTI */
\r
2340 DoS3MEffectQ, /* UNI_S3MEFFECTQ */
\r
2341 DoS3MEffectR, /* UNI_S3MEFFECTR */
\r
2342 DoS3MEffectT, /* UNI_S3MEFFECTT */
\r
2343 DoS3MEffectU, /* UNI_S3MEFFECTU */
\r
2344 DoKeyOff, /* UNI_KEYOFF */
\r
2345 DoKeyFade, /* UNI_KEYFADE */
\r
2346 DoVolEffects, /* UNI_VOLEFFECTS */
\r
2347 DoPTEffect4, /* UNI_XMEFFECT4 */
\r
2348 DoXMEffect6, /* UNI_XMEFFECT6 */
\r
2349 DoXMEffectA, /* UNI_XMEFFECTA */
\r
2350 DoXMEffectE1, /* UNI_XMEFFECTE1 */
\r
2351 DoXMEffectE2, /* UNI_XMEFFECTE2 */
\r
2352 DoXMEffectEA, /* UNI_XMEFFECTEA */
\r
2353 DoXMEffectEB, /* UNI_XMEFFECTEB */
\r
2354 DoXMEffectG, /* UNI_XMEFFECTG */
\r
2355 DoXMEffectH, /* UNI_XMEFFECTH */
\r
2356 DoXMEffectL, /* UNI_XMEFFECTL */
\r
2357 DoXMEffectP, /* UNI_XMEFFECTP */
\r
2358 DoXMEffectX1, /* UNI_XMEFFECTX1 */
\r
2359 DoXMEffectX2, /* UNI_XMEFFECTX2 */
\r
2360 DoITEffectG, /* UNI_ITEFFECTG */
\r
2361 DoITEffectH, /* UNI_ITEFFECTH */
\r
2362 DoITEffectI, /* UNI_ITEFFECTI */
\r
2363 DoITEffectM, /* UNI_ITEFFECTM */
\r
2364 DoITEffectN, /* UNI_ITEFFECTN */
\r
2365 DoITEffectP, /* UNI_ITEFFECTP */
\r
2366 DoITEffectT, /* UNI_ITEFFECTT */
\r
2367 DoITEffectU, /* UNI_ITEFFECTU */
\r
2368 DoITEffectW, /* UNI_ITEFFECTW */
\r
2369 DoITEffectY, /* UNI_ITEFFECTY */
\r
2370 DoNothing, /* UNI_ITEFFECTZ */
\r
2371 DoITEffectS0, /* UNI_ITEFFECTS0 */
\r
2372 DoULTEffect9, /* UNI_ULTEFFECT9 */
\r
2373 DoMEDSpeed, /* UNI_MEDSPEED */
\r
2374 DoMEDEffectF1, /* UNI_MEDEFFECTF1 */
\r
2375 DoMEDEffectF2, /* UNI_MEDEFFECTF2 */
\r
2376 DoMEDEffectF3, /* UNI_MEDEFFECTF3 */
\r
2377 DoOktArp, /* UNI_OKTARP */
\r
2380 static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a)
\r
2382 UWORD tick = mod->vbtick;
\r
2383 UWORD flags = mod->flags;
\r
2385 int explicitslides = 0;
\r
2388 while((c=UniGetByte())) {
\r
2390 if (f != DoNothing)
\r
2392 explicitslides |= f(tick, flags, a, mod, channel);
\r
2394 return explicitslides;
\r
2397 static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat)
\r
2403 aout=(a->slave)?a->slave:NULL;
\r
2406 case 0x0: /* past note cut */
\r
2407 for (t=0;t<md_sngchn;t++)
\r
2408 if (mod->voice[t].master==a)
\r
2409 mod->voice[t].main.fadevol=0;
\r
2411 case 0x1: /* past note off */
\r
2412 for (t=0;t<md_sngchn;t++)
\r
2413 if (mod->voice[t].master==a) {
\r
2414 mod->voice[t].main.keyoff|=KEY_OFF;
\r
2415 if ((!(mod->voice[t].venv.flg & EF_ON))||
\r
2416 (mod->voice[t].venv.flg & EF_LOOP))
\r
2417 mod->voice[t].main.keyoff=KEY_KILL;
\r
2420 case 0x2: /* past note fade */
\r
2421 for (t=0;t<md_sngchn;t++)
\r
2422 if (mod->voice[t].master==a)
\r
2423 mod->voice[t].main.keyoff|=KEY_FADE;
\r
2425 case 0x3: /* set NNA note cut */
\r
2426 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT;
\r
2428 case 0x4: /* set NNA note continue */
\r
2429 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE;
\r
2431 case 0x5: /* set NNA note off */
\r
2432 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF;
\r
2434 case 0x6: /* set NNA note fade */
\r
2435 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE;
\r
2437 case 0x7: /* disable volume envelope */
\r
2439 aout->main.volflg&=~EF_ON;
\r
2441 case 0x8: /* enable volume envelope */
\r
2443 aout->main.volflg|=EF_ON;
\r
2445 case 0x9: /* disable panning envelope */
\r
2447 aout->main.panflg&=~EF_ON;
\r
2449 case 0xa: /* enable panning envelope */
\r
2451 aout->main.panflg|=EF_ON;
\r
2453 case 0xb: /* disable pitch envelope */
\r
2455 aout->main.pitflg&=~EF_ON;
\r
2457 case 0xc: /* enable pitch envelope */
\r
2459 aout->main.pitflg|=EF_ON;
\r
2464 void pt_UpdateVoices(MODULE *mod, int max_volume)
\r
2466 SWORD envpan,envvol,envpit,channel;
\r
2468 SLONG vibval,vibdpt;
\r
2475 mod->totalchn=mod->realchn=0;
\r
2476 for (channel=0;channel<md_sngchn;channel++) {
\r
2477 aout=&mod->voice[channel];
\r
2481 if (!s || !s->length) continue;
\r
2483 if (aout->main.period<40)
\r
2484 aout->main.period=40;
\r
2485 else if (aout->main.period>50000)
\r
2486 aout->main.period=50000;
\r
2488 if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) {
\r
2489 Voice_Play_internal(channel,s,(aout->main.start==-1)?
\r
2490 ((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->main.start);
\r
2491 aout->main.fadevol=32768;
\r
2496 envpan = PAN_CENTER;
\r
2498 if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) {
\r
2499 if (aout->main.volflg & EF_ON)
\r
2500 envvol = StartEnvelope(&aout->venv,aout->main.volflg,
\r
2501 i->volpts,i->volsusbeg,i->volsusend,
\r
2502 i->volbeg,i->volend,i->volenv,aout->main.keyoff);
\r
2503 if (aout->main.panflg & EF_ON)
\r
2504 envpan = StartEnvelope(&aout->penv,aout->main.panflg,
\r
2505 i->panpts,i->pansusbeg,i->pansusend,
\r
2506 i->panbeg,i->panend,i->panenv,aout->main.keyoff);
\r
2507 if (aout->main.pitflg & EF_ON)
\r
2508 envpit = StartEnvelope(&aout->cenv,aout->main.pitflg,
\r
2509 i->pitpts,i->pitsusbeg,i->pitsusend,
\r
2510 i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff);
\r
2512 if (aout->cenv.flg & EF_ON)
\r
2513 aout->masterperiod=GetPeriod(mod->flags,
\r
2514 (UWORD)aout->main.note<<1, aout->master->speed);
\r
2516 if (aout->main.volflg & EF_ON)
\r
2517 envvol = ProcessEnvelope(aout,&aout->venv,256);
\r
2518 if (aout->main.panflg & EF_ON)
\r
2519 envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER);
\r
2520 if (aout->main.pitflg & EF_ON)
\r
2521 envpit = ProcessEnvelope(aout,&aout->cenv,32);
\r
2523 if (aout->main.kick == KICK_NOTE) {
\r
2524 aout->main.kick_flag = 1;
\r
2526 aout->main.kick=KICK_ABSENT;
\r
2528 tmpvol = aout->main.fadevol; /* max 32768 */
\r
2529 tmpvol *= aout->main.chanvol; /* * max 64 */
\r
2530 tmpvol *= aout->main.outvolume; /* * max 256 */
\r
2531 tmpvol /= (256 * 64); /* tmpvol is max 32768 again */
\r
2532 aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */
\r
2533 tmpvol *= envvol; /* * max 256 */
\r
2534 tmpvol *= mod->volume; /* * max 128 */
\r
2535 tmpvol /= (128 * 256 * 128);
\r
2538 if (mod->sngpos>=mod->numpos)
\r
2541 tmpvol=(tmpvol*max_volume)/128;
\r
2543 if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted)
\r
2544 Voice_SetVolume_internal(channel,0);
\r
2546 Voice_SetVolume_internal(channel,tmpvol);
\r
2547 if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
\r
2552 if (aout->main.panning==PAN_SURROUND)
\r
2553 Voice_SetPanning_internal(channel,PAN_SURROUND);
\r
2555 if ((mod->panflag)&&(aout->penv.flg & EF_ON))
\r
2556 Voice_SetPanning_internal(channel,
\r
2557 DoPan(envpan,aout->main.panning));
\r
2559 Voice_SetPanning_internal(channel,aout->main.panning);
\r
2561 if (aout->main.period && s->vibdepth)
\r
2562 switch (s->vibtype) {
\r
2564 vibval=avibtab[s->avibpos&127];
\r
2565 if (aout->avibpos & 0x80) vibval=-vibval;
\r
2569 if (aout->avibpos & 0x80) vibval=-vibval;
\r
2572 vibval=63-(((aout->avibpos+128)&255)>>1);
\r
2575 vibval=(((aout->avibpos+128)&255)>>1)-64;
\r
2581 if (s->vibflags & AV_IT) {
\r
2582 if ((aout->aswppos>>8)<s->vibdepth) {
\r
2583 aout->aswppos += s->vibsweep;
\r
2584 vibdpt=aout->aswppos;
\r
2586 vibdpt=s->vibdepth<<8;
\r
2587 vibval=(vibval*vibdpt)>>16;
\r
2588 if (aout->mflag) {
\r
2589 if (!(mod->flags&UF_LINEAR)) vibval>>=1;
\r
2590 aout->main.period-=vibval;
\r
2593 /* do XM style auto-vibrato */
\r
2594 if (!(aout->main.keyoff & KEY_OFF)) {
\r
2595 if (aout->aswppos<s->vibsweep) {
\r
2596 vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep;
\r
2599 vibdpt=s->vibdepth;
\r
2601 /* keyoff -> depth becomes 0 if final depth wasn't reached or
\r
2602 stays at final level if depth WAS reached */
\r
2603 if (aout->aswppos>=s->vibsweep)
\r
2604 vibdpt=s->vibdepth;
\r
2608 vibval=(vibval*vibdpt)>>8;
\r
2609 aout->main.period-=vibval;
\r
2612 /* update vibrato position */
\r
2613 aout->avibpos=(aout->avibpos+s->vibrate)&0xff;
\r
2615 /* process pitch envelope */
\r
2616 playperiod=aout->main.period;
\r
2618 if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) {
\r
2622 if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1);
\r
2624 p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit,
\r
2625 aout->master->speed)-aout->masterperiod;
\r
2627 if ((UWORD)(playperiod+p1)<=playperiod) {
\r
2629 aout->main.keyoff|=KEY_OFF;
\r
2631 } else if (p1<0) {
\r
2632 if ((UWORD)(playperiod+p1)>=playperiod) {
\r
2634 aout->main.keyoff|=KEY_OFF;
\r
2640 if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */
\r
2641 Voice_Stop_internal(channel);
\r
2643 if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
\r
2646 Voice_SetFrequency_internal(channel,
\r
2647 getfrequency(mod->flags,playperiod));
\r
2649 /* if keyfade, start substracting fadeoutspeed from fadevol: */
\r
2650 if ((i)&&(aout->main.keyoff&KEY_FADE)) {
\r
2651 if (aout->main.fadevol>=i->volfade)
\r
2652 aout->main.fadevol-=i->volfade;
\r
2654 aout->main.fadevol=0;
\r
2658 md_bpm=mod->bpm+mod->relspd;
\r
2661 else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255)
\r
2666 /* Handles new notes or instruments */
\r
2667 void pt_Notes(MODULE *mod)
\r
2672 int tr,funky; /* funky is set to indicate note or instrument change */
\r
2674 for (channel=0;channel<mod->numchn;channel++) {
\r
2675 a=&mod->control[channel];
\r
2677 if (mod->sngpos>=mod->numpos) {
\r
2681 tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel];
\r
2682 mod->numrow=mod->pattrows[mod->positions[mod->sngpos]];
\r
2685 a->row=(tr<mod->numtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL;
\r
2687 if (!mod->vbtick) a->main.notedelay=0;
\r
2689 if (!a->row) continue;
\r
2690 UniSetRow(a->row);
\r
2693 while((c=UniGetByte()))
\r
2697 a->oldnote=a->anote,a->anote=UniGetByte();
\r
2698 a->main.kick =KICK_NOTE;
\r
2702 /* retrig tremolo and vibrato waves ? */
\r
2703 if (!(a->wavecontrol & 0x80)) a->trmpos=0;
\r
2704 if (!(a->wavecontrol & 0x08)) a->vibpos=0;
\r
2705 if (!a->panbwave) a->panbpos=0;
\r
2707 case UNI_INSTRUMENT:
\r
2708 inst=UniGetByte();
\r
2709 if (inst>=mod->numins) break; /* safety valve */
\r
2711 a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL;
\r
2715 a->main.sample=inst;
\r
2726 if ((i=a->main.i)) {
\r
2727 if (i->samplenumber[a->anote] >= mod->numsmp) continue;
\r
2728 s=&mod->samples[i->samplenumber[a->anote]];
\r
2729 a->main.note=i->samplenote[a->anote];
\r
2731 a->main.note=a->anote;
\r
2732 s=&mod->samples[a->main.sample];
\r
2735 if (a->main.s!=s) {
\r
2737 a->newsamp=a->main.period;
\r
2740 /* channel or instrument determined panning ? */
\r
2741 a->main.panning=mod->panning[channel];
\r
2742 if (s->flags & SF_OWNPAN)
\r
2743 a->main.panning=s->panning;
\r
2744 else if ((i)&&(i->flags & IF_OWNPAN))
\r
2745 a->main.panning=i->panning;
\r
2747 a->main.handle=s->handle;
\r
2748 a->speed=s->speed;
\r
2751 if ((mod->panflag)&&(i->flags & IF_PITCHPAN)
\r
2752 &&(a->main.panning!=PAN_SURROUND)){
\r
2754 ((a->anote-i->pitpancenter)*i->pitpansep)/8;
\r
2755 if (a->main.panning<PAN_LEFT)
\r
2756 a->main.panning=PAN_LEFT;
\r
2757 else if (a->main.panning>PAN_RIGHT)
\r
2758 a->main.panning=PAN_RIGHT;
\r
2760 a->main.pitflg=i->pitflg;
\r
2761 a->main.volflg=i->volflg;
\r
2762 a->main.panflg=i->panflg;
\r
2763 a->main.nna=i->nnatype;
\r
2767 a->main.pitflg=a->main.volflg=a->main.panflg=0;
\r
2768 a->main.nna=a->dca=0;
\r
2772 if (funky&2) /* instrument change */ {
\r
2773 /* IT random volume variations: 0:8 bit fixed, and one bit for
\r
2775 a->volume=a->tmpvolume=s->volume;
\r
2778 a->volume=a->tmpvolume=s->volume+
\r
2779 ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512)
\r
2782 a->volume=a->tmpvolume=0;
\r
2783 else if (a->volume>64)
\r
2784 a->volume=a->tmpvolume=64;
\r
2786 if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) {
\r
2787 a->main.panning+=((a->main.panning*((SLONG)i->rpanvar*
\r
2788 (SLONG)getrandom(512)))/25600);
\r
2789 if (a->main.panning<PAN_LEFT)
\r
2790 a->main.panning=PAN_LEFT;
\r
2791 else if (a->main.panning>PAN_RIGHT)
\r
2792 a->main.panning=PAN_RIGHT;
\r
2797 a->wantedperiod=a->tmpperiod=
\r
2798 GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed);
\r
2799 a->main.keyoff=KEY_KICK;
\r
2804 /* Handles effects */
\r
2805 void pt_EffectsPass1(MODULE *mod)
\r
2810 int explicitslides;
\r
2812 for (channel=0;channel<mod->numchn;channel++) {
\r
2813 a=&mod->control[channel];
\r
2815 if ((aout=a->slave)) {
\r
2816 a->main.fadevol=aout->main.fadevol;
\r
2817 a->main.period=aout->main.period;
\r
2818 if (a->main.kick==KICK_KEYOFF)
\r
2819 a->main.keyoff=aout->main.keyoff;
\r
2822 if (!a->row) continue;
\r
2823 UniSetRow(a->row);
\r
2825 a->ownper=a->ownvol=0;
\r
2826 explicitslides = pt_playeffects(mod, channel, a);
\r
2828 /* continue volume slide if necessary for XM and IT */
\r
2829 if (mod->flags&UF_BGSLIDES) {
\r
2830 if (!explicitslides && a->sliding)
\r
2831 DoS3MVolSlide(mod->vbtick, mod->flags, a, 0);
\r
2832 else if (a->tmpvolume)
\r
2833 a->sliding = explicitslides;
\r
2837 a->main.period=a->tmpperiod;
\r
2839 a->volume=a->tmpvolume;
\r
2843 a->main.outvolume=
\r
2844 (a->volume*a->main.s->globvol*a->main.i->globvol)>>10;
\r
2846 a->main.outvolume=(a->volume*a->main.s->globvol)>>4;
\r
2847 if (a->main.outvolume>256)
\r
2848 a->main.outvolume=256;
\r
2849 else if (a->main.outvolume<0)
\r
2850 a->main.outvolume=0;
\r
2855 /* NNA management */
\r
2856 void pt_NNA(MODULE *mod)
\r
2861 for (channel=0;channel<mod->numchn;channel++) {
\r
2862 a=&mod->control[channel];
\r
2864 if (a->main.kick==KICK_NOTE) {
\r
2871 if (aout->main.nna & NNA_MASK) {
\r
2872 /* Make sure the old MP_VOICE channel knows it has no
\r
2875 /* assume the channel is taken by NNA */
\r
2878 switch (aout->main.nna) {
\r
2879 case NNA_CONTINUE: /* continue note, do nothing */
\r
2881 case NNA_OFF: /* note off */
\r
2882 aout->main.keyoff|=KEY_OFF;
\r
2883 if ((!(aout->main.volflg & EF_ON))||
\r
2884 (aout->main.volflg & EF_LOOP))
\r
2885 aout->main.keyoff=KEY_KILL;
\r
2888 aout->main.keyoff |= KEY_FADE;
\r
2894 if (a->dct!=DCT_OFF) {
\r
2897 for (t=0;t<md_sngchn;t++)
\r
2898 if ((!Voice_Stopped_internal(t))&&
\r
2899 (mod->voice[t].masterchn==channel)&&
\r
2900 (a->main.sample==mod->voice[t].main.sample)) {
\r
2904 if (a->main.note==mod->voice[t].main.note)
\r
2908 if (a->main.handle==mod->voice[t].main.handle)
\r
2918 mod->voice[t].main.fadevol=0;
\r
2921 mod->voice[t].main.keyoff|=KEY_OFF;
\r
2922 if ((!(mod->voice[t].main.volflg&EF_ON))||
\r
2923 (mod->voice[t].main.volflg&EF_LOOP))
\r
2924 mod->voice[t].main.keyoff=KEY_KILL;
\r
2927 mod->voice[t].main.keyoff|=KEY_FADE;
\r
2932 } /* if (a->main.kick==KICK_NOTE) */
\r
2936 /* Setup module and NNA voices */
\r
2937 void pt_SetupVoices(MODULE *mod)
\r
2943 for (channel=0;channel<mod->numchn;channel++) {
\r
2944 a=&mod->control[channel];
\r
2946 if (a->main.notedelay) continue;
\r
2947 if (a->main.kick==KICK_NOTE) {
\r
2948 /* if no channel was cut above, find an empty or quiet channel
\r
2950 if (mod->flags&UF_NNA) {
\r
2954 if ((newchn=MP_FindEmptyChannel(mod))!=-1)
\r
2955 a->slave=&mod->voice[a->slavechn=newchn];
\r
2958 a->slave=&mod->voice[a->slavechn=channel];
\r
2960 /* assign parts of MP_VOICE only done for a KICK_NOTE */
\r
2961 if ((aout=a->slave)) {
\r
2962 if (aout->mflag && aout->master) aout->master->slave=NULL;
\r
2965 aout->masterchn=channel;
\r
2972 aout->main=a->main;
\r
2973 a->main.kick=KICK_ABSENT;
\r
2977 /* second effect pass */
\r
2978 void pt_EffectsPass2(MODULE *mod)
\r
2984 for (channel=0;channel<mod->numchn;channel++) {
\r
2985 a=&mod->control[channel];
\r
2987 if (!a->row) continue;
\r
2988 UniSetRow(a->row);
\r
2990 while((c=UniGetByte()))
\r
2991 if (c==UNI_ITEFFECTS0) {
\r
2993 if ((c>>4)==SS_S7EFFECTS)
\r
2994 DoNNAEffects(mod, a, c&0xf);
\r
3000 void Player_HandleTick(void)
\r
3006 /* don't handle the very first ticks, this allows the other hardware to
\r
3007 settle down so we don't loose any starting notes */
\r
3014 if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return;
\r
3016 /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */
\r
3017 pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */
\r
3018 pf->sngtime+=pf->sngremainder/pf->bpm;
\r
3019 pf->sngremainder%=pf->bpm;
\r
3021 if (++pf->vbtick>=pf->sngspd) {
\r
3022 if (pf->pat_repcrazy)
\r
3023 pf->pat_repcrazy=0; /* play 2 times row 0 */
\r
3028 /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is
\r
3029 the command memory. */
\r
3031 pf->patdly2=pf->patdly,pf->patdly=0;
\r
3032 if (pf->patdly2) {
\r
3033 /* patterndelay active */
\r
3034 if (--pf->patdly2)
\r
3035 /* so turn back pf->patpos by 1 */
\r
3036 if (pf->patpos) pf->patpos--;
\r
3039 /* do we have to get a new patternpointer ? (when pf->patpos reaches the
\r
3040 pattern size, or when a patternbreak is active) */
\r
3041 if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp))
\r
3045 pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0;
\r
3046 pf->pat_repcrazy=0;
\r
3047 pf->sngpos+=(pf->posjmp-2);
\r
3048 for (channel=0;channel<pf->numchn;channel++)
\r
3049 pf->control[channel].pat_reppos=-1;
\r
3051 pf->patbrk=pf->posjmp=0;
\r
3052 /* handle the "---" (end of song) pattern since it can occur
\r
3053 *inside* the module in some formats */
\r
3054 if ((pf->sngpos>=pf->numpos)||
\r
3055 (pf->positions[pf->sngpos]==LAST_PATTERN)) {
\r
3056 if (!pf->wrap) return;
\r
3057 if (!(pf->sngpos=pf->reppos)) {
\r
3058 pf->volume=pf->initvolume>128?128:pf->initvolume;
\r
3059 if(pf->initspeed!=0)
\r
3060 pf->sngspd=pf->initspeed<32?pf->initspeed:32;
\r
3063 pf->bpm=pf->inittempo<32?32:pf->inittempo;
\r
3066 if (pf->sngpos<0) pf->sngpos=pf->numpos-1;
\r
3073 /* Fade global volume if enabled and we're playing the last pattern */
\r
3074 if (((pf->sngpos==pf->numpos-1)||
\r
3075 (pf->positions[pf->sngpos+1]==LAST_PATTERN))&&
\r
3077 max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0;
\r
3081 pt_EffectsPass1(pf);
\r
3082 if (pf->flags&UF_NNA)
\r
3084 pt_SetupVoices(pf);
\r
3085 pt_EffectsPass2(pf);
\r
3087 /* now set up the actual hardware channel playback information */
\r
3088 pt_UpdateVoices(pf, max_volume);
\r
3091 static void Player_Init_internal(MODULE* mod)
\r
3095 for (t=0;t<mod->numchn;t++) {
\r
3096 mod->control[t].main.chanvol=mod->chanvol[t];
\r
3097 mod->control[t].main.panning=mod->panning[t];
\r
3101 mod->sngremainder=0;
\r
3103 mod->pat_repcrazy=0;
\r
3105 if(mod->initspeed!=0)
\r
3106 mod->sngspd=mod->initspeed<32?mod->initspeed:32;
\r
3109 mod->volume=mod->initvolume>128?128:mod->initvolume;
\r
3111 mod->vbtick=mod->sngspd;
\r
3114 mod->bpm=mod->inittempo<32?32:mod->inittempo;
\r
3118 mod->posjmp=2; /* make sure the player fetches the first note */
\r
3123 BOOL Player_Init(MODULE* mod)
\r
3133 /* make sure the player doesn't start with garbage */
\r
3134 if (!(mod->control=(MP_CONTROL*)_mm_calloc(mod->numchn,sizeof(MP_CONTROL))))
\r
3136 if (!(mod->voice=(MP_VOICE*)_mm_calloc(md_sngchn,sizeof(MP_VOICE))))
\r
3139 Player_Init_internal(mod);
\r
3143 void Player_Exit_internal(MODULE* mod)
\r
3148 /* Stop playback if necessary */
\r
3150 Player_Stop_internal();
\r
3155 free(mod->control);
\r
3158 mod->control=NULL;
\r
3162 void Player_Exit(MODULE* mod)
\r
3165 Player_Exit_internal(mod);
\r
3166 MUTEX_UNLOCK(vars);
\r
3169 MIKMODAPI void Player_SetVolume(SWORD volume)
\r
3173 pf->volume=(volume<0)?0:(volume>128)?128:volume;
\r
3174 MUTEX_UNLOCK(vars);
\r
3177 MIKMODAPI MODULE* Player_GetModule(void)
\r
3183 MUTEX_UNLOCK(vars);
\r
3188 MIKMODAPI void Player_Start(MODULE *mod)
\r
3195 if (!MikMod_Active())
\r
3196 MikMod_EnableOutput();
\r
3202 /* new song is being started, so completely stop out the old one. */
\r
3203 if (pf) pf->forbid=1;
\r
3204 for (t=0;t<md_sngchn;t++) Voice_Stop_internal(t);
\r
3207 MUTEX_UNLOCK(vars);
\r
3210 void Player_Stop_internal(void)
\r
3212 if (!md_sfxchn) MikMod_DisableOutput_internal();
\r
3213 if (pf) pf->forbid=1;
\r
3217 MIKMODAPI void Player_Stop(void)
\r
3220 Player_Stop_internal();
\r
3221 MUTEX_UNLOCK(vars);
\r
3224 MIKMODAPI BOOL Player_Active(void)
\r
3230 result=(!(pf->sngpos>=pf->numpos));
\r
3231 MUTEX_UNLOCK(vars);
\r
3236 MIKMODAPI void Player_NextPosition(void)
\r
3245 pf->vbtick=pf->sngspd;
\r
3247 for (t=0;t<md_sngchn;t++) {
\r
3248 Voice_Stop_internal(t);
\r
3249 pf->voice[t].main.i=NULL;
\r
3250 pf->voice[t].main.s=NULL;
\r
3252 for (t=0;t<pf->numchn;t++) {
\r
3253 pf->control[t].main.i=NULL;
\r
3254 pf->control[t].main.s=NULL;
\r
3258 MUTEX_UNLOCK(vars);
\r
3261 MIKMODAPI void Player_PrevPosition(void)
\r
3270 pf->vbtick=pf->sngspd;
\r
3272 for (t=0;t<md_sngchn;t++) {
\r
3273 Voice_Stop_internal(t);
\r
3274 pf->voice[t].main.i=NULL;
\r
3275 pf->voice[t].main.s=NULL;
\r
3277 for (t=0;t<pf->numchn;t++) {
\r
3278 pf->control[t].main.i=NULL;
\r
3279 pf->control[t].main.s=NULL;
\r
3283 MUTEX_UNLOCK(vars);
\r
3286 MIKMODAPI void Player_SetPosition(UWORD pos)
\r
3293 if (pos>=pf->numpos) pos=pf->numpos;
\r
3297 pf->vbtick=pf->sngspd;
\r
3299 for (t=0;t<md_sngchn;t++) {
\r
3300 Voice_Stop_internal(t);
\r
3301 pf->voice[t].main.i=NULL;
\r
3302 pf->voice[t].main.s=NULL;
\r
3304 for (t=0;t<pf->numchn;t++) {
\r
3305 pf->control[t].main.i=NULL;
\r
3306 pf->control[t].main.s=NULL;
\r
3311 Player_Init_internal(pf);
\r
3313 MUTEX_UNLOCK(vars);
\r
3316 static void Player_Unmute_internal(SLONG arg1,va_list ap)
\r
3318 SLONG t,arg2,arg3=0;
\r
3322 case MUTE_INCLUSIVE:
\r
3323 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3324 (arg2>arg3)||(arg3>=pf->numchn))
\r
3326 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
\r
3327 pf->control[arg2].muted=0;
\r
3329 case MUTE_EXCLUSIVE:
\r
3330 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3331 (arg2>arg3)||(arg3>=pf->numchn))
\r
3333 for (t=0;t<pf->numchn;t++) {
\r
3334 if ((t>=arg2) && (t<=arg3))
\r
3336 pf->control[t].muted=0;
\r
3340 if (arg1<pf->numchn) pf->control[arg1].muted=0;
\r
3346 MIKMODAPI void Player_Unmute(SLONG arg1, ...)
\r
3350 va_start(args,arg1);
\r
3352 Player_Unmute_internal(arg1,args);
\r
3353 MUTEX_UNLOCK(vars);
\r
3357 static void Player_Mute_internal(SLONG arg1,va_list ap)
\r
3359 SLONG t,arg2,arg3=0;
\r
3363 case MUTE_INCLUSIVE:
\r
3364 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3365 (arg2>arg3)||(arg3>=pf->numchn))
\r
3367 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
\r
3368 pf->control[arg2].muted=1;
\r
3370 case MUTE_EXCLUSIVE:
\r
3371 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3372 (arg2>arg3)||(arg3>=pf->numchn))
\r
3374 for (t=0;t<pf->numchn;t++) {
\r
3375 if ((t>=arg2) && (t<=arg3))
\r
3377 pf->control[t].muted=1;
\r
3381 if (arg1<pf->numchn)
\r
3382 pf->control[arg1].muted=1;
\r
3388 MIKMODAPI void Player_Mute(SLONG arg1,...)
\r
3392 va_start(args,arg1);
\r
3394 Player_Mute_internal(arg1,args);
\r
3395 MUTEX_UNLOCK(vars);
\r
3399 static void Player_ToggleMute_internal(SLONG arg1,va_list ap)
\r
3401 SLONG arg2,arg3=0;
\r
3406 case MUTE_INCLUSIVE:
\r
3407 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3408 (arg2>arg3)||(arg3>=pf->numchn))
\r
3410 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
\r
3411 pf->control[arg2].muted=1-pf->control[arg2].muted;
\r
3413 case MUTE_EXCLUSIVE:
\r
3414 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
\r
3415 (arg2>arg3)||(arg3>=pf->numchn))
\r
3417 for (t=0;t<pf->numchn;t++) {
\r
3418 if ((t>=arg2) && (t<=arg3))
\r
3420 pf->control[t].muted=1-pf->control[t].muted;
\r
3424 if (arg1<pf->numchn)
\r
3425 pf->control[arg1].muted=1-pf->control[arg1].muted;
\r
3431 MIKMODAPI void Player_ToggleMute(SLONG arg1,...)
\r
3435 va_start(args,arg1);
\r
3437 Player_ToggleMute_internal(arg1,args);
\r
3438 MUTEX_UNLOCK(vars);
\r
3442 MIKMODAPI BOOL Player_Muted(UBYTE chan)
\r
3448 result=(chan<pf->numchn)?pf->control[chan].muted:1;
\r
3449 MUTEX_UNLOCK(vars);
\r
3454 MIKMODAPI int Player_GetChannelVoice(UBYTE chan)
\r
3460 result=(chan<pf->numchn)?pf->control[chan].slavechn:-1;
\r
3461 MUTEX_UNLOCK(vars);
\r
3466 MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan)
\r
3472 result=(chan<pf->numchn)?pf->control[chan].main.period:0;
\r
3473 MUTEX_UNLOCK(vars);
\r
3478 BOOL Player_Paused_internal(void)
\r
3480 return pf?pf->forbid:1;
\r
3483 MIKMODAPI BOOL Player_Paused(void)
\r
3488 result=Player_Paused_internal();
\r
3489 MUTEX_UNLOCK(vars);
\r
3494 MIKMODAPI void Player_TogglePause(void)
\r
3498 pf->forbid=1-pf->forbid;
\r
3499 MUTEX_UNLOCK(vars);
\r
3502 MIKMODAPI void Player_SetSpeed(UWORD speed)
\r
3506 pf->sngspd=speed?(speed<32?speed:32):1;
\r
3507 MUTEX_UNLOCK(vars);
\r
3510 MIKMODAPI void Player_SetTempo(UWORD tempo)
\r
3512 if (tempo<32) tempo=32;
\r
3515 if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255;
\r
3518 MUTEX_UNLOCK(vars);
\r
3521 MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo)
\r
3525 if (numvoices > md_sngchn)
\r
3526 numvoices = md_sngchn;
\r
3530 for (i = 0; i < md_sngchn; i++) {
\r
3531 vinfo [i].i = pf->voice[i].main.i;
\r
3532 vinfo [i].s = pf->voice[i].main.s;
\r
3533 vinfo [i].panning = pf->voice [i].main.panning;
\r
3534 vinfo [i].volume = pf->voice [i].main.chanvol;
\r
3535 vinfo [i].period = pf->voice [i].main.period;
\r
3536 vinfo [i].kick = pf->voice [i].main.kick_flag;
\r
3537 pf->voice [i].main.kick_flag = 0;
\r
3539 MUTEX_UNLOCK(vars);
\r
3546 /* ex:set ts=4: */
\r