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