summaryrefslogtreecommitdiff
path: root/apps/plugins/mikmod/mplayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/mikmod/mplayer.c')
-rw-r--r--apps/plugins/mikmod/mplayer.c3409
1 files changed, 3409 insertions, 0 deletions
diff --git a/apps/plugins/mikmod/mplayer.c b/apps/plugins/mikmod/mplayer.c
new file mode 100644
index 0000000000..92585f0320
--- /dev/null
+++ b/apps/plugins/mikmod/mplayer.c
@@ -0,0 +1,3409 @@
1/* MikMod sound library
2 (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
3 for complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19*/
20
21/*==============================================================================
22
23 $Id: mplayer.c,v 1.4 2006/08/08 00:06:31 realtech Exp $
24
25 The Protracker Player Driver
26
27 The protracker driver supports all base Protracker 3.x commands and features.
28
29==============================================================================*/
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <string.h>
36#include <stdarg.h>
37#ifdef SRANDOM_IN_MATH_H
38#include <math.h>
39#else
40#include <stdlib.h>
41#endif
42
43#include "mikmod_internals.h"
44
45#ifdef SUNOS
46extern int fprintf(FILE *, const char *, ...);
47extern long int random(void);
48#endif
49
50/* The currently playing module */
51MODULE *pf = NULL;
52
53#define HIGH_OCTAVE 2 /* number of above-range octaves */
54
55static UWORD oldperiods[OCTAVE*2]={
56 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
57 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
58 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160,
59 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
60};
61
62static UBYTE VibratoTable[32]={
63 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
64 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
65};
66
67static UBYTE avibtab[128]={
68 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
69 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
70 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
71 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
72 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
73 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
74 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
75 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
76};
77
78/* Triton's linear periods to frequency translation table (for XM modules) */
79static ULONG lintab[768]={
80 535232,534749,534266,533784,533303,532822,532341,531861,
81 531381,530902,530423,529944,529466,528988,528511,528034,
82 527558,527082,526607,526131,525657,525183,524709,524236,
83 523763,523290,522818,522346,521875,521404,520934,520464,
84 519994,519525,519057,518588,518121,517653,517186,516720,
85 516253,515788,515322,514858,514393,513929,513465,513002,
86 512539,512077,511615,511154,510692,510232,509771,509312,
87 508852,508393,507934,507476,507018,506561,506104,505647,
88 505191,504735,504280,503825,503371,502917,502463,502010,
89 501557,501104,500652,500201,499749,499298,498848,498398,
90 497948,497499,497050,496602,496154,495706,495259,494812,
91 494366,493920,493474,493029,492585,492140,491696,491253,
92 490809,490367,489924,489482,489041,488600,488159,487718,
93 487278,486839,486400,485961,485522,485084,484647,484210,
94 483773,483336,482900,482465,482029,481595,481160,480726,
95 480292,479859,479426,478994,478562,478130,477699,477268,
96 476837,476407,475977,475548,475119,474690,474262,473834,
97 473407,472979,472553,472126,471701,471275,470850,470425,
98 470001,469577,469153,468730,468307,467884,467462,467041,
99 466619,466198,465778,465358,464938,464518,464099,463681,
100 463262,462844,462427,462010,461593,461177,460760,460345,
101 459930,459515,459100,458686,458272,457859,457446,457033,
102 456621,456209,455797,455386,454975,454565,454155,453745,
103 453336,452927,452518,452110,451702,451294,450887,450481,
104 450074,449668,449262,448857,448452,448048,447644,447240,
105 446836,446433,446030,445628,445226,444824,444423,444022,
106 443622,443221,442821,442422,442023,441624,441226,440828,
107 440430,440033,439636,439239,438843,438447,438051,437656,
108 437261,436867,436473,436079,435686,435293,434900,434508,
109 434116,433724,433333,432942,432551,432161,431771,431382,
110 430992,430604,430215,429827,429439,429052,428665,428278,
111 427892,427506,427120,426735,426350,425965,425581,425197,
112 424813,424430,424047,423665,423283,422901,422519,422138,
113 421757,421377,420997,420617,420237,419858,419479,419101,
114 418723,418345,417968,417591,417214,416838,416462,416086,
115 415711,415336,414961,414586,414212,413839,413465,413092,
116 412720,412347,411975,411604,411232,410862,410491,410121,
117 409751,409381,409012,408643,408274,407906,407538,407170,
118 406803,406436,406069,405703,405337,404971,404606,404241,
119 403876,403512,403148,402784,402421,402058,401695,401333,
120 400970,400609,400247,399886,399525,399165,398805,398445,
121 398086,397727,397368,397009,396651,396293,395936,395579,
122 395222,394865,394509,394153,393798,393442,393087,392733,
123 392378,392024,391671,391317,390964,390612,390259,389907,
124 389556,389204,388853,388502,388152,387802,387452,387102,
125 386753,386404,386056,385707,385359,385012,384664,384317,
126 383971,383624,383278,382932,382587,382242,381897,381552,
127 381208,380864,380521,380177,379834,379492,379149,378807,
128 378466,378124,377783,377442,377102,376762,376422,376082,
129 375743,375404,375065,374727,374389,374051,373714,373377,
130 373040,372703,372367,372031,371695,371360,371025,370690,
131 370356,370022,369688,369355,369021,368688,368356,368023,
132 367691,367360,367028,366697,366366,366036,365706,365376,
133 365046,364717,364388,364059,363731,363403,363075,362747,
134 362420,362093,361766,361440,361114,360788,360463,360137,
135 359813,359488,359164,358840,358516,358193,357869,357547,
136 357224,356902,356580,356258,355937,355616,355295,354974,
137 354654,354334,354014,353695,353376,353057,352739,352420,
138 352103,351785,351468,351150,350834,350517,350201,349885,
139 349569,349254,348939,348624,348310,347995,347682,347368,
140 347055,346741,346429,346116,345804,345492,345180,344869,
141 344558,344247,343936,343626,343316,343006,342697,342388,
142 342079,341770,341462,341154,340846,340539,340231,339924,
143 339618,339311,339005,338700,338394,338089,337784,337479,
144 337175,336870,336566,336263,335959,335656,335354,335051,
145 334749,334447,334145,333844,333542,333242,332941,332641,
146 332341,332041,331741,331442,331143,330844,330546,330247,
147 329950,329652,329355,329057,328761,328464,328168,327872,
148 327576,327280,326985,326690,326395,326101,325807,325513,
149 325219,324926,324633,324340,324047,323755,323463,323171,
150 322879,322588,322297,322006,321716,321426,321136,320846,
151 320557,320267,319978,319690,319401,319113,318825,318538,
152 318250,317963,317676,317390,317103,316817,316532,316246,
153 315961,315676,315391,315106,314822,314538,314254,313971,
154 313688,313405,313122,312839,312557,312275,311994,311712,
155 311431,311150,310869,310589,310309,310029,309749,309470,
156 309190,308911,308633,308354,308076,307798,307521,307243,
157 306966,306689,306412,306136,305860,305584,305308,305033,
158 304758,304483,304208,303934,303659,303385,303112,302838,
159 302565,302292,302019,301747,301475,301203,300931,300660,
160 300388,300117,299847,299576,299306,299036,298766,298497,
161 298227,297958,297689,297421,297153,296884,296617,296349,
162 296082,295815,295548,295281,295015,294749,294483,294217,
163 293952,293686,293421,293157,292892,292628,292364,292100,
164 291837,291574,291311,291048,290785,290523,290261,289999,
165 289737,289476,289215,288954,288693,288433,288173,287913,
166 287653,287393,287134,286875,286616,286358,286099,285841,
167 285583,285326,285068,284811,284554,284298,284041,283785,
168 283529,283273,283017,282762,282507,282252,281998,281743,
169 281489,281235,280981,280728,280475,280222,279969,279716,
170 279464,279212,278960,278708,278457,278206,277955,277704,
171 277453,277203,276953,276703,276453,276204,275955,275706,
172 275457,275209,274960,274712,274465,274217,273970,273722,
173 273476,273229,272982,272736,272490,272244,271999,271753,
174 271508,271263,271018,270774,270530,270286,270042,269798,
175 269555,269312,269069,268826,268583,268341,268099,267857
176};
177
178#define LOGFAC 2*16
179static UWORD logtab[104]={
180 LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,
181 LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
182 LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,
183 LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
184 LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,
185 LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
186 LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,
187 LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
188 LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,
189 LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
190 LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,
191 LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
192 LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,
193 LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
194 LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,
195 LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
196 LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,
197 LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
198 LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,
199 LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
200 LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,
201 LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
202 LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,
203 LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
204 LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,
205 LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
206};
207
208static SBYTE PanbrelloTable[256]={
209 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
210 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
211 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
212 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
213 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
214 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
215 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
216 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
217 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
218 -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
219 -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
220 -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
221 -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
222 -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
223 -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
224 -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
225};
226
227/* returns a random value between 0 and ceil-1, ceil must be a power of two */
228static int getrandom(int ceil)
229{
230#ifdef HAVE_SRANDOM
231 return random()&(ceil-1);
232#else
233 return (rand()*ceil)/(RAND_MAX+1.0);
234#endif
235}
236
237/* New Note Action Scoring System :
238 --------------------------------
239 1) total-volume (fadevol, chanvol, volume) is the main scorer.
240 2) a looping sample is a bonus x2
241 3) a foreground channel is a bonus x4
242 4) an active envelope with keyoff is a handicap -x2
243*/
244static int MP_FindEmptyChannel(MODULE *mod)
245{
246 MP_VOICE *a;
247 ULONG t,k,tvol,pp;
248
249 for (t=0;t<md_sngchn;t++)
250 if (((mod->voice[t].main.kick==KICK_ABSENT)||
251 (mod->voice[t].main.kick==KICK_ENV))&&
252 Voice_Stopped_internal(t))
253 return t;
254
255 tvol=0xffffffUL;t=-1;a=mod->voice;
256 for (k=0;k<md_sngchn;k++,a++) {
257 /* allow us to take over a nonexisting sample */
258 if (!a->main.s)
259 return k;
260
261 if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) {
262 pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0);
263 if ((a->master)&&(a==a->master->slave))
264 pp<<=2;
265
266 if (pp<tvol) {
267 tvol=pp;
268 t=k;
269 }
270 }
271 }
272
273 if (tvol>8000*7) return -1;
274 return t;
275}
276
277static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2)
278{
279 if ((p1==p2)||(p==p1)) return v1;
280 return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1));
281}
282
283UWORD getlinearperiod(UWORD note,ULONG fine)
284{
285 UWORD t;
286
287 t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1);
288 return t;
289}
290
291static UWORD getlogperiod(UWORD note,ULONG fine)
292{
293 UWORD n,o;
294 UWORD p1,p2;
295 ULONG i;
296
297 n=note%(2*OCTAVE);
298 o=note/(2*OCTAVE);
299 i=(n<<2)+(fine>>4); /* n*8 + fine/16 */
300
301 p1=logtab[i];
302 p2=logtab[i+1];
303
304 return (Interpolate(fine>>4,0,15,p1,p2)>>o);
305}
306
307static UWORD getoldperiod(UWORD note,ULONG speed)
308{
309 UWORD n,o;
310
311 /* This happens sometimes on badly converted AMF, and old MOD */
312 if (!speed) {
313#ifdef MIKMOD_DEBUG
314 fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note);
315#endif
316 return 4242; /* <- prevent divide overflow.. (42 hehe) */
317 }
318
319 n=note%(2*OCTAVE);
320 o=note/(2*OCTAVE);
321 return ((8363L*(ULONG)oldperiods[n])>>o)/speed;
322}
323
324static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed)
325{
326 if (flags & UF_XMPERIODS) {
327 if (flags & UF_LINEAR)
328 return getlinearperiod(note, speed);
329 else
330 return getlogperiod(note, speed);
331 } else
332 return getoldperiod(note, speed);
333}
334
335static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b)
336{
337 return (Interpolate(p,a->pos,b->pos,a->val,b->val));
338}
339
340static SWORD DoPan(SWORD envpan,SWORD pan)
341{
342 int newpan;
343
344 newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128);
345
346 return (newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT?PAN_RIGHT:newpan);
347}
348
349static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff)
350{
351 t->flg=flg;
352 t->pts=pts;
353 t->susbeg=susbeg;
354 t->susend=susend;
355 t->beg=beg;
356 t->end=end;
357 t->env=p;
358 t->p=0;
359 t->a=0;
360 t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1;
361
362 /* Imago Orpheus sometimes stores an extra initial point in the envelope */
363 if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) {
364 t->a++;t->b++;
365 }
366
367 /* Fit in the envelope, still */
368 if (t->a >= t->pts)
369 t->a = t->pts - 1;
370 if (t->b >= t->pts)
371 t->b = t->pts-1;
372
373 return t->env[t->a].val;
374}
375
376/* This procedure processes all envelope types, include volume, pitch, and
377 panning. Envelopes are defined by a set of points, each with a magnitude
378 [relating either to volume, panning position, or pitch modifier] and a tick
379 position.
380
381 Envelopes work in the following manner:
382
383 (a) Each tick the envelope is moved a point further in its progression. For
384 an accurate progression, magnitudes between two envelope points are
385 interpolated.
386
387 (b) When progression reaches a defined point on the envelope, values are
388 shifted to interpolate between this point and the next, and checks for
389 loops or envelope end are done.
390
391 Misc:
392 Sustain loops are loops that are only active as long as the keyoff flag is
393 clear. When a volume envelope terminates, so does the current fadeout.
394*/
395static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)
396{
397 if (t->flg & EF_ON) {
398 UBYTE a, b; /* actual points in the envelope */
399 UWORD p; /* the 'tick counter' - real point being played */
400
401 a = t->a;
402 b = t->b;
403 p = t->p;
404
405 /*
406 * Sustain loop on one point (XM type).
407 * Not processed if KEYOFF.
408 * Don't move and don't interpolate when the point is reached
409 */
410 if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend &&
411 (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) {
412 v = t->env[t->susbeg].val;
413 } else {
414 /*
415 * All following situations will require interpolation between
416 * two envelope points.
417 */
418
419 /*
420 * Sustain loop between two points (IT type).
421 * Not processed if KEYOFF.
422 */
423 /* if we were on a loop point, loop now */
424 if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) &&
425 a >= t->susend) {
426 a = t->susbeg;
427 b = (t->susbeg==t->susend)?a:a+1;
428 p = t->env[a].pos;
429 v = t->env[a].val;
430 } else
431 /*
432 * Regular loop.
433 * Be sure to correctly handle single point loops.
434 */
435 if ((t->flg & EF_LOOP) && a >= t->end) {
436 a = t->beg;
437 b = t->beg == t->end ? a : a + 1;
438 p = t->env[a].pos;
439 v = t->env[a].val;
440 } else
441 /*
442 * Non looping situations.
443 */
444 if (a != b)
445 v = InterpolateEnv(p, &t->env[a], &t->env[b]);
446 else
447 v = t->env[a].val;
448
449 /*
450 * Start to fade if the volume envelope is finished.
451 */
452 if (p >= t->env[t->pts - 1].pos) {
453 if (t->flg & EF_VOLENV) {
454 aout->main.keyoff |= KEY_FADE;
455 if (!v)
456 aout->main.fadevol = 0;
457 }
458 } else {
459 p++;
460 /* did pointer reach point b? */
461 if (p >= t->env[b].pos)
462 a = b++; /* shift points a and b */
463 }
464 t->a = a;
465 t->b = b;
466 t->p = p;
467 }
468 }
469 return v;
470}
471
472/* XM linear period to frequency conversion */
473ULONG getfrequency(UWORD flags,ULONG period)
474{
475 if (flags & UF_LINEAR) {
476 SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE;
477
478 if (shift >= 0)
479 return lintab[period % 768] >> shift;
480 else
481 return lintab[period % 768] << (-shift);
482 } else
483 return (8363L*1712L)/(period?period:1);
484}
485
486/*========== Protracker effects */
487
488static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style)
489{
490 UBYTE note=a->main.note;
491
492 if (a->arpmem) {
493 switch (style) {
494 case 0: /* mod style: N, N+x, N+y */
495 switch (tick % 3) {
496 /* case 0: unchanged */
497 case 1:
498 note += (a->arpmem >> 4);
499 break;
500 case 2:
501 note += (a->arpmem & 0xf);
502 break;
503 }
504 break;
505 case 3: /* okt arpeggio 3: N-x, N, N+y */
506 switch (tick % 3) {
507 case 0:
508 note -= (a->arpmem >> 4);
509 break;
510 /* case 1: unchanged */
511 case 2:
512 note += (a->arpmem & 0xf);
513 break;
514 }
515 break;
516 case 4: /* okt arpeggio 4: N, N+y, N, N-x */
517 switch (tick % 4) {
518 /* case 0, case 2: unchanged */
519 case 1:
520 note += (a->arpmem & 0xf);
521 break;
522 case 3:
523 note -= (a->arpmem >> 4);
524 break;
525 }
526 break;
527 case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */
528 if (!tick)
529 break;
530 switch (tick % 3) {
531 /* case 0: unchanged */
532 case 1:
533 note -= (a->arpmem >> 4);
534 break;
535 case 2:
536 note += (a->arpmem & 0xf);
537 break;
538 }
539 break;
540 }
541 a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed);
542 a->ownper = 1;
543 }
544}
545
546static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
547{
548 UBYTE dat;
549
550 dat = UniGetByte();
551 if (!tick) {
552 if (!dat && (flags & UF_ARPMEM))
553 dat=a->arpmem;
554 else
555 a->arpmem=dat;
556 }
557 if (a->main.period)
558 DoArpeggio(tick, flags, a, 0);
559
560 return 0;
561}
562
563static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
564{
565 UBYTE dat;
566
567 dat = UniGetByte();
568 if (!tick && dat)
569 a->slidespeed = (UWORD)dat << 2;
570 if (a->main.period)
571 if (tick)
572 a->tmpperiod -= a->slidespeed;
573
574 return 0;
575}
576
577static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
578{
579 UBYTE dat;
580
581 dat = UniGetByte();
582 if (!tick && dat)
583 a->slidespeed = (UWORD)dat << 2;
584 if (a->main.period)
585 if (tick)
586 a->tmpperiod += a->slidespeed;
587
588 return 0;
589}
590
591static void DoToneSlide(UWORD tick, MP_CONTROL *a)
592{
593 if (!a->main.fadevol)
594 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF;
595 else
596 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT;
597
598 if (tick != 0) {
599 int dist;
600
601 /* We have to slide a->main.period towards a->wantedperiod, so compute
602 the difference between those two values */
603 dist=a->main.period-a->wantedperiod;
604
605 /* if they are equal or if portamentospeed is too big ...*/
606 if (dist == 0 || a->portspeed > abs(dist))
607 /* ...make tmpperiod equal tperiod */
608 a->tmpperiod=a->main.period=a->wantedperiod;
609 else if (dist>0) {
610 a->tmpperiod-=a->portspeed;
611 a->main.period-=a->portspeed; /* dist>0, slide up */
612 } else {
613 a->tmpperiod+=a->portspeed;
614 a->main.period+=a->portspeed; /* dist<0, slide down */
615 }
616 } else
617 a->tmpperiod=a->main.period;
618 a->ownper = 1;
619}
620
621static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
622{
623 UBYTE dat;
624
625 dat=UniGetByte();
626 if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2;
627 if (a->main.period)
628 DoToneSlide(tick, a);
629
630 return 0;
631}
632
633static void DoVibrato(UWORD tick, MP_CONTROL *a)
634{
635 UBYTE q;
636 UWORD temp = 0; /* silence warning */
637
638 if (!tick)
639 return;
640
641 q=(a->vibpos>>2)&0x1f;
642
643 switch (a->wavecontrol&3) {
644 case 0: /* sine */
645 temp=VibratoTable[q];
646 break;
647 case 1: /* ramp down */
648 q<<=3;
649 if (a->vibpos<0) q=255-q;
650 temp=q;
651 break;
652 case 2: /* square wave */
653 temp=255;
654 break;
655 case 3: /* random wave */
656 temp=getrandom(256);
657 break;
658 }
659
660 temp*=a->vibdepth;
661 temp>>=7;temp<<=2;
662
663 if (a->vibpos>=0)
664 a->main.period=a->tmpperiod+temp;
665 else
666 a->main.period=a->tmpperiod-temp;
667 a->ownper = 1;
668
669 if (tick != 0)
670 a->vibpos+=a->vibspd;
671}
672
673static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
674{
675 UBYTE dat;
676
677 dat=UniGetByte();
678 if (!tick) {
679 if (dat&0x0f) a->vibdepth=dat&0xf;
680 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
681 }
682 if (a->main.period)
683 DoVibrato(tick, a);
684
685 return 0;
686}
687
688static void DoVolSlide(MP_CONTROL *a, UBYTE dat)
689{
690 if (dat&0xf) {
691 a->tmpvolume-=(dat&0x0f);
692 if (a->tmpvolume<0)
693 a->tmpvolume=0;
694 } else {
695 a->tmpvolume+=(dat>>4);
696 if (a->tmpvolume>64)
697 a->tmpvolume=64;
698 }
699}
700
701static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
702{
703 UBYTE dat;
704
705 dat=UniGetByte();
706 if (a->main.period)
707 DoToneSlide(tick, a);
708
709 if (tick)
710 DoVolSlide(a, dat);
711
712 return 0;
713}
714
715/* DoPTEffect6 after DoPTEffectA */
716
717static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
718{
719 UBYTE dat;
720 UBYTE q;
721 UWORD temp = 0; /* silence warning */
722
723 dat=UniGetByte();
724 if (!tick) {
725 if (dat&0x0f) a->trmdepth=dat&0xf;
726 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
727 }
728 if (a->main.period) {
729 q=(a->trmpos>>2)&0x1f;
730
731 switch ((a->wavecontrol>>4)&3) {
732 case 0: /* sine */
733 temp=VibratoTable[q];
734 break;
735 case 1: /* ramp down */
736 q<<=3;
737 if (a->trmpos<0) q=255-q;
738 temp=q;
739 break;
740 case 2: /* square wave */
741 temp=255;
742 break;
743 case 3: /* random wave */
744 temp=getrandom(256);
745 break;
746 }
747 temp*=a->trmdepth;
748 temp>>=6;
749
750 if (a->trmpos>=0) {
751 a->volume=a->tmpvolume+temp;
752 if (a->volume>64) a->volume=64;
753 } else {
754 a->volume=a->tmpvolume-temp;
755 if (a->volume<0) a->volume=0;
756 }
757 a->ownvol = 1;
758
759 if (tick)
760 a->trmpos+=a->trmspd;
761 }
762
763 return 0;
764}
765
766static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
767{
768 UBYTE dat;
769
770 dat = UniGetByte();
771 if (mod->panflag)
772 a->main.panning = mod->panning[channel] = dat;
773
774 return 0;
775}
776
777static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
778{
779 UBYTE dat;
780
781 dat=UniGetByte();
782 if (!tick) {
783 if (dat) a->soffset=(UWORD)dat<<8;
784 a->main.start=a->hioffset|a->soffset;
785
786 if ((a->main.s)&&(a->main.start>a->main.s->length))
787 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
788 a->main.s->loopstart:a->main.s->length;
789 }
790
791 return 0;
792}
793
794static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
795{
796 UBYTE dat;
797
798 dat=UniGetByte();
799 if (tick)
800 DoVolSlide(a, dat);
801
802 return 0;
803}
804
805static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
806{
807 if (a->main.period)
808 DoVibrato(tick, a);
809 DoPTEffectA(tick, flags, a, mod, channel);
810
811 return 0;
812}
813
814static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
815{
816 UBYTE dat;
817
818 dat=UniGetByte();
819
820 if (tick || mod->patdly2)
821 return 0;
822
823 /* Vincent Voois uses a nasty trick in "Universal Bolero" */
824 if (dat == mod->sngpos && mod->patbrk == mod->patpos)
825 return 0;
826
827 if (!mod->loop && !mod->patbrk &&
828 (dat < mod->sngpos ||
829 (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) ||
830 (dat == mod->sngpos && (flags & UF_NOWRAP))
831 )) {
832 /* if we don't loop, better not to skip the end of the
833 pattern, after all... so:
834 mod->patbrk=0; */
835 mod->posjmp=3;
836 } else {
837 /* if we were fading, adjust... */
838 if (mod->sngpos == (mod->numpos-1))
839 mod->volume=mod->initvolume>128?128:mod->initvolume;
840 mod->sngpos=dat;
841 mod->posjmp=2;
842 mod->patpos=0;
843 }
844
845 return 0;
846}
847
848static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
849{
850 UBYTE dat;
851
852 dat=UniGetByte();
853 if (tick) return 0;
854 if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */
855 else if (dat>64) dat=64;
856 a->tmpvolume=dat;
857
858 return 0;
859}
860
861static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
862{
863 UBYTE dat;
864
865 dat=UniGetByte();
866 if ((tick)||(mod->patdly2)) return 0;
867 if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&&
868 (dat>mod->pattrows[mod->positions[mod->sngpos]]))
869 dat=mod->pattrows[mod->positions[mod->sngpos]];
870 mod->patbrk=dat;
871 if (!mod->posjmp) {
872 /* don't ask me to explain this code - it makes
873 backwards.s3m and children.xm (heretic's version) play
874 correctly, among others. Take that for granted, or write
875 the page of comments yourself... you might need some
876 aspirin - Miod */
877 if ((mod->sngpos==mod->numpos-1)&&(dat)&&((mod->loop)||
878 (mod->positions[mod->sngpos]==(mod->numpat-1)
879 && !(flags&UF_NOWRAP)))) {
880 mod->sngpos=0;
881 mod->posjmp=2;
882 } else
883 mod->posjmp=3;
884 }
885
886 return 0;
887}
888
889static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
890 SWORD channel, UBYTE dat)
891{
892 UBYTE nib = dat & 0xf;
893
894 switch (dat>>4) {
895 case 0x0: /* hardware filter toggle, not supported */
896 break;
897 case 0x1: /* fineslide up */
898 if (a->main.period)
899 if (!tick)
900 a->tmpperiod-=(nib<<2);
901 break;
902 case 0x2: /* fineslide dn */
903 if (a->main.period)
904 if (!tick)
905 a->tmpperiod+=(nib<<2);
906 break;
907 case 0x3: /* glissando ctrl */
908 a->glissando=nib;
909 break;
910 case 0x4: /* set vibrato waveform */
911 a->wavecontrol&=0xf0;
912 a->wavecontrol|=nib;
913 break;
914 case 0x5: /* set finetune */
915 if (a->main.period) {
916 if (flags&UF_XMPERIODS)
917 a->speed=nib+128;
918 else
919 a->speed=finetune[nib];
920 a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed);
921 }
922 break;
923 case 0x6: /* set patternloop */
924 if (tick)
925 break;
926 if (nib) { /* set reppos or repcnt ? */
927 /* set repcnt, so check if repcnt already is set, which means we
928 are already looping */
929 if (a->pat_repcnt)
930 a->pat_repcnt--; /* already looping, decrease counter */
931 else {
932#if 0
933 /* this would make walker.xm, shipped with Xsoundtracker,
934 play correctly, but it's better to remain compatible
935 with FT2 */
936 if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))
937#endif
938 a->pat_repcnt=nib; /* not yet looping, so set repcnt */
939 }
940
941 if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */
942 if (a->pat_reppos==POS_NONE)
943 a->pat_reppos=mod->patpos-1;
944 if (a->pat_reppos==-1) {
945 mod->pat_repcrazy=1;
946 mod->patpos=0;
947 } else
948 mod->patpos=a->pat_reppos;
949 } else a->pat_reppos=POS_NONE;
950 } else
951 a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */
952 break;
953 case 0x7: /* set tremolo waveform */
954 a->wavecontrol&=0x0f;
955 a->wavecontrol|=nib<<4;
956 break;
957 case 0x8: /* set panning */
958 if (mod->panflag) {
959 if (nib<=8) nib<<=4;
960 else nib*=17;
961 a->main.panning=mod->panning[channel]=nib;
962 }
963 break;
964 case 0x9: /* retrig note */
965 /* do not retrigger on tick 0, until we are emulating FT2 and effect
966 data is zero */
967 if (!tick && !((flags & UF_FT2QUIRKS) && (!nib)))
968 break;
969 /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */
970 if (nib || !tick) {
971 if (!a->retrig) {
972 /* when retrig counter reaches 0, reset counter and restart
973 the sample */
974 if (a->main.period) a->main.kick=KICK_NOTE;
975 a->retrig=nib;
976 }
977 a->retrig--; /* countdown */
978 }
979 break;
980 case 0xa: /* fine volume slide up */
981 if (tick)
982 break;
983 a->tmpvolume+=nib;
984 if (a->tmpvolume>64) a->tmpvolume=64;
985 break;
986 case 0xb: /* fine volume slide dn */
987 if (tick)
988 break;
989 a->tmpvolume-=nib;
990 if (a->tmpvolume<0)a->tmpvolume=0;
991 break;
992 case 0xc: /* cut note */
993 /* When tick reaches the cut-note value, turn the volume to
994 zero (just like on the amiga) */
995 if (tick>=nib)
996 a->tmpvolume=0; /* just turn the volume down */
997 break;
998 case 0xd: /* note delay */
999 /* delay the start of the sample until tick==nib */
1000 if (!tick)
1001 a->main.notedelay=nib;
1002 else if (a->main.notedelay)
1003 a->main.notedelay--;
1004 break;
1005 case 0xe: /* pattern delay */
1006 if (!tick)
1007 if (!mod->patdly2)
1008 mod->patdly=nib+1; /* only once, when tick=0 */
1009 break;
1010 case 0xf: /* invert loop, not supported */
1011 break;
1012 }
1013}
1014
1015static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1016{
1017 DoEEffects(tick, flags, a, mod, channel, UniGetByte());
1018
1019 return 0;
1020}
1021
1022static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1023{
1024 UBYTE dat;
1025
1026 dat=UniGetByte();
1027 if (tick||mod->patdly2) return 0;
1028 if (mod->extspd&&(dat>=mod->bpmlimit))
1029 mod->bpm=dat;
1030 else
1031 if (dat) {
1032 mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat;
1033 mod->vbtick=0;
1034 }
1035
1036 return 0;
1037}
1038
1039/*========== Scream Tracker effects */
1040
1041static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1042{
1043 UBYTE speed;
1044
1045 speed = UniGetByte();
1046
1047 if (tick || mod->patdly2)
1048 return 0;
1049
1050 if (speed > 128)
1051 speed -= 128;
1052 if (speed) {
1053 mod->sngspd = speed;
1054 mod->vbtick = 0;
1055 }
1056
1057 return 0;
1058}
1059
1060static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf)
1061{
1062 UBYTE lo, hi;
1063
1064 if (inf)
1065 a->s3mvolslide=inf;
1066 else
1067 inf=a->s3mvolslide;
1068
1069 lo=inf&0xf;
1070 hi=inf>>4;
1071
1072 if (!lo) {
1073 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi;
1074 } else
1075 if (!hi) {
1076 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo;
1077 } else
1078 if (lo==0xf) {
1079 if (!tick) a->tmpvolume+=(hi?hi:0xf);
1080 } else
1081 if (hi==0xf) {
1082 if (!tick) a->tmpvolume-=(lo?lo:0xf);
1083 } else
1084 return;
1085
1086 if (a->tmpvolume<0)
1087 a->tmpvolume=0;
1088 else if (a->tmpvolume>64)
1089 a->tmpvolume=64;
1090}
1091
1092static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1093{
1094 DoS3MVolSlide(tick, flags, a, UniGetByte());
1095
1096 return 1;
1097}
1098
1099static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf)
1100{
1101 UBYTE hi,lo;
1102
1103 if (inf)
1104 a->slidespeed=inf;
1105 else
1106 inf=a->slidespeed;
1107
1108 hi=inf>>4;
1109 lo=inf&0xf;
1110
1111 if (hi==0xf) {
1112 if (!tick) a->tmpperiod+=(UWORD)lo<<2;
1113 } else
1114 if (hi==0xe) {
1115 if (!tick) a->tmpperiod+=lo;
1116 } else {
1117 if (tick) a->tmpperiod+=(UWORD)inf<<2;
1118 }
1119}
1120
1121static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1122{
1123 UBYTE dat;
1124
1125 dat=UniGetByte();
1126 if (a->main.period)
1127 DoS3MSlideDn(tick, a,dat);
1128
1129 return 0;
1130}
1131
1132static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf)
1133{
1134 UBYTE hi,lo;
1135
1136 if (inf) a->slidespeed=inf;
1137 else inf=a->slidespeed;
1138
1139 hi=inf>>4;
1140 lo=inf&0xf;
1141
1142 if (hi==0xf) {
1143 if (!tick) a->tmpperiod-=(UWORD)lo<<2;
1144 } else
1145 if (hi==0xe) {
1146 if (!tick) a->tmpperiod-=lo;
1147 } else {
1148 if (tick) a->tmpperiod-=(UWORD)inf<<2;
1149 }
1150}
1151
1152static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1153{
1154 UBYTE dat;
1155
1156 dat=UniGetByte();
1157 if (a->main.period)
1158 DoS3MSlideUp(tick, a,dat);
1159
1160 return 0;
1161}
1162
1163static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1164{
1165 UBYTE inf, on, off;
1166
1167 inf = UniGetByte();
1168 if (inf)
1169 a->s3mtronof = inf;
1170 else {
1171 inf = a->s3mtronof;
1172 if (!inf)
1173 return 0;
1174 }
1175
1176 if (!tick)
1177 return 0;
1178
1179 on=(inf>>4)+1;
1180 off=(inf&0xf)+1;
1181 a->s3mtremor%=(on+off);
1182 a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
1183 a->ownvol=1;
1184 a->s3mtremor++;
1185
1186 return 0;
1187}
1188
1189static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1190{
1191 UBYTE inf;
1192
1193 inf = UniGetByte();
1194 if (a->main.period) {
1195 if (inf) {
1196 a->s3mrtgslide=inf>>4;
1197 a->s3mrtgspeed=inf&0xf;
1198 }
1199
1200 /* only retrigger if low nibble > 0 */
1201 if (a->s3mrtgspeed>0) {
1202 if (!a->retrig) {
1203 /* when retrig counter reaches 0, reset counter and restart the
1204 sample */
1205 if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF;
1206 a->retrig=a->s3mrtgspeed;
1207
1208 if ((tick)||(flags&UF_S3MSLIDES)) {
1209 switch (a->s3mrtgslide) {
1210 case 1:
1211 case 2:
1212 case 3:
1213 case 4:
1214 case 5:
1215 a->tmpvolume-=(1<<(a->s3mrtgslide-1));
1216 break;
1217 case 6:
1218 a->tmpvolume=(2*a->tmpvolume)/3;
1219 break;
1220 case 7:
1221 a->tmpvolume>>=1;
1222 break;
1223 case 9:
1224 case 0xa:
1225 case 0xb:
1226 case 0xc:
1227 case 0xd:
1228 a->tmpvolume+=(1<<(a->s3mrtgslide-9));
1229 break;
1230 case 0xe:
1231 a->tmpvolume=(3*a->tmpvolume)>>1;
1232 break;
1233 case 0xf:
1234 a->tmpvolume=a->tmpvolume<<1;
1235 break;
1236 }
1237 if (a->tmpvolume<0)
1238 a->tmpvolume=0;
1239 else if (a->tmpvolume>64)
1240 a->tmpvolume=64;
1241 }
1242 }
1243 a->retrig--; /* countdown */
1244 }
1245 }
1246
1247 return 0;
1248}
1249
1250static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1251{
1252 UBYTE dat, q;
1253 UWORD temp=0; /* silence warning */
1254
1255 dat = UniGetByte();
1256 if (!tick) {
1257 if (dat&0x0f) a->trmdepth=dat&0xf;
1258 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
1259 }
1260
1261 q=(a->trmpos>>2)&0x1f;
1262
1263 switch ((a->wavecontrol>>4)&3) {
1264 case 0: /* sine */
1265 temp=VibratoTable[q];
1266 break;
1267 case 1: /* ramp down */
1268 q<<=3;
1269 if (a->trmpos<0) q=255-q;
1270 temp=q;
1271 break;
1272 case 2: /* square wave */
1273 temp=255;
1274 break;
1275 case 3: /* random */
1276 temp=getrandom(256);
1277 break;
1278 }
1279
1280 temp*=a->trmdepth;
1281 temp>>=7;
1282
1283 if (a->trmpos>=0) {
1284 a->volume=a->tmpvolume+temp;
1285 if (a->volume>64) a->volume=64;
1286 } else {
1287 a->volume=a->tmpvolume-temp;
1288 if (a->volume<0) a->volume=0;
1289 }
1290 a->ownvol = 1;
1291
1292 if (tick)
1293 a->trmpos+=a->trmspd;
1294
1295 return 0;
1296}
1297
1298static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1299{
1300 UBYTE tempo;
1301
1302 tempo = UniGetByte();
1303
1304 if (tick || mod->patdly2)
1305 return 0;
1306
1307 mod->bpm = (tempo < 32) ? 32 : tempo;
1308
1309 return 0;
1310}
1311
1312static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1313{
1314 UBYTE dat, q;
1315 UWORD temp = 0; /* silence warning */
1316
1317 dat = UniGetByte();
1318 if (!tick) {
1319 if (dat&0x0f) a->vibdepth=dat&0xf;
1320 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
1321 } else
1322 if (a->main.period) {
1323 q=(a->vibpos>>2)&0x1f;
1324
1325 switch (a->wavecontrol&3) {
1326 case 0: /* sine */
1327 temp=VibratoTable[q];
1328 break;
1329 case 1: /* ramp down */
1330 q<<=3;
1331 if (a->vibpos<0) q=255-q;
1332 temp=q;
1333 break;
1334 case 2: /* square wave */
1335 temp=255;
1336 break;
1337 case 3: /* random */
1338 temp=getrandom(256);
1339 break;
1340 }
1341
1342 temp*=a->vibdepth;
1343 temp>>=8;
1344
1345 if (a->vibpos>=0)
1346 a->main.period=a->tmpperiod+temp;
1347 else
1348 a->main.period=a->tmpperiod-temp;
1349 a->ownper = 1;
1350
1351 a->vibpos+=a->vibspd;
1352 }
1353
1354 return 0;
1355}
1356
1357/*========== Envelope helpers */
1358
1359static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1360{
1361 a->main.keyoff|=KEY_OFF;
1362 if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP))
1363 a->main.keyoff=KEY_KILL;
1364
1365 return 0;
1366}
1367
1368static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1369{
1370 UBYTE dat;
1371
1372 dat=UniGetByte();
1373 if ((tick>=dat)||(tick==mod->sngspd-1)) {
1374 a->main.keyoff=KEY_KILL;
1375 if (!(a->main.volflg&EF_ON))
1376 a->main.fadevol=0;
1377 }
1378
1379 return 0;
1380}
1381
1382/*========== Fast Tracker effects */
1383
1384/* DoXMEffect6 after DoXMEffectA */
1385
1386static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1387{
1388 UBYTE inf, lo, hi;
1389
1390 inf = UniGetByte();
1391 if (inf)
1392 a->s3mvolslide = inf;
1393 else
1394 inf = a->s3mvolslide;
1395
1396 if (tick) {
1397 lo=inf&0xf;
1398 hi=inf>>4;
1399
1400 if (!hi) {
1401 a->tmpvolume-=lo;
1402 if (a->tmpvolume<0) a->tmpvolume=0;
1403 } else {
1404 a->tmpvolume+=hi;
1405 if (a->tmpvolume>64) a->tmpvolume=64;
1406 }
1407 }
1408
1409 return 0;
1410}
1411
1412static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1413{
1414 if (a->main.period)
1415 DoVibrato(tick, a);
1416
1417 return DoXMEffectA(tick, flags, a, mod, channel);
1418}
1419
1420static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1421{
1422 UBYTE dat;
1423
1424 dat=UniGetByte();
1425 if (!tick) {
1426 if (dat) a->fportupspd=dat;
1427 if (a->main.period)
1428 a->tmpperiod-=(a->fportupspd<<2);
1429 }
1430
1431 return 0;
1432}
1433
1434static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1435{
1436 UBYTE dat;
1437
1438 dat=UniGetByte();
1439 if (!tick) {
1440 if (dat) a->fportdnspd=dat;
1441 if (a->main.period)
1442 a->tmpperiod+=(a->fportdnspd<<2);
1443 }
1444
1445 return 0;
1446}
1447
1448static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1449{
1450 UBYTE dat;
1451
1452 dat=UniGetByte();
1453 if (!tick)
1454 if (dat) a->fslideupspd=dat;
1455 a->tmpvolume+=a->fslideupspd;
1456 if (a->tmpvolume>64) a->tmpvolume=64;
1457
1458 return 0;
1459}
1460
1461static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1462{
1463 UBYTE dat;
1464
1465 dat=UniGetByte();
1466 if (!tick)
1467 if (dat) a->fslidednspd=dat;
1468 a->tmpvolume-=a->fslidednspd;
1469 if (a->tmpvolume<0) a->tmpvolume=0;
1470
1471 return 0;
1472}
1473
1474static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1475{
1476 mod->volume=UniGetByte()<<1;
1477 if (mod->volume>128) mod->volume=128;
1478
1479 return 0;
1480}
1481
1482static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1483{
1484 UBYTE inf;
1485
1486 inf = UniGetByte();
1487
1488 if (tick) {
1489 if (inf) mod->globalslide=inf;
1490 else inf=mod->globalslide;
1491 if (inf & 0xf0) inf&=0xf0;
1492 mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2;
1493
1494 if (mod->volume<0)
1495 mod->volume=0;
1496 else if (mod->volume>128)
1497 mod->volume=128;
1498 }
1499
1500 return 0;
1501}
1502
1503static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1504{
1505 UBYTE dat;
1506
1507 dat=UniGetByte();
1508 if ((!tick)&&(a->main.i)) {
1509 UWORD points;
1510 INSTRUMENT *i=a->main.i;
1511 MP_VOICE *aout;
1512
1513 if ((aout=a->slave)) {
1514 if (aout->venv.env) {
1515 points=i->volenv[i->volpts-1].pos;
1516 aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos;
1517 }
1518 if (aout->penv.env) {
1519 points=i->panenv[i->panpts-1].pos;
1520 aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos;
1521 }
1522 }
1523 }
1524
1525 return 0;
1526}
1527
1528static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1529{
1530 UBYTE inf, lo, hi;
1531 SWORD pan;
1532
1533 inf = UniGetByte();
1534 if (!mod->panflag)
1535 return 0;
1536
1537 if (inf)
1538 a->pansspd = inf;
1539 else
1540 inf =a->pansspd;
1541
1542 if (tick) {
1543 lo=inf&0xf;
1544 hi=inf>>4;
1545
1546 /* slide right has absolute priority */
1547 if (hi)
1548 lo = 0;
1549
1550 pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo;
1551 a->main.panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
1552 }
1553
1554 return 0;
1555}
1556
1557static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1558{
1559 UBYTE dat;
1560
1561 dat = UniGetByte();
1562 if (dat)
1563 a->ffportupspd = dat;
1564 else
1565 dat = a->ffportupspd;
1566
1567 if (a->main.period)
1568 if (!tick) {
1569 a->main.period-=dat;
1570 a->tmpperiod-=dat;
1571 a->ownper = 1;
1572 }
1573
1574 return 0;
1575}
1576
1577static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1578{
1579 UBYTE dat;
1580
1581 dat = UniGetByte();
1582 if (dat)
1583 a->ffportdnspd=dat;
1584 else
1585 dat = a->ffportdnspd;
1586
1587 if (a->main.period)
1588 if (!tick) {
1589 a->main.period+=dat;
1590 a->tmpperiod+=dat;
1591 a->ownper = 1;
1592 }
1593
1594 return 0;
1595}
1596
1597/*========== Impulse Tracker effects */
1598
1599static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat)
1600{
1601 if (dat)
1602 a->portspeed = dat;
1603
1604 /* if we don't come from another note, ignore the slide and play the note
1605 as is */
1606 if (!a->oldnote || !a->main.period)
1607 return;
1608
1609 if ((!tick)&&(a->newsamp)){
1610 a->main.kick=KICK_NOTE;
1611 a->main.start=-1;
1612 } else
1613 a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT;
1614
1615 if (tick) {
1616 int dist;
1617
1618 /* We have to slide a->main.period towards a->wantedperiod, compute the
1619 difference between those two values */
1620 dist=a->main.period-a->wantedperiod;
1621
1622 /* if they are equal or if portamentospeed is too big... */
1623 if ((!dist)||((a->portspeed<<2)>abs(dist)))
1624 /* ... make tmpperiod equal tperiod */
1625 a->tmpperiod=a->main.period=a->wantedperiod;
1626 else
1627 if (dist>0) {
1628 a->tmpperiod-=a->portspeed<<2;
1629 a->main.period-=a->portspeed<<2; /* dist>0 slide up */
1630 } else {
1631 a->tmpperiod+=a->portspeed<<2;
1632 a->main.period+=a->portspeed<<2; /* dist<0 slide down */
1633 }
1634 } else
1635 a->tmpperiod=a->main.period;
1636 a->ownper=1;
1637}
1638
1639static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1640{
1641 DoITToneSlide(tick, a, UniGetByte());
1642
1643 return 0;
1644}
1645
1646static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)
1647{
1648 UBYTE q;
1649 UWORD temp=0;
1650
1651 if (!tick) {
1652 if (dat&0x0f) a->vibdepth=dat&0xf;
1653 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
1654 }
1655 if (!a->main.period)
1656 return;
1657
1658 q=(a->vibpos>>2)&0x1f;
1659
1660 switch (a->wavecontrol&3) {
1661 case 0: /* sine */
1662 temp=VibratoTable[q];
1663 break;
1664 case 1: /* square wave */
1665 temp=255;
1666 break;
1667 case 2: /* ramp down */
1668 q<<=3;
1669 if (a->vibpos<0) q=255-q;
1670 temp=q;
1671 break;
1672 case 3: /* random */
1673 temp=getrandom(256);
1674 break;
1675 }
1676
1677 temp*=a->vibdepth;
1678 temp>>=8;
1679 temp<<=2;
1680
1681 if (a->vibpos>=0)
1682 a->main.period=a->tmpperiod+temp;
1683 else
1684 a->main.period=a->tmpperiod-temp;
1685 a->ownper=1;
1686
1687 a->vibpos+=a->vibspd;
1688}
1689
1690static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1691{
1692 DoITVibrato(tick, a, UniGetByte());
1693
1694 return 0;
1695}
1696
1697static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1698{
1699 UBYTE inf, on, off;
1700
1701 inf = UniGetByte();
1702 if (inf)
1703 a->s3mtronof = inf;
1704 else {
1705 inf = a->s3mtronof;
1706 if (!inf)
1707 return 0;
1708 }
1709
1710 on=(inf>>4);
1711 off=(inf&0xf);
1712
1713 a->s3mtremor%=(on+off);
1714 a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
1715 a->ownvol = 1;
1716 a->s3mtremor++;
1717
1718 return 0;
1719}
1720
1721static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1722{
1723 a->main.chanvol=UniGetByte();
1724 if (a->main.chanvol>64)
1725 a->main.chanvol=64;
1726 else if (a->main.chanvol<0)
1727 a->main.chanvol=0;
1728
1729 return 0;
1730}
1731
1732static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1733{
1734 UBYTE inf, lo, hi;
1735
1736 inf = UniGetByte();
1737
1738 if (inf)
1739 a->chanvolslide = inf;
1740 else
1741 inf = a->chanvolslide;
1742
1743 lo=inf&0xf;
1744 hi=inf>>4;
1745
1746 if (!hi)
1747 a->main.chanvol-=lo;
1748 else
1749 if (!lo) {
1750 a->main.chanvol+=hi;
1751 } else
1752 if (hi==0xf) {
1753 if (!tick) a->main.chanvol-=lo;
1754 } else
1755 if (lo==0xf) {
1756 if (!tick) a->main.chanvol+=hi;
1757 }
1758
1759 if (a->main.chanvol<0)
1760 a->main.chanvol=0;
1761 else if (a->main.chanvol>64)
1762 a->main.chanvol=64;
1763
1764 return 0;
1765}
1766
1767static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1768{
1769 UBYTE inf, lo, hi;
1770 SWORD pan;
1771
1772 inf = UniGetByte();
1773 if (inf)
1774 a->pansspd = inf;
1775 else
1776 inf = a->pansspd;
1777
1778 if (!mod->panflag)
1779 return 0;
1780
1781 lo=inf&0xf;
1782 hi=inf>>4;
1783
1784 pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning;
1785
1786 if (!hi)
1787 pan+=lo<<2;
1788 else
1789 if (!lo) {
1790 pan-=hi<<2;
1791 } else
1792 if (hi==0xf) {
1793 if (!tick) pan+=lo<<2;
1794 } else
1795 if (lo==0xf) {
1796 if (!tick) pan-=hi<<2;
1797 }
1798 a->main.panning=
1799 (pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
1800
1801 return 0;
1802}
1803
1804static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1805{
1806 UBYTE tempo;
1807 SWORD temp;
1808
1809 tempo = UniGetByte();
1810
1811 if (mod->patdly2)
1812 return 0;
1813
1814 temp = mod->bpm;
1815 if (tempo & 0x10)
1816 temp += (tempo & 0x0f);
1817 else
1818 temp -= tempo;
1819
1820 mod->bpm=(temp>255)?255:(temp<1?1:temp);
1821
1822 return 0;
1823}
1824
1825static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1826{
1827 UBYTE dat, q;
1828 UWORD temp = 0; /* silence warning */
1829
1830 dat = UniGetByte();
1831 if (!tick) {
1832 if (dat&0x0f) a->vibdepth=dat&0xf;
1833 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
1834 }
1835 if (a->main.period) {
1836 q=(a->vibpos>>2)&0x1f;
1837
1838 switch (a->wavecontrol&3) {
1839 case 0: /* sine */
1840 temp=VibratoTable[q];
1841 break;
1842 case 1: /* square wave */
1843 temp=255;
1844 break;
1845 case 2: /* ramp down */
1846 q<<=3;
1847 if (a->vibpos<0) q=255-q;
1848 temp=q;
1849 break;
1850 case 3: /* random */
1851 temp=getrandom(256);
1852 break;
1853 }
1854
1855 temp*=a->vibdepth;
1856 temp>>=8;
1857
1858 if (a->vibpos>=0)
1859 a->main.period=a->tmpperiod+temp;
1860 else
1861 a->main.period=a->tmpperiod-temp;
1862 a->ownper = 1;
1863
1864 a->vibpos+=a->vibspd;
1865 }
1866
1867 return 0;
1868}
1869
1870static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1871{
1872 UBYTE inf, lo, hi;
1873
1874 inf = UniGetByte();
1875
1876 if (inf)
1877 mod->globalslide = inf;
1878 else
1879 inf = mod->globalslide;
1880
1881 lo=inf&0xf;
1882 hi=inf>>4;
1883
1884 if (!lo) {
1885 if (tick) mod->volume+=hi;
1886 } else
1887 if (!hi) {
1888 if (tick) mod->volume-=lo;
1889 } else
1890 if (lo==0xf) {
1891 if (!tick) mod->volume+=hi;
1892 } else
1893 if (hi==0xf) {
1894 if (!tick) mod->volume-=lo;
1895 }
1896
1897 if (mod->volume<0)
1898 mod->volume=0;
1899 else if (mod->volume>128)
1900 mod->volume=128;
1901
1902 return 0;
1903}
1904
1905static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1906{
1907 UBYTE dat, q;
1908 SLONG temp = 0; /* silence warning */
1909
1910
1911 dat=UniGetByte();
1912 if (!tick) {
1913 if (dat&0x0f) a->panbdepth=(dat&0xf);
1914 if (dat&0xf0) a->panbspd=(dat&0xf0)>>4;
1915 }
1916 if (mod->panflag) {
1917 q=a->panbpos;
1918
1919 switch (a->panbwave) {
1920 case 0: /* sine */
1921 temp=PanbrelloTable[q];
1922 break;
1923 case 1: /* square wave */
1924 temp=(q<0x80)?64:0;
1925 break;
1926 case 2: /* ramp down */
1927 q<<=3;
1928 temp=q;
1929 break;
1930 case 3: /* random */
1931 temp=getrandom(256);
1932 break;
1933 }
1934
1935 temp*=a->panbdepth;
1936 temp=(temp/8)+mod->panning[channel];
1937
1938 a->main.panning=
1939 (temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
1940 a->panbpos+=a->panbspd;
1941
1942 }
1943
1944 return 0;
1945}
1946
1947static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE);
1948
1949/* Impulse/Scream Tracker Sxx effects.
1950 All Sxx effects share the same memory space. */
1951static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
1952{
1953 UBYTE dat, inf, c;
1954
1955 dat = UniGetByte();
1956 inf=dat&0xf;
1957 c=dat>>4;
1958
1959 if (!dat) {
1960 c=a->sseffect;
1961 inf=a->ssdata;
1962 } else {
1963 a->sseffect=c;
1964 a->ssdata=inf;
1965 }
1966
1967 switch (c) {
1968 case SS_GLISSANDO: /* S1x set glissando voice */
1969 DoEEffects(tick, flags, a, mod, channel, 0x30|inf);
1970 break;
1971 case SS_FINETUNE: /* S2x set finetune */
1972 DoEEffects(tick, flags, a, mod, channel, 0x50|inf);
1973 break;
1974 case SS_VIBWAVE: /* S3x set vibrato waveform */
1975 DoEEffects(tick, flags, a, mod, channel, 0x40|inf);
1976 break;
1977 case SS_TREMWAVE: /* S4x set tremolo waveform */
1978 DoEEffects(tick, flags, a, mod, channel, 0x70|inf);
1979 break;
1980 case SS_PANWAVE: /* S5x panbrello */
1981 a->panbwave=inf;
1982 break;
1983 case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */
1984 DoEEffects(tick, flags, a, mod, channel, 0xe0|inf);
1985 break;
1986 case SS_S7EFFECTS: /* S7x instrument / NNA commands */
1987 DoNNAEffects(mod, a, inf);
1988 break;
1989 case SS_PANNING: /* S8x set panning position */
1990 DoEEffects(tick, flags, a, mod, channel, 0x80 | inf);
1991 break;
1992 case SS_SURROUND: /* S9x set surround sound */
1993 if (mod->panflag)
1994 a->main.panning = mod->panning[channel] = PAN_SURROUND;
1995 break;
1996 case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */
1997 if (!tick) {
1998 a->hioffset=inf<<16;
1999 a->main.start=a->hioffset|a->soffset;
2000
2001 if ((a->main.s)&&(a->main.start>a->main.s->length))
2002 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
2003 a->main.s->loopstart:a->main.s->length;
2004 }
2005 break;
2006 case SS_PATLOOP: /* SBx pattern loop */
2007 DoEEffects(tick, flags, a, mod, channel, 0x60|inf);
2008 break;
2009 case SS_NOTECUT: /* SCx notecut */
2010 if (!inf) inf = 1;
2011 DoEEffects(tick, flags, a, mod, channel, 0xC0|inf);
2012 break;
2013 case SS_NOTEDELAY: /* SDx notedelay */
2014 DoEEffects(tick, flags, a, mod, channel, 0xD0|inf);
2015 break;
2016 case SS_PATDELAY: /* SEx patterndelay */
2017 DoEEffects(tick, flags, a, mod, channel, 0xE0|inf);
2018 break;
2019 }
2020
2021 return 0;
2022}
2023
2024/*========== Impulse Tracker Volume/Pan Column effects */
2025
2026/*
2027 * All volume/pan column effects share the same memory space.
2028 */
2029
2030static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2031{
2032 UBYTE c, inf;
2033
2034 c = UniGetByte();
2035 inf = UniGetByte();
2036
2037 if ((!c)&&(!inf)) {
2038 c=a->voleffect;
2039 inf=a->voldata;
2040 } else {
2041 a->voleffect=c;
2042 a->voldata=inf;
2043 }
2044
2045 if (c)
2046 switch (c) {
2047 case VOL_VOLUME:
2048 if (tick) break;
2049 if (inf>64) inf=64;
2050 a->tmpvolume=inf;
2051 break;
2052 case VOL_PANNING:
2053 if (mod->panflag)
2054 a->main.panning=inf;
2055 break;
2056 case VOL_VOLSLIDE:
2057 DoS3MVolSlide(tick, flags, a, inf);
2058 return 1;
2059 case VOL_PITCHSLIDEDN:
2060 if (a->main.period)
2061 DoS3MSlideDn(tick, a, inf);
2062 break;
2063 case VOL_PITCHSLIDEUP:
2064 if (a->main.period)
2065 DoS3MSlideUp(tick, a, inf);
2066 break;
2067 case VOL_PORTAMENTO:
2068 DoITToneSlide(tick, a, inf);
2069 break;
2070 case VOL_VIBRATO:
2071 DoITVibrato(tick, a, inf);
2072 break;
2073 }
2074
2075 return 0;
2076}
2077
2078/*========== UltraTracker effects */
2079
2080static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2081{
2082 UWORD offset=UniGetWord();
2083
2084 if (offset)
2085 a->ultoffset=offset;
2086
2087 a->main.start=a->ultoffset<<2;
2088 if ((a->main.s)&&(a->main.start>a->main.s->length))
2089 a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
2090 a->main.s->loopstart:a->main.s->length;
2091
2092 return 0;
2093}
2094
2095/*========== OctaMED effects */
2096
2097static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2098{
2099 UWORD speed=UniGetWord();
2100
2101 mod->bpm=speed;
2102
2103 return 0;
2104}
2105
2106static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2107{
2108 DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2));
2109
2110 return 0;
2111}
2112
2113static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2114{
2115 DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2));
2116
2117 return 0;
2118}
2119
2120static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2121{
2122 DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3));
2123
2124 return 0;
2125}
2126
2127/*========== Oktalyzer effects */
2128
2129static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2130{
2131 UBYTE dat, dat2;
2132
2133 dat2 = UniGetByte(); /* arpeggio style */
2134 dat = UniGetByte();
2135 if (!tick) {
2136 if (!dat && (flags & UF_ARPMEM))
2137 dat=a->arpmem;
2138 else
2139 a->arpmem=dat;
2140 }
2141 if (a->main.period)
2142 DoArpeggio(tick, flags, a, dat2);
2143
2144 return 0;
2145}
2146
2147/*========== General player functions */
2148
2149static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
2150{
2151 UniSkipOpcode();
2152
2153 return 0;
2154}
2155
2156typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD);
2157
2158static effect_func effects[UNI_LAST] = {
2159 DoNothing, /* 0 */
2160 DoNothing, /* UNI_NOTE */
2161 DoNothing, /* UNI_INSTRUMENT */
2162 DoPTEffect0, /* UNI_PTEFFECT0 */
2163 DoPTEffect1, /* UNI_PTEFFECT1 */
2164 DoPTEffect2, /* UNI_PTEFFECT2 */
2165 DoPTEffect3, /* UNI_PTEFFECT3 */
2166 DoPTEffect4, /* UNI_PTEFFECT4 */
2167 DoPTEffect5, /* UNI_PTEFFECT5 */
2168 DoPTEffect6, /* UNI_PTEFFECT6 */
2169 DoPTEffect7, /* UNI_PTEFFECT7 */
2170 DoPTEffect8, /* UNI_PTEFFECT8 */
2171 DoPTEffect9, /* UNI_PTEFFECT9 */
2172 DoPTEffectA, /* UNI_PTEFFECTA */
2173 DoPTEffectB, /* UNI_PTEFFECTB */
2174 DoPTEffectC, /* UNI_PTEFFECTC */
2175 DoPTEffectD, /* UNI_PTEFFECTD */
2176 DoPTEffectE, /* UNI_PTEFFECTE */
2177 DoPTEffectF, /* UNI_PTEFFECTF */
2178 DoS3MEffectA, /* UNI_S3MEFFECTA */
2179 DoS3MEffectD, /* UNI_S3MEFFECTD */
2180 DoS3MEffectE, /* UNI_S3MEFFECTE */
2181 DoS3MEffectF, /* UNI_S3MEFFECTF */
2182 DoS3MEffectI, /* UNI_S3MEFFECTI */
2183 DoS3MEffectQ, /* UNI_S3MEFFECTQ */
2184 DoS3MEffectR, /* UNI_S3MEFFECTR */
2185 DoS3MEffectT, /* UNI_S3MEFFECTT */
2186 DoS3MEffectU, /* UNI_S3MEFFECTU */
2187 DoKeyOff, /* UNI_KEYOFF */
2188 DoKeyFade, /* UNI_KEYFADE */
2189 DoVolEffects, /* UNI_VOLEFFECTS */
2190 DoPTEffect4, /* UNI_XMEFFECT4 */
2191 DoXMEffect6, /* UNI_XMEFFECT6 */
2192 DoXMEffectA, /* UNI_XMEFFECTA */
2193 DoXMEffectE1, /* UNI_XMEFFECTE1 */
2194 DoXMEffectE2, /* UNI_XMEFFECTE2 */
2195 DoXMEffectEA, /* UNI_XMEFFECTEA */
2196 DoXMEffectEB, /* UNI_XMEFFECTEB */
2197 DoXMEffectG, /* UNI_XMEFFECTG */
2198 DoXMEffectH, /* UNI_XMEFFECTH */
2199 DoXMEffectL, /* UNI_XMEFFECTL */
2200 DoXMEffectP, /* UNI_XMEFFECTP */
2201 DoXMEffectX1, /* UNI_XMEFFECTX1 */
2202 DoXMEffectX2, /* UNI_XMEFFECTX2 */
2203 DoITEffectG, /* UNI_ITEFFECTG */
2204 DoITEffectH, /* UNI_ITEFFECTH */
2205 DoITEffectI, /* UNI_ITEFFECTI */
2206 DoITEffectM, /* UNI_ITEFFECTM */
2207 DoITEffectN, /* UNI_ITEFFECTN */
2208 DoITEffectP, /* UNI_ITEFFECTP */
2209 DoITEffectT, /* UNI_ITEFFECTT */
2210 DoITEffectU, /* UNI_ITEFFECTU */
2211 DoITEffectW, /* UNI_ITEFFECTW */
2212 DoITEffectY, /* UNI_ITEFFECTY */
2213 DoNothing, /* UNI_ITEFFECTZ */
2214 DoITEffectS0, /* UNI_ITEFFECTS0 */
2215 DoULTEffect9, /* UNI_ULTEFFECT9 */
2216 DoMEDSpeed, /* UNI_MEDSPEED */
2217 DoMEDEffectF1, /* UNI_MEDEFFECTF1 */
2218 DoMEDEffectF2, /* UNI_MEDEFFECTF2 */
2219 DoMEDEffectF3, /* UNI_MEDEFFECTF3 */
2220 DoOktArp, /* UNI_OKTARP */
2221};
2222
2223static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a)
2224{
2225 UWORD tick = mod->vbtick;
2226 UWORD flags = mod->flags;
2227 UBYTE c;
2228 int explicitslides = 0;
2229 effect_func f;
2230
2231 while((c=UniGetByte())) {
2232 f = effects[c];
2233 if (f != DoNothing)
2234 a->sliding = 0;
2235 explicitslides |= f(tick, flags, a, mod, channel);
2236 }
2237 return explicitslides;
2238}
2239
2240static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat)
2241{
2242 int t;
2243 MP_VOICE *aout;
2244
2245 dat&=0xf;
2246 aout=(a->slave)?a->slave:NULL;
2247
2248 switch (dat) {
2249 case 0x0: /* past note cut */
2250 for (t=0;t<md_sngchn;t++)
2251 if (mod->voice[t].master==a)
2252 mod->voice[t].main.fadevol=0;
2253 break;
2254 case 0x1: /* past note off */
2255 for (t=0;t<md_sngchn;t++)
2256 if (mod->voice[t].master==a) {
2257 mod->voice[t].main.keyoff|=KEY_OFF;
2258 if ((!(mod->voice[t].venv.flg & EF_ON))||
2259 (mod->voice[t].venv.flg & EF_LOOP))
2260 mod->voice[t].main.keyoff=KEY_KILL;
2261 }
2262 break;
2263 case 0x2: /* past note fade */
2264 for (t=0;t<md_sngchn;t++)
2265 if (mod->voice[t].master==a)
2266 mod->voice[t].main.keyoff|=KEY_FADE;
2267 break;
2268 case 0x3: /* set NNA note cut */
2269 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT;
2270 break;
2271 case 0x4: /* set NNA note continue */
2272 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE;
2273 break;
2274 case 0x5: /* set NNA note off */
2275 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF;
2276 break;
2277 case 0x6: /* set NNA note fade */
2278 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE;
2279 break;
2280 case 0x7: /* disable volume envelope */
2281 if (aout)
2282 aout->main.volflg&=~EF_ON;
2283 break;
2284 case 0x8: /* enable volume envelope */
2285 if (aout)
2286 aout->main.volflg|=EF_ON;
2287 break;
2288 case 0x9: /* disable panning envelope */
2289 if (aout)
2290 aout->main.panflg&=~EF_ON;
2291 break;
2292 case 0xa: /* enable panning envelope */
2293 if (aout)
2294 aout->main.panflg|=EF_ON;
2295 break;
2296 case 0xb: /* disable pitch envelope */
2297 if (aout)
2298 aout->main.pitflg&=~EF_ON;
2299 break;
2300 case 0xc: /* enable pitch envelope */
2301 if (aout)
2302 aout->main.pitflg|=EF_ON;
2303 break;
2304 }
2305}
2306
2307void pt_UpdateVoices(MODULE *mod, int max_volume)
2308{
2309 SWORD envpan,envvol,envpit,channel;
2310 UWORD playperiod;
2311 SLONG vibval,vibdpt;
2312 ULONG tmpvol;
2313
2314 MP_VOICE *aout;
2315 INSTRUMENT *i;
2316 SAMPLE *s;
2317
2318 mod->totalchn=mod->realchn=0;
2319 for (channel=0;channel<md_sngchn;channel++) {
2320 aout=&mod->voice[channel];
2321 i=aout->main.i;
2322 s=aout->main.s;
2323
2324 if (!s || !s->length) continue;
2325
2326 if (aout->main.period<40)
2327 aout->main.period=40;
2328 else if (aout->main.period>50000)
2329 aout->main.period=50000;
2330
2331 if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) {
2332 Voice_Play_internal(channel,s,(aout->main.start==-1)?
2333 ((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->main.start);
2334 aout->main.fadevol=32768;
2335 aout->aswppos=0;
2336 }
2337
2338 envvol = 256;
2339 envpan = PAN_CENTER;
2340 envpit = 32;
2341 if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) {
2342 if (aout->main.volflg & EF_ON)
2343 envvol = StartEnvelope(&aout->venv,aout->main.volflg,
2344 i->volpts,i->volsusbeg,i->volsusend,
2345 i->volbeg,i->volend,i->volenv,aout->main.keyoff);
2346 if (aout->main.panflg & EF_ON)
2347 envpan = StartEnvelope(&aout->penv,aout->main.panflg,
2348 i->panpts,i->pansusbeg,i->pansusend,
2349 i->panbeg,i->panend,i->panenv,aout->main.keyoff);
2350 if (aout->main.pitflg & EF_ON)
2351 envpit = StartEnvelope(&aout->cenv,aout->main.pitflg,
2352 i->pitpts,i->pitsusbeg,i->pitsusend,
2353 i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff);
2354
2355 if (aout->cenv.flg & EF_ON)
2356 aout->masterperiod=GetPeriod(mod->flags,
2357 (UWORD)aout->main.note<<1, aout->master->speed);
2358 } else {
2359 if (aout->main.volflg & EF_ON)
2360 envvol = ProcessEnvelope(aout,&aout->venv,256);
2361 if (aout->main.panflg & EF_ON)
2362 envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER);
2363 if (aout->main.pitflg & EF_ON)
2364 envpit = ProcessEnvelope(aout,&aout->cenv,32);
2365 }
2366 if (aout->main.kick == KICK_NOTE) {
2367 aout->main.kick_flag = 1;
2368 }
2369 aout->main.kick=KICK_ABSENT;
2370
2371 tmpvol = aout->main.fadevol; /* max 32768 */
2372 tmpvol *= aout->main.chanvol; /* * max 64 */
2373 tmpvol *= aout->main.outvolume; /* * max 256 */
2374 tmpvol /= (256 * 64); /* tmpvol is max 32768 again */
2375 aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */
2376 tmpvol *= envvol; /* * max 256 */
2377 tmpvol *= mod->volume; /* * max 128 */
2378 tmpvol /= (128 * 256 * 128);
2379
2380 /* fade out */
2381 if (mod->sngpos>=mod->numpos)
2382 tmpvol=0;
2383 else
2384 tmpvol=(tmpvol*max_volume)/128;
2385
2386 if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted)
2387 Voice_SetVolume_internal(channel,0);
2388 else {
2389 Voice_SetVolume_internal(channel,tmpvol);
2390 if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
2391 mod->realchn++;
2392 mod->totalchn++;
2393 }
2394
2395 if (aout->main.panning==PAN_SURROUND)
2396 Voice_SetPanning_internal(channel,PAN_SURROUND);
2397 else
2398 if ((mod->panflag)&&(aout->penv.flg & EF_ON))
2399 Voice_SetPanning_internal(channel,
2400 DoPan(envpan,aout->main.panning));
2401 else
2402 Voice_SetPanning_internal(channel,aout->main.panning);
2403
2404 if (aout->main.period && s->vibdepth)
2405 switch (s->vibtype) {
2406 case 0:
2407 vibval=avibtab[s->avibpos&127];
2408 if (aout->avibpos & 0x80) vibval=-vibval;
2409 break;
2410 case 1:
2411 vibval=64;
2412 if (aout->avibpos & 0x80) vibval=-vibval;
2413 break;
2414 case 2:
2415 vibval=63-(((aout->avibpos+128)&255)>>1);
2416 break;
2417 default:
2418 vibval=(((aout->avibpos+128)&255)>>1)-64;
2419 break;
2420 }
2421 else
2422 vibval=0;
2423
2424 if (s->vibflags & AV_IT) {
2425 if ((aout->aswppos>>8)<s->vibdepth) {
2426 aout->aswppos += s->vibsweep;
2427 vibdpt=aout->aswppos;
2428 } else
2429 vibdpt=s->vibdepth<<8;
2430 vibval=(vibval*vibdpt)>>16;
2431 if (aout->mflag) {
2432 if (!(mod->flags&UF_LINEAR)) vibval>>=1;
2433 aout->main.period-=vibval;
2434 }
2435 } else {
2436 /* do XM style auto-vibrato */
2437 if (!(aout->main.keyoff & KEY_OFF)) {
2438 if (aout->aswppos<s->vibsweep) {
2439 vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep;
2440 aout->aswppos++;
2441 } else
2442 vibdpt=s->vibdepth;
2443 } else {
2444 /* keyoff -> depth becomes 0 if final depth wasn't reached or
2445 stays at final level if depth WAS reached */
2446 if (aout->aswppos>=s->vibsweep)
2447 vibdpt=s->vibdepth;
2448 else
2449 vibdpt=0;
2450 }
2451 vibval=(vibval*vibdpt)>>8;
2452 aout->main.period-=vibval;
2453 }
2454
2455 /* update vibrato position */
2456 aout->avibpos=(aout->avibpos+s->vibrate)&0xff;
2457
2458 /* process pitch envelope */
2459 playperiod=aout->main.period;
2460
2461 if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) {
2462 long p1;
2463
2464 envpit-=32;
2465 if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1);
2466
2467 p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit,
2468 aout->master->speed)-aout->masterperiod;
2469 if (p1>0) {
2470 if ((UWORD)(playperiod+p1)<=playperiod) {
2471 p1=0;
2472 aout->main.keyoff|=KEY_OFF;
2473 }
2474 } else if (p1<0) {
2475 if ((UWORD)(playperiod+p1)>=playperiod) {
2476 p1=0;
2477 aout->main.keyoff|=KEY_OFF;
2478 }
2479 }
2480 playperiod+=p1;
2481 }
2482
2483 if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */
2484 Voice_Stop_internal(channel);
2485 mod->totalchn--;
2486 if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
2487 mod->realchn--;
2488 } else {
2489 Voice_SetFrequency_internal(channel,
2490 getfrequency(mod->flags,playperiod));
2491
2492 /* if keyfade, start substracting fadeoutspeed from fadevol: */
2493 if ((i)&&(aout->main.keyoff&KEY_FADE)) {
2494 if (aout->main.fadevol>=i->volfade)
2495 aout->main.fadevol-=i->volfade;
2496 else
2497 aout->main.fadevol=0;
2498 }
2499 }
2500
2501 md_bpm=mod->bpm+mod->relspd;
2502 if (md_bpm<32)
2503 md_bpm=32;
2504 else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255)
2505 md_bpm=255;
2506 }
2507}
2508
2509/* Handles new notes or instruments */
2510void pt_Notes(MODULE *mod)
2511{
2512 SWORD channel;
2513 MP_CONTROL *a;
2514 UBYTE c,inst;
2515 int tr,funky; /* funky is set to indicate note or instrument change */
2516
2517 for (channel=0;channel<mod->numchn;channel++) {
2518 a=&mod->control[channel];
2519
2520 if (mod->sngpos>=mod->numpos) {
2521 tr=mod->numtrk;
2522 mod->numrow=0;
2523 } else {
2524 tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel];
2525 mod->numrow=mod->pattrows[mod->positions[mod->sngpos]];
2526 }
2527
2528 a->row=(tr<mod->numtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL;
2529 a->newsamp=0;
2530 if (!mod->vbtick) a->main.notedelay=0;
2531
2532 if (!a->row) continue;
2533 UniSetRow(a->row);
2534 funky=0;
2535
2536 while((c=UniGetByte()))
2537 switch (c) {
2538 case UNI_NOTE:
2539 funky|=1;
2540 a->oldnote=a->anote,a->anote=UniGetByte();
2541 a->main.kick =KICK_NOTE;
2542 a->main.start=-1;
2543 a->sliding=0;
2544
2545 /* retrig tremolo and vibrato waves ? */
2546 if (!(a->wavecontrol & 0x80)) a->trmpos=0;
2547 if (!(a->wavecontrol & 0x08)) a->vibpos=0;
2548 if (!a->panbwave) a->panbpos=0;
2549 break;
2550 case UNI_INSTRUMENT:
2551 inst=UniGetByte();
2552 if (inst>=mod->numins) break; /* safety valve */
2553 funky|=2;
2554 a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL;
2555 a->retrig=0;
2556 a->s3mtremor=0;
2557 a->ultoffset=0;
2558 a->main.sample=inst;
2559 break;
2560 default:
2561 UniSkipOpcode();
2562 break;
2563 }
2564
2565 if (funky) {
2566 INSTRUMENT *i;
2567 SAMPLE *s;
2568
2569 if ((i=a->main.i)) {
2570 if (i->samplenumber[a->anote] >= mod->numsmp) continue;
2571 s=&mod->samples[i->samplenumber[a->anote]];
2572 a->main.note=i->samplenote[a->anote];
2573 } else {
2574 a->main.note=a->anote;
2575 s=&mod->samples[a->main.sample];
2576 }
2577
2578 if (a->main.s!=s) {
2579 a->main.s=s;
2580 a->newsamp=a->main.period;
2581 }
2582
2583 /* channel or instrument determined panning ? */
2584 a->main.panning=mod->panning[channel];
2585 if (s->flags & SF_OWNPAN)
2586 a->main.panning=s->panning;
2587 else if ((i)&&(i->flags & IF_OWNPAN))
2588 a->main.panning=i->panning;
2589
2590 a->main.handle=s->handle;
2591 a->speed=s->speed;
2592
2593 if (i) {
2594 if ((mod->panflag)&&(i->flags & IF_PITCHPAN)
2595 &&(a->main.panning!=PAN_SURROUND)){
2596 a->main.panning+=
2597 ((a->anote-i->pitpancenter)*i->pitpansep)/8;
2598 if (a->main.panning<PAN_LEFT)
2599 a->main.panning=PAN_LEFT;
2600 else if (a->main.panning>PAN_RIGHT)
2601 a->main.panning=PAN_RIGHT;
2602 }
2603 a->main.pitflg=i->pitflg;
2604 a->main.volflg=i->volflg;
2605 a->main.panflg=i->panflg;
2606 a->main.nna=i->nnatype;
2607 a->dca=i->dca;
2608 a->dct=i->dct;
2609 } else {
2610 a->main.pitflg=a->main.volflg=a->main.panflg=0;
2611 a->main.nna=a->dca=0;
2612 a->dct=DCT_OFF;
2613 }
2614
2615 if (funky&2) /* instrument change */ {
2616 /* IT random volume variations: 0:8 bit fixed, and one bit for
2617 sign. */
2618 a->volume=a->tmpvolume=s->volume;
2619 if ((s)&&(i)) {
2620 if (i->rvolvar) {
2621 a->volume=a->tmpvolume=s->volume+
2622 ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512)
2623 ))/25600);
2624 if (a->volume<0)
2625 a->volume=a->tmpvolume=0;
2626 else if (a->volume>64)
2627 a->volume=a->tmpvolume=64;
2628 }
2629 if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) {
2630 a->main.panning+=((a->main.panning*((SLONG)i->rpanvar*
2631 (SLONG)getrandom(512)))/25600);
2632 if (a->main.panning<PAN_LEFT)
2633 a->main.panning=PAN_LEFT;
2634 else if (a->main.panning>PAN_RIGHT)
2635 a->main.panning=PAN_RIGHT;
2636 }
2637 }
2638 }
2639
2640 a->wantedperiod=a->tmpperiod=
2641 GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed);
2642 a->main.keyoff=KEY_KICK;
2643 }
2644 }
2645}
2646
2647/* Handles effects */
2648void pt_EffectsPass1(MODULE *mod)
2649{
2650 SWORD channel;
2651 MP_CONTROL *a;
2652 MP_VOICE *aout;
2653 int explicitslides;
2654
2655 for (channel=0;channel<mod->numchn;channel++) {
2656 a=&mod->control[channel];
2657
2658 if ((aout=a->slave)) {
2659 a->main.fadevol=aout->main.fadevol;
2660 a->main.period=aout->main.period;
2661 if (a->main.kick==KICK_KEYOFF)
2662 a->main.keyoff=aout->main.keyoff;
2663 }
2664
2665 if (!a->row) continue;
2666 UniSetRow(a->row);
2667
2668 a->ownper=a->ownvol=0;
2669 explicitslides = pt_playeffects(mod, channel, a);
2670
2671 /* continue volume slide if necessary for XM and IT */
2672 if (mod->flags&UF_BGSLIDES) {
2673 if (!explicitslides && a->sliding)
2674 DoS3MVolSlide(mod->vbtick, mod->flags, a, 0);
2675 else if (a->tmpvolume)
2676 a->sliding = explicitslides;
2677 }
2678
2679 if (!a->ownper)
2680 a->main.period=a->tmpperiod;
2681 if (!a->ownvol)
2682 a->volume=a->tmpvolume;
2683
2684 if (a->main.s) {
2685 if (a->main.i)
2686 a->main.outvolume=
2687 (a->volume*a->main.s->globvol*a->main.i->globvol)>>10;
2688 else
2689 a->main.outvolume=(a->volume*a->main.s->globvol)>>4;
2690 if (a->main.outvolume>256)
2691 a->main.outvolume=256;
2692 else if (a->main.outvolume<0)
2693 a->main.outvolume=0;
2694 }
2695 }
2696}
2697
2698/* NNA management */
2699void pt_NNA(MODULE *mod)
2700{
2701 SWORD channel;
2702 MP_CONTROL *a;
2703
2704 for (channel=0;channel<mod->numchn;channel++) {
2705 a=&mod->control[channel];
2706
2707 if (a->main.kick==KICK_NOTE) {
2708 int kill=0;
2709
2710 if (a->slave) {
2711 MP_VOICE *aout;
2712
2713 aout=a->slave;
2714 if (aout->main.nna & NNA_MASK) {
2715 /* Make sure the old MP_VOICE channel knows it has no
2716 master now ! */
2717 a->slave=NULL;
2718 /* assume the channel is taken by NNA */
2719 aout->mflag=0;
2720
2721 switch (aout->main.nna) {
2722 case NNA_CONTINUE: /* continue note, do nothing */
2723 break;
2724 case NNA_OFF: /* note off */
2725 aout->main.keyoff|=KEY_OFF;
2726 if ((!(aout->main.volflg & EF_ON))||
2727 (aout->main.volflg & EF_LOOP))
2728 aout->main.keyoff=KEY_KILL;
2729 break;
2730 case NNA_FADE:
2731 aout->main.keyoff |= KEY_FADE;
2732 break;
2733 }
2734 }
2735 }
2736
2737 if (a->dct!=DCT_OFF) {
2738 int t;
2739
2740 for (t=0;t<md_sngchn;t++)
2741 if ((!Voice_Stopped_internal(t))&&
2742 (mod->voice[t].masterchn==channel)&&
2743 (a->main.sample==mod->voice[t].main.sample)) {
2744 kill=0;
2745 switch (a->dct) {
2746 case DCT_NOTE:
2747 if (a->main.note==mod->voice[t].main.note)
2748 kill=1;
2749 break;
2750 case DCT_SAMPLE:
2751 if (a->main.handle==mod->voice[t].main.handle)
2752 kill=1;
2753 break;
2754 case DCT_INST:
2755 kill=1;
2756 break;
2757 }
2758 if (kill)
2759 switch (a->dca) {
2760 case DCA_CUT:
2761 mod->voice[t].main.fadevol=0;
2762 break;
2763 case DCA_OFF:
2764 mod->voice[t].main.keyoff|=KEY_OFF;
2765 if ((!(mod->voice[t].main.volflg&EF_ON))||
2766 (mod->voice[t].main.volflg&EF_LOOP))
2767 mod->voice[t].main.keyoff=KEY_KILL;
2768 break;
2769 case DCA_FADE:
2770 mod->voice[t].main.keyoff|=KEY_FADE;
2771 break;
2772 }
2773 }
2774 }
2775 } /* if (a->main.kick==KICK_NOTE) */
2776 }
2777}
2778
2779/* Setup module and NNA voices */
2780void pt_SetupVoices(MODULE *mod)
2781{
2782 SWORD channel;
2783 MP_CONTROL *a;
2784 MP_VOICE *aout;
2785
2786 for (channel=0;channel<mod->numchn;channel++) {
2787 a=&mod->control[channel];
2788
2789 if (a->main.notedelay) continue;
2790 if (a->main.kick==KICK_NOTE) {
2791 /* if no channel was cut above, find an empty or quiet channel
2792 here */
2793 if (mod->flags&UF_NNA) {
2794 if (!a->slave) {
2795 int newchn;
2796
2797 if ((newchn=MP_FindEmptyChannel(mod))!=-1)
2798 a->slave=&mod->voice[a->slavechn=newchn];
2799 }
2800 } else
2801 a->slave=&mod->voice[a->slavechn=channel];
2802
2803 /* assign parts of MP_VOICE only done for a KICK_NOTE */
2804 if ((aout=a->slave)) {
2805 if (aout->mflag && aout->master) aout->master->slave=NULL;
2806 aout->master=a;
2807 a->slave=aout;
2808 aout->masterchn=channel;
2809 aout->mflag=1;
2810 }
2811 } else
2812 aout=a->slave;
2813
2814 if (aout)
2815 aout->main=a->main;
2816 a->main.kick=KICK_ABSENT;
2817 }
2818}
2819
2820/* second effect pass */
2821void pt_EffectsPass2(MODULE *mod)
2822{
2823 SWORD channel;
2824 MP_CONTROL *a;
2825 UBYTE c;
2826
2827 for (channel=0;channel<mod->numchn;channel++) {
2828 a=&mod->control[channel];
2829
2830 if (!a->row) continue;
2831 UniSetRow(a->row);
2832
2833 while((c=UniGetByte()))
2834 if (c==UNI_ITEFFECTS0) {
2835 c=UniGetByte();
2836 if ((c>>4)==SS_S7EFFECTS)
2837 DoNNAEffects(mod, a, c&0xf);
2838 } else
2839 UniSkipOpcode();
2840 }
2841}
2842
2843void Player_HandleTick(void)
2844{
2845 SWORD channel;
2846 int max_volume;
2847
2848#if 0
2849 /* don't handle the very first ticks, this allows the other hardware to
2850 settle down so we don't loose any starting notes */
2851 if (isfirst) {
2852 isfirst--;
2853 return;
2854 }
2855#endif
2856
2857 if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return;
2858
2859 /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */
2860 pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */
2861 pf->sngtime+=pf->sngremainder/pf->bpm;
2862 pf->sngremainder%=pf->bpm;
2863
2864 if (++pf->vbtick>=pf->sngspd) {
2865 if (pf->pat_repcrazy)
2866 pf->pat_repcrazy=0; /* play 2 times row 0 */
2867 else
2868 pf->patpos++;
2869 pf->vbtick=0;
2870
2871 /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is
2872 the command memory. */
2873 if (pf->patdly)
2874 pf->patdly2=pf->patdly,pf->patdly=0;
2875 if (pf->patdly2) {
2876 /* patterndelay active */
2877 if (--pf->patdly2)
2878 /* so turn back pf->patpos by 1 */
2879 if (pf->patpos) pf->patpos--;
2880 }
2881
2882 /* do we have to get a new patternpointer ? (when pf->patpos reaches the
2883 pattern size, or when a patternbreak is active) */
2884 if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp))
2885 pf->posjmp=3;
2886
2887 if (pf->posjmp) {
2888 pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0;
2889 pf->pat_repcrazy=0;
2890 pf->sngpos+=(pf->posjmp-2);
2891 for (channel=0;channel<pf->numchn;channel++)
2892 pf->control[channel].pat_reppos=-1;
2893
2894 pf->patbrk=pf->posjmp=0;
2895 /* handle the "---" (end of song) pattern since it can occur
2896 *inside* the module in some formats */
2897 if ((pf->sngpos>=pf->numpos)||
2898 (pf->positions[pf->sngpos]==LAST_PATTERN)) {
2899 if (!pf->wrap) return;
2900 if (!(pf->sngpos=pf->reppos)) {
2901 pf->volume=pf->initvolume>128?128:pf->initvolume;
2902 if(pf->initspeed!=0)
2903 pf->sngspd=pf->initspeed<32?pf->initspeed:32;
2904 else
2905 pf->sngspd=6;
2906 pf->bpm=pf->inittempo<32?32:pf->inittempo;
2907 }
2908 }
2909 if (pf->sngpos<0) pf->sngpos=pf->numpos-1;
2910 }
2911
2912 if (!pf->patdly2)
2913 pt_Notes(pf);
2914 }
2915
2916 /* Fade global volume if enabled and we're playing the last pattern */
2917 if (((pf->sngpos==pf->numpos-1)||
2918 (pf->positions[pf->sngpos+1]==LAST_PATTERN))&&
2919 (pf->fadeout))
2920 max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0;
2921 else
2922 max_volume=128;
2923
2924 pt_EffectsPass1(pf);
2925 if (pf->flags&UF_NNA)
2926 pt_NNA(pf);
2927 pt_SetupVoices(pf);
2928 pt_EffectsPass2(pf);
2929
2930 /* now set up the actual hardware channel playback information */
2931 pt_UpdateVoices(pf, max_volume);
2932}
2933
2934static void Player_Init_internal(MODULE* mod)
2935{
2936 int t;
2937
2938 for (t=0;t<mod->numchn;t++) {
2939 mod->control[t].main.chanvol=mod->chanvol[t];
2940 mod->control[t].main.panning=mod->panning[t];
2941 }
2942
2943 mod->sngtime=0;
2944 mod->sngremainder=0;
2945
2946 mod->pat_repcrazy=0;
2947 mod->sngpos=0;
2948 if(mod->initspeed!=0)
2949 mod->sngspd=mod->initspeed<32?mod->initspeed:32;
2950 else
2951 mod->sngspd=6;
2952 mod->volume=mod->initvolume>128?128:mod->initvolume;
2953
2954 mod->vbtick=mod->sngspd;
2955 mod->patdly=0;
2956 mod->patdly2=0;
2957 mod->bpm=mod->inittempo<32?32:mod->inittempo;
2958 mod->realchn=0;
2959
2960 mod->patpos=0;
2961 mod->posjmp=2; /* make sure the player fetches the first note */
2962 mod->numrow=-1;
2963 mod->patbrk=0;
2964}
2965
2966int Player_Init(MODULE* mod)
2967{
2968 mod->extspd=1;
2969 mod->panflag=1;
2970 mod->wrap=0;
2971 mod->loop=1;
2972 mod->fadeout=0;
2973
2974 mod->relspd=0;
2975
2976 /* make sure the player doesn't start with garbage */
2977 if (!(mod->control=(MP_CONTROL*)MikMod_calloc(mod->numchn,sizeof(MP_CONTROL))))
2978 return 1;
2979 if (!(mod->voice=(MP_VOICE*)MikMod_calloc(md_sngchn,sizeof(MP_VOICE))))
2980 return 1;
2981
2982 Player_Init_internal(mod);
2983 return 0;
2984}
2985
2986void Player_Exit_internal(MODULE* mod)
2987{
2988 if (!mod)
2989 return;
2990
2991 /* Stop playback if necessary */
2992 if (mod==pf) {
2993 Player_Stop_internal();
2994 pf=NULL;
2995 }
2996
2997 if (mod->control)
2998 MikMod_free(mod->control);
2999 if (mod->voice)
3000 MikMod_free(mod->voice);
3001 mod->control=NULL;
3002 mod->voice=NULL;
3003}
3004
3005void Player_Exit(MODULE* mod)
3006{
3007 MUTEX_LOCK(vars);
3008 Player_Exit_internal(mod);
3009 MUTEX_UNLOCK(vars);
3010}
3011
3012MIKMODAPI void Player_SetVolume(SWORD volume)
3013{
3014 MUTEX_LOCK(vars);
3015 if (pf)
3016 pf->volume=(volume<0)?0:(volume>128)?128:volume;
3017 MUTEX_UNLOCK(vars);
3018}
3019
3020MIKMODAPI MODULE* Player_GetModule(void)
3021{
3022 MODULE* result;
3023
3024 MUTEX_LOCK(vars);
3025 result=pf;
3026 MUTEX_UNLOCK(vars);
3027
3028 return result;
3029}
3030
3031MIKMODAPI void Player_Start(MODULE *mod)
3032{
3033 int t;
3034
3035 if (!mod)
3036 return;
3037
3038 if (!MikMod_Active())
3039 MikMod_EnableOutput();
3040
3041 mod->forbid=0;
3042
3043 MUTEX_LOCK(vars);
3044 if (pf!=mod) {
3045 /* new song is being started, so completely stop out the old one. */
3046 if (pf) pf->forbid=1;
3047 for (t=0;t<md_sngchn;t++) Voice_Stop_internal(t);
3048 }
3049 pf=mod;
3050 MUTEX_UNLOCK(vars);
3051}
3052
3053void Player_Stop_internal(void)
3054{
3055 if (!md_sfxchn) MikMod_DisableOutput_internal();
3056 if (pf) pf->forbid=1;
3057 pf=NULL;
3058}
3059
3060MIKMODAPI void Player_Stop(void)
3061{
3062 MUTEX_LOCK(vars);
3063 Player_Stop_internal();
3064 MUTEX_UNLOCK(vars);
3065}
3066
3067MIKMODAPI int Player_Active(void)
3068{
3069 int result=0;
3070
3071 MUTEX_LOCK(vars);
3072 if (pf)
3073 result=(!(pf->sngpos>=pf->numpos));
3074 MUTEX_UNLOCK(vars);
3075
3076 return result;
3077}
3078
3079MIKMODAPI void Player_NextPosition(void)
3080{
3081 MUTEX_LOCK(vars);
3082 if (pf) {
3083 int t;
3084
3085 pf->forbid=1;
3086 pf->posjmp=3;
3087 pf->patbrk=0;
3088 pf->vbtick=pf->sngspd;
3089
3090 for (t=0;t<md_sngchn;t++) {
3091 Voice_Stop_internal(t);
3092 pf->voice[t].main.i=NULL;
3093 pf->voice[t].main.s=NULL;
3094 }
3095 for (t=0;t<pf->numchn;t++) {
3096 pf->control[t].main.i=NULL;
3097 pf->control[t].main.s=NULL;
3098 }
3099 pf->forbid=0;
3100 }
3101 MUTEX_UNLOCK(vars);
3102}
3103
3104MIKMODAPI void Player_PrevPosition(void)
3105{
3106 MUTEX_LOCK(vars);
3107 if (pf) {
3108 int t;
3109
3110 pf->forbid=1;
3111 pf->posjmp=1;
3112 pf->patbrk=0;
3113 pf->vbtick=pf->sngspd;
3114
3115 for (t=0;t<md_sngchn;t++) {
3116 Voice_Stop_internal(t);
3117 pf->voice[t].main.i=NULL;
3118 pf->voice[t].main.s=NULL;
3119 }
3120 for (t=0;t<pf->numchn;t++) {
3121 pf->control[t].main.i=NULL;
3122 pf->control[t].main.s=NULL;
3123 }
3124 pf->forbid=0;
3125 }
3126 MUTEX_UNLOCK(vars);
3127}
3128
3129MIKMODAPI void Player_SetPosition(UWORD pos)
3130{
3131 MUTEX_LOCK(vars);
3132 if (pf) {
3133 int t;
3134
3135 pf->forbid=1;
3136 if (pos>=pf->numpos) pos=pf->numpos;
3137 pf->posjmp=2;
3138 pf->patbrk=0;
3139 pf->sngpos=pos;
3140 pf->vbtick=pf->sngspd;
3141
3142 for (t=0;t<md_sngchn;t++) {
3143 Voice_Stop_internal(t);
3144 pf->voice[t].main.i=NULL;
3145 pf->voice[t].main.s=NULL;
3146 }
3147 for (t=0;t<pf->numchn;t++) {
3148 pf->control[t].main.i=NULL;
3149 pf->control[t].main.s=NULL;
3150 }
3151 pf->forbid=0;
3152
3153 if (!pos)
3154 Player_Init_internal(pf);
3155 }
3156 MUTEX_UNLOCK(vars);
3157}
3158
3159static void Player_Unmute_internal(SLONG arg1,va_list ap)
3160{
3161 SLONG t,arg2,arg3=0;
3162
3163 if (pf) {
3164 switch (arg1) {
3165 case MUTE_INCLUSIVE:
3166 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3167 (arg2>arg3)||(arg3>=pf->numchn))
3168 return;
3169 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
3170 pf->control[arg2].muted=0;
3171 break;
3172 case MUTE_EXCLUSIVE:
3173 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3174 (arg2>arg3)||(arg3>=pf->numchn))
3175 return;
3176 for (t=0;t<pf->numchn;t++) {
3177 if ((t>=arg2) && (t<=arg3))
3178 continue;
3179 pf->control[t].muted=0;
3180 }
3181 break;
3182 default:
3183 if (arg1<pf->numchn) pf->control[arg1].muted=0;
3184 break;
3185 }
3186 }
3187}
3188
3189MIKMODAPI void Player_Unmute(SLONG arg1, ...)
3190{
3191 va_list args;
3192
3193 va_start(args,arg1);
3194 MUTEX_LOCK(vars);
3195 Player_Unmute_internal(arg1,args);
3196 MUTEX_UNLOCK(vars);
3197 va_end(args);
3198}
3199
3200static void Player_Mute_internal(SLONG arg1,va_list ap)
3201{
3202 SLONG t,arg2,arg3=0;
3203
3204 if (pf) {
3205 switch (arg1) {
3206 case MUTE_INCLUSIVE:
3207 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3208 (arg2>arg3)||(arg3>=pf->numchn))
3209 return;
3210 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
3211 pf->control[arg2].muted=1;
3212 break;
3213 case MUTE_EXCLUSIVE:
3214 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3215 (arg2>arg3)||(arg3>=pf->numchn))
3216 return;
3217 for (t=0;t<pf->numchn;t++) {
3218 if ((t>=arg2) && (t<=arg3))
3219 continue;
3220 pf->control[t].muted=1;
3221 }
3222 break;
3223 default:
3224 if (arg1<pf->numchn)
3225 pf->control[arg1].muted=1;
3226 break;
3227 }
3228 }
3229}
3230
3231MIKMODAPI void Player_Mute(SLONG arg1,...)
3232{
3233 va_list args;
3234
3235 va_start(args,arg1);
3236 MUTEX_LOCK(vars);
3237 Player_Mute_internal(arg1,args);
3238 MUTEX_UNLOCK(vars);
3239 va_end(args);
3240}
3241
3242static void Player_ToggleMute_internal(SLONG arg1,va_list ap)
3243{
3244 SLONG arg2,arg3=0;
3245 ULONG t;
3246
3247 if (pf) {
3248 switch (arg1) {
3249 case MUTE_INCLUSIVE:
3250 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3251 (arg2>arg3)||(arg3>=pf->numchn))
3252 return;
3253 for (;arg2<pf->numchn && arg2<=arg3;arg2++)
3254 pf->control[arg2].muted=1-pf->control[arg2].muted;
3255 break;
3256 case MUTE_EXCLUSIVE:
3257 if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
3258 (arg2>arg3)||(arg3>=pf->numchn))
3259 return;
3260 for (t=0;t<pf->numchn;t++) {
3261 if ((t>=arg2) && (t<=arg3))
3262 continue;
3263 pf->control[t].muted=1-pf->control[t].muted;
3264 }
3265 break;
3266 default:
3267 if (arg1<pf->numchn)
3268 pf->control[arg1].muted=1-pf->control[arg1].muted;
3269 break;
3270 }
3271 }
3272}
3273
3274MIKMODAPI void Player_ToggleMute(SLONG arg1,...)
3275{
3276 va_list args;
3277
3278 va_start(args,arg1);
3279 MUTEX_LOCK(vars);
3280 Player_ToggleMute_internal(arg1,args);
3281 MUTEX_UNLOCK(vars);
3282 va_end(args);
3283}
3284
3285MIKMODAPI int Player_Muted(UBYTE chan)
3286{
3287 int result=1;
3288
3289 MUTEX_LOCK(vars);
3290 if (pf)
3291 result=(chan<pf->numchn)?pf->control[chan].muted:1;
3292 MUTEX_UNLOCK(vars);
3293
3294 return result;
3295}
3296
3297MIKMODAPI int Player_GetChannelVoice(UBYTE chan)
3298{
3299 int result=0;
3300
3301 MUTEX_LOCK(vars);
3302 if (pf)
3303 result=(chan<pf->numchn)?pf->control[chan].slavechn:-1;
3304 MUTEX_UNLOCK(vars);
3305
3306 return result;
3307}
3308
3309MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan)
3310{
3311 UWORD result=0;
3312
3313 MUTEX_LOCK(vars);
3314 if (pf)
3315 result=(chan<pf->numchn)?pf->control[chan].main.period:0;
3316 MUTEX_UNLOCK(vars);
3317
3318 return result;
3319}
3320
3321int Player_Paused_internal(void)
3322{
3323 return pf?pf->forbid:1;
3324}
3325
3326MIKMODAPI int Player_Paused(void)
3327{
3328 int result;
3329
3330 MUTEX_LOCK(vars);
3331 result=Player_Paused_internal();
3332 MUTEX_UNLOCK(vars);
3333
3334 return result;
3335}
3336
3337MIKMODAPI void Player_TogglePause(void)
3338{
3339 MUTEX_LOCK(vars);
3340 if (pf)
3341 pf->forbid=1-pf->forbid;
3342 MUTEX_UNLOCK(vars);
3343}
3344
3345MIKMODAPI void Player_SetSpeed(UWORD speed)
3346{
3347 MUTEX_LOCK(vars);
3348 if (pf)
3349 pf->sngspd=speed?(speed<32?speed:32):1;
3350 MUTEX_UNLOCK(vars);
3351}
3352
3353MIKMODAPI void Player_SetTempo(UWORD tempo)
3354{
3355 if (tempo<32) tempo=32;
3356 MUTEX_LOCK(vars);
3357 if (pf) {
3358 if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255;
3359 pf->bpm=tempo;
3360 }
3361 MUTEX_UNLOCK(vars);
3362}
3363
3364MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo)
3365{
3366 int i;
3367
3368 if (numvoices > md_sngchn)
3369 numvoices = md_sngchn;
3370
3371 MUTEX_LOCK(vars);
3372 if (pf)
3373 for (i = 0; i < md_sngchn; i++) {
3374 vinfo [i].i = pf->voice[i].main.i;
3375 vinfo [i].s = pf->voice[i].main.s;
3376 vinfo [i].panning = pf->voice [i].main.panning;
3377 vinfo [i].volume = pf->voice [i].main.chanvol;
3378 vinfo [i].period = pf->voice [i].main.period;
3379 vinfo [i].kick = pf->voice [i].main.kick_flag;
3380 pf->voice [i].main.kick_flag = 0;
3381 }
3382 MUTEX_UNLOCK(vars);
3383
3384 return numvoices;
3385}
3386
3387
3388// Get current module order
3389MIKMODAPI int Player_GetOrder(void)
3390{
3391 int ret;
3392 MUTEX_LOCK(vars);
3393 ret = pf ? pf->sngpos :0; // pf->positions[pf->sngpos ? pf->sngpos-1 : 0]: 0;
3394 MUTEX_UNLOCK(vars);
3395 return ret;
3396}
3397
3398// Get current module row
3399MIKMODAPI int Player_GetRow(void)
3400{
3401 int ret;
3402 MUTEX_LOCK(vars);
3403 ret = pf ? pf->patpos : 0;
3404 MUTEX_UNLOCK(vars);
3405 return ret;
3406}
3407
3408
3409/* ex:set ts=4: */