Add "MikMod for Rockbox 0.1" from 2007-06-29
[mikmod-rockbox.git] / apps / plugins / mikmod / playercode / mplayer.c
1 /*      MikMod sound library\r
2         (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS\r
3         for complete list.\r
4 \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
9  \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
14  \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
18         02111-1307, USA.\r
19 */\r
20 \r
21 /*==============================================================================\r
22 \r
23   $Id: mplayer.c,v 1.5 2004/01/31 22:40:22 raph Exp $\r
24 \r
25   The Protracker Player Driver\r
26 \r
27   The protracker driver supports all base Protracker 3.x commands and features.\r
28 \r
29 ==============================================================================*/\r
30 \r
31 #ifdef HAVE_CONFIG_H\r
32 #include "config.h"\r
33 #endif\r
34 \r
35 #include <string.h>\r
36 #include <stdarg.h>\r
37 #ifdef SRANDOM_IN_MATH_H\r
38 #include <math.h>\r
39 #else\r
40 #include <stdlib.h>\r
41 #endif\r
42 \r
43 #include "mikmod_internals.h"\r
44 \r
45 #ifdef SUNOS\r
46 extern int fprintf(FILE *, const char *, ...);\r
47 extern long int random(void);\r
48 #endif\r
49 \r
50 /* The currently playing module */\r
51 /* This variable should better be static, but it would break the ABI, so this\r
52    will wait */\r
53 /*static*/ MODULE *pf = NULL;\r
54 \r
55 #define HIGH_OCTAVE             2       /* number of above-range octaves */\r
56 \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
62 };\r
63 \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
67 };\r
68 \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
78 };\r
79 \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
178 };\r
179 \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
208 };\r
209 \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
227 };\r
228 \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
231 {\r
232 #ifdef HAVE_SRANDOM\r
233         return random()&(ceil-1);\r
234 #else\r
235         return (rand()*ceil)/(RAND_MAX+1.0);\r
236         //return ceil - 1;\r
237 #endif\r
238 }\r
239 \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
246 */\r
247 static int MP_FindEmptyChannel(MODULE *mod)\r
248 {\r
249         MP_VOICE *a;\r
250         ULONG t,k,tvol,pp;\r
251 \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
256                         return t;\r
257 \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
261                 if (!a->main.s)\r
262                         return k;\r
263 \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
267                                 pp<<=2;\r
268 \r
269                         if (pp<tvol) {\r
270                                 tvol=pp;\r
271                                 t=k;\r
272                         }\r
273                 }\r
274         }\r
275 \r
276         if (tvol>8000*7) return -1;\r
277         return t;\r
278 }\r
279 \r
280 static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2)\r
281 {\r
282         if ((p1==p2)||(p==p1)) return v1;\r
283         return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1));\r
284 }\r
285 \r
286 UWORD getlinearperiod(UWORD note,ULONG fine)\r
287 {\r
288         UWORD t;\r
289 \r
290         t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1);\r
291         return t;\r
292 }\r
293 \r
294 static UWORD getlogperiod(UWORD note,ULONG fine)\r
295 {\r
296         UWORD n,o;\r
297         UWORD p1,p2;\r
298         ULONG i;\r
299 \r
300         n=note%(2*OCTAVE);\r
301         o=note/(2*OCTAVE);\r
302         i=(n<<2)+(fine>>4); /* n*8 + fine/16 */\r
303 \r
304         p1=logtab[i];\r
305         p2=logtab[i+1];\r
306 \r
307         return (Interpolate(fine>>4,0,15,p1,p2)>>o);\r
308 }\r
309 \r
310 static UWORD getoldperiod(UWORD note,ULONG speed)\r
311 {\r
312         UWORD n,o;\r
313 \r
314         /* This happens sometimes on badly converted AMF, and old MOD */\r
315         if (!speed) {\r
316 #ifdef MIKMOD_DEBUG\r
317                 fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note);\r
318 #endif\r
319                 return 4242; /* <- prevent divide overflow.. (42 hehe) */\r
320         }\r
321 \r
322         n=note%(2*OCTAVE);\r
323         o=note/(2*OCTAVE);\r
324         return ((8363L*(ULONG)oldperiods[n])>>o)/speed;\r
325 }\r
326 \r
327 static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed)\r
328 {\r
329         if (flags & UF_XMPERIODS) {\r
330                 if (flags & UF_LINEAR)\r
331                                 return getlinearperiod(note, speed);\r
332                 else\r
333                                 return getlogperiod(note, speed);\r
334         } else\r
335                 return getoldperiod(note, speed);\r
336 }\r
337 \r
338 static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b)\r
339 {\r
340         return (Interpolate(p,a->pos,b->pos,a->val,b->val));\r
341 }\r
342 \r
343 static SWORD DoPan(SWORD envpan,SWORD pan)\r
344 {\r
345         int newpan;\r
346 \r
347         newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128);\r
348 \r
349         return (newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT?PAN_RIGHT:newpan);\r
350 }\r
351 \r
352 static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff)\r
353 {\r
354         t->flg=flg;\r
355         t->pts=pts;\r
356         t->susbeg=susbeg;\r
357         t->susend=susend;\r
358         t->beg=beg;\r
359         t->end=end;\r
360         t->env=p;\r
361         t->p=0;\r
362         t->a=0;\r
363         t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1;\r
364 \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
367                 t->a++;t->b++;\r
368         }\r
369 \r
370         /* Fit in the envelope, still */\r
371         if (t->a >= t->pts)\r
372                 t->a = t->pts - 1;\r
373         if (t->b >= t->pts)\r
374                 t->b = t->pts-1;\r
375 \r
376         return t->env[t->a].val;\r
377 }\r
378 \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
382    position.\r
383 \r
384    Envelopes work in the following manner:\r
385 \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
388        interpolated.\r
389 \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
393 \r
394    Misc:\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
397 */\r
398 static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)\r
399 {\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
403 \r
404                 a = t->a;\r
405                 b = t->b;\r
406                 p = t->p;\r
407 \r
408                 /*\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
412                  */\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
416                 } else {\r
417                         /*\r
418                          * All following situations will require interpolation between\r
419                          * two envelope points.\r
420                          */\r
421 \r
422                         /*\r
423                          * Sustain loop between two points (IT type).\r
424                          * Not processed if KEYOFF.\r
425                          */\r
426                         /* if we were on a loop point, loop now */\r
427                         if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) &&\r
428                            a >= t->susend) {\r
429                                 a = t->susbeg;\r
430                                 b = (t->susbeg==t->susend)?a:a+1;\r
431                                 p = t->env[a].pos;\r
432                                 v = t->env[a].val;\r
433                         } else\r
434                         /*\r
435                          * Regular loop.\r
436                          * Be sure to correctly handle single point loops.\r
437                          */\r
438                         if ((t->flg & EF_LOOP) && a >= t->end) {\r
439                                 a = t->beg;\r
440                                 b = t->beg == t->end ? a : a + 1;\r
441                                 p = t->env[a].pos;\r
442                                 v = t->env[a].val;\r
443                         } else\r
444                         /*\r
445                          * Non looping situations.\r
446                          */\r
447                         if (a != b)\r
448                                 v = InterpolateEnv(p, &t->env[a], &t->env[b]);\r
449                         else\r
450                                 v = t->env[a].val;\r
451 \r
452                         /*\r
453                          * Start to fade if the volume envelope is finished.\r
454                          */\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
458                                         if (!v)\r
459                                                 aout->main.fadevol = 0;\r
460                                 }\r
461                         } else {\r
462                                 p++;\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
466                         }\r
467                         t->a = a;\r
468                         t->b = b;\r
469                         t->p = p;\r
470                 }\r
471         }\r
472         return v;\r
473 }\r
474 \r
475 /* XM linear period to frequency conversion */\r
476 ULONG getfrequency(UWORD flags,ULONG period)\r
477 {\r
478         if (flags & UF_LINEAR) {\r
479                 SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE;\r
480 \r
481                 if (shift >= 0)\r
482                         return lintab[period % 768] >> shift;\r
483                 else\r
484                         return lintab[period % 768] << (-shift);\r
485         } else\r
486                 return (8363L*1712L)/(period?period:1);\r
487 }\r
488 \r
489 /*========== Protracker effects */\r
490 \r
491 static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style)\r
492 {\r
493         UBYTE note=a->main.note;\r
494 \r
495         if (a->arpmem) {\r
496                 switch (style) {\r
497                 case 0:         /* mod style: N, N+x, N+y */\r
498                         switch (tick % 3) {\r
499                         /* case 0: unchanged */\r
500                         case 1:\r
501                                 note += (a->arpmem >> 4);\r
502                                 break;\r
503                         case 2:\r
504                                 note += (a->arpmem & 0xf);\r
505                                 break;\r
506                         }\r
507                         break;\r
508                 case 3:         /* okt arpeggio 3: N-x, N, N+y */\r
509                         switch (tick % 3) {\r
510                         case 0:\r
511                                 note -= (a->arpmem >> 4);\r
512                                 break;\r
513                         /* case 1: unchanged */\r
514                         case 2:\r
515                                 note += (a->arpmem & 0xf);\r
516                                 break;\r
517                         }\r
518                         break;\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
522                         case 1:\r
523                                 note += (a->arpmem & 0xf);\r
524                                 break;\r
525                         case 3:\r
526                                 note -= (a->arpmem >> 4);\r
527                                 break;\r
528                         }\r
529                         break;\r
530                 case 5:         /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */\r
531                         if (!tick)\r
532                                 break;\r
533                         switch (tick % 3) {\r
534                         /* case 0: unchanged */\r
535                         case 1:\r
536                                 note -= (a->arpmem >> 4);\r
537                                 break;\r
538                         case 2:\r
539                                 note += (a->arpmem & 0xf);\r
540                                 break;\r
541                         }\r
542                         break;\r
543                 }\r
544                 a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed);\r
545                 a->ownper = 1;\r
546         }\r
547 }\r
548 \r
549 static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
550 {\r
551         UBYTE dat;\r
552         (void)mod;\r
553         (void)channel;\r
554 \r
555         dat = UniGetByte();\r
556         if (!tick) {\r
557                 if (!dat && (flags & UF_ARPMEM))\r
558                         dat=a->arpmem;\r
559                 else\r
560                         a->arpmem=dat;\r
561         }\r
562         if (a->main.period)\r
563                 DoArpeggio(tick, flags, a, 0);\r
564 \r
565         return 0;\r
566 }\r
567 \r
568 static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
569 {\r
570         UBYTE dat;\r
571         (void)flags;\r
572         (void)mod;\r
573         (void)channel;\r
574 \r
575         dat = UniGetByte();\r
576         if (!tick && dat)\r
577                 a->slidespeed = (UWORD)dat << 2;\r
578         if (a->main.period)\r
579                 if (tick)\r
580                         a->tmpperiod -= a->slidespeed;\r
581 \r
582         return 0;\r
583 }\r
584 \r
585 static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
586 {\r
587         UBYTE dat;\r
588         (void)flags;\r
589         (void)mod;\r
590         (void)channel;\r
591 \r
592         dat = UniGetByte();\r
593         if (!tick && dat)\r
594                 a->slidespeed = (UWORD)dat << 2;\r
595         if (a->main.period)\r
596                 if (tick)\r
597                         a->tmpperiod += a->slidespeed;\r
598 \r
599         return 0;\r
600 }\r
601 \r
602 static void DoToneSlide(UWORD tick, MP_CONTROL *a)\r
603 {\r
604         if (!a->main.fadevol)\r
605                 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF;\r
606         else\r
607                 a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT;\r
608 \r
609         if (tick != 0) {\r
610                 int dist;\r
611 \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
615 \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
620                 else if (dist>0) {\r
621                         a->tmpperiod-=a->portspeed;     \r
622                         a->main.period-=a->portspeed; /* dist>0, slide up */\r
623                 } else {\r
624                         a->tmpperiod+=a->portspeed;     \r
625                         a->main.period+=a->portspeed; /* dist<0, slide down */\r
626                 }\r
627         } else\r
628                 a->tmpperiod=a->main.period;\r
629         a->ownper = 1;\r
630 }\r
631 \r
632 static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
633 {\r
634         UBYTE dat;\r
635         (void)flags;\r
636         (void)mod;\r
637         (void)channel;\r
638 \r
639         dat=UniGetByte();\r
640         if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2;\r
641         if (a->main.period)\r
642                 DoToneSlide(tick, a);\r
643 \r
644         return 0;\r
645 }\r
646 \r
647 static void DoVibrato(UWORD tick, MP_CONTROL *a)\r
648 {\r
649         UBYTE q;\r
650         UWORD temp = 0; /* silence warning */\r
651 \r
652         if (!tick)\r
653                 return;\r
654 \r
655         q=(a->vibpos>>2)&0x1f;\r
656 \r
657         switch (a->wavecontrol&3) {\r
658         case 0: /* sine */\r
659                 temp=VibratoTable[q];\r
660                 break;\r
661         case 1: /* ramp down */\r
662                 q<<=3;\r
663                 if (a->vibpos<0) q=255-q;\r
664                 temp=q;\r
665                 break;\r
666         case 2: /* square wave */\r
667                 temp=255;\r
668                 break;\r
669         case 3: /* random wave */\r
670                 temp=getrandom(256);\r
671                 break;\r
672         }\r
673 \r
674         temp*=a->vibdepth;\r
675         temp>>=7;temp<<=2;\r
676 \r
677         if (a->vibpos>=0)\r
678                 a->main.period=a->tmpperiod+temp;\r
679         else\r
680                 a->main.period=a->tmpperiod-temp;\r
681         a->ownper = 1;\r
682 \r
683         if (tick != 0)\r
684                 a->vibpos+=a->vibspd;\r
685 }\r
686 \r
687 static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
688 {\r
689         UBYTE dat;\r
690         (void)flags;\r
691         (void)mod;\r
692         (void)channel;\r
693 \r
694         dat=UniGetByte();\r
695         if (!tick) {\r
696                 if (dat&0x0f) a->vibdepth=dat&0xf;\r
697                 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;\r
698         }\r
699         if (a->main.period)\r
700                 DoVibrato(tick, a);\r
701 \r
702         return 0;\r
703 }\r
704 \r
705 static void DoVolSlide(MP_CONTROL *a, UBYTE dat)\r
706 {\r
707         if (dat&0xf) {\r
708                 a->tmpvolume-=(dat&0x0f);\r
709                 if (a->tmpvolume<0)\r
710                         a->tmpvolume=0;\r
711         } else {\r
712                 a->tmpvolume+=(dat>>4);\r
713                 if (a->tmpvolume>64)\r
714                         a->tmpvolume=64;\r
715         }\r
716 }\r
717 \r
718 static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
719 {\r
720         UBYTE dat;\r
721         (void)flags;\r
722         (void)mod;\r
723         (void)channel;\r
724 \r
725         dat=UniGetByte();\r
726         if (a->main.period)\r
727                 DoToneSlide(tick, a);\r
728 \r
729         if (tick)\r
730                 DoVolSlide(a, dat);\r
731 \r
732         return 0;\r
733 }\r
734 \r
735 /* DoPTEffect6 after DoPTEffectA */\r
736 \r
737 static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
738 {\r
739         UBYTE dat;\r
740         UBYTE q;\r
741         UWORD temp = 0; /* silence warning */\r
742         (void)flags;\r
743         (void)mod;\r
744         (void)channel;\r
745 \r
746         dat=UniGetByte();\r
747         if (!tick) {\r
748                 if (dat&0x0f) a->trmdepth=dat&0xf;\r
749                 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;\r
750         }\r
751         if (a->main.period) {\r
752                 q=(a->trmpos>>2)&0x1f;\r
753 \r
754                 switch ((a->wavecontrol>>4)&3) {\r
755                 case 0: /* sine */\r
756                         temp=VibratoTable[q];\r
757                         break;\r
758                 case 1: /* ramp down */\r
759                         q<<=3;\r
760                         if (a->trmpos<0) q=255-q;\r
761                         temp=q;\r
762                         break;\r
763                 case 2: /* square wave */\r
764                         temp=255;\r
765                         break;\r
766                 case 3: /* random wave */\r
767                         temp=getrandom(256);\r
768                         break;\r
769                 }\r
770                 temp*=a->trmdepth;\r
771                 temp>>=6;\r
772 \r
773                 if (a->trmpos>=0) {\r
774                         a->volume=a->tmpvolume+temp;\r
775                         if (a->volume>64) a->volume=64;\r
776                 } else {\r
777                         a->volume=a->tmpvolume-temp;\r
778                         if (a->volume<0) a->volume=0;\r
779                 }\r
780                 a->ownvol = 1;\r
781 \r
782                 if (tick)\r
783                         a->trmpos+=a->trmspd;\r
784         }\r
785 \r
786         return 0;\r
787 }\r
788 \r
789 static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
790 {\r
791         UBYTE dat;\r
792         (void)flags;\r
793         (void)tick;\r
794 \r
795         dat = UniGetByte();\r
796         if (mod->panflag)\r
797                 a->main.panning = mod->panning[channel] = dat;\r
798 \r
799         return 0;\r
800 }\r
801 \r
802 static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
803 {\r
804         UBYTE dat;\r
805         (void)flags;\r
806         (void)mod;\r
807         (void)channel;\r
808 \r
809         dat=UniGetByte();\r
810         if (!tick) {\r
811                 if (dat) a->soffset=(UWORD)dat<<8;\r
812                 a->main.start=a->hioffset|a->soffset;\r
813 \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
817         }\r
818 \r
819         return 0;\r
820 }\r
821 \r
822 static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
823 {\r
824         UBYTE dat;\r
825         (void)flags;\r
826         (void)mod;\r
827         (void)channel;\r
828 \r
829         dat=UniGetByte();\r
830         if (tick)\r
831                 DoVolSlide(a, dat);\r
832 \r
833         return 0;\r
834 }\r
835 \r
836 static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
837 {\r
838         if (a->main.period)\r
839                 DoVibrato(tick, a);\r
840         DoPTEffectA(tick, flags, a, mod, channel);\r
841 \r
842         return 0;\r
843 }\r
844 \r
845 static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
846 {\r
847         UBYTE dat;\r
848         (void)a;\r
849         (void)channel;\r
850 \r
851         dat=UniGetByte();\r
852 \r
853         if (tick || mod->patdly2)\r
854                 return 0;\r
855 \r
856         /* Vincent Voois uses a nasty trick in "Universal Bolero" */\r
857         if (dat == mod->sngpos && mod->patbrk == mod->patpos)\r
858                 return 0;\r
859 \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
864                 )) {\r
865                 /* if we don't loop, better not to skip the end of the\r
866                    pattern, after all... so:\r
867                 mod->patbrk=0; */\r
868                 mod->posjmp=3;\r
869         } else {\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
873                 mod->sngpos=dat;\r
874                 mod->posjmp=2;\r
875                 mod->patpos=0;\r
876         }\r
877 \r
878         return 0;\r
879 }\r
880 \r
881 static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
882 {\r
883         UBYTE dat;\r
884         (void)flags;\r
885         (void)mod;\r
886         (void)channel;\r
887 \r
888                         dat=UniGetByte();\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
892                         a->tmpvolume=dat;\r
893 \r
894         return 0;\r
895 }\r
896 \r
897 static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
898 {\r
899         UBYTE dat;\r
900         (void)a;\r
901         (void)channel;\r
902 \r
903                         dat=UniGetByte();\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
908                         mod->patbrk=dat;\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
914                                    aspirin - Miod */\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
918                                         mod->sngpos=0;\r
919                                         mod->posjmp=2;\r
920                                 } else\r
921                                         mod->posjmp=3;\r
922                         }\r
923 \r
924         return 0;\r
925 }\r
926 \r
927 static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,\r
928         SWORD channel, UBYTE dat)\r
929 {\r
930         UBYTE nib = dat & 0xf;\r
931 \r
932         switch (dat>>4) {\r
933         case 0x0: /* hardware filter toggle, not supported */\r
934                 break;\r
935         case 0x1: /* fineslide up */\r
936                 if (a->main.period)\r
937                         if (!tick)\r
938                                 a->tmpperiod-=(nib<<2);\r
939                 break;\r
940         case 0x2: /* fineslide dn */\r
941                 if (a->main.period)\r
942                         if (!tick)\r
943                                 a->tmpperiod+=(nib<<2);\r
944                 break;\r
945         case 0x3: /* glissando ctrl */\r
946                 a->glissando=nib;\r
947                 break;\r
948         case 0x4: /* set vibrato waveform */\r
949                 a->wavecontrol&=0xf0;\r
950                 a->wavecontrol|=nib;\r
951                 break;\r
952         case 0x5: /* set finetune */\r
953                 if (a->main.period) {\r
954                         if (flags&UF_XMPERIODS)\r
955                                 a->speed=nib+128;\r
956                         else\r
957                                 a->speed=finetune[nib];\r
958                         a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed);\r
959                 }\r
960                 break;\r
961         case 0x6: /* set patternloop */\r
962                 if (tick)\r
963                         break;\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
967                         if (a->pat_repcnt)\r
968                                 a->pat_repcnt--; /* already looping, decrease counter */\r
969                         else {\r
970 #if 0\r
971                                 /* this would make walker.xm, shipped with Xsoundtracker,\r
972                                    play correctly, but it's better to remain compatible\r
973                                    with FT2 */\r
974                                 if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))\r
975 #endif\r
976                                         a->pat_repcnt=nib; /* not yet looping, so set repcnt */\r
977                         }\r
978 \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
984                                         mod->patpos=0;\r
985                                 } else\r
986                                         mod->patpos=a->pat_reppos;\r
987                         } else a->pat_reppos=POS_NONE;\r
988                 } else\r
989                         a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */\r
990                 break;\r
991         case 0x7: /* set tremolo waveform */\r
992                 a->wavecontrol&=0x0f;\r
993                 a->wavecontrol|=nib<<4;\r
994                 break;\r
995         case 0x8: /* set panning */\r
996                 if (mod->panflag) {\r
997                         if (nib<=8) nib<<=4;\r
998                         else nib*=17;\r
999                         a->main.panning=mod->panning[channel]=nib;\r
1000                 }\r
1001                 break;\r
1002         case 0x9: /* retrig note */\r
1003                 /* do not retrigger on tick 0, until we are emulating FT2 and effect\r
1004                    data is zero */\r
1005                 if (!tick && !((flags & UF_FT2QUIRKS) && (!nib)))\r
1006                         break;\r
1007                 /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */\r
1008                 if (nib || !tick) {\r
1009                         if (!a->retrig) {\r
1010                                 /* when retrig counter reaches 0, reset counter and restart\r
1011                                    the sample */\r
1012                                 if (a->main.period) a->main.kick=KICK_NOTE;\r
1013                                 a->retrig=nib;\r
1014                         }\r
1015                         a->retrig--; /* countdown */\r
1016                 }\r
1017                 break;\r
1018         case 0xa: /* fine volume slide up */\r
1019                 if (tick)\r
1020                         break;\r
1021                 a->tmpvolume+=nib;\r
1022                 if (a->tmpvolume>64) a->tmpvolume=64;\r
1023                 break;\r
1024         case 0xb: /* fine volume slide dn  */\r
1025                 if (tick)\r
1026                         break;\r
1027                 a->tmpvolume-=nib;\r
1028                 if (a->tmpvolume<0)a->tmpvolume=0;\r
1029                 break;\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
1033                 if (tick>=nib)\r
1034                         a->tmpvolume=0; /* just turn the volume down */\r
1035                 break;\r
1036         case 0xd: /* note delay */\r
1037                 /* delay the start of the sample until tick==nib */\r
1038                 if (!tick)\r
1039                         a->main.notedelay=nib;\r
1040                 else if (a->main.notedelay)\r
1041                         a->main.notedelay--;\r
1042                 break;\r
1043         case 0xe: /* pattern delay */\r
1044                 if (!tick)\r
1045                         if (!mod->patdly2)\r
1046                                 mod->patdly=nib+1; /* only once, when tick=0 */\r
1047                 break;\r
1048         case 0xf: /* invert loop, not supported  */\r
1049                 break;\r
1050         }\r
1051 }\r
1052 \r
1053 static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1054 {\r
1055         DoEEffects(tick, flags, a, mod, channel, UniGetByte());\r
1056 \r
1057         return 0;\r
1058 }\r
1059 \r
1060 static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1061 {\r
1062         UBYTE dat;\r
1063         (void)flags;\r
1064         (void)a;\r
1065         (void)channel;\r
1066 \r
1067         dat=UniGetByte();\r
1068         if (tick||mod->patdly2) return 0;\r
1069         if (mod->extspd&&(dat>=mod->bpmlimit))\r
1070                 mod->bpm=dat;\r
1071         else \r
1072           if (dat) {\r
1073                 mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat;\r
1074                 mod->vbtick=0;\r
1075         }\r
1076 \r
1077         return 0;\r
1078 }\r
1079 \r
1080 /*========== Scream Tracker effects */\r
1081 \r
1082 static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1083 {\r
1084         UBYTE speed;\r
1085         (void)flags;\r
1086         (void)a;\r
1087         (void)channel;\r
1088 \r
1089         speed = UniGetByte();\r
1090 \r
1091         if (tick || mod->patdly2)\r
1092                 return 0;\r
1093 \r
1094         if (speed > 128)\r
1095                 speed -= 128;\r
1096         if (speed) {\r
1097                 mod->sngspd = speed;\r
1098                 mod->vbtick = 0;\r
1099         }\r
1100 \r
1101         return 0;\r
1102 }\r
1103 \r
1104 static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf)\r
1105 {\r
1106         UBYTE lo, hi;\r
1107 \r
1108         if (inf)\r
1109                 a->s3mvolslide=inf;\r
1110         else\r
1111                 inf=a->s3mvolslide;\r
1112 \r
1113         lo=inf&0xf;\r
1114         hi=inf>>4;\r
1115 \r
1116         if (!lo) {\r
1117                 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi;\r
1118         } else\r
1119           if (!hi) {\r
1120                 if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo;\r
1121         } else\r
1122           if (lo==0xf) {\r
1123                 if (!tick) a->tmpvolume+=(hi?hi:0xf);\r
1124         } else\r
1125           if (hi==0xf) {\r
1126                 if (!tick) a->tmpvolume-=(lo?lo:0xf);\r
1127         } else\r
1128           return;\r
1129 \r
1130         if (a->tmpvolume<0)\r
1131                 a->tmpvolume=0;\r
1132         else if (a->tmpvolume>64)\r
1133                 a->tmpvolume=64;\r
1134 }\r
1135 \r
1136 static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1137 {\r
1138         (void)mod;\r
1139         (void)channel;\r
1140 \r
1141         DoS3MVolSlide(tick, flags, a, UniGetByte());\r
1142 \r
1143         return 1;\r
1144 }\r
1145 \r
1146 static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf)\r
1147 {\r
1148         UBYTE hi,lo;\r
1149 \r
1150         if (inf)\r
1151                 a->slidespeed=inf;\r
1152         else\r
1153                 inf=a->slidespeed;\r
1154 \r
1155         hi=inf>>4;\r
1156         lo=inf&0xf;\r
1157 \r
1158         if (hi==0xf) {\r
1159                 if (!tick) a->tmpperiod+=(UWORD)lo<<2;\r
1160         } else\r
1161           if (hi==0xe) {\r
1162                 if (!tick) a->tmpperiod+=lo;\r
1163         } else {\r
1164                 if (tick) a->tmpperiod+=(UWORD)inf<<2;\r
1165         }\r
1166 }\r
1167 \r
1168 static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1169 {\r
1170         UBYTE dat;\r
1171         (void)flags;\r
1172         (void)mod;\r
1173         (void)channel;\r
1174 \r
1175         dat=UniGetByte();\r
1176         if (a->main.period)\r
1177                 DoS3MSlideDn(tick, a,dat);\r
1178 \r
1179         return 0;\r
1180 }\r
1181 \r
1182 static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf)\r
1183 {\r
1184         UBYTE hi,lo;\r
1185 \r
1186         if (inf) a->slidespeed=inf;\r
1187         else inf=a->slidespeed;\r
1188 \r
1189         hi=inf>>4;\r
1190         lo=inf&0xf;\r
1191 \r
1192         if (hi==0xf) {\r
1193                 if (!tick) a->tmpperiod-=(UWORD)lo<<2;\r
1194         } else\r
1195           if (hi==0xe) {\r
1196                 if (!tick) a->tmpperiod-=lo;\r
1197         } else {\r
1198                 if (tick) a->tmpperiod-=(UWORD)inf<<2;\r
1199         }\r
1200 }\r
1201 \r
1202 static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1203 {\r
1204         UBYTE dat;\r
1205         (void)flags;\r
1206         (void)mod;\r
1207         (void)channel;\r
1208 \r
1209         dat=UniGetByte();\r
1210         if (a->main.period)\r
1211                 DoS3MSlideUp(tick, a,dat);\r
1212 \r
1213         return 0;\r
1214 }\r
1215 \r
1216 static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1217 {\r
1218         UBYTE inf, on, off;\r
1219         (void)flags;\r
1220         (void)mod;\r
1221         (void)channel;\r
1222 \r
1223         inf = UniGetByte();\r
1224         if (inf)\r
1225                 a->s3mtronof = inf;\r
1226         else {\r
1227                 inf = a->s3mtronof;\r
1228                 if (!inf)\r
1229                         return 0;\r
1230         }\r
1231 \r
1232         if (!tick)\r
1233                 return 0;\r
1234 \r
1235         on=(inf>>4)+1;\r
1236         off=(inf&0xf)+1;\r
1237         a->s3mtremor%=(on+off);\r
1238         a->volume=(a->s3mtremor<on)?a->tmpvolume:0;\r
1239         a->ownvol=1;\r
1240         a->s3mtremor++;\r
1241 \r
1242         return 0;\r
1243 }\r
1244 \r
1245 static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1246 {\r
1247         UBYTE inf;\r
1248         (void)mod;\r
1249         (void)channel;\r
1250 \r
1251         inf = UniGetByte();\r
1252         if (a->main.period) {\r
1253                 if (inf) {\r
1254                         a->s3mrtgslide=inf>>4;\r
1255                         a->s3mrtgspeed=inf&0xf;\r
1256                 }\r
1257 \r
1258                 /* only retrigger if low nibble > 0 */\r
1259                 if (a->s3mrtgspeed>0) {\r
1260                         if (!a->retrig) {\r
1261                                 /* when retrig counter reaches 0, reset counter and restart the\r
1262                                    sample */\r
1263                                 if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF;\r
1264                                 a->retrig=a->s3mrtgspeed;\r
1265 \r
1266                                 if ((tick)||(flags&UF_S3MSLIDES)) {\r
1267                                         switch (a->s3mrtgslide) {\r
1268                                         case 1:\r
1269                                         case 2:\r
1270                                         case 3:\r
1271                                         case 4:\r
1272                                         case 5:\r
1273                                                 a->tmpvolume-=(1<<(a->s3mrtgslide-1));\r
1274                                                 break;\r
1275                                         case 6:\r
1276                                                 a->tmpvolume=(2*a->tmpvolume)/3;\r
1277                                                 break;\r
1278                                         case 7:\r
1279                                                 a->tmpvolume>>=1;\r
1280                                                 break;\r
1281                                         case 9:\r
1282                                         case 0xa:\r
1283                                         case 0xb:\r
1284                                         case 0xc:\r
1285                                         case 0xd:\r
1286                                                 a->tmpvolume+=(1<<(a->s3mrtgslide-9));\r
1287                                                 break;\r
1288                                         case 0xe:\r
1289                                                 a->tmpvolume=(3*a->tmpvolume)>>1;\r
1290                                                 break;\r
1291                                         case 0xf:\r
1292                                                 a->tmpvolume=a->tmpvolume<<1;\r
1293                                                 break;\r
1294                                         }\r
1295                                         if (a->tmpvolume<0)\r
1296                                                 a->tmpvolume=0;\r
1297                                         else if (a->tmpvolume>64)\r
1298                                                 a->tmpvolume=64;\r
1299                                 }\r
1300                         }\r
1301                         a->retrig--; /* countdown  */\r
1302                 }\r
1303         }\r
1304 \r
1305         return 0;\r
1306 }\r
1307 \r
1308 static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1309 {\r
1310         UBYTE dat, q;\r
1311         UWORD temp=0;   /* silence warning */\r
1312         (void)flags;\r
1313         (void)mod;\r
1314         (void)channel;\r
1315 \r
1316         dat = UniGetByte();\r
1317         if (!tick) {\r
1318                 if (dat&0x0f) a->trmdepth=dat&0xf;\r
1319                 if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;\r
1320         }\r
1321 \r
1322         q=(a->trmpos>>2)&0x1f;\r
1323 \r
1324         switch ((a->wavecontrol>>4)&3) {\r
1325         case 0: /* sine */\r
1326                 temp=VibratoTable[q];\r
1327                 break;\r
1328         case 1: /* ramp down */\r
1329                 q<<=3;\r
1330                 if (a->trmpos<0) q=255-q;\r
1331                 temp=q;\r
1332                 break;\r
1333         case 2: /* square wave */\r
1334                 temp=255;\r
1335                 break;\r
1336         case 3: /* random */\r
1337                 temp=getrandom(256);\r
1338                 break;\r
1339         }\r
1340 \r
1341         temp*=a->trmdepth;\r
1342         temp>>=7;\r
1343 \r
1344         if (a->trmpos>=0) {\r
1345                 a->volume=a->tmpvolume+temp;\r
1346                 if (a->volume>64) a->volume=64;\r
1347         } else {\r
1348                 a->volume=a->tmpvolume-temp;\r
1349                 if (a->volume<0) a->volume=0;\r
1350         }\r
1351         a->ownvol = 1;\r
1352 \r
1353         if (tick)\r
1354                 a->trmpos+=a->trmspd;\r
1355 \r
1356         return 0;\r
1357 }\r
1358 \r
1359 static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1360 {\r
1361         UBYTE tempo;\r
1362         (void)flags;\r
1363         (void)a;\r
1364         (void)channel;\r
1365 \r
1366         tempo = UniGetByte();\r
1367 \r
1368         if (tick || mod->patdly2)\r
1369                 return 0;\r
1370 \r
1371         mod->bpm = (tempo < 32) ? 32 : tempo;\r
1372 \r
1373         return 0;\r
1374 }\r
1375 \r
1376 static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1377 {\r
1378         UBYTE dat, q;\r
1379         UWORD temp = 0; /* silence warning */\r
1380         (void)flags;\r
1381         (void)mod;\r
1382         (void)channel;\r
1383 \r
1384         dat = UniGetByte();\r
1385         if (!tick) {\r
1386                 if (dat&0x0f) a->vibdepth=dat&0xf;\r
1387                 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;\r
1388         } else\r
1389                 if (a->main.period) {\r
1390                         q=(a->vibpos>>2)&0x1f;\r
1391 \r
1392                         switch (a->wavecontrol&3) {\r
1393                         case 0: /* sine */\r
1394                                 temp=VibratoTable[q];\r
1395                                 break;\r
1396                         case 1: /* ramp down */\r
1397                                 q<<=3;\r
1398                                 if (a->vibpos<0) q=255-q;\r
1399                                 temp=q;\r
1400                                 break;\r
1401                         case 2: /* square wave */\r
1402                                 temp=255;\r
1403                                 break;\r
1404                         case 3: /* random */\r
1405                                 temp=getrandom(256);\r
1406                                 break;\r
1407                         }\r
1408 \r
1409                         temp*=a->vibdepth;\r
1410                         temp>>=8;\r
1411 \r
1412                         if (a->vibpos>=0)\r
1413                                 a->main.period=a->tmpperiod+temp;\r
1414                         else\r
1415                                 a->main.period=a->tmpperiod-temp;\r
1416                         a->ownper = 1;\r
1417 \r
1418                         a->vibpos+=a->vibspd;\r
1419         }\r
1420 \r
1421         return 0;\r
1422 }\r
1423 \r
1424 /*========== Envelope helpers */\r
1425 \r
1426 static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1427 {\r
1428         (void)tick;\r
1429         (void)flags;\r
1430         (void)mod;\r
1431         (void)channel;\r
1432 \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
1436 \r
1437         return 0;\r
1438 }\r
1439 \r
1440 static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1441 {\r
1442         UBYTE dat;\r
1443         (void)flags;\r
1444         (void)channel;\r
1445 \r
1446         dat=UniGetByte();\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
1451         }\r
1452 \r
1453         return 0;\r
1454 }\r
1455 \r
1456 /*========== Fast Tracker effects */\r
1457 \r
1458 /* DoXMEffect6 after DoXMEffectA */\r
1459 \r
1460 static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1461 {\r
1462         UBYTE inf, lo, hi;\r
1463         (void)flags;\r
1464         (void)mod;\r
1465         (void)channel;\r
1466 \r
1467         inf = UniGetByte();\r
1468         if (inf)\r
1469                 a->s3mvolslide = inf;\r
1470         else\r
1471                 inf = a->s3mvolslide;\r
1472         \r
1473         if (tick) {\r
1474                 lo=inf&0xf;\r
1475                 hi=inf>>4;\r
1476 \r
1477                 if (!hi) {\r
1478                         a->tmpvolume-=lo;\r
1479                         if (a->tmpvolume<0) a->tmpvolume=0;\r
1480                 } else {\r
1481                         a->tmpvolume+=hi;\r
1482                         if (a->tmpvolume>64) a->tmpvolume=64;\r
1483                 }\r
1484         }\r
1485 \r
1486         return 0;\r
1487 }\r
1488 \r
1489 static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1490 {\r
1491         if (a->main.period)\r
1492                 DoVibrato(tick, a);\r
1493 \r
1494         return DoXMEffectA(tick, flags, a, mod, channel);\r
1495 }\r
1496 \r
1497 static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1498 {\r
1499         UBYTE dat;\r
1500         (void)flags;\r
1501         (void)mod;\r
1502         (void)channel;\r
1503 \r
1504         dat=UniGetByte();\r
1505         if (!tick) {\r
1506                 if (dat) a->fportupspd=dat;\r
1507                 if (a->main.period)\r
1508                         a->tmpperiod-=(a->fportupspd<<2);\r
1509         }\r
1510 \r
1511         return 0;\r
1512 }\r
1513 \r
1514 static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1515 {\r
1516         UBYTE dat;\r
1517         (void)flags;\r
1518         (void)mod;\r
1519         (void)channel;\r
1520 \r
1521         dat=UniGetByte();\r
1522         if (!tick) {\r
1523                 if (dat) a->fportdnspd=dat;\r
1524                 if (a->main.period)\r
1525                         a->tmpperiod+=(a->fportdnspd<<2);\r
1526         }\r
1527 \r
1528         return 0;\r
1529 }\r
1530 \r
1531 static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1532 {\r
1533         UBYTE dat;\r
1534         (void)flags;\r
1535         (void)mod;\r
1536         (void)channel;\r
1537 \r
1538         dat=UniGetByte();\r
1539         if (!tick)\r
1540                 if (dat) a->fslideupspd=dat;\r
1541         a->tmpvolume+=a->fslideupspd;\r
1542         if (a->tmpvolume>64) a->tmpvolume=64;\r
1543 \r
1544         return 0;\r
1545 }\r
1546 \r
1547 static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1548 {\r
1549         UBYTE dat;\r
1550         (void)flags;\r
1551         (void)mod;\r
1552         (void)channel;\r
1553 \r
1554         dat=UniGetByte();\r
1555         if (!tick)\r
1556                 if (dat) a->fslidednspd=dat;\r
1557         a->tmpvolume-=a->fslidednspd;\r
1558         if (a->tmpvolume<0) a->tmpvolume=0;\r
1559 \r
1560         return 0;\r
1561 }\r
1562 \r
1563 static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1564 {\r
1565         (void)tick;\r
1566         (void)flags;\r
1567         (void)a;\r
1568         (void)channel;\r
1569 \r
1570         mod->volume=UniGetByte()<<1;\r
1571         if (mod->volume>128) mod->volume=128;\r
1572 \r
1573         return 0;\r
1574 }\r
1575 \r
1576 static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1577 {\r
1578         UBYTE inf;\r
1579         (void)flags;\r
1580         (void)a;\r
1581         (void)channel;\r
1582 \r
1583         inf = UniGetByte();\r
1584 \r
1585         if (tick) {\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
1590 \r
1591                 if (mod->volume<0)\r
1592                         mod->volume=0;\r
1593                 else if (mod->volume>128)\r
1594                         mod->volume=128;\r
1595         }\r
1596 \r
1597         return 0;\r
1598 }\r
1599 \r
1600 static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1601 {\r
1602         UBYTE dat;\r
1603         (void)flags;\r
1604         (void)mod;\r
1605         (void)channel;\r
1606 \r
1607         dat=UniGetByte();\r
1608         if ((!tick)&&(a->main.i)) {\r
1609                 UWORD points;\r
1610                 INSTRUMENT *i=a->main.i;\r
1611                 MP_VOICE *aout;\r
1612 \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
1617                         }\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
1621                         }\r
1622                 }\r
1623         }\r
1624 \r
1625         return 0;\r
1626 }\r
1627 \r
1628 static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1629 {\r
1630         UBYTE inf, lo, hi;\r
1631         SWORD pan;\r
1632         (void)flags;\r
1633         (void)channel;\r
1634 \r
1635         inf = UniGetByte();\r
1636         if (!mod->panflag)\r
1637                 return 0;\r
1638 \r
1639         if (inf)\r
1640                 a->pansspd = inf;\r
1641         else\r
1642                 inf =a->pansspd;\r
1643 \r
1644         if (tick) {\r
1645                 lo=inf&0xf;\r
1646                 hi=inf>>4;\r
1647 \r
1648                 /* slide right has absolute priority */\r
1649                 if (hi)\r
1650                         lo = 0;\r
1651 \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
1654         }\r
1655 \r
1656         return 0;\r
1657 }\r
1658 \r
1659 static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1660 {\r
1661         UBYTE dat;\r
1662         (void)flags;\r
1663         (void)mod;\r
1664         (void)channel;\r
1665 \r
1666         dat = UniGetByte();\r
1667         if (dat)\r
1668                 a->ffportupspd = dat;\r
1669         else\r
1670                 dat = a->ffportupspd;\r
1671 \r
1672         if (a->main.period)\r
1673                 if (!tick) {\r
1674                         a->main.period-=dat;\r
1675                         a->tmpperiod-=dat;\r
1676                         a->ownper = 1;\r
1677                 }\r
1678 \r
1679         return 0;\r
1680 }\r
1681 \r
1682 static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1683 {\r
1684         UBYTE dat;\r
1685         (void)flags;\r
1686         (void)mod;\r
1687         (void)channel;\r
1688 \r
1689         dat = UniGetByte();\r
1690         if (dat)\r
1691                 a->ffportdnspd=dat;\r
1692         else\r
1693                 dat = a->ffportdnspd;\r
1694 \r
1695         if (a->main.period)\r
1696                 if (!tick) {\r
1697                         a->main.period+=dat;\r
1698                         a->tmpperiod+=dat;\r
1699                         a->ownper = 1;\r
1700                 }\r
1701 \r
1702         return 0;\r
1703 }\r
1704 \r
1705 /*========== Impulse Tracker effects */\r
1706 \r
1707 static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat)\r
1708 {\r
1709         if (dat)\r
1710                 a->portspeed = dat;\r
1711 \r
1712         /* if we don't come from another note, ignore the slide and play the note\r
1713            as is */\r
1714         if (!a->oldnote || !a->main.period)\r
1715                         return;\r
1716 \r
1717         if ((!tick)&&(a->newsamp)){\r
1718                 a->main.kick=KICK_NOTE;\r
1719                 a->main.start=-1;\r
1720         } else\r
1721                 a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT;\r
1722 \r
1723         if (tick) {\r
1724                 int dist;\r
1725 \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
1729 \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
1734                 else\r
1735                   if (dist>0) { \r
1736                         a->tmpperiod-=a->portspeed<<2;\r
1737                         a->main.period-=a->portspeed<<2; /* dist>0 slide up */\r
1738                 } else {                                \r
1739                         a->tmpperiod+=a->portspeed<<2;\r
1740                         a->main.period+=a->portspeed<<2; /* dist<0 slide down */\r
1741                 }\r
1742         } else\r
1743                 a->tmpperiod=a->main.period;\r
1744         a->ownper=1;\r
1745 }\r
1746 \r
1747 static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1748 {\r
1749         (void)flags;\r
1750         (void)mod;\r
1751         (void)channel;\r
1752 \r
1753         DoITToneSlide(tick, a, UniGetByte());\r
1754 \r
1755         return 0;\r
1756 }\r
1757 \r
1758 static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)\r
1759 {\r
1760         UBYTE q;\r
1761         UWORD temp=0;\r
1762 \r
1763         if (!tick) {\r
1764                 if (dat&0x0f) a->vibdepth=dat&0xf;\r
1765                 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;\r
1766         }\r
1767         if (!a->main.period)\r
1768                         return;\r
1769 \r
1770         q=(a->vibpos>>2)&0x1f;\r
1771 \r
1772         switch (a->wavecontrol&3) {\r
1773         case 0: /* sine */\r
1774                 temp=VibratoTable[q];\r
1775                 break;\r
1776         case 1: /* square wave */\r
1777                 temp=255;\r
1778                 break;\r
1779         case 2: /* ramp down */\r
1780                 q<<=3;\r
1781                 if (a->vibpos<0) q=255-q;\r
1782                 temp=q;\r
1783                 break;\r
1784         case 3: /* random */\r
1785                 temp=getrandom(256);\r
1786                 break;\r
1787         }\r
1788 \r
1789         temp*=a->vibdepth;\r
1790         temp>>=8;\r
1791         temp<<=2;\r
1792 \r
1793         if (a->vibpos>=0)\r
1794                 a->main.period=a->tmpperiod+temp;\r
1795         else\r
1796                 a->main.period=a->tmpperiod-temp;\r
1797         a->ownper=1;\r
1798 \r
1799         a->vibpos+=a->vibspd;\r
1800 }\r
1801 \r
1802 static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1803 {\r
1804         (void)flags;\r
1805         (void)mod;\r
1806         (void)channel;\r
1807 \r
1808         DoITVibrato(tick, a, UniGetByte());\r
1809 \r
1810         return 0;\r
1811 }\r
1812 \r
1813 static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1814 {\r
1815         UBYTE inf, on, off;\r
1816         (void)tick;\r
1817         (void)flags;\r
1818         (void)mod;\r
1819         (void)channel;\r
1820 \r
1821         inf = UniGetByte();\r
1822         if (inf)\r
1823                 a->s3mtronof = inf;\r
1824         else {\r
1825                 inf = a->s3mtronof;\r
1826                 if (!inf)\r
1827                         return 0;\r
1828         }\r
1829 \r
1830         on=(inf>>4);\r
1831         off=(inf&0xf);\r
1832 \r
1833         a->s3mtremor%=(on+off);\r
1834         a->volume=(a->s3mtremor<on)?a->tmpvolume:0;\r
1835         a->ownvol = 1;\r
1836         a->s3mtremor++;\r
1837 \r
1838         return 0;\r
1839 }\r
1840 \r
1841 static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1842 {\r
1843         (void)tick;\r
1844         (void)flags;\r
1845         (void)mod;\r
1846         (void)channel;\r
1847 \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
1853 \r
1854         return 0;\r
1855 }\r
1856 \r
1857 static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1858 {\r
1859         UBYTE inf, lo, hi;\r
1860         (void)flags;\r
1861         (void)mod;\r
1862         (void)channel;\r
1863 \r
1864         inf = UniGetByte();\r
1865 \r
1866         if (inf)\r
1867                 a->chanvolslide = inf;\r
1868         else\r
1869                 inf = a->chanvolslide;\r
1870 \r
1871         lo=inf&0xf;\r
1872         hi=inf>>4;\r
1873 \r
1874         if (!hi) \r
1875                 a->main.chanvol-=lo;\r
1876         else\r
1877           if (!lo) {\r
1878                 a->main.chanvol+=hi;\r
1879         } else\r
1880           if (hi==0xf) {\r
1881                 if (!tick) a->main.chanvol-=lo;\r
1882         } else\r
1883           if (lo==0xf) {\r
1884                 if (!tick) a->main.chanvol+=hi;\r
1885         }\r
1886 \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
1891 \r
1892         return 0;\r
1893 }\r
1894 \r
1895 static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1896 {\r
1897         UBYTE inf, lo, hi;\r
1898         SWORD pan;\r
1899         (void)flags;\r
1900         (void)channel;\r
1901 \r
1902         inf = UniGetByte();\r
1903         if (inf)\r
1904                 a->pansspd = inf;\r
1905         else\r
1906                 inf = a->pansspd;\r
1907 \r
1908         if (!mod->panflag)\r
1909                 return 0;\r
1910 \r
1911         lo=inf&0xf;\r
1912         hi=inf>>4;\r
1913 \r
1914         pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning;\r
1915 \r
1916         if (!hi)\r
1917                 pan+=lo<<2;\r
1918         else\r
1919           if (!lo) {\r
1920                 pan-=hi<<2;\r
1921         } else\r
1922           if (hi==0xf) {\r
1923                 if (!tick) pan+=lo<<2;\r
1924         } else\r
1925           if (lo==0xf) {\r
1926                 if (!tick) pan-=hi<<2;\r
1927         }\r
1928         a->main.panning=\r
1929           (pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);\r
1930 \r
1931         return 0;\r
1932 }\r
1933 \r
1934 static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1935 {\r
1936         UBYTE tempo;\r
1937         SWORD temp;\r
1938         (void)tick;\r
1939         (void)flags;\r
1940         (void)a;\r
1941         (void)channel;\r
1942 \r
1943         tempo = UniGetByte();\r
1944 \r
1945         if (mod->patdly2)\r
1946                 return 0;\r
1947 \r
1948         temp = mod->bpm;\r
1949         if (tempo & 0x10)\r
1950                 temp += (tempo & 0x0f);\r
1951         else\r
1952                 temp -= tempo;\r
1953 \r
1954         mod->bpm=(temp>255)?255:(temp<1?1:temp);\r
1955 \r
1956         return 0;\r
1957 }\r
1958 \r
1959 static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
1960 {\r
1961         UBYTE dat, q;\r
1962         UWORD temp = 0; /* silence warning */\r
1963         (void)flags;\r
1964         (void)mod;\r
1965         (void)channel;\r
1966 \r
1967         dat = UniGetByte();\r
1968         if (!tick) {\r
1969                 if (dat&0x0f) a->vibdepth=dat&0xf;\r
1970                 if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;\r
1971         }\r
1972         if (a->main.period) {\r
1973                 q=(a->vibpos>>2)&0x1f;\r
1974 \r
1975                 switch (a->wavecontrol&3) {\r
1976                 case 0: /* sine */\r
1977                         temp=VibratoTable[q];\r
1978                         break;\r
1979                 case 1: /* square wave */\r
1980                         temp=255;\r
1981                         break;\r
1982                 case 2: /* ramp down */\r
1983                         q<<=3;\r
1984                         if (a->vibpos<0) q=255-q;\r
1985                         temp=q;\r
1986                         break;\r
1987                 case 3: /* random */\r
1988                         temp=getrandom(256);\r
1989                         break;\r
1990                 }\r
1991 \r
1992                 temp*=a->vibdepth;\r
1993                 temp>>=8;\r
1994 \r
1995                 if (a->vibpos>=0)\r
1996                         a->main.period=a->tmpperiod+temp;\r
1997                 else\r
1998                         a->main.period=a->tmpperiod-temp;\r
1999                 a->ownper = 1;\r
2000 \r
2001                 a->vibpos+=a->vibspd;\r
2002         }\r
2003 \r
2004         return 0;\r
2005 }\r
2006 \r
2007 static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2008 {\r
2009         UBYTE inf, lo, hi;\r
2010         (void)flags;\r
2011         (void)a;\r
2012         (void)channel;\r
2013 \r
2014         inf = UniGetByte();\r
2015 \r
2016         if (inf)\r
2017                 mod->globalslide = inf;\r
2018         else\r
2019                 inf = mod->globalslide;\r
2020 \r
2021         lo=inf&0xf;\r
2022         hi=inf>>4;\r
2023 \r
2024         if (!lo) {\r
2025                 if (tick) mod->volume+=hi;\r
2026         } else\r
2027           if (!hi) {\r
2028                 if (tick) mod->volume-=lo;\r
2029         } else\r
2030           if (lo==0xf) {\r
2031                 if (!tick) mod->volume+=hi;\r
2032         } else\r
2033           if (hi==0xf) {\r
2034                 if (!tick) mod->volume-=lo;\r
2035         }\r
2036 \r
2037         if (mod->volume<0)\r
2038                 mod->volume=0;\r
2039         else if (mod->volume>128)\r
2040                 mod->volume=128;\r
2041 \r
2042         return 0;\r
2043 }\r
2044 \r
2045 static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2046 {\r
2047         UBYTE dat, q;\r
2048         SLONG temp = 0; /* silence warning */\r
2049         (void)flags;\r
2050 \r
2051 \r
2052         dat=UniGetByte();\r
2053         if (!tick) {\r
2054                 if (dat&0x0f) a->panbdepth=(dat&0xf);\r
2055                 if (dat&0xf0) a->panbspd=(dat&0xf0)>>4;\r
2056         }\r
2057         if (mod->panflag) {\r
2058                 q=a->panbpos;\r
2059 \r
2060                 switch (a->panbwave) {\r
2061                 case 0: /* sine */\r
2062                         temp=PanbrelloTable[q];\r
2063                         break;\r
2064                 case 1: /* square wave */\r
2065                         temp=(q<0x80)?64:0;\r
2066                         break;\r
2067                 case 2: /* ramp down */\r
2068                         q<<=3;\r
2069                         temp=q;\r
2070                         break;\r
2071                 case 3: /* random */\r
2072                         temp=getrandom(256);\r
2073                         break;\r
2074                 }\r
2075 \r
2076                 temp*=a->panbdepth;\r
2077                 temp=(temp/8)+mod->panning[channel];\r
2078 \r
2079                 a->main.panning=\r
2080                         (temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);\r
2081                 a->panbpos+=a->panbspd;\r
2082 \r
2083         }\r
2084 \r
2085         return 0;\r
2086 }\r
2087 \r
2088 static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE);\r
2089 \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
2093 {\r
2094         UBYTE dat, inf, c;\r
2095 \r
2096         dat = UniGetByte();\r
2097         inf=dat&0xf;\r
2098         c=dat>>4;\r
2099 \r
2100         if (!dat) {\r
2101                 c=a->sseffect;\r
2102                 inf=a->ssdata;\r
2103         } else {\r
2104                 a->sseffect=c;\r
2105                 a->ssdata=inf;\r
2106         }\r
2107 \r
2108         switch (c) {\r
2109         case SS_GLISSANDO: /* S1x set glissando voice */\r
2110                 DoEEffects(tick, flags, a, mod, channel, 0x30|inf);\r
2111                 break;              \r
2112         case SS_FINETUNE: /* S2x set finetune */\r
2113                 DoEEffects(tick, flags, a, mod, channel, 0x50|inf);\r
2114                 break;\r
2115         case SS_VIBWAVE: /* S3x set vibrato waveform */\r
2116                 DoEEffects(tick, flags, a, mod, channel, 0x40|inf);\r
2117                 break;   \r
2118         case SS_TREMWAVE: /* S4x set tremolo waveform */\r
2119                 DoEEffects(tick, flags, a, mod, channel, 0x70|inf);\r
2120                 break;\r
2121         case SS_PANWAVE: /* S5x panbrello */\r
2122                 a->panbwave=inf;\r
2123                 break;\r
2124         case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */\r
2125                 DoEEffects(tick, flags, a, mod, channel, 0xe0|inf);\r
2126                 break;\r
2127         case SS_S7EFFECTS: /* S7x instrument / NNA commands */\r
2128                 DoNNAEffects(mod, a, inf);\r
2129                 break;\r
2130         case SS_PANNING: /* S8x set panning position */\r
2131                 DoEEffects(tick, flags, a, mod, channel, 0x80 | inf);\r
2132                 break;\r
2133         case SS_SURROUND: /* S9x set surround sound */\r
2134                 if (mod->panflag)\r
2135                         a->main.panning = mod->panning[channel] = PAN_SURROUND;\r
2136                 break;    \r
2137         case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */\r
2138                 if (!tick) {\r
2139                         a->hioffset=inf<<16;\r
2140                         a->main.start=a->hioffset|a->soffset;\r
2141 \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
2145                 }\r
2146                 break;\r
2147         case SS_PATLOOP: /* SBx pattern loop */\r
2148                 DoEEffects(tick, flags, a, mod, channel, 0x60|inf);\r
2149                 break;\r
2150         case SS_NOTECUT: /* SCx notecut */\r
2151                 if (!inf) inf = 1;\r
2152                 DoEEffects(tick, flags, a, mod, channel, 0xC0|inf);\r
2153                 break;\r
2154         case SS_NOTEDELAY: /* SDx notedelay */\r
2155                 DoEEffects(tick, flags, a, mod, channel, 0xD0|inf);\r
2156                 break;\r
2157         case SS_PATDELAY: /* SEx patterndelay */\r
2158                 DoEEffects(tick, flags, a, mod, channel, 0xE0|inf);\r
2159                 break;\r
2160         }\r
2161 \r
2162         return 0;\r
2163 }\r
2164 \r
2165 /*========== Impulse Tracker Volume/Pan Column effects */\r
2166 \r
2167 /*\r
2168  * All volume/pan column effects share the same memory space.\r
2169  */\r
2170 \r
2171 static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2172 {\r
2173         UBYTE c, inf;\r
2174         (void)channel;\r
2175         \r
2176         c = UniGetByte(); \r
2177         inf = UniGetByte(); \r
2178 \r
2179         if ((!c)&&(!inf)) {\r
2180                 c=a->voleffect;\r
2181                 inf=a->voldata;\r
2182         } else {\r
2183                 a->voleffect=c;\r
2184                 a->voldata=inf;\r
2185         }\r
2186 \r
2187         if (c)\r
2188                 switch (c) {\r
2189                 case VOL_VOLUME:\r
2190                         if (tick) break;\r
2191                         if (inf>64) inf=64;\r
2192                         a->tmpvolume=inf;\r
2193                         break;\r
2194                 case VOL_PANNING:\r
2195                         if (mod->panflag)\r
2196                                 a->main.panning=inf;\r
2197                         break;\r
2198                 case VOL_VOLSLIDE:\r
2199                         DoS3MVolSlide(tick, flags, a, inf);\r
2200                         return 1;\r
2201                 case VOL_PITCHSLIDEDN:\r
2202                         if (a->main.period)\r
2203                                 DoS3MSlideDn(tick, a, inf);\r
2204                         break;\r
2205                 case VOL_PITCHSLIDEUP:\r
2206                         if (a->main.period)\r
2207                                 DoS3MSlideUp(tick, a, inf);\r
2208                         break;\r
2209                 case VOL_PORTAMENTO:\r
2210                         DoITToneSlide(tick, a, inf);\r
2211                         break;\r
2212                 case VOL_VIBRATO:\r
2213                         DoITVibrato(tick, a, inf);\r
2214                         break;\r
2215         }\r
2216 \r
2217         return 0;\r
2218 }\r
2219 \r
2220 /*========== UltraTracker effects */\r
2221 \r
2222 static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2223 {\r
2224         UWORD offset=UniGetWord();\r
2225         (void)tick;\r
2226         (void)flags;\r
2227         (void)mod;\r
2228         (void)channel;\r
2229 \r
2230         if (offset)\r
2231                 a->ultoffset=offset;\r
2232 \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
2237 \r
2238         return 0;\r
2239 }\r
2240 \r
2241 /*========== OctaMED effects */\r
2242 \r
2243 static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2244 {\r
2245         UWORD speed=UniGetWord();\r
2246         (void)tick;\r
2247         (void)flags;\r
2248         (void)a;\r
2249         (void)channel;\r
2250 \r
2251         mod->bpm=speed;\r
2252 \r
2253         return 0;\r
2254 }\r
2255 \r
2256 static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2257 {\r
2258         DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2));\r
2259 \r
2260         return 0;\r
2261 }\r
2262 \r
2263 static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2264 {\r
2265         DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2));\r
2266 \r
2267         return 0;\r
2268 }\r
2269 \r
2270 static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2271 {\r
2272         DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3));\r
2273 \r
2274         return 0;\r
2275 }\r
2276 \r
2277 /*========== Oktalyzer effects */\r
2278 \r
2279 static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2280 {\r
2281         UBYTE dat, dat2;\r
2282         (void)mod;\r
2283         (void)channel;\r
2284 \r
2285         dat2 = UniGetByte();    /* arpeggio style */\r
2286         dat = UniGetByte();\r
2287         if (!tick) {\r
2288                 if (!dat && (flags & UF_ARPMEM))\r
2289                         dat=a->arpmem;\r
2290                 else\r
2291                         a->arpmem=dat;\r
2292         }\r
2293         if (a->main.period)\r
2294                 DoArpeggio(tick, flags, a, dat2);\r
2295 \r
2296         return 0;\r
2297 }\r
2298 \r
2299 /*========== General player functions */\r
2300 \r
2301 static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)\r
2302 {\r
2303         UniSkipOpcode();\r
2304         (void)tick;\r
2305         (void)flags;\r
2306         (void)a;\r
2307         (void)mod;\r
2308         (void)channel;\r
2309 \r
2310         return 0;\r
2311 }\r
2312 \r
2313 typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD);\r
2314 \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
2378 };\r
2379 \r
2380 static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a)\r
2381 {\r
2382         UWORD tick = mod->vbtick;\r
2383         UWORD flags = mod->flags;\r
2384         UBYTE c;\r
2385         int explicitslides = 0;\r
2386         effect_func f;\r
2387 \r
2388         while((c=UniGetByte())) {\r
2389                 f = effects[c];\r
2390                 if (f != DoNothing)\r
2391                                 a->sliding = 0;\r
2392                 explicitslides |= f(tick, flags, a, mod, channel);\r
2393         }\r
2394         return explicitslides;\r
2395 }\r
2396 \r
2397 static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat)\r
2398 {\r
2399         int t;\r
2400         MP_VOICE *aout;\r
2401 \r
2402         dat&=0xf; \r
2403         aout=(a->slave)?a->slave:NULL;\r
2404 \r
2405         switch (dat) {\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
2410                 break;\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
2418                         }\r
2419                 break;\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
2424                 break;\r
2425         case 0x3: /* set NNA note cut */\r
2426                 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT;\r
2427                 break;\r
2428         case 0x4: /* set NNA note continue */\r
2429                 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE;\r
2430                 break;\r
2431         case 0x5: /* set NNA note off */\r
2432                 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF;\r
2433                 break;   \r
2434         case 0x6: /* set NNA note fade */\r
2435                 a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE;\r
2436                 break;\r
2437         case 0x7: /* disable volume envelope */\r
2438                 if (aout)\r
2439                         aout->main.volflg&=~EF_ON;\r
2440                 break;\r
2441         case 0x8: /* enable volume envelope  */\r
2442                 if (aout)\r
2443                         aout->main.volflg|=EF_ON;\r
2444                 break;\r
2445         case 0x9: /* disable panning envelope */\r
2446                 if (aout)\r
2447                         aout->main.panflg&=~EF_ON;\r
2448                 break;    \r
2449         case 0xa: /* enable panning envelope */\r
2450                 if (aout)\r
2451                         aout->main.panflg|=EF_ON;\r
2452                 break;\r
2453         case 0xb: /* disable pitch envelope */\r
2454                 if (aout)\r
2455                         aout->main.pitflg&=~EF_ON;\r
2456                 break;\r
2457         case 0xc: /* enable pitch envelope */\r
2458                 if (aout)\r
2459                         aout->main.pitflg|=EF_ON;\r
2460                 break;\r
2461         }\r
2462 }\r
2463 \r
2464 void pt_UpdateVoices(MODULE *mod, int max_volume)\r
2465 {\r
2466         SWORD envpan,envvol,envpit,channel;\r
2467         UWORD playperiod;\r
2468         SLONG vibval,vibdpt;\r
2469         ULONG tmpvol;\r
2470 \r
2471         MP_VOICE *aout;\r
2472         INSTRUMENT *i;\r
2473         SAMPLE *s;\r
2474 \r
2475         mod->totalchn=mod->realchn=0;\r
2476         for (channel=0;channel<md_sngchn;channel++) {\r
2477                 aout=&mod->voice[channel];\r
2478                 i=aout->main.i;\r
2479                 s=aout->main.s;\r
2480 \r
2481                 if (!s || !s->length) continue;\r
2482 \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
2487 \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
2492                         aout->aswppos=0;\r
2493                 }\r
2494 \r
2495                 envvol = 256;\r
2496                 envpan = PAN_CENTER;\r
2497                 envpit = 32;\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
2511 \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
2515                 } else {\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
2522                 }\r
2523                 if (aout->main.kick == KICK_NOTE) {\r
2524                         aout->main.kick_flag = 1;\r
2525                 }\r
2526                 aout->main.kick=KICK_ABSENT;\r
2527 \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
2536 \r
2537                 /* fade out */\r
2538                 if (mod->sngpos>=mod->numpos)\r
2539                         tmpvol=0;\r
2540                 else\r
2541                         tmpvol=(tmpvol*max_volume)/128;\r
2542 \r
2543                 if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted)\r
2544                         Voice_SetVolume_internal(channel,0);\r
2545                 else {\r
2546                         Voice_SetVolume_internal(channel,tmpvol);\r
2547                         if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))\r
2548                                 mod->realchn++;\r
2549                         mod->totalchn++;\r
2550                 }\r
2551 \r
2552                 if (aout->main.panning==PAN_SURROUND)\r
2553                         Voice_SetPanning_internal(channel,PAN_SURROUND);\r
2554                 else\r
2555                         if ((mod->panflag)&&(aout->penv.flg & EF_ON))\r
2556                                 Voice_SetPanning_internal(channel,\r
2557                                     DoPan(envpan,aout->main.panning));\r
2558                         else\r
2559                                 Voice_SetPanning_internal(channel,aout->main.panning);\r
2560 \r
2561                 if (aout->main.period && s->vibdepth)\r
2562                         switch (s->vibtype) {\r
2563                         case 0:\r
2564                                 vibval=avibtab[s->avibpos&127];\r
2565                                 if (aout->avibpos & 0x80) vibval=-vibval;\r
2566                                 break;\r
2567                         case 1:\r
2568                                 vibval=64;\r
2569                                 if (aout->avibpos & 0x80) vibval=-vibval;\r
2570                                 break;\r
2571                         case 2:\r
2572                                 vibval=63-(((aout->avibpos+128)&255)>>1);\r
2573                                 break;\r
2574                         default:\r
2575                                 vibval=(((aout->avibpos+128)&255)>>1)-64;\r
2576                                 break;\r
2577                         }\r
2578                 else\r
2579                         vibval=0;\r
2580 \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
2585                         } else\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
2591                         }\r
2592                 } else {\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
2597                                         aout->aswppos++;\r
2598                                 } else\r
2599                                         vibdpt=s->vibdepth;\r
2600                         } else {\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
2605                                 else\r
2606                                         vibdpt=0;\r
2607                         }\r
2608                         vibval=(vibval*vibdpt)>>8;\r
2609                         aout->main.period-=vibval;\r
2610                 }\r
2611 \r
2612                 /* update vibrato position */\r
2613                 aout->avibpos=(aout->avibpos+s->vibrate)&0xff;\r
2614 \r
2615                 /* process pitch envelope */\r
2616                 playperiod=aout->main.period;\r
2617 \r
2618                 if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) {\r
2619                         long p1;\r
2620 \r
2621                         envpit-=32;\r
2622                         if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1);\r
2623 \r
2624                         p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit,\r
2625                             aout->master->speed)-aout->masterperiod;\r
2626                         if (p1>0) {\r
2627                                 if ((UWORD)(playperiod+p1)<=playperiod) {\r
2628                                         p1=0;\r
2629                                         aout->main.keyoff|=KEY_OFF;\r
2630                                 }\r
2631                         } else if (p1<0) {\r
2632                                 if ((UWORD)(playperiod+p1)>=playperiod) {\r
2633                                         p1=0;\r
2634                                         aout->main.keyoff|=KEY_OFF;\r
2635                                 }\r
2636                         }\r
2637                         playperiod+=p1;\r
2638                 }\r
2639 \r
2640                 if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */\r
2641                         Voice_Stop_internal(channel);\r
2642                         mod->totalchn--;\r
2643                         if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))\r
2644                                 mod->realchn--;\r
2645                 } else {\r
2646                         Voice_SetFrequency_internal(channel,\r
2647                                                     getfrequency(mod->flags,playperiod));\r
2648 \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
2653                                 else\r
2654                                         aout->main.fadevol=0;\r
2655                         }\r
2656                 }\r
2657 \r
2658                 md_bpm=mod->bpm+mod->relspd;\r
2659                 if (md_bpm<32)\r
2660                         md_bpm=32;\r
2661                 else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255)\r
2662                         md_bpm=255;\r
2663         }\r
2664 }\r
2665 \r
2666 /* Handles new notes or instruments */\r
2667 void pt_Notes(MODULE *mod)\r
2668 {\r
2669         SWORD channel;\r
2670         MP_CONTROL *a;\r
2671         UBYTE c,inst;\r
2672         int tr,funky; /* funky is set to indicate note or instrument change */\r
2673 \r
2674         for (channel=0;channel<mod->numchn;channel++) {\r
2675                 a=&mod->control[channel];\r
2676 \r
2677                 if (mod->sngpos>=mod->numpos) {\r
2678                         tr=mod->numtrk;\r
2679                         mod->numrow=0;\r
2680                 } else {\r
2681                         tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel];\r
2682                         mod->numrow=mod->pattrows[mod->positions[mod->sngpos]];\r
2683                 }\r
2684 \r
2685                 a->row=(tr<mod->numtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL;\r
2686                 a->newsamp=0;\r
2687                 if (!mod->vbtick) a->main.notedelay=0;\r
2688 \r
2689                 if (!a->row) continue;\r
2690                 UniSetRow(a->row);\r
2691                 funky=0;\r
2692 \r
2693                 while((c=UniGetByte()))\r
2694                         switch (c) {\r
2695                         case UNI_NOTE:\r
2696                                 funky|=1;\r
2697                                 a->oldnote=a->anote,a->anote=UniGetByte();\r
2698                                 a->main.kick =KICK_NOTE;\r
2699                                 a->main.start=-1;\r
2700                                 a->sliding=0;\r
2701 \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
2706                                 break;\r
2707                         case UNI_INSTRUMENT:\r
2708                                 inst=UniGetByte();\r
2709                                 if (inst>=mod->numins) break; /* safety valve */\r
2710                                 funky|=2;\r
2711                                 a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL;\r
2712                                 a->retrig=0;\r
2713                                 a->s3mtremor=0;\r
2714                                 a->ultoffset=0;\r
2715                                 a->main.sample=inst;\r
2716                                 break;\r
2717                         default:\r
2718                                 UniSkipOpcode();\r
2719                                 break;\r
2720                         }\r
2721 \r
2722                 if (funky) {\r
2723                         INSTRUMENT *i;\r
2724                         SAMPLE *s;\r
2725 \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
2730                         } else {\r
2731                                 a->main.note=a->anote;\r
2732                                 s=&mod->samples[a->main.sample];\r
2733                         }\r
2734 \r
2735                         if (a->main.s!=s) {\r
2736                                 a->main.s=s;\r
2737                                 a->newsamp=a->main.period;\r
2738                         }\r
2739 \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
2746 \r
2747                         a->main.handle=s->handle;\r
2748                         a->speed=s->speed;\r
2749 \r
2750                         if (i) {\r
2751                                 if ((mod->panflag)&&(i->flags & IF_PITCHPAN)\r
2752                                    &&(a->main.panning!=PAN_SURROUND)){\r
2753                                         a->main.panning+=\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
2759                                 }\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
2764                                 a->dca=i->dca;\r
2765                                 a->dct=i->dct;\r
2766                         } else {\r
2767                                 a->main.pitflg=a->main.volflg=a->main.panflg=0;\r
2768                                 a->main.nna=a->dca=0;\r
2769                                 a->dct=DCT_OFF;\r
2770                         }\r
2771 \r
2772                         if (funky&2) /* instrument change */ {\r
2773                                 /* IT random volume variations: 0:8 bit fixed, and one bit for\r
2774                                    sign. */\r
2775                                 a->volume=a->tmpvolume=s->volume;\r
2776                                 if ((s)&&(i)) {\r
2777                                         if (i->rvolvar) {\r
2778                                                 a->volume=a->tmpvolume=s->volume+\r
2779                                                   ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512)\r
2780                                                   ))/25600);\r
2781                                                 if (a->volume<0)\r
2782                                                         a->volume=a->tmpvolume=0;\r
2783                                                 else if (a->volume>64)\r
2784                                                         a->volume=a->tmpvolume=64;\r
2785                                         }\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
2793                                         }\r
2794                                 }\r
2795                         }\r
2796 \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
2800                 }\r
2801         }\r
2802 }\r
2803 \r
2804 /* Handles effects */\r
2805 void pt_EffectsPass1(MODULE *mod)\r
2806 {\r
2807         SWORD channel;\r
2808         MP_CONTROL *a;\r
2809         MP_VOICE *aout;\r
2810         int explicitslides;\r
2811 \r
2812         for (channel=0;channel<mod->numchn;channel++) {\r
2813                 a=&mod->control[channel];\r
2814 \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
2820                 }\r
2821 \r
2822                 if (!a->row) continue;\r
2823                 UniSetRow(a->row);\r
2824 \r
2825                 a->ownper=a->ownvol=0;\r
2826                 explicitslides = pt_playeffects(mod, channel, a);\r
2827 \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
2834                 }\r
2835 \r
2836                 if (!a->ownper)\r
2837                         a->main.period=a->tmpperiod;\r
2838                 if (!a->ownvol)\r
2839                         a->volume=a->tmpvolume;\r
2840 \r
2841                 if (a->main.s) {\r
2842                         if (a->main.i)\r
2843                                 a->main.outvolume=\r
2844                                     (a->volume*a->main.s->globvol*a->main.i->globvol)>>10;\r
2845                         else\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
2851                 }\r
2852         }\r
2853 }\r
2854 \r
2855 /* NNA management */\r
2856 void pt_NNA(MODULE *mod)\r
2857 {\r
2858         SWORD channel;\r
2859         MP_CONTROL *a;\r
2860 \r
2861         for (channel=0;channel<mod->numchn;channel++) {\r
2862                 a=&mod->control[channel];\r
2863 \r
2864                 if (a->main.kick==KICK_NOTE) {\r
2865                         BOOL kill=0;\r
2866 \r
2867                         if (a->slave) {\r
2868                                 MP_VOICE *aout;\r
2869 \r
2870                                 aout=a->slave;\r
2871                                 if (aout->main.nna & NNA_MASK) {\r
2872                                         /* Make sure the old MP_VOICE channel knows it has no\r
2873                                            master now ! */\r
2874                                         a->slave=NULL;\r
2875                                         /* assume the channel is taken by NNA */\r
2876                                         aout->mflag=0;\r
2877 \r
2878                                         switch (aout->main.nna) {\r
2879                                         case NNA_CONTINUE: /* continue note, do nothing */\r
2880                                                 break;\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
2886                                                 break;\r
2887                                         case NNA_FADE:\r
2888                                                 aout->main.keyoff |= KEY_FADE;\r
2889                                                 break;\r
2890                                         }\r
2891                                 }\r
2892                         }\r
2893 \r
2894                         if (a->dct!=DCT_OFF) {\r
2895                                 int t;\r
2896 \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
2901                                                 kill=0;\r
2902                                                 switch (a->dct) {\r
2903                                                 case DCT_NOTE:\r
2904                                                         if (a->main.note==mod->voice[t].main.note)\r
2905                                                                 kill=1;\r
2906                                                         break;\r
2907                                                 case DCT_SAMPLE:\r
2908                                                         if (a->main.handle==mod->voice[t].main.handle)\r
2909                                                                 kill=1;\r
2910                                                         break;\r
2911                                                 case DCT_INST:\r
2912                                                         kill=1;\r
2913                                                         break;\r
2914                                                 }\r
2915                                                 if (kill)\r
2916                                                         switch (a->dca) {\r
2917                                                         case DCA_CUT:\r
2918                                                                 mod->voice[t].main.fadevol=0;\r
2919                                                                 break;\r
2920                                                         case DCA_OFF:\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
2925                                                                 break;\r
2926                                                         case DCA_FADE:\r
2927                                                                 mod->voice[t].main.keyoff|=KEY_FADE;\r
2928                                                                 break;\r
2929                                                         }\r
2930                                         }\r
2931                         }\r
2932                 } /* if (a->main.kick==KICK_NOTE) */\r
2933         }\r
2934 }\r
2935 \r
2936 /* Setup module and NNA voices */\r
2937 void pt_SetupVoices(MODULE *mod)\r
2938 {\r
2939         SWORD channel;\r
2940         MP_CONTROL *a;\r
2941         MP_VOICE *aout;\r
2942 \r
2943         for (channel=0;channel<mod->numchn;channel++) {\r
2944                 a=&mod->control[channel];\r
2945 \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
2949                            here */\r
2950                         if (mod->flags&UF_NNA) {\r
2951                                 if (!a->slave) {\r
2952                                         int newchn;\r
2953 \r
2954                                         if ((newchn=MP_FindEmptyChannel(mod))!=-1)\r
2955                                                 a->slave=&mod->voice[a->slavechn=newchn];\r
2956                                 }\r
2957                         } else \r
2958                                 a->slave=&mod->voice[a->slavechn=channel];\r
2959 \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
2963                                 aout->master=a;\r
2964                                 a->slave=aout;\r
2965                                 aout->masterchn=channel;\r
2966                                 aout->mflag=1;\r
2967                         }\r
2968                 } else\r
2969                         aout=a->slave;\r
2970 \r
2971                 if (aout)\r
2972                         aout->main=a->main;\r
2973                 a->main.kick=KICK_ABSENT;\r
2974         }\r
2975 }\r
2976 \r
2977 /* second effect pass */\r
2978 void pt_EffectsPass2(MODULE *mod)\r
2979 {\r
2980         SWORD channel;\r
2981         MP_CONTROL *a;\r
2982         UBYTE c;\r
2983 \r
2984         for (channel=0;channel<mod->numchn;channel++) {\r
2985                 a=&mod->control[channel];\r
2986 \r
2987                 if (!a->row) continue;\r
2988                 UniSetRow(a->row);\r
2989 \r
2990                 while((c=UniGetByte()))\r
2991                         if (c==UNI_ITEFFECTS0) {\r
2992                                 c=UniGetByte();\r
2993                                 if ((c>>4)==SS_S7EFFECTS)\r
2994                                         DoNNAEffects(mod, a, c&0xf);\r
2995                         } else\r
2996                                 UniSkipOpcode();\r
2997         }\r
2998 }\r
2999 \r
3000 void Player_HandleTick(void)\r
3001 {\r
3002         SWORD channel;\r
3003         int max_volume;\r
3004 \r
3005 #if 0\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
3008         if (isfirst) {\r
3009                 isfirst--;\r
3010                 return;\r
3011         }\r
3012 #endif\r
3013 \r
3014         if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return;\r
3015 \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
3020 \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
3024                 else\r
3025                         pf->patpos++;\r
3026                 pf->vbtick=0;\r
3027 \r
3028                 /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is\r
3029                    the command memory. */\r
3030                 if (pf->patdly)\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
3037                 }\r
3038 \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
3042                         pf->posjmp=3;\r
3043 \r
3044                 if (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
3050 \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
3061                                         else\r
3062                                                 pf->sngspd=6;\r
3063                                         pf->bpm=pf->inittempo<32?32:pf->inittempo;\r
3064                                 }\r
3065                         }\r
3066                         if (pf->sngpos<0) pf->sngpos=pf->numpos-1;\r
3067                 }\r
3068 \r
3069                 if (!pf->patdly2)\r
3070                         pt_Notes(pf);\r
3071         }\r
3072 \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
3076             (pf->fadeout))\r
3077                 max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0;\r
3078         else\r
3079                 max_volume=128;\r
3080 \r
3081         pt_EffectsPass1(pf);\r
3082         if (pf->flags&UF_NNA)\r
3083                 pt_NNA(pf);\r
3084         pt_SetupVoices(pf);\r
3085         pt_EffectsPass2(pf);\r
3086 \r
3087         /* now set up the actual hardware channel playback information */\r
3088         pt_UpdateVoices(pf, max_volume);\r
3089 }\r
3090 \r
3091 static void Player_Init_internal(MODULE* mod)\r
3092 {\r
3093         int t;\r
3094 \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
3098         }\r
3099         \r
3100         mod->sngtime=0;\r
3101         mod->sngremainder=0;\r
3102 \r
3103         mod->pat_repcrazy=0;\r
3104         mod->sngpos=0;\r
3105         if(mod->initspeed!=0)\r
3106                 mod->sngspd=mod->initspeed<32?mod->initspeed:32;\r
3107         else\r
3108                 mod->sngspd=6;\r
3109         mod->volume=mod->initvolume>128?128:mod->initvolume;\r
3110 \r
3111         mod->vbtick=mod->sngspd;\r
3112         mod->patdly=0;\r
3113         mod->patdly2=0;\r
3114         mod->bpm=mod->inittempo<32?32:mod->inittempo;\r
3115         mod->realchn=0;\r
3116 \r
3117         mod->patpos=0;\r
3118         mod->posjmp=2; /* make sure the player fetches the first note */\r
3119         mod->numrow=-1;\r
3120         mod->patbrk=0;\r
3121 }\r
3122 \r
3123 BOOL Player_Init(MODULE* mod)\r
3124 {\r
3125         mod->extspd=1;\r
3126         mod->panflag=1;\r
3127         mod->wrap=0;\r
3128         mod->loop=1;\r
3129         mod->fadeout=0;\r
3130 \r
3131         mod->relspd=0;\r
3132 \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
3135                 return 1;\r
3136         if (!(mod->voice=(MP_VOICE*)_mm_calloc(md_sngchn,sizeof(MP_VOICE))))\r
3137                 return 1;\r
3138 \r
3139         Player_Init_internal(mod);\r
3140         return 0;\r
3141 }\r
3142 \r
3143 void Player_Exit_internal(MODULE* mod)\r
3144 {\r
3145         if (!mod)\r
3146                 return;\r
3147 \r
3148         /* Stop playback if necessary */\r
3149         if (mod==pf) {\r
3150                 Player_Stop_internal();\r
3151                 pf=NULL;\r
3152         }\r
3153 \r
3154         if (mod->control)\r
3155                 free(mod->control);\r
3156         if (mod->voice)\r
3157                 free(mod->voice);\r
3158         mod->control=NULL;\r
3159         mod->voice=NULL;\r
3160 }\r
3161 \r
3162 void Player_Exit(MODULE* mod)\r
3163 {\r
3164         MUTEX_LOCK(vars);\r
3165         Player_Exit_internal(mod);\r
3166         MUTEX_UNLOCK(vars);\r
3167 }\r
3168 \r
3169 MIKMODAPI void Player_SetVolume(SWORD volume)\r
3170 {\r
3171         MUTEX_LOCK(vars);\r
3172         if (pf)\r
3173                 pf->volume=(volume<0)?0:(volume>128)?128:volume;\r
3174         MUTEX_UNLOCK(vars);\r
3175 }\r
3176 \r
3177 MIKMODAPI MODULE* Player_GetModule(void)\r
3178 {\r
3179         MODULE* result;\r
3180 \r
3181         MUTEX_LOCK(vars);\r
3182         result=pf;\r
3183         MUTEX_UNLOCK(vars);\r
3184 \r
3185         return result;\r
3186 }\r
3187 \r
3188 MIKMODAPI void Player_Start(MODULE *mod)\r
3189 {\r
3190         int t;\r
3191 \r
3192         if (!mod)\r
3193                 return;\r
3194 \r
3195         if (!MikMod_Active())\r
3196                 MikMod_EnableOutput();\r
3197 \r
3198         mod->forbid=0;\r
3199 \r
3200         MUTEX_LOCK(vars);\r
3201         if (pf!=mod) {\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
3205         }\r
3206         pf=mod;\r
3207         MUTEX_UNLOCK(vars);\r
3208 }\r
3209 \r
3210 void Player_Stop_internal(void)\r
3211 {\r
3212         if (!md_sfxchn) MikMod_DisableOutput_internal();\r
3213         if (pf) pf->forbid=1;\r
3214         pf=NULL;\r
3215 }\r
3216 \r
3217 MIKMODAPI void Player_Stop(void)\r
3218 {\r
3219         MUTEX_LOCK(vars);\r
3220                 Player_Stop_internal();\r
3221         MUTEX_UNLOCK(vars);\r
3222 }\r
3223 \r
3224 MIKMODAPI BOOL Player_Active(void)\r
3225 {\r
3226         BOOL result=0;\r
3227 \r
3228         MUTEX_LOCK(vars);\r
3229         if (pf)\r
3230                 result=(!(pf->sngpos>=pf->numpos));\r
3231         MUTEX_UNLOCK(vars);\r
3232 \r
3233         return result;\r
3234 }\r
3235 \r
3236 MIKMODAPI void Player_NextPosition(void)\r
3237 {\r
3238         MUTEX_LOCK(vars);\r
3239         if (pf) {\r
3240                 int t;\r
3241 \r
3242                 pf->forbid=1;\r
3243                 pf->posjmp=3;\r
3244                 pf->patbrk=0;\r
3245                 pf->vbtick=pf->sngspd;\r
3246 \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
3251                 }\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
3255                 }\r
3256                 pf->forbid=0;\r
3257         }\r
3258         MUTEX_UNLOCK(vars);\r
3259 }\r
3260 \r
3261 MIKMODAPI void Player_PrevPosition(void)\r
3262 {\r
3263         MUTEX_LOCK(vars);\r
3264         if (pf) {\r
3265                 int t;\r
3266 \r
3267                 pf->forbid=1;\r
3268                 pf->posjmp=1;\r
3269                 pf->patbrk=0;\r
3270                 pf->vbtick=pf->sngspd;\r
3271 \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
3276                 }\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
3280                 }\r
3281                 pf->forbid=0;\r
3282         }\r
3283         MUTEX_UNLOCK(vars);\r
3284 }\r
3285 \r
3286 MIKMODAPI void Player_SetPosition(UWORD pos)\r
3287 {\r
3288         MUTEX_LOCK(vars);\r
3289         if (pf) {\r
3290                 int t;\r
3291 \r
3292                 pf->forbid=1;\r
3293                 if (pos>=pf->numpos) pos=pf->numpos;\r
3294                 pf->posjmp=2;\r
3295                 pf->patbrk=0;\r
3296                 pf->sngpos=pos;\r
3297                 pf->vbtick=pf->sngspd;\r
3298 \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
3303                 }\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
3307                 }\r
3308                 pf->forbid=0;\r
3309                 \r
3310                 if (!pos)\r
3311                         Player_Init_internal(pf);\r
3312         }\r
3313         MUTEX_UNLOCK(vars);\r
3314 }    \r
3315 \r
3316 static void Player_Unmute_internal(SLONG arg1,va_list ap)\r
3317 {\r
3318         SLONG t,arg2,arg3=0;\r
3319 \r
3320         if (pf) {\r
3321                 switch (arg1) {\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
3325                                 return;\r
3326                         for (;arg2<pf->numchn && arg2<=arg3;arg2++)\r
3327                                 pf->control[arg2].muted=0;\r
3328                         break;\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
3332                                 return;\r
3333                         for (t=0;t<pf->numchn;t++) {\r
3334                                 if ((t>=arg2) && (t<=arg3))\r
3335                                         continue;\r
3336                                 pf->control[t].muted=0;\r
3337                         }\r
3338                         break;\r
3339                 default:\r
3340                         if (arg1<pf->numchn) pf->control[arg1].muted=0;\r
3341                         break;\r
3342                 }\r
3343         }\r
3344 }\r
3345 \r
3346 MIKMODAPI void Player_Unmute(SLONG arg1, ...)\r
3347 {\r
3348         va_list args;\r
3349 \r
3350         va_start(args,arg1);\r
3351         MUTEX_LOCK(vars);\r
3352         Player_Unmute_internal(arg1,args);\r
3353         MUTEX_UNLOCK(vars);\r
3354         va_end(args);\r
3355 }\r
3356 \r
3357 static void Player_Mute_internal(SLONG arg1,va_list ap)\r
3358 {\r
3359         SLONG t,arg2,arg3=0;\r
3360 \r
3361         if (pf) {\r
3362                 switch (arg1) {\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
3366                                 return;\r
3367                         for (;arg2<pf->numchn && arg2<=arg3;arg2++)\r
3368                                 pf->control[arg2].muted=1;\r
3369                         break;\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
3373                                 return;\r
3374                         for (t=0;t<pf->numchn;t++) {\r
3375                                 if ((t>=arg2) && (t<=arg3))\r
3376                                         continue;\r
3377                                 pf->control[t].muted=1;\r
3378                         }\r
3379                         break;\r
3380                 default:\r
3381                         if (arg1<pf->numchn)\r
3382                                 pf->control[arg1].muted=1;\r
3383                         break;\r
3384                 }\r
3385         }\r
3386 }\r
3387 \r
3388 MIKMODAPI void Player_Mute(SLONG arg1,...)\r
3389 {\r
3390         va_list args;\r
3391 \r
3392         va_start(args,arg1);\r
3393         MUTEX_LOCK(vars);\r
3394         Player_Mute_internal(arg1,args);\r
3395         MUTEX_UNLOCK(vars);\r
3396         va_end(args);\r
3397 }\r
3398 \r
3399 static void Player_ToggleMute_internal(SLONG arg1,va_list ap)\r
3400 {\r
3401         SLONG arg2,arg3=0;\r
3402         ULONG t;\r
3403 \r
3404         if (pf) {\r
3405                 switch (arg1) {\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
3409                                 return;\r
3410                         for (;arg2<pf->numchn && arg2<=arg3;arg2++)\r
3411                                 pf->control[arg2].muted=1-pf->control[arg2].muted;\r
3412                         break;\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
3416                                 return;\r
3417                         for (t=0;t<pf->numchn;t++) {\r
3418                                 if ((t>=arg2) && (t<=arg3))\r
3419                                         continue;\r
3420                                 pf->control[t].muted=1-pf->control[t].muted;\r
3421                         }\r
3422                         break;\r
3423                 default:\r
3424                         if (arg1<pf->numchn) \r
3425                                 pf->control[arg1].muted=1-pf->control[arg1].muted;\r
3426                         break;\r
3427                 }\r
3428         }\r
3429 }\r
3430 \r
3431 MIKMODAPI void Player_ToggleMute(SLONG arg1,...)\r
3432 {\r
3433         va_list args;\r
3434 \r
3435         va_start(args,arg1);\r
3436         MUTEX_LOCK(vars);\r
3437         Player_ToggleMute_internal(arg1,args);\r
3438         MUTEX_UNLOCK(vars);\r
3439         va_end(args);\r
3440 }\r
3441 \r
3442 MIKMODAPI BOOL Player_Muted(UBYTE chan)\r
3443 {\r
3444         BOOL result=1;\r
3445 \r
3446         MUTEX_LOCK(vars);\r
3447         if (pf)\r
3448                 result=(chan<pf->numchn)?pf->control[chan].muted:1;\r
3449         MUTEX_UNLOCK(vars);\r
3450 \r
3451         return result;\r
3452 }\r
3453 \r
3454 MIKMODAPI int Player_GetChannelVoice(UBYTE chan)\r
3455 {\r
3456         int result=0;\r
3457 \r
3458         MUTEX_LOCK(vars);\r
3459         if (pf)\r
3460                 result=(chan<pf->numchn)?pf->control[chan].slavechn:-1;\r
3461         MUTEX_UNLOCK(vars);\r
3462 \r
3463         return result;\r
3464 }\r
3465 \r
3466 MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan)\r
3467 {\r
3468         UWORD result=0;\r
3469 \r
3470         MUTEX_LOCK(vars);\r
3471     if (pf)\r
3472             result=(chan<pf->numchn)?pf->control[chan].main.period:0;\r
3473         MUTEX_UNLOCK(vars);\r
3474 \r
3475         return result;\r
3476 }\r
3477 \r
3478 BOOL Player_Paused_internal(void)\r
3479 {\r
3480         return pf?pf->forbid:1;\r
3481 }\r
3482 \r
3483 MIKMODAPI BOOL Player_Paused(void)\r
3484 {\r
3485         BOOL result;\r
3486 \r
3487         MUTEX_LOCK(vars);\r
3488         result=Player_Paused_internal();\r
3489         MUTEX_UNLOCK(vars);\r
3490 \r
3491         return result;\r
3492 }\r
3493 \r
3494 MIKMODAPI void Player_TogglePause(void)\r
3495 {\r
3496         MUTEX_LOCK(vars);\r
3497         if (pf)\r
3498                 pf->forbid=1-pf->forbid;\r
3499         MUTEX_UNLOCK(vars);\r
3500 }\r
3501 \r
3502 MIKMODAPI void Player_SetSpeed(UWORD speed)\r
3503 {\r
3504         MUTEX_LOCK(vars);\r
3505         if (pf) \r
3506                 pf->sngspd=speed?(speed<32?speed:32):1;\r
3507         MUTEX_UNLOCK(vars);\r
3508 }\r
3509 \r
3510 MIKMODAPI void Player_SetTempo(UWORD tempo)\r
3511 {\r
3512         if (tempo<32) tempo=32;\r
3513         MUTEX_LOCK(vars);\r
3514         if (pf) {\r
3515                 if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255;\r
3516                 pf->bpm=tempo;\r
3517         }\r
3518         MUTEX_UNLOCK(vars);\r
3519 }\r
3520 \r
3521 MIKMODAPI int Player_QueryVoices(UWORD numvoices, VOICEINFO *vinfo)\r
3522 {\r
3523         int i;\r
3524 \r
3525         if (numvoices > md_sngchn)\r
3526                 numvoices = md_sngchn;\r
3527 \r
3528         MUTEX_LOCK(vars);\r
3529         if (pf)\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
3538                 }\r
3539         MUTEX_UNLOCK(vars);\r
3540 \r
3541         return numvoices;\r
3542 }\r
3543 \r
3544 \r
3545 \r
3546 /* ex:set ts=4: */\r