daily pastebin goal
8%
SHARE
TWEET

pt2play v1.48

8bitbubsy Sep 29th, 2013 (edited) 922 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. ** PT2PLAY v1.48 - 27th of October 2017 - http://16-bits.org
  3. ** =========================================================
  4. **             - NOT BIG ENDIAN SAFE! -
  5. **
  6. ** Very accurate C port of ProTracker 2.3D's replayer, by
  7. ** Olav "8bitbubsy" Sørensen. Based on a ProTracker 2.3D disassembly
  8. ** using the PT1.2A source code for labels (names).
  9. ** It is NOT recommended to use pt2play with non-ProTracker .MODs.
  10. ** Also, do NOT use a lower audio rate than 32kHz! This will cause problems
  11. ** with low sample periods (high rates) and BLEP.
  12. **
  13. ** The BLEP (Band-Limited Step) and filter routines were coded by aciddose.
  14. ** This makes the replayer sound much similar to a real Amiga.
  15. **
  16. ** You need to link winmm.lib for this to compile (-lwinmm).
  17. ** Alternatively, you can wrap around your own audio callback for using
  18. ** pt2play on other platforms.
  19. **
  20. ** pt2play.h:
  21. **
  22. ** #include <stdint.h>
  23. **
  24. ** enum
  25. ** {
  26. **     CIA_TEMPO_MODE    = 0,
  27. **     VBLANK_TEMPO_MODE = 1
  28. ** };
  29. **
  30. ** int8_t pt2play_Init(uint32_t outputFreq);
  31. ** void pt2play_Close(void);
  32. ** void pt2play_PauseSong(int8_t pause);
  33. ** void pt2play_PlaySong(uint8_t *moduleData, int8_t tempoMode);
  34. ** void pt2play_SetStereoSep(uint8_t percentage);
  35. */
  36.  
  37. /* == USER ADJUSTABLE SETTINGS == */
  38. #define STEREO_SEP (15)    /* --> Stereo separation in percent - 0 = mono, 100 = hard pan (like Amiga) */
  39. #define NORM_FACTOR (4.0f) /* --> Slightly increase this value if the song is too loud. Decrease if too quiet... */
  40. #define USE_HIGHPASS       /* --> 5.2Hz high-pass filter present in all Amigas - comment out for a speed-up */
  41. //#define USE_LOWPASS      /* --> 4.4kHz low-pass filter in all Amigas except A1200 - comment out for sharper sound */
  42. #define USE_BLEP           /* --> Reduces some unwanted aliasing (closer to real Amiga) - comment out to disable */
  43. //#define ENABLE_E8_EFFECT /* --> Enable E8x (Karplus-Strong) - comment out this line if E8x is used for something else */
  44. #define LED_FILTER         /* --> Process the "LED" filter - comment out to disable */
  45.  
  46. #ifdef _MSC_VER
  47. #define inline __forceinline
  48. #endif
  49.  
  50. /* used for faster windows.h parsing when compiling */
  51. #ifndef WIN32_LEAN_AND_MEAN
  52. #define WIN32_LEAN_AND_MEAN
  53. #endif
  54.  
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <stdint.h>
  58. #include <math.h>     /* tanf() */
  59. #include <windows.h>  /* win32 audio mixer */
  60. #include <mmsystem.h> /* win32 audio mixer */
  61.  
  62. enum
  63. {
  64.     CIA_TEMPO_MODE    = 0,
  65.     VBLANK_TEMPO_MODE = 1
  66. };
  67.  
  68. #define PAULA_PAL_CLK 3546895
  69. #define CIA_PAL_CLK   709379
  70.  
  71. /* don't change these unless you know what you're doing... */
  72. #define MIX_BUF_NUM 7
  73. #define MIX_BUF_LEN 2048
  74. #define MAX_SAMPLE_LEN 131070
  75.  
  76. #ifdef USE_BLEP /* do not change these! */
  77. #define BLEP_ZC 8
  78. #define BLEP_OS 5
  79. #define BLEP_SP 5
  80. #define BLEP_NS (BLEP_ZC * BLEP_OS / BLEP_SP)
  81. #define BLEP_RNS 7
  82. #endif
  83.  
  84. /* STRUCTS */
  85.  
  86. #ifdef USE_BLEP
  87. typedef struct blep_t
  88. {
  89.     int32_t index, samplesLeft;
  90.     float buffer[BLEP_RNS + 1], lastValue;
  91. } blep_t;
  92. #endif
  93.  
  94. typedef struct ptChannel_t
  95. {
  96.     int8_t *n_start, *n_wavestart, *n_loopstart, n_index, n_volume;
  97.     int8_t n_toneportdirec, n_vibratopos, n_tremolopos, n_pattpos, n_loopcount;
  98.     uint8_t n_wavecontrol, n_glissfunk, n_sampleoffset, n_toneportspeed;
  99.     uint8_t n_vibratocmd, n_tremolocmd, n_finetune, n_funkoffset;
  100.     int16_t n_period, n_note, n_wantedperiod;
  101.     uint16_t n_cmd, n_length, n_replen;
  102. } ptChannel_t;
  103.  
  104. typedef struct paulaVoice_t
  105. {
  106.     volatile uint8_t DMA_ON;
  107.     const int8_t *SRC_DAT, *DMA_DAT;
  108.     int32_t SRC_LEN, DMA_LEN, DMA_POS;
  109.     float SRC_VOL, DELTA, FRAC, LASTDELTA, LASTFRAC, PANL, PANR;
  110. } paulaVoice_t;
  111.  
  112. #if defined(USE_HIGHPASS) || defined(USE_LOWPASS)
  113. typedef struct lossyIntegrator_t
  114. {
  115.     float buffer[2], coeff[2];
  116. } lossyIntegrator_t;
  117. #endif
  118.  
  119. #ifdef LED_FILTER
  120. typedef struct ledFilter_t
  121. {
  122.     float led[4];
  123. } ledFilter_t;
  124.  
  125. typedef struct ledFilterCoeff_t
  126. {
  127.     float led, ledFb;
  128. } ledFilterCoeff_t;
  129. #endif
  130.  
  131. /* STATIC DATA */
  132. static int8_t *SampleStarts[31], *sampleData = NULL, TempoMode, SongPosition, PBreakFlag;
  133. static int8_t DisableMixer, PBreakPosition, PattDelTime, PattDelTime2, PosJumpAssert;
  134. static uint8_t *SongDataPtr, LowMask, Counter, CurrSpeed, SetBPMFlag, SongPlaying;
  135. static uint16_t PatternPos;
  136. static int32_t soundBufferSize, emptySampleDataOffset;
  137. static uint32_t PattPosOff;
  138. static float f_outputFreq;
  139. static WAVEHDR waveBlocks[MIX_BUF_NUM];
  140. static ptChannel_t ChanTemp[4];
  141. static paulaVoice_t AUD[4];
  142. static HWAVEOUT hWaveOut;
  143. static WAVEFORMATEX wfx;
  144. #ifdef USE_BLEP
  145. static blep_t blep[4], blepVol[4];
  146. #endif
  147. #ifdef USE_HIGHPASS
  148. static lossyIntegrator_t filterHi;
  149. #endif
  150. #ifdef USE_LOWPASS
  151. static lossyIntegrator_t filterLo;
  152. #endif
  153. #ifdef LED_FILTER
  154. static ledFilterCoeff_t filterLEDC;
  155. static ledFilter_t filterLED;
  156. static uint8_t LEDStatus;
  157. #endif
  158. static float *masterBufferL = NULL, *masterBufferR = NULL;
  159. static int8_t *mixerBuffer = NULL;
  160. static int32_t samplesLeft;
  161. static uint32_t samplesPerFrame;
  162. static volatile int8_t mixingMutex, isMixing;
  163.  
  164. /* TABLES */
  165. static const uint8_t FunkTable[16] =
  166. {
  167.     0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D,
  168.     0x10, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80
  169. };
  170.  
  171. static const uint8_t VibratoTable[32] =
  172. {
  173.     0x00, 0x18, 0x31, 0x4A, 0x61, 0x78, 0x8D, 0xA1,
  174.     0xB4, 0xC5, 0xD4, 0xE0, 0xEB, 0xF4, 0xFA, 0xFD,
  175.     0xFF, 0xFD, 0xFA, 0xF4, 0xEB, 0xE0, 0xD4, 0xC5,
  176.     0xB4, 0xA1, 0x8D, 0x78, 0x61, 0x4A, 0x31, 0x18
  177. };
  178.  
  179. static const int16_t PeriodTable[606] =
  180. {
  181.     856,808,762,720,678,640,604,570,538,508,480,453,
  182.     428,404,381,360,339,320,302,285,269,254,240,226,
  183.     214,202,190,180,170,160,151,143,135,127,120,113,0,
  184.     850,802,757,715,674,637,601,567,535,505,477,450,
  185.     425,401,379,357,337,318,300,284,268,253,239,225,
  186.     213,201,189,179,169,159,150,142,134,126,119,113,0,
  187.     844,796,752,709,670,632,597,563,532,502,474,447,
  188.     422,398,376,355,335,316,298,282,266,251,237,224,
  189.     211,199,188,177,167,158,149,141,133,125,118,112,0,
  190.     838,791,746,704,665,628,592,559,528,498,470,444,
  191.     419,395,373,352,332,314,296,280,264,249,235,222,
  192.     209,198,187,176,166,157,148,140,132,125,118,111,0,
  193.     832,785,741,699,660,623,588,555,524,495,467,441,
  194.     416,392,370,350,330,312,294,278,262,247,233,220,
  195.     208,196,185,175,165,156,147,139,131,124,117,110,0,
  196.     826,779,736,694,655,619,584,551,520,491,463,437,
  197.     413,390,368,347,328,309,292,276,260,245,232,219,
  198.     206,195,184,174,164,155,146,138,130,123,116,109,0,
  199.     820,774,730,689,651,614,580,547,516,487,460,434,
  200.     410,387,365,345,325,307,290,274,258,244,230,217,
  201.     205,193,183,172,163,154,145,137,129,122,115,109,0,
  202.     814,768,725,684,646,610,575,543,513,484,457,431,
  203.     407,384,363,342,323,305,288,272,256,242,228,216,
  204.     204,192,181,171,161,152,144,136,128,121,114,108,0,
  205.     907,856,808,762,720,678,640,604,570,538,508,480,
  206.     453,428,404,381,360,339,320,302,285,269,254,240,
  207.     226,214,202,190,180,170,160,151,143,135,127,120,0,
  208.     900,850,802,757,715,675,636,601,567,535,505,477,
  209.     450,425,401,379,357,337,318,300,284,268,253,238,
  210.     225,212,200,189,179,169,159,150,142,134,126,119,0,
  211.     894,844,796,752,709,670,632,597,563,532,502,474,
  212.     447,422,398,376,355,335,316,298,282,266,251,237,
  213.     223,211,199,188,177,167,158,149,141,133,125,118,0,
  214.     887,838,791,746,704,665,628,592,559,528,498,470,
  215.     444,419,395,373,352,332,314,296,280,264,249,235,
  216.     222,209,198,187,176,166,157,148,140,132,125,118,0,
  217.     881,832,785,741,699,660,623,588,555,524,494,467,
  218.     441,416,392,370,350,330,312,294,278,262,247,233,
  219.     220,208,196,185,175,165,156,147,139,131,123,117,0,
  220.     875,826,779,736,694,655,619,584,551,520,491,463,
  221.     437,413,390,368,347,328,309,292,276,260,245,232,
  222.     219,206,195,184,174,164,155,146,138,130,123,116,0,
  223.     868,820,774,730,689,651,614,580,547,516,487,460,
  224.     434,410,387,365,345,325,307,290,274,258,244,230,
  225.     217,205,193,183,172,163,154,145,137,129,122,115,0,
  226.     862,814,768,725,684,646,610,575,543,513,484,457,
  227.     431,407,384,363,342,323,305,288,272,256,242,228,
  228.     216,203,192,181,171,161,152,144,136,128,121,114,0,
  229.  
  230.     /* PT BUGFIX: overflowing arpeggio on -1 finetuned samples, add extra zeroes. */
  231.     0,0,0,0,0,0,0,0,0,0,0,0,0,0
  232. };
  233.  
  234. #ifdef USE_BLEP
  235. static const uint32_t blepData[48] =
  236. {
  237.     0x3F7FE1F1, 0x3F7FD548, 0x3F7FD6A3, 0x3F7FD4E3,
  238.     0x3F7FAD85, 0x3F7F2152, 0x3F7DBFAE, 0x3F7ACCDF,
  239.     0x3F752F1E, 0x3F6B7384, 0x3F5BFBCB, 0x3F455CF2,
  240.     0x3F26E524, 0x3F0128C4, 0x3EACC7DC, 0x3E29E86B,
  241.     0x3C1C1D29, 0xBDE4BBE6, 0xBE3AAE04, 0xBE48DEDD,
  242.     0xBE22AD7E, 0xBDB2309A, 0xBB82B620, 0x3D881411,
  243.     0x3DDADBF3, 0x3DE2C81D, 0x3DAAA01F, 0x3D1E769A,
  244.     0xBBC116D7, 0xBD1402E8, 0xBD38A069, 0xBD0C53BB,
  245.     0xBC3FFB8C, 0x3C465FD2, 0x3CEA5764, 0x3D0A51D6,
  246.     0x3CEAE2D5, 0x3C92AC5A, 0x3BE4CBF7, 0x00000000,
  247.     0x00000000, 0x00000000, 0x00000000, 0x00000000,
  248.     0x00000000, 0x00000000, 0x00000000, 0x00000000
  249. };
  250. #endif
  251.  
  252. /* MACROS */
  253.  
  254. #define SWAP16(x) ((uint16_t)(((x) << 8) | ((x) >> 8)))
  255. #define PTR2WORD(x) ((uint16_t *)(x))
  256. #define LERP(x, y, z) ((x) + ((y) - (x)) * (z))
  257. #define CLAMP16(i) \
  258. { \
  259.     if ((int16_t)(i) != i) \
  260.         i = 0x7FFF ^ (i >> 31); \
  261. }
  262.  
  263. /* CIA/PAULA/FILTER/BLEP RELATED CODE */
  264.  
  265. static void SetReplayerBPM(uint8_t bpm)
  266. {
  267.     uint16_t ciaVal;
  268.     float f_hz, f_smp;
  269.  
  270.     if (f_outputFreq == 0.0f)
  271.         return;
  272.  
  273.     if (bpm < 32)
  274.         bpm = 32;
  275.  
  276.     ciaVal = (uint16_t)(1773447 / bpm); /* yes, truncate here */
  277.     f_hz   = (float)(CIA_PAL_CLK) / ciaVal;
  278.     f_smp  = f_outputFreq / f_hz;
  279.  
  280.     samplesPerFrame = (uint32_t)(f_smp + 0.5f);
  281. }
  282.  
  283. static void PaulaRestartDMA(uint8_t i)
  284. {
  285.     const int8_t *dat;
  286.     int32_t len;
  287.  
  288.     dat = AUD[i].SRC_DAT;
  289.     if (dat == NULL)
  290.         dat = &sampleData[emptySampleDataOffset];
  291.  
  292.     len = AUD[i].SRC_LEN;
  293.     if (len < 2)
  294.         len = 2;
  295.  
  296.     AUD[i].FRAC    = 0.0f;
  297.     AUD[i].DMA_POS = 0;
  298.     AUD[i].DMA_DAT = dat;
  299.     AUD[i].DMA_LEN = len;
  300.     AUD[i].DMA_ON  = 1;
  301. }
  302.  
  303. static void PaulaSetPeriod(uint8_t i, uint16_t period)
  304. {
  305.     if (period == 0)
  306.     {
  307.         AUD[i].DELTA = 0.0f;
  308.     }
  309.     else
  310.     {
  311.         /* confirmed behavior on real Amiga */
  312.         if (period < 113)
  313.             period = 113;
  314.  
  315.         AUD[i].DELTA = ((float)(PAULA_PAL_CLK) / period) / f_outputFreq;
  316.     }
  317.  
  318.     if (AUD[i].LASTDELTA == 0.0f)
  319.         AUD[i].LASTDELTA = AUD[i].DELTA;
  320. }
  321.  
  322. static void PaulaSetVolume(uint8_t i, uint16_t vol)
  323. {
  324.     vol &= 0x007F;
  325.     if (vol > 0x40)
  326.         vol = 0x40;
  327.  
  328.     AUD[i].SRC_VOL = vol * (1.0f / 64.0f);
  329. }
  330.  
  331. static void PaulaSetLength(uint8_t i, uint16_t len)
  332. {
  333.     AUD[i].SRC_LEN = 2 * len;
  334. }
  335.  
  336. static void PaulaSetData(uint8_t i, const int8_t *src)
  337. {
  338.     if (src == NULL)
  339.         src = &sampleData[emptySampleDataOffset];
  340.  
  341.     AUD[i].SRC_DAT = src;
  342. }
  343.  
  344. #if defined(USE_HIGHPASS) || defined(USE_LOWPASS)
  345. static void calcCoeffLossyIntegrator(float sr, float hz, lossyIntegrator_t *filter)
  346. {
  347.     filter->coeff[0] = tanf((3.1415927f * hz) / sr);
  348.     filter->coeff[1] = 1.0f / (1.0f + filter->coeff[0]);
  349. }
  350.  
  351. static void clearLossyIntegrator(lossyIntegrator_t *filter)
  352. {
  353.     filter->buffer[0] = 0.0f;
  354.     filter->buffer[1] = 0.0f;
  355. }
  356.  
  357. static inline void lossyIntegrator(lossyIntegrator_t *filter, float *in, float *out)
  358. {
  359.     float output;
  360.  
  361.     /* left channel low-pass */
  362.     output = (filter->coeff[0] * in[0] + filter->buffer[0]) * filter->coeff[1];
  363.     filter->buffer[0] = filter->coeff[0] * (in[0] - output) + output + 1e-10f;
  364.     out[0] = output;
  365.  
  366.     /* right channel low-pass */
  367.     output = (filter->coeff[0] * in[1] + filter->buffer[1]) * filter->coeff[1];
  368.     filter->buffer[1] = filter->coeff[0] * (in[1] - output) + output + 1e-10f;
  369.     out[1] = output;
  370. }
  371.  
  372. static inline void lossyIntegratorHighPass(lossyIntegrator_t *filter, float *in, float *out)
  373. {
  374.     float low[2];
  375.  
  376.     lossyIntegrator(filter, in, low);
  377.  
  378.     out[0] = in[0] - low[0];
  379.     out[1] = in[1] - low[1];
  380. }
  381. #endif
  382.  
  383. #ifdef LED_FILTER
  384. static void calcCoeffLED(float sr, float hz, ledFilterCoeff_t *filter)
  385. {
  386.     if (hz < (sr / 2.0f))
  387.         filter->led = ((2.0f * 3.1415927f) * hz) / sr;
  388.     else
  389.         filter->led = 1.0f;
  390.  
  391.     filter->ledFb = 0.125f + (0.125f / (1.0f - filter->led)); /* Fb = 0.125 : Q ~= 1/sqrt(2) (Butterworth) */
  392. }
  393.  
  394. static void clearLEDFilter(ledFilter_t *filter)
  395. {
  396.     filter->led[0] = 0.0f;
  397.     filter->led[1] = 0.0f;
  398.     filter->led[2] = 0.0f;
  399.     filter->led[3] = 0.0f;
  400. }
  401.  
  402. static inline void lossyIntegratorLED(ledFilterCoeff_t filterC, ledFilter_t *filter, float *in, float *out)
  403. {
  404.     /* left channel */
  405.     filter->led[0] += (filterC.led * (in[0] - filter->led[0])
  406.         + filterC.ledFb * (filter->led[0] - filter->led[1]) + 1e-10f);
  407.     filter->led[1] += (filterC.led * (filter->led[0] - filter->led[1]) + 1e-10f);
  408.     out[0] = filter->led[1];
  409.  
  410.     /* right channel */
  411.     filter->led[2] += (filterC.led * (in[1] - filter->led[2])
  412.         + filterC.ledFb * (filter->led[2] - filter->led[3]) + 1e-10f);
  413.     filter->led[3] += (filterC.led * (filter->led[2] - filter->led[3]) + 1e-10f);
  414.     out[1] = filter->led[3];
  415. }
  416. #endif
  417.  
  418. #ifdef USE_BLEP
  419. static inline void blepAdd(blep_t *b, float offset, float amplitude)
  420. {
  421.     int8_t n;
  422.     uint32_t i;
  423.     const float *blepSrc;
  424.     float f;
  425.  
  426.     if ((offset < 0.0f) || (offset > 1.0f))
  427.         return;
  428.  
  429.     i  = (uint32_t)(offset * BLEP_SP);
  430.     blepSrc = (const float *)(blepData) + i + BLEP_OS;
  431.  
  432.     f = (offset * BLEP_SP) - i;
  433.     i = b->index;
  434.  
  435.     n = BLEP_NS;
  436.     while (n--)
  437.     {
  438.         b->buffer[i] += (amplitude * LERP(blepSrc[0], blepSrc[1], f));
  439.  
  440.         i++;
  441.         i &= BLEP_RNS;
  442.  
  443.         blepSrc += BLEP_SP;
  444.     }
  445.  
  446.     b->samplesLeft = BLEP_NS;
  447. }
  448.  
  449. static inline float blepRun(blep_t *b)
  450. {
  451.     float blepOutput;
  452.  
  453.     blepOutput = b->buffer[b->index];
  454.     b->buffer[b->index] = 0.0f;
  455.  
  456.     b->index++;
  457.     b->index &= BLEP_RNS;
  458.  
  459.     b->samplesLeft--;
  460.  
  461.     return (blepOutput);
  462. }
  463. #endif
  464.  
  465. /* REPLAYER RELATED CODE */
  466.  
  467. static void UpdateFunk(ptChannel_t *ch)
  468. {
  469.     int8_t funkspeed;
  470.  
  471.     funkspeed = ch->n_glissfunk >> 4;
  472.     if (funkspeed > 0)
  473.     {
  474.         ch->n_funkoffset += FunkTable[funkspeed];
  475.         if (ch->n_funkoffset >= 128)
  476.         {
  477.             ch->n_funkoffset = 0;
  478.  
  479.             if ((ch->n_loopstart != NULL) && (ch->n_wavestart != NULL)) /* SAFETY BUG FIX */
  480.             {
  481.                 if (++ch->n_wavestart >= (ch->n_loopstart + (ch->n_replen * 2)))
  482.                       ch->n_wavestart  =  ch->n_loopstart;
  483.  
  484.                 *ch->n_wavestart = -1 - *ch->n_wavestart;
  485.             }
  486.         }
  487.     }
  488. }
  489.  
  490. static void SetGlissControl(ptChannel_t *ch)
  491. {
  492.     ch->n_glissfunk = (ch->n_glissfunk & 0xF0) | (ch->n_cmd & 0x000F);
  493. }
  494.  
  495. static void SetVibratoControl(ptChannel_t *ch)
  496. {
  497.     ch->n_wavecontrol = (ch->n_wavecontrol & 0xF0) | (ch->n_cmd & 0x000F);
  498. }
  499.  
  500. static void SetFineTune(ptChannel_t *ch)
  501. {
  502.     ch->n_finetune = ch->n_cmd & 0x000F;
  503. }
  504.  
  505. static void JumpLoop(ptChannel_t *ch)
  506. {
  507.     if (!Counter)
  508.     {
  509.         if (!(ch->n_cmd & 0x000F))
  510.         {
  511.             ch->n_pattpos = (PatternPos / 16) & 0x00FF;
  512.         }
  513.         else
  514.         {
  515.             if (!ch->n_loopcount)
  516.             {
  517.                 ch->n_loopcount = ch->n_cmd & 0x000F;
  518.             }
  519.             else
  520.             {
  521.                 if (!--ch->n_loopcount)
  522.                     return;
  523.             }
  524.  
  525.             PBreakPosition = ch->n_pattpos;
  526.             PBreakFlag = 1;
  527.         }
  528.     }
  529. }
  530.  
  531. static void SetTremoloControl(ptChannel_t *ch)
  532. {
  533.     ch->n_wavecontrol = ((ch->n_cmd & 0x000F) << 4) | (ch->n_wavecontrol & 0x0F);
  534. }
  535.  
  536. static void KarplusStrong(ptChannel_t *ch)
  537. {
  538. #ifdef ENABLE_E8_EFFECT
  539.     int8_t *smpPtr;
  540.     int16_t dat;
  541.     uint16_t len;
  542.  
  543.     smpPtr = ch->n_loopstart;
  544.     if (smpPtr != NULL) /* SAFETY BUG FIX */
  545.     {
  546.         len = ((ch->n_replen * 2) & 0xFFFF) - 1;
  547.         while (len--)
  548.         {
  549.             dat = smpPtr[1] + smpPtr[0];
  550.  
  551.             /* "arithmetic shift right" on signed number simulation */
  552.             if (dat < 0)
  553.                 dat = 0x8000 | ((uint16_t)(dat) >> 1); /* 0x8000 = 2^16 - 2^(16-1) */
  554.             else
  555.                 dat /= 2;
  556.  
  557.             *smpPtr++ = dat & 0x00FF;
  558.         }
  559.  
  560.         dat = ch->n_loopstart[0] + smpPtr[0];
  561.  
  562.         /* "arithmetic shift right" on signed number simulation */
  563.         if (dat < 0)
  564.             dat = 0x8000 | ((uint16_t)(dat) >> 1);
  565.         else
  566.             dat /= 2;
  567.  
  568.         *smpPtr = dat & 0x00FF;
  569.     }
  570. #else
  571.     (void)(ch);
  572. #endif
  573. }
  574.  
  575. static void DoRetrg(ptChannel_t *ch)
  576. {
  577.     PaulaSetData(ch->n_index, ch->n_start); /* n_start is increased on 9xx */
  578.     PaulaSetLength(ch->n_index, ch->n_length);
  579.     PaulaSetPeriod(ch->n_index, ch->n_period);
  580.     PaulaRestartDMA(ch->n_index);
  581.  
  582.     /* these take effect after the current DMA cycle is done */
  583.     PaulaSetData(ch->n_index, ch->n_loopstart);
  584.     PaulaSetLength(ch->n_index, ch->n_replen);
  585. }
  586.  
  587. static void RetrigNote(ptChannel_t *ch)
  588. {
  589.     if (ch->n_cmd & 0x000F)
  590.     {
  591.         if (!Counter)
  592.         {
  593.             if (ch->n_note & 0x0FFF)
  594.                 return;
  595.         }
  596.  
  597.         if (!(Counter % (ch->n_cmd & 0x000F)))
  598.             DoRetrg(ch);
  599.     }
  600. }
  601.  
  602. static void VolumeSlide(ptChannel_t *ch)
  603. {
  604.     uint8_t cmd;
  605.  
  606.     cmd = ch->n_cmd & 0x00FF;
  607.     if (!(cmd & 0xF0))
  608.     {
  609.         ch->n_volume -= (cmd & 0x0F);
  610.         if (ch->n_volume < 0)
  611.             ch->n_volume = 0;
  612.     }
  613.     else
  614.     {
  615.         ch->n_volume += (cmd >> 4);
  616.         if (ch->n_volume > 64)
  617.             ch->n_volume = 64;
  618.     }
  619. }
  620.  
  621. static void VolumeFineUp(ptChannel_t *ch)
  622. {
  623.     if (!Counter)
  624.     {
  625.         ch->n_volume += (ch->n_cmd & 0x000F);
  626.         if (ch->n_volume > 64)
  627.             ch->n_volume = 64;
  628.     }
  629. }
  630.  
  631. static void VolumeFineDown(ptChannel_t *ch)
  632. {
  633.     if (!Counter)
  634.     {
  635.         ch->n_volume -= (ch->n_cmd & 0x000F);
  636.         if (ch->n_volume < 0)
  637.             ch->n_volume = 0;
  638.     }
  639. }
  640.  
  641. static void NoteCut(ptChannel_t *ch)
  642. {
  643.     if (Counter == (ch->n_cmd & 0x000F))
  644.         ch->n_volume = 0;
  645. }
  646.  
  647. static void NoteDelay(ptChannel_t *ch)
  648. {
  649.     if (Counter == (ch->n_cmd & 0x000F))
  650.     {
  651.         if (ch->n_note & 0x0FFF)
  652.             DoRetrg(ch);
  653.     }
  654. }
  655.  
  656. static void PatternDelay(ptChannel_t *ch)
  657. {
  658.     if (!Counter)
  659.     {
  660.         if (!PattDelTime2)
  661.             PattDelTime = (ch->n_cmd & 0x000F) + 1;
  662.     }
  663. }
  664.  
  665. static void FunkIt(ptChannel_t *ch)
  666. {
  667.     if (!Counter)
  668.     {
  669.         ch->n_glissfunk = ((ch->n_cmd & 0x000F) << 4) | (ch->n_glissfunk & 0x0F);
  670.  
  671.         if (ch->n_glissfunk & 0xF0)
  672.             UpdateFunk(ch);
  673.     }
  674. }
  675.  
  676. static void PositionJump(ptChannel_t *ch)
  677. {
  678.     SongPosition   = (ch->n_cmd & 0x00FF) - 1; /* 0xFF (B00) jumps to pat 0 */
  679.     PBreakPosition = 0;
  680.     PosJumpAssert  = 1;
  681. }
  682.  
  683. static void VolumeChange(ptChannel_t *ch)
  684. {
  685.     ch->n_volume = ch->n_cmd & 0x00FF;
  686.     if ((uint8_t)(ch->n_volume) > 64)
  687.         ch->n_volume = 64;
  688. }
  689.  
  690. static void PatternBreak(ptChannel_t *ch)
  691. {
  692.     PBreakPosition = (((ch->n_cmd & 0x00F0) >> 4) * 10) + (ch->n_cmd & 0x000F);
  693.     if ((uint8_t)(PBreakPosition) > 63)
  694.         PBreakPosition = 0;
  695.  
  696.     PosJumpAssert = 1;
  697. }
  698.  
  699. static void SetSpeed(ptChannel_t *ch)
  700. {
  701.     if (ch->n_cmd & 0x00FF)
  702.     {
  703.         Counter = 0;
  704.  
  705.         if ((TempoMode == VBLANK_TEMPO_MODE) || ((ch->n_cmd & 0x00FF) < 32))
  706.             CurrSpeed = ch->n_cmd & 0x00FF;
  707.         else
  708.             SetBPMFlag = ch->n_cmd & 0x00FF; /* CIA doesn't refresh its registers until the next interrupt, so change it later */
  709.     }
  710. }
  711.  
  712. static void Arpeggio(ptChannel_t *ch)
  713. {
  714.     uint8_t i, dat;
  715.     const int16_t *arpPointer;
  716.  
  717.     dat = Counter % 3;
  718.     if (!dat)
  719.     {
  720.         PaulaSetPeriod(ch->n_index, ch->n_period);
  721.     }
  722.     else
  723.     {
  724.              if (dat == 1) dat = (ch->n_cmd & 0x00F0) >> 4;
  725.         else if (dat == 2) dat =  ch->n_cmd & 0x000F;
  726.  
  727.         arpPointer = &PeriodTable[37 * ch->n_finetune];
  728.         for (i = 0; i < 37; ++i)
  729.         {
  730.             if (ch->n_period >= arpPointer[i])
  731.             {
  732.                 PaulaSetPeriod(ch->n_index, arpPointer[i + dat]);
  733.                 break;
  734.             }
  735.         }
  736.     }
  737. }
  738.  
  739. static void PortaUp(ptChannel_t *ch)
  740. {
  741.     ch->n_period -= ((ch->n_cmd & 0x00FF) & LowMask);
  742.     LowMask = 0xFF;
  743.  
  744.     if ((ch->n_period & 0x0FFF) < 113)
  745.         ch->n_period = (ch->n_period & 0xF000) | 113;
  746.  
  747.     PaulaSetPeriod(ch->n_index, ch->n_period & 0x0FFF);
  748. }
  749.  
  750. static void PortaDown(ptChannel_t *ch)
  751. {
  752.     ch->n_period += ((ch->n_cmd & 0x00FF) & LowMask);
  753.     LowMask = 0xFF;
  754.  
  755.     if ((ch->n_period & 0x0FFF) > 856)
  756.         ch->n_period = (ch->n_period & 0xF000) | 856;
  757.  
  758.     PaulaSetPeriod(ch->n_index, ch->n_period & 0x0FFF);
  759. }
  760.  
  761. static void FilterOnOff(ptChannel_t *ch)
  762. {
  763. #ifdef LED_FILTER
  764.     LEDStatus = !(ch->n_cmd & 0x0001);
  765. #endif
  766. }
  767.  
  768. static void FinePortaUp(ptChannel_t *ch)
  769. {
  770.     if (!Counter)
  771.     {
  772.         LowMask = 0x0F;
  773.         PortaUp(ch);
  774.     }
  775. }
  776.  
  777. static void FinePortaDown(ptChannel_t *ch)
  778. {
  779.     if (!Counter)
  780.     {
  781.         LowMask = 0x0F;
  782.         PortaDown(ch);
  783.     }
  784. }
  785.  
  786. static void SetTonePorta(ptChannel_t *ch)
  787. {
  788.     uint8_t i;
  789.     const int16_t *portaPointer;
  790.     uint16_t note;
  791.  
  792.     note = ch->n_note & 0x0FFF;
  793.     portaPointer = &PeriodTable[37 * ch->n_finetune];
  794.  
  795.     i = 0;
  796.     for (;;)
  797.     {
  798.         /* portaPointer[36] = 0, so i=36 is safe */
  799.         if (note >= portaPointer[i])
  800.             break;
  801.  
  802.         if (++i >= 37)
  803.         {
  804.             i = 35;
  805.             break;
  806.         }
  807.     }
  808.  
  809.     if ((ch->n_finetune & 8) && i) i--;
  810.  
  811.     ch->n_wantedperiod  = portaPointer[i];
  812.     ch->n_toneportdirec = 0;
  813.  
  814.          if (ch->n_period == ch->n_wantedperiod) ch->n_wantedperiod  = 0;
  815.     else if (ch->n_period  > ch->n_wantedperiod) ch->n_toneportdirec = 1;
  816. }
  817.  
  818. static void TonePortNoChange(ptChannel_t *ch)
  819. {
  820.     uint8_t i;
  821.     const int16_t *portaPointer;
  822.  
  823.     if (ch->n_wantedperiod)
  824.     {
  825.         if (ch->n_toneportdirec)
  826.         {
  827.             ch->n_period -= ch->n_toneportspeed;
  828.             if (ch->n_period <= ch->n_wantedperiod)
  829.             {
  830.                 ch->n_period = ch->n_wantedperiod;
  831.                 ch->n_wantedperiod = 0;
  832.             }
  833.         }
  834.         else
  835.         {
  836.             ch->n_period += ch->n_toneportspeed;
  837.             if (ch->n_period >= ch->n_wantedperiod)
  838.             {
  839.                 ch->n_period = ch->n_wantedperiod;
  840.                 ch->n_wantedperiod = 0;
  841.             }
  842.         }
  843.  
  844.         if (!(ch->n_glissfunk & 0x0F))
  845.         {
  846.             PaulaSetPeriod(ch->n_index, ch->n_period);
  847.         }
  848.         else
  849.         {
  850.             portaPointer = &PeriodTable[37 * ch->n_finetune];
  851.  
  852.             i = 0;
  853.             for (;;)
  854.             {
  855.                 /* portaPointer[36] = 0, so i=36 is safe */
  856.                 if (ch->n_period >= portaPointer[i])
  857.                     break;
  858.  
  859.                 if (++i >= 37)
  860.                 {
  861.                     i = 35;
  862.                     break;
  863.                 }
  864.             }
  865.  
  866.             PaulaSetPeriod(ch->n_index, portaPointer[i]);
  867.         }
  868.     }
  869. }
  870.  
  871. static void TonePortamento(ptChannel_t *ch)
  872. {
  873.     if (ch->n_cmd & 0x00FF)
  874.     {
  875.         ch->n_toneportspeed = ch->n_cmd & 0x00FF;
  876.         ch->n_cmd &= 0xFF00;
  877.     }
  878.  
  879.     TonePortNoChange(ch);
  880. }
  881.  
  882. static void VibratoNoChange(ptChannel_t *ch)
  883. {
  884.     uint8_t vibratoTemp;
  885.     int16_t vibratoData;
  886.  
  887.     vibratoTemp = (ch->n_vibratopos / 4) & 31;
  888.     vibratoData = ch->n_wavecontrol & 3;
  889.  
  890.     if (!vibratoData)
  891.     {
  892.         vibratoData = VibratoTable[vibratoTemp];
  893.     }
  894.     else
  895.     {
  896.         if (vibratoData == 1)
  897.         {
  898.             if (ch->n_vibratopos < 0)
  899.                 vibratoData = 255 - (vibratoTemp * 8);
  900.             else
  901.                 vibratoData = vibratoTemp * 8;
  902.         }
  903.         else
  904.         {
  905.             vibratoData = 255;
  906.         }
  907.     }
  908.  
  909.     vibratoData = (vibratoData * (ch->n_vibratocmd & 0x0F)) / 128;
  910.  
  911.     if (ch->n_vibratopos < 0)
  912.         vibratoData = ch->n_period - vibratoData;
  913.     else
  914.         vibratoData = ch->n_period + vibratoData;
  915.  
  916.     PaulaSetPeriod(ch->n_index, vibratoData);
  917.  
  918.     ch->n_vibratopos += ((ch->n_vibratocmd >> 4) * 4);
  919. }
  920.  
  921. static void Vibrato(ptChannel_t *ch)
  922. {
  923.     if (ch->n_cmd & 0x00FF)
  924.     {
  925.         if (ch->n_cmd & 0x000F)
  926.             ch->n_vibratocmd = (ch->n_vibratocmd & 0xF0) | (ch->n_cmd & 0x000F);
  927.  
  928.         if (ch->n_cmd & 0x00F0)
  929.             ch->n_vibratocmd = (ch->n_cmd & 0x00F0) | (ch->n_vibratocmd & 0x0F);
  930.     }
  931.  
  932.     VibratoNoChange(ch);
  933. }
  934.  
  935. static void TonePlusVolSlide(ptChannel_t *ch)
  936. {
  937.     TonePortNoChange(ch);
  938.     VolumeSlide(ch);
  939. }
  940.  
  941. static void VibratoPlusVolSlide(ptChannel_t *ch)
  942. {
  943.     VibratoNoChange(ch);
  944.     VolumeSlide(ch);
  945. }
  946.  
  947. static void Tremolo(ptChannel_t *ch)
  948. {
  949.     int8_t tremoloTemp;
  950.     int16_t tremoloData;
  951.  
  952.     if (ch->n_cmd & 0x00FF)
  953.     {
  954.         if (ch->n_cmd & 0x000F)
  955.             ch->n_tremolocmd = (ch->n_tremolocmd & 0xF0) | (ch->n_cmd & 0x000F);
  956.  
  957.         if (ch->n_cmd & 0x00F0)
  958.             ch->n_tremolocmd = (ch->n_cmd & 0x00F0) | (ch->n_tremolocmd & 0x0F);
  959.     }
  960.  
  961.     tremoloTemp = (ch->n_tremolopos / 4) & 31;
  962.     tremoloData = (ch->n_wavecontrol >> 4) & 3;
  963.  
  964.     if (!tremoloData)
  965.     {
  966.         tremoloData = VibratoTable[tremoloTemp];
  967.     }
  968.     else
  969.     {
  970.         if (tremoloData == 1)
  971.         {
  972.             if (ch->n_vibratopos < 0) /* PT bug, should've been n_tremolopos */
  973.                 tremoloData = 255 - (tremoloTemp * 8);
  974.             else
  975.                 tremoloData = tremoloTemp * 8;
  976.         }
  977.         else
  978.         {
  979.             tremoloData = 255;
  980.         }
  981.     }
  982.  
  983.     tremoloData = (tremoloData * (ch->n_tremolocmd & 0x0F)) / 64;
  984.  
  985.     if (ch->n_tremolopos < 0)
  986.     {
  987.         tremoloData = ch->n_volume - tremoloData;
  988.         if (tremoloData < 0)
  989.             tremoloData = 0;
  990.     }
  991.     else
  992.     {
  993.         tremoloData = ch->n_volume + tremoloData;
  994.         if (tremoloData > 64)
  995.             tremoloData = 64;
  996.     }
  997.  
  998.     PaulaSetVolume(ch->n_index, tremoloData);
  999.  
  1000.     ch->n_tremolopos += ((ch->n_tremolocmd >> 4) * 4);
  1001. }
  1002.  
  1003. static void SampleOffset(ptChannel_t *ch)
  1004. {
  1005.     uint16_t newOffset;
  1006.  
  1007.     if (ch->n_cmd & 0x00FF)
  1008.         ch->n_sampleoffset = ch->n_cmd & 0x00FF;
  1009.  
  1010.     newOffset = ch->n_sampleoffset * 128;
  1011.     if ((ch->n_length <= 32767) && (newOffset < ch->n_length))
  1012.     {
  1013.         ch->n_length -=  newOffset;
  1014.         ch->n_start  += (newOffset * 2);
  1015.     }
  1016.     else
  1017.     {
  1018.         ch->n_length = 1; /* this must NOT be set to 0! 1 is the correct value */
  1019.     }
  1020. }
  1021.  
  1022. static void E_Commands(ptChannel_t *ch)
  1023. {
  1024.     switch ((ch->n_cmd & 0x00F0) >> 4)
  1025.     {
  1026.         case 0x00: FilterOnOff(ch);       break;
  1027.         case 0x01: FinePortaUp(ch);       break;
  1028.         case 0x02: FinePortaDown(ch);     break;
  1029.         case 0x03: SetGlissControl(ch);   break;
  1030.         case 0x04: SetVibratoControl(ch); break;
  1031.         case 0x05: SetFineTune(ch);       break;
  1032.         case 0x06: JumpLoop(ch);          break;
  1033.         case 0x07: SetTremoloControl(ch); break;
  1034.         case 0x08: KarplusStrong(ch);     break;
  1035.         case 0x09: RetrigNote(ch);        break;
  1036.         case 0x0A: VolumeFineUp(ch);      break;
  1037.         case 0x0B: VolumeFineDown(ch);    break;
  1038.         case 0x0C: NoteCut(ch);           break;
  1039.         case 0x0D: NoteDelay(ch);         break;
  1040.         case 0x0E: PatternDelay(ch);      break;
  1041.         case 0x0F: FunkIt(ch);            break;
  1042.     }
  1043. }
  1044.  
  1045. static void CheckMoreEffects(ptChannel_t *ch)
  1046. {
  1047.     switch ((ch->n_cmd & 0x0F00) >> 8)
  1048.     {
  1049.         case 0x09: SampleOffset(ch); break;
  1050.         case 0x0B: PositionJump(ch); break;
  1051.         case 0x0D: PatternBreak(ch); break;
  1052.         case 0x0E: E_Commands(ch);   break;
  1053.         case 0x0F: SetSpeed(ch);     break;
  1054.         case 0x0C: VolumeChange(ch); break;
  1055.  
  1056.         default: PaulaSetPeriod(ch->n_index, ch->n_period); break;
  1057.     }
  1058. }
  1059.  
  1060. static void CheckEffects(ptChannel_t *ch)
  1061. {
  1062.     uint8_t effect;
  1063.  
  1064.     UpdateFunk(ch);
  1065.  
  1066.     effect = (ch->n_cmd & 0x0F00) >> 8;
  1067.     if (ch->n_cmd & 0x0FFF)
  1068.     {
  1069.         switch (effect)
  1070.         {
  1071.             case 0x00: Arpeggio(ch);            break;
  1072.             case 0x01: PortaUp(ch);             break;
  1073.             case 0x02: PortaDown(ch);           break;
  1074.             case 0x03: TonePortamento(ch);      break;
  1075.             case 0x04: Vibrato(ch);             break;
  1076.             case 0x05: TonePlusVolSlide(ch);    break;
  1077.             case 0x06: VibratoPlusVolSlide(ch); break;
  1078.             case 0x0E: E_Commands(ch);          break;
  1079.             case 0x07:
  1080.                 PaulaSetPeriod(ch->n_index, ch->n_period);
  1081.                 Tremolo(ch);
  1082.             break;
  1083.             case 0x0A:
  1084.                 PaulaSetPeriod(ch->n_index, ch->n_period);
  1085.                 VolumeSlide(ch);
  1086.             break;
  1087.  
  1088.             default: PaulaSetPeriod(ch->n_index, ch->n_period); break;
  1089.         }
  1090.     }
  1091.  
  1092.     if (effect != 0x07)
  1093.         PaulaSetVolume(ch->n_index, ch->n_volume);
  1094. }
  1095.  
  1096. static void SetPeriod(ptChannel_t *ch)
  1097. {
  1098.     uint8_t i;
  1099.     uint16_t note;
  1100.  
  1101.     note = ch->n_note & 0x0FFF;
  1102.     for (i = 0; i < 37; ++i)
  1103.     {
  1104.         /* PeriodTable[36] = 0, so i=36 is safe */
  1105.         if (note >= PeriodTable[i])
  1106.             break;
  1107.     }
  1108.  
  1109.     /* BUG: yes it's 'safe' if i=37 because of padding at the end of period table */
  1110.     ch->n_period = PeriodTable[(37 * ch->n_finetune) + i];
  1111.  
  1112.     if ((ch->n_cmd & 0x0FF0) != 0x0ED0) /* no note delay */
  1113.     {
  1114.         if (!(ch->n_wavecontrol & 0x04)) ch->n_vibratopos = 0;
  1115.         if (!(ch->n_wavecontrol & 0x40)) ch->n_tremolopos = 0;
  1116.  
  1117.         PaulaSetLength(ch->n_index, ch->n_length);
  1118.         PaulaSetData(ch->n_index, ch->n_start);
  1119.  
  1120.         if (ch->n_start == NULL)
  1121.         {
  1122.             ch->n_loopstart = NULL;
  1123.             PaulaSetLength(ch->n_index, 1);
  1124.             ch->n_replen = 1;
  1125.         }
  1126.  
  1127.         PaulaSetPeriod(ch->n_index, ch->n_period);
  1128.         PaulaRestartDMA(ch->n_index);
  1129.     }
  1130.  
  1131.     CheckMoreEffects(ch);
  1132. }
  1133.  
  1134. static void PlayVoice(ptChannel_t *ch)
  1135. {
  1136.     uint8_t *dataPtr, sample, cmd;
  1137.     uint16_t sampleOffset, repeat;
  1138.  
  1139.     if (!ch->n_note && !ch->n_cmd)
  1140.         PaulaSetPeriod(ch->n_index, ch->n_period);
  1141.  
  1142.     dataPtr = &SongDataPtr[PattPosOff];
  1143.  
  1144.     ch->n_note = (dataPtr[0] << 8) | dataPtr[1];
  1145.     ch->n_cmd  = (dataPtr[2] << 8) | dataPtr[3];
  1146.  
  1147.     sample = (dataPtr[0] & 0xF0) | (dataPtr[2] >> 4);
  1148.     if ((sample >= 1) && (sample <= 31)) /* SAFETY BUG FIX: don't handle sample-numbers >31 */
  1149.     {
  1150.         sample--;
  1151.         sampleOffset = 42 + (30 * sample);
  1152.  
  1153.         ch->n_start    = SampleStarts[sample];
  1154.         ch->n_finetune = SongDataPtr[sampleOffset + 2] & 0x0F; /* SAFETY BUG FIX: mask finetune */
  1155.         ch->n_volume   = SongDataPtr[sampleOffset + 3];
  1156.         ch->n_length   = *PTR2WORD(&SongDataPtr[sampleOffset + 0]);
  1157.         ch->n_replen   = *PTR2WORD(&SongDataPtr[sampleOffset + 6]);
  1158.  
  1159.         repeat = *PTR2WORD(&SongDataPtr[sampleOffset + 4]);
  1160.         if (repeat > 0)
  1161.         {
  1162.             ch->n_loopstart = ch->n_start + (repeat * 2);
  1163.             ch->n_wavestart = ch->n_loopstart;
  1164.             ch->n_length    = repeat + ch->n_replen;
  1165.         }
  1166.         else
  1167.         {
  1168.             ch->n_loopstart = ch->n_start;
  1169.             ch->n_wavestart = ch->n_start;
  1170.         }
  1171.  
  1172.         if (ch->n_length == 0)
  1173.             ch->n_loopstart = ch->n_wavestart = &sampleData[emptySampleDataOffset]; /* dummy sample */
  1174.     }
  1175.  
  1176.     if (ch->n_note & 0x0FFF)
  1177.     {
  1178.         if ((ch->n_cmd & 0x0FF0) == 0x0E50) /* set finetune */
  1179.         {
  1180.             SetFineTune(ch);
  1181.             SetPeriod(ch);
  1182.         }
  1183.         else
  1184.         {
  1185.             cmd = (ch->n_cmd & 0x0F00) >> 8;
  1186.             if ((cmd == 0x03) || (cmd == 0x05))
  1187.             {
  1188.                 SetTonePorta(ch);
  1189.                 CheckMoreEffects(ch);
  1190.             }
  1191.             else if (cmd == 0x09)
  1192.             {
  1193.                 CheckMoreEffects(ch);
  1194.                 SetPeriod(ch);
  1195.             }
  1196.             else
  1197.             {
  1198.                 SetPeriod(ch);
  1199.             }
  1200.         }
  1201.     }
  1202.     else
  1203.     {
  1204.         CheckMoreEffects(ch);
  1205.     }
  1206.  
  1207.     PattPosOff += 4;
  1208. }
  1209.  
  1210. static void NextPosition(void)
  1211. {
  1212.     PatternPos     = PBreakPosition * 16;
  1213.     PBreakPosition = 0;
  1214.     PosJumpAssert  = 0;
  1215.  
  1216.     SongPosition = (SongPosition + 1) & 0x7F;
  1217.     if (SongPosition >= SongDataPtr[950])
  1218.         SongPosition = 0;
  1219. }
  1220.  
  1221. static void IntMusic(void) /* called (SongBPM/2.5) times a second (duration: 1000/(SongBPM/2.5) milliseconds) */
  1222. {
  1223.     uint8_t i;
  1224.  
  1225.     if (!SongPlaying) return;
  1226.  
  1227.     /* PT quirk: CIA refreshes its timer values on the next interrupt, so do the real tempo change here */
  1228.     if (SetBPMFlag != 0)
  1229.     {
  1230.         SetReplayerBPM(SetBPMFlag);
  1231.         SetBPMFlag = 0;
  1232.     }
  1233.  
  1234.     if (++Counter >= CurrSpeed)
  1235.     {
  1236.         Counter = 0;
  1237.  
  1238.         if (!PattDelTime2)
  1239.         {
  1240.             PattPosOff = (1084 + (SongDataPtr[952 + SongPosition] * 1024)) + PatternPos;
  1241.  
  1242.             for (i = 0; i < 4; ++i)
  1243.             {
  1244.                 PlayVoice(&ChanTemp[i]);
  1245.                 PaulaSetVolume(i, ChanTemp[i].n_volume);
  1246.  
  1247.                 /* these take effect after the current DMA cycle is done */
  1248.                 PaulaSetData(i, ChanTemp[i].n_loopstart);
  1249.                 PaulaSetLength(i, ChanTemp[i].n_replen);
  1250.             }
  1251.         }
  1252.         else
  1253.         {
  1254.             for (i = 0; i < 4; ++i)
  1255.                 CheckEffects(&ChanTemp[i]);
  1256.         }
  1257.  
  1258.         PatternPos += 16;
  1259.  
  1260.         if (PattDelTime)
  1261.         {
  1262.             PattDelTime2 = PattDelTime;
  1263.             PattDelTime  = 0;
  1264.         }
  1265.  
  1266.         if (PattDelTime2)
  1267.         {
  1268.             PattDelTime2--;
  1269.             if (PattDelTime2) PatternPos -= 16;
  1270.         }
  1271.  
  1272.         if (PBreakFlag)
  1273.         {
  1274.             PatternPos = PBreakPosition * 16;
  1275.             PBreakPosition = 0;
  1276.             PBreakFlag = 0;
  1277.         }
  1278.  
  1279.         if ((PatternPos >= 1024) || PosJumpAssert)
  1280.             NextPosition();
  1281.     }
  1282.     else
  1283.     {
  1284.         for (i = 0; i < 4; ++i)
  1285.             CheckEffects(&ChanTemp[i]);
  1286.  
  1287.         if (PosJumpAssert)
  1288.             NextPosition();
  1289.     }
  1290. }
  1291.  
  1292. static int8_t moduleInit(uint8_t *moduleData)
  1293. {
  1294.     int8_t pattNum, *songSampleData;
  1295.     uint8_t i;
  1296.     uint16_t *p;
  1297.     int32_t loopOverflow, totalSampleSize, sampleDataOffset;
  1298.     ptChannel_t *ch;
  1299.  
  1300.     if (sampleData != NULL)
  1301.     {
  1302.         free(sampleData);
  1303.         sampleData = NULL;
  1304.     }
  1305.  
  1306.     for (i = 0; i < 4; ++i)
  1307.     {
  1308.         ch = &ChanTemp[i];
  1309.  
  1310.         ch->n_index     = i;
  1311.         ch->n_start     = NULL;
  1312.         ch->n_wavestart = NULL;
  1313.         ch->n_loopstart = NULL;
  1314.     }
  1315.  
  1316.     SongDataPtr = moduleData;
  1317.  
  1318.     pattNum = 0;
  1319.     for (i = 0; i < 128; ++i)
  1320.     {
  1321.         if (SongDataPtr[952 + i] > pattNum)
  1322.             pattNum = SongDataPtr[952 + i];
  1323.     }
  1324.     pattNum++;
  1325.  
  1326.     /* first count total sample size to allocate */
  1327.     totalSampleSize = 0;
  1328.     for (i = 0; i < 31; ++i)
  1329.     {
  1330.         p = PTR2WORD(&SongDataPtr[42 + (i * 30)]);
  1331.         totalSampleSize += (SWAP16(p[0]) * 2);
  1332.     }
  1333.  
  1334.     emptySampleDataOffset = totalSampleSize;
  1335.  
  1336.     sampleData = (int8_t *)(malloc(totalSampleSize + MAX_SAMPLE_LEN));
  1337.     if (sampleData == NULL)
  1338.         return (0);
  1339.  
  1340.     /* wipe reserved sample data area */
  1341.     memset(&sampleData[emptySampleDataOffset], 0, MAX_SAMPLE_LEN);
  1342.  
  1343.     /* setup and load samples */
  1344.     songSampleData = (int8_t *)(&SongDataPtr[1084 + (pattNum * 1024)]);
  1345.  
  1346.     sampleDataOffset = 0;
  1347.     for (i = 0; i < 31; ++i)
  1348.     {
  1349.         p = PTR2WORD(&SongDataPtr[42 + (i * 30)]);
  1350.  
  1351.         /* swap bytes in words (Amiga word -> Intel word) */
  1352.         p[0] = SWAP16(p[0]); /* n_length */
  1353.         p[2] = SWAP16(p[2]); /* n_repeat */
  1354.         p[3] = SWAP16(p[3]); /* n_replen */
  1355.  
  1356.         /* set up sample pointer and load sample */
  1357.         if (p[0] == 0)
  1358.         {
  1359.             SampleStarts[i] = &sampleData[emptySampleDataOffset];
  1360.         }
  1361.         else
  1362.         {
  1363.             SampleStarts[i] = &sampleData[sampleDataOffset];
  1364.             memcpy(SampleStarts[i], songSampleData, p[0] * 2);
  1365.  
  1366.             sampleDataOffset += (p[0] * 2);
  1367.             songSampleData   += (p[0] * 2);
  1368.         }
  1369.  
  1370.         if (p[3] == 0)
  1371.             p[3] = 1; /* fix illegal loop length (f.ex. from "Fasttracker II" .MODs) */
  1372.  
  1373.         /* adjust sample length if loop was overflowing */
  1374.         if ((p[3] > 1) && (p[2] + p[3]) > p[0])
  1375.         {
  1376.             loopOverflow = (p[2] + p[3]) - p[0];
  1377.             if ((p[0] + loopOverflow) <= (MAX_SAMPLE_LEN / 2))
  1378.             {
  1379.                 p[0] += loopOverflow;
  1380.             }
  1381.             else
  1382.             {
  1383.                 p[2] = 0;
  1384.                 p[3] = 2;
  1385.             }
  1386.         }
  1387.  
  1388.         if ((p[0] >= 1) && ((p[2] + p[3]) <= 1))
  1389.         {
  1390.             /* if no loop, zero first two samples of data to prevent "beep" */
  1391.             SampleStarts[i][0] = 0;
  1392.             SampleStarts[i][1] = 0;
  1393.         }
  1394.     }
  1395.  
  1396.     return (1);
  1397. }
  1398.  
  1399. /* MIXER RELATED CODE */
  1400.  
  1401. /* these are used to create equal powered stereo separation */
  1402. static float sinApx(float x)
  1403. {
  1404.     x = x * (2.0f - x);
  1405.     return (x * 1.09742972f + x * x * 0.31678383f);
  1406. }
  1407.  
  1408. static float cosApx(float x)
  1409. {
  1410.     x = (1.0f - x) * (1.0f + x);
  1411.     return (x * 1.09742972f + x * x * 0.31678383f);
  1412. }
  1413. /* ------------------------------------------------- */
  1414.  
  1415. static void calculatePans(int8_t stereoSeparation)
  1416. {
  1417.     uint8_t scaledPanPos;
  1418.     float p;
  1419.  
  1420.     if (stereoSeparation > 100)
  1421.         stereoSeparation = 100;
  1422.  
  1423.     scaledPanPos = (stereoSeparation * 128) / 100;
  1424.  
  1425.     p = (128 - scaledPanPos) * (1.0f / 256.0f);
  1426.     AUD[0].PANL = cosApx(p);
  1427.     AUD[0].PANR = sinApx(p);
  1428.     AUD[3].PANL = cosApx(p);
  1429.     AUD[3].PANR = sinApx(p);
  1430.  
  1431.     p = (128 + scaledPanPos) * (1.0f / 256.0f);
  1432.     AUD[1].PANL = cosApx(p);
  1433.     AUD[1].PANR = sinApx(p);
  1434.     AUD[2].PANL = cosApx(p);
  1435.     AUD[2].PANR = sinApx(p);
  1436. }
  1437.  
  1438. static void mixSampleBlock(int16_t *streamOut, uint32_t numSamples)
  1439. {
  1440.     const int8_t *dataPtr;
  1441.     uint8_t i;
  1442.     int16_t *sndOut;
  1443.     uint16_t j;
  1444.     int32_t smpL, smpR;
  1445.     float tempSample, tempVolume, out_f[2];
  1446.     paulaVoice_t *v;
  1447. #ifdef USE_BLEP
  1448.     blep_t *bSmp, *bVol;
  1449. #endif
  1450.  
  1451.     memset(masterBufferL, 0, sizeof (float) * numSamples);
  1452.     memset(masterBufferR, 0, sizeof (float) * numSamples);
  1453.  
  1454.     for (i = 0; i < 4; ++i)
  1455.     {
  1456.         v = &AUD[i];
  1457.  
  1458. #ifdef USE_BLEP
  1459.         bSmp = &blep[i];
  1460.         bVol = &blepVol[i];
  1461. #endif
  1462.         for (j = 0; v->DMA_ON && (j < numSamples); ++j)
  1463.         {
  1464.             dataPtr = v->DMA_DAT;
  1465.             if (dataPtr == NULL)
  1466.             {
  1467.                 tempSample = 0.0f;
  1468.                 tempVolume = 0.0f;
  1469.             }
  1470.             else
  1471.             {
  1472.                 tempSample = dataPtr[v->DMA_POS] * (1.0f / 128.0f);
  1473.                 tempVolume = v->SRC_VOL;
  1474.             }
  1475.  
  1476. #ifdef USE_BLEP
  1477.             if (tempSample != bSmp->lastValue)
  1478.             {
  1479.                 if ((v->LASTDELTA > 0.0f) && (v->LASTDELTA > v->LASTFRAC))
  1480.                     blepAdd(bSmp, v->LASTFRAC / v->LASTDELTA, bSmp->lastValue - tempSample);
  1481.  
  1482.                 bSmp->lastValue = tempSample;
  1483.             }
  1484.  
  1485.             if (tempVolume != bVol->lastValue)
  1486.             {
  1487.                 blepAdd(bVol, 0.0f, bVol->lastValue - tempVolume);
  1488.                 bVol->lastValue = tempVolume;
  1489.             }
  1490.  
  1491.             if (bSmp->samplesLeft) tempSample += blepRun(bSmp);
  1492.             if (bVol->samplesLeft) tempVolume += blepRun(bVol);
  1493. #endif
  1494.  
  1495.             tempSample *= tempVolume;
  1496.  
  1497.             masterBufferL[j] += (tempSample * v->PANL);
  1498.             masterBufferR[j] += (tempSample * v->PANR);
  1499.  
  1500.             v->FRAC += v->DELTA;
  1501.             if (v->FRAC >= 1.0f)
  1502.             {
  1503.                 v->FRAC -= 1.0f;
  1504.  
  1505.                 v->LASTFRAC  = v->FRAC;
  1506.                 v->LASTDELTA = v->DELTA;
  1507.  
  1508.                 if (++v->DMA_POS >= v->DMA_LEN)
  1509.                 {
  1510.                     v->DMA_POS = 0;
  1511.  
  1512.                     /* re-fetch Paula register values now */
  1513.                     v->DMA_LEN = v->SRC_LEN;
  1514.                     v->DMA_DAT = v->SRC_DAT;
  1515.                 }
  1516.             }
  1517.         }
  1518.     }
  1519.  
  1520.     sndOut = streamOut;
  1521.     for (j = 0; j < numSamples; ++j)
  1522.     {
  1523.         if (!DisableMixer)
  1524.         {
  1525.             out_f[0] = masterBufferL[j];
  1526.             out_f[1] = masterBufferR[j];
  1527.  
  1528. #ifdef USE_LOWPASS
  1529.             lossyIntegrator(&filterLo, out_f, out_f);
  1530. #endif
  1531.  
  1532. #ifdef LED_FILTER
  1533.             if (LEDStatus)
  1534.                 lossyIntegratorLED(filterLEDC, &filterLED, out_f, out_f);
  1535. #endif
  1536.  
  1537. #ifdef USE_HIGHPASS
  1538.             lossyIntegratorHighPass(&filterHi, out_f, out_f);
  1539. #endif
  1540.  
  1541.             /* normalize amplitude */
  1542.             out_f[0] *= (32767.0f / NORM_FACTOR);
  1543.             out_f[1] *= (32767.0f / NORM_FACTOR);
  1544.  
  1545.             /* truncate to signed 32-bit integer */
  1546.             smpL = (int32_t)(out_f[0]);
  1547.             smpR = (int32_t)(out_f[1]);
  1548.  
  1549.             /* clamp to 16-bit signed range */
  1550.             CLAMP16(smpL);
  1551.             CLAMP16(smpR);
  1552.  
  1553.             /* truncate to signed 16-bit integer and store in output buffer */
  1554.             *sndOut++ = (int16_t)(smpL);
  1555.             *sndOut++ = (int16_t)(smpR);
  1556.         }
  1557.         else
  1558.         {
  1559.             *sndOut++ = 0;
  1560.             *sndOut++ = 0;
  1561.         }
  1562.     }
  1563. }
  1564.  
  1565. static void CALLBACK waveOutProc(HWAVEOUT _hWaveOut, UINT uMsg,
  1566.     DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
  1567. {
  1568.     int16_t *outputStream;
  1569.     int32_t sampleBlock, samplesTodo;
  1570.     WAVEHDR *waveBlockHeader;
  1571.  
  1572.     /* make compiler happy! (warning C4100) */
  1573.     (void)(dwParam2);
  1574.     (void)(dwInstance);
  1575.  
  1576.     if (uMsg == MM_WOM_DONE)
  1577.     {
  1578.         mixingMutex = 1;
  1579.  
  1580.         waveBlockHeader = (WAVEHDR *)(dwParam1);
  1581.         waveOutUnprepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  1582.  
  1583.         if (isMixing)
  1584.         {
  1585.             memcpy(waveBlockHeader->lpData, mixerBuffer, soundBufferSize);
  1586.  
  1587.             waveOutPrepareHeader(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  1588.             waveOutWrite(_hWaveOut, waveBlockHeader, sizeof (WAVEHDR));
  1589.  
  1590.             outputStream = (int16_t *)(mixerBuffer);
  1591.             sampleBlock  = soundBufferSize / 4;
  1592.  
  1593.             while (sampleBlock)
  1594.             {
  1595.                 samplesTodo = (sampleBlock < samplesLeft) ? sampleBlock : samplesLeft;
  1596.                 if (samplesTodo > soundBufferSize)
  1597.                     samplesTodo = soundBufferSize;
  1598.  
  1599.                 if (samplesTodo > 0)
  1600.                 {
  1601.                     mixSampleBlock(outputStream, samplesTodo);
  1602.                     outputStream += (samplesTodo * 2);
  1603.  
  1604.                     sampleBlock -= samplesTodo;
  1605.                     samplesLeft -= samplesTodo;
  1606.                 }
  1607.                 else
  1608.                 {
  1609.                     if (!DisableMixer)
  1610.                         IntMusic();
  1611.  
  1612.                     samplesLeft = samplesPerFrame;
  1613.                 }
  1614.             }
  1615.         }
  1616.  
  1617.         mixingMutex = 0;
  1618.     }
  1619. }
  1620.  
  1621. void pt2play_PauseSong(int8_t pause)
  1622. {
  1623.     DisableMixer = pause ? 1 : 0;
  1624. }
  1625.  
  1626. int8_t pt2play_PlaySong(uint8_t *moduleData, int8_t tempoMode)
  1627. {
  1628.     uint8_t i;
  1629.  
  1630.     if (!moduleInit(moduleData))
  1631.         return (0);
  1632.  
  1633.     DisableMixer = 1;
  1634.  
  1635.     memset(AUD, 0, sizeof (AUD));
  1636.     for (i = 0; i < 4; ++i)
  1637.     {
  1638.         AUD[i].DMA_DAT = NULL;
  1639.         AUD[i].SRC_DAT = NULL;
  1640.     }
  1641.  
  1642.     calculatePans(STEREO_SEP);
  1643.  
  1644. #ifdef USE_BLEP
  1645.     memset(blep,    0, sizeof (blep));
  1646.     memset(blepVol, 0, sizeof (blepVol));
  1647. #endif
  1648.  
  1649. #ifdef USE_LOWPASS
  1650.     clearLossyIntegrator(&filterLo);
  1651. #endif
  1652.  
  1653. #ifdef LED_FILTER
  1654.     clearLEDFilter(&filterLED);
  1655. #endif
  1656.  
  1657. #ifdef USE_HIGHPASS
  1658.     clearLossyIntegrator(&filterHi);
  1659. #endif
  1660.  
  1661.     CurrSpeed      = 6;
  1662.     Counter        = 0;
  1663.     SongPosition   = 0;
  1664.     PatternPos     = 0;
  1665.     PattDelTime    = 0;
  1666.     PattDelTime2   = 0;
  1667.     PBreakPosition = 0;
  1668.     PosJumpAssert  = 0;
  1669.     PBreakFlag     = 0;
  1670.     LowMask        = 0xFF;
  1671.     TempoMode      = tempoMode ? VBLANK_TEMPO_MODE : CIA_TEMPO_MODE;
  1672.     SongPlaying    = 1;
  1673.     DisableMixer   = 0;
  1674.  
  1675. #ifdef LED_FILTER
  1676.     LEDStatus = 0;
  1677. #endif
  1678.  
  1679.     return (1);
  1680. }
  1681.  
  1682. void pt2play_SetStereoSep(uint8_t percentage)
  1683. {
  1684.     calculatePans(percentage);
  1685. }
  1686.  
  1687. static int8_t openMixer(uint32_t _samplingFrequency, uint32_t _soundBufferSize)
  1688. {
  1689.     uint8_t i;
  1690.     MMRESULT r;
  1691.  
  1692.     if (!hWaveOut)
  1693.     {
  1694.         f_outputFreq        = (float)(_samplingFrequency);
  1695.         soundBufferSize     = _soundBufferSize;
  1696.         masterBufferL       = (float *)(malloc(soundBufferSize * sizeof (float)));
  1697.         masterBufferR       = (float *)(malloc(soundBufferSize * sizeof (float)));
  1698.         wfx.nSamplesPerSec  = _samplingFrequency;
  1699.         wfx.wBitsPerSample  = 16;
  1700.         wfx.nChannels       = 2;
  1701.         wfx.cbSize          = 0;
  1702.         wfx.wFormatTag      = WAVE_FORMAT_PCM;
  1703.         wfx.nBlockAlign     = (wfx.wBitsPerSample * wfx.nChannels) / 8;
  1704.         wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
  1705.  
  1706.         if ((masterBufferL == NULL) || (masterBufferR == NULL))
  1707.             return (0); /* gets free'd later */
  1708.  
  1709.         r = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)(waveOutProc), 0L, CALLBACK_FUNCTION);
  1710.         if (r != MMSYSERR_NOERROR) return (0);
  1711.  
  1712.         for (i = 0; i < MIX_BUF_NUM; ++i)
  1713.         {
  1714.             waveBlocks[i].dwBufferLength = soundBufferSize;
  1715.             waveBlocks[i].lpData = (LPSTR)(calloc(soundBufferSize, 1));
  1716.             if (waveBlocks[i].lpData == NULL)
  1717.                 return (0); /* gets free'd later */
  1718.  
  1719.             waveOutPrepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  1720.             waveOutWrite(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  1721.         }
  1722.  
  1723.         mixerBuffer = (int8_t *)(calloc(soundBufferSize, 1));
  1724.         if (mixerBuffer == NULL)
  1725.             return (0); /* gets free'd later */
  1726.  
  1727.         SetReplayerBPM(125);
  1728.  
  1729. #ifdef USE_LOWPASS
  1730.         // Amiga 500 RC low-pass filter (R = 360 ohm, C = 0.1uF)
  1731.         // hz = 1 / (2pi * R * C)    = ~4421.0Hz
  1732.         calcCoeffLossyIntegrator(f_outputFreq, 4421.0f, &filterLo);
  1733. #endif
  1734.  
  1735. #ifdef LED_FILTER
  1736.         // Amiga 500 Sallen-Key "LED" filter (R1 = 10k ohm, R2 = 10k ohm, C1 = 6800pf, C2 = 3900pf)
  1737.         // hz = 1 / (2pi * root(R1 * R2 * C1 * C2))    = ~3090.5Hz
  1738.         calcCoeffLED(f_outputFreq, 3090.5f, &filterLEDC);
  1739. #endif
  1740.  
  1741. #ifdef USE_HIGHPASS
  1742.         // Amiga 500 RC high-pass filter (R = 1390 ohm, C = 22uF)
  1743.         // hz = 1 / (2pi * R * C)    = ~5.2Hz
  1744.         calcCoeffLossyIntegrator(f_outputFreq, 5.2f, &filterHi);
  1745. #endif
  1746.  
  1747.         isMixing = 1;
  1748.         return (1);
  1749.     }
  1750.  
  1751.     return (1);
  1752. }
  1753.  
  1754. void pt2play_Close(void)
  1755. {
  1756.     uint8_t i;
  1757.  
  1758.     DisableMixer = 1;
  1759.  
  1760.     if (isMixing)
  1761.     {
  1762.         isMixing = 0;
  1763.         while (mixingMutex);
  1764.  
  1765.         if (hWaveOut)
  1766.         {
  1767.             for (i = 0; i < MIX_BUF_NUM; ++i)
  1768.                 waveOutUnprepareHeader(hWaveOut, &waveBlocks[i], sizeof (WAVEHDR));
  1769.  
  1770.             for (i = 0; i < MIX_BUF_NUM; ++i)
  1771.             {
  1772.                 while (waveBlocks[i].dwFlags & WHDR_PREPARED) SleepEx(1, 1); // wait
  1773.  
  1774.                 if (waveBlocks[i].lpData != NULL)
  1775.                 {
  1776.                     free(waveBlocks[i].lpData);
  1777.                     waveBlocks[i].lpData = NULL;
  1778.                 }
  1779.             }
  1780.  
  1781.             waveOutReset(hWaveOut);
  1782.             waveOutClose(hWaveOut);
  1783.  
  1784.             hWaveOut = 0;
  1785.  
  1786.             if (mixerBuffer != NULL)
  1787.             {
  1788.                 free(mixerBuffer);
  1789.                 mixerBuffer = NULL;
  1790.             }
  1791.  
  1792.             if (masterBufferL != NULL)
  1793.             {
  1794.                 free(masterBufferL);
  1795.                 masterBufferL = NULL;
  1796.             }
  1797.  
  1798.             if (masterBufferR != NULL)
  1799.             {
  1800.                 free(masterBufferR);
  1801.                 masterBufferR = NULL;
  1802.             }
  1803.         }
  1804.     }
  1805.  
  1806.     if (sampleData != NULL)
  1807.     {
  1808.         free(sampleData);
  1809.         sampleData = NULL;
  1810.     }
  1811. }
  1812.  
  1813. int8_t pt2play_Init(uint32_t outputFreq)
  1814. {
  1815.     SongPlaying = 0;
  1816.  
  1817.     if (!openMixer(outputFreq, MIX_BUF_LEN))
  1818.     {
  1819.         pt2play_Close();
  1820.         return (0);
  1821.     }
  1822.  
  1823.     return (1);
  1824. }
  1825. /* END OF FILE */
RAW Paste Data
Top