daily pastebin goal
32%
SHARE
TWEET

fc14play v1.26

8bitbubsy Nov 17th, 2015 (edited) 753 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. ** fc14play v1.26 - 30th of March 2019 - https://16-bits.org
  3. ** =========================================================
  4. **                 - NOT BIG ENDIAN SAFE! -
  5. **
  6. ** Very accurate C port of Future Composer 1.4's replayer,
  7. ** by Olav "8bitbubsy" Sørensen, using a FC1.4 disassembly (its supplied replayer code was buggy).
  8. ** Works correctly with v1.0..v1.3 modules as well.
  9. **
  10. ** The BLEP (Band-Limited Step) and filter routines were coded by aciddose.
  11. ** This makes the replayer sound much similar to a real Amiga.
  12. **
  13. ** You need to link winmm.lib for this to compile (-lwinmm).f
  14. ** Alternatively, you can change out the mixer functions at the bottom with
  15. ** your own for your OS.
  16. **
  17. ** Example of fc14play usage:
  18. ** #include "fc14play.h"
  19. ** #include "songdata.h"
  20. **
  21. ** fc14play_PlaySong(songData, songDataLength, 44100);
  22. ** mainLoop();
  23. ** fc14play_Close();
  24. **
  25. ** To turn a song into an include file like in the example, you can use my win32
  26. ** bin2h tool from here: https://16-bits.org/etc/bin2h.zip
  27. **
  28. ** Changes in v1.26:
  29. ** - Removed the "DMA Wait" stuff, this is not needed in the way I do things.
  30. **   This actually fixes "double kickdrum" in tristar-scoopex.fc.
  31. ** - Mixer is now using double-precision instead of single-precision accuracy.
  32. **
  33. ** Changes in v1.25:
  34. ** - Code cleanup (uses the "bool" type now, spaces -> tabs, comment style change)
  35. **
  36. ** Changes in v1.24:
  37. ** - Some code cleanup
  38. ** - Small optimziation to audio mixer
  39. **
  40. ** Changes in v1.23:
  41. ** - WinMM mixer has been rewritten to be safe (DON'T use syscalls in callback -MSDN)
  42. ** - Some small changes to the fc14play functions (easier to use and safer!)
  43. */
  44.  
  45. /* fc14play.h:
  46.  
  47. #ifndef __FC14PLAY_H
  48. #define __FC14PLAY_H
  49.  
  50. #include <stdint.h>
  51. #include <stdbool.h>
  52.  
  53. bool fc14play_PlaySong(const uint8_t *moduleData, uint32_t dataLength, uint32_t audioFreq);
  54. void fc14play_Close(void);
  55. void fc14play_PauseSong(bool flag); // true/false
  56. void fc14play_TogglePause(void);
  57. void fc14play_SetStereoSep(uint8_t percentage); // 0..100
  58. uint32_t fc14play_GetMixerTicks(void); // returns the amount of milliseconds of mixed audio (not realtime)
  59.  
  60. #endif
  61. */
  62.  
  63. // == USER ADJUSTABLE SETTINGS ==
  64. #define STEREO_SEP (19)    /* --> Stereo separation in percent - 0 = mono, 100 = hard pan (like Amiga) */
  65. #define USE_HIGHPASS       /* --> ~5Hz HP filter present in all Amigas - comment out for a tiny speed-up */
  66. //#define USE_LOWPASS      /* --> ~5kHz LP filter present in all Amigas except A1200 - comment out for sharper sound (and tiny speed-up) */
  67. #define USE_BLEP           /* --> Reduces some aliasing in the sound (closer to real Amiga) - comment out for a speed-up */
  68. #define MIX_BUF_SAMPLES 4096
  69.  
  70. #ifdef _MSC_VER
  71. #define inline __forceinline
  72. #endif
  73.  
  74. #include <stdio.h>
  75. #include <stdlib.h>
  76. #include <string.h>
  77. #include <stdint.h>
  78. #include <stdbool.h>
  79. #include <math.h> // tanf()
  80.  
  81. // main crystal oscillator
  82. #define AMIGA_PAL_XTAL_HZ 28375160
  83.  
  84. #define PAULA_CLK (AMIGA_PAL_XTAL_HZ / 8)
  85. #define PAULA_VOICES 4
  86. #define SEQ_SIZE 13
  87. #define PAT_END_MARKER 0x49
  88. #define NUM_SAMPLES 10
  89. #define NUM_WAVEFORMS 80
  90. #define NUM_WAVEFORMS_SMOD 47
  91.  
  92. #define BLEP_ZC 8
  93. #define BLEP_OS 5
  94. #define BLEP_SP 5
  95. #define BLEP_NS (BLEP_ZC * BLEP_OS / BLEP_SP)
  96. #define BLEP_RNS 7 // RNS = (2^ > NS) - 1
  97.  
  98. #ifdef USE_BLEP
  99. typedef struct blep_t
  100. {
  101.     int32_t index, samplesLeft;
  102.     double dBuffer[BLEP_RNS + 1], dLastValue;
  103. } blep_t;
  104. #endif
  105.  
  106. typedef struct paulaVoice_t
  107. {
  108.     volatile bool active;
  109.     const int8_t *data, *newData;
  110.     int32_t length, newLength, pos;
  111.     double dVolume, dDelta, dPhase, dPanL, dPanR;
  112. #ifdef USE_BLEP
  113.     double dLastDelta, dLastPhase;
  114. #endif
  115. } paulaVoice_t;
  116.  
  117. #if defined(USE_HIGHPASS) || defined(USE_LOWPASS)
  118. typedef struct lossyIntegrator_t
  119. {
  120.     double dBuffer[2], dCoeff[2];
  121. } lossyIntegrator_t;
  122. #endif
  123.  
  124. typedef struct soundInfo_t // do not touch!
  125. {
  126.     int8_t *data;
  127.     uint16_t length;
  128.     int8_t *repeat;
  129.     uint16_t replen;
  130. } soundInfo_t;
  131.  
  132. typedef struct fcChannel_t
  133. {
  134.     bool vibratoUp, portaDelay, pitchBendDelay, volSlideDelay;
  135.     int8_t pitchBendValue, pitchBendCounter, note, noteTranspose;
  136.     int8_t soundTranspose, *loopStart, volume, periodTranspose;
  137.     const uint8_t *freqTabPtr, *volTabPtr;
  138.     uint8_t voiceIndex, *seqStartPtr, *patPtr;
  139.     uint8_t freqSusCounter, volSusCounter;
  140.     uint8_t vibratoSpeed, vibratoDepth, vibratoCounter;
  141.     uint8_t vibratoDelay, volSlideSpeed;
  142.     uint8_t volSlideCounter, portaParam, volDelayCounter;
  143.     uint8_t volDelayLength;
  144.     int16_t portaValue;
  145.     uint16_t loopLength, freqTabPos, volTabPos, patPos;
  146.     uint32_t seqPos;
  147. } fcChannel_t;
  148.  
  149. static volatile bool musicPaused;
  150. static bool fc14;
  151. static int8_t *ptr8s_1, *ptr8s_1;
  152. static uint8_t *songData, *ptr8u_1, *ptr8u_2, spdtemp, spdtemp2, respcnt, repspd;
  153. static uint8_t *SEQpoint, *PATpoint, *FRQpoint, *VOLpoint, stereoSep = STEREO_SEP;
  154. static uint16_t oldPeriod;
  155. static int32_t soundBufferSize, samplesPerFrameLeft, samplesPerFrame;
  156. static uint32_t audioRate, numSequences, sampleCounter;
  157. static double oldVoiceDelta, *dMixerBufferL, *dMixerBufferR, dAudioRate;
  158. static paulaVoice_t paula[PAULA_VOICES];
  159. static fcChannel_t Channel[PAULA_VOICES];
  160. static soundInfo_t samples[NUM_SAMPLES + NUM_WAVEFORMS];
  161. #ifdef USE_BLEP
  162. static blep_t blep[PAULA_VOICES], blepVol[PAULA_VOICES];
  163. #endif
  164. #ifdef USE_HIGHPASS
  165. static lossyIntegrator_t filterHi;
  166. #endif
  167. #ifdef USE_LOWPASS
  168. static lossyIntegrator_t filterLo;
  169. #endif
  170.  
  171. #define LERP(x, y, z) ((x) + ((y) - (x)) * (z))
  172. #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
  173. #define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31);
  174. #define PTR2LONG(x) ((uint32_t *)(x))
  175. #define PTR2WORD(x) ((uint16_t *)(x))
  176. #define SWAP16(x) ((uint16_t)(((x) << 8) | ((x) >> 8)))
  177. #define SWAP32(value) \
  178. ( \
  179.     (((uint32_t)((value) & 0x000000FF)) << 24) | \
  180.     (((uint32_t)((value) & 0x0000FF00)) <<  8) | \
  181.     (((uint32_t)((value) & 0x00FF0000)) >>  8) | \
  182.     (((uint32_t)((value) & 0xFF000000)) >> 24)   \
  183. )
  184.  
  185. static const uint8_t silentTable[8] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE1 };
  186.  
  187. static const uint16_t periods[128] =
  188. {
  189.     // 1.0..1.3 periods
  190.     0x06B0,0x0650,0x05F4,0x05A0,0x054C,0x0500,0x04B8,0x0474,0x0434,0x03F8,0x03C0,0x038A,
  191.     0x0358,0x0328,0x02FA,0x02D0,0x02A6,0x0280,0x025C,0x023A,0x021A,0x01FC,0x01E0,0x01C5,
  192.     0x01AC,0x0194,0x017D,0x0168,0x0153,0x0140,0x012E,0x011D,0x010D,0x00FE,0x00F0,0x00E2,
  193.     0x00D6,0x00CA,0x00BE,0x00B4,0x00AA,0x00A0,0x0097,0x008F,0x0087,0x007F,0x0078,0x0071,
  194.     0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,
  195.  
  196.     // 1.4 periods (one extra octave)
  197.     0x0D60,0x0CA0,0x0BE8,0x0B40,0x0A98,0x0A00,0x0970,0x08E8,0x0868,0x07F0,0x0780,0x0714,
  198.     0x06B0,0x0650,0x05F4,0x05A0,0x054C,0x0500,0x04B8,0x0474,0x0434,0x03F8,0x03C0,0x038A,
  199.     0x0358,0x0328,0x02FA,0x02D0,0x02A6,0x0280,0x025C,0x023A,0x021A,0x01FC,0x01E0,0x01C5,
  200.     0x01AC,0x0194,0x017D,0x0168,0x0153,0x0140,0x012E,0x011D,0x010D,0x00FE,0x00F0,0x00E2,
  201.     0x00D6,0x00CA,0x00BE,0x00B4,0x00AA,0x00A0,0x0097,0x008F,0x0087,0x007F,0x0078,0x0071,
  202.     0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071,0x0071
  203. };
  204.  
  205. static const int8_t waveformDatas[1344] =
  206. {
  207.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  208.     0x3F,0x37,0x2F,0x27,0x1F,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  209.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  210.     0xC0,0x37,0x2F,0x27,0x1F,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  211.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  212.     0xC0,0xB8,0x2F,0x27,0x1F,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  213.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  214.     0xC0,0xB8,0xB0,0x27,0x1F,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  215.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  216.     0xC0,0xB8,0xB0,0xA8,0x1F,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  217.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  218.     0xC0,0xB8,0xB0,0xA8,0xA0,0x17,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  219.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  220.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x0F,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  221.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  222.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x07,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  223.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  224.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0xFF,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  225.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  226.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x07,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  227.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  228.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x0F,0x17,0x1F,0x27,0x2F,0x37,
  229.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  230.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x90,0x17,0x1F,0x27,0x2F,0x37,
  231.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  232.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x90,0x98,0x1F,0x27,0x2F,0x37,
  233.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  234.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x90,0x98,0xA0,0x27,0x2F,0x37,
  235.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  236.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x90,0x98,0xA0,0xA8,0x2F,0x37,
  237.     0xC0,0xC0,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,0x00,0xF8,0xF0,0xE8,0xE0,0xD8,0xD0,0xC8,
  238.     0xC0,0xB8,0xB0,0xA8,0xA0,0x98,0x90,0x88,0x80,0x88,0x90,0x98,0xA0,0xA8,0xB0,0x37,
  239.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  240.     0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  241.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  242.     0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  243.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  244.     0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  245.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  246.     0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  247.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  248.     0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  249.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  250.     0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  251.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  252.     0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  253.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  254.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  255.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  256.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  257.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  258.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  259.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  260.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  261.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  262.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,0x7F,
  263.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  264.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,0x7F,
  265.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,
  266.     0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x81,0x7F,0x7F,0x7F,
  267.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
  268.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7F,0x7F,
  269.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
  270.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7F,
  271.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  272.     0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  273.     0x80,0x80,0x80,0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  274.     0x80,0x80,0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  275.     0x80,0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  276.     0x80,0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  277.     0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  278.     0x80,0x80,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
  279.     0x80,0x80,0x90,0x98,0xA0,0xA8,0xB0,0xB8,0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,
  280.     0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x7F,
  281.     0x80,0x80,0xA0,0xB0,0xC0,0xD0,0xE0,0xF0,0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,
  282.     0x45,0x45,0x79,0x7D,0x7A,0x77,0x70,0x66,0x61,0x58,0x53,0x4D,0x2C,0x20,0x18,0x12,
  283.     0x04,0xDB,0xD3,0xCD,0xC6,0xBC,0xB5,0xAE,0xA8,0xA3,0x9D,0x99,0x93,0x8E,0x8B,0x8A,
  284.     0x45,0x45,0x79,0x7D,0x7A,0x77,0x70,0x66,0x5B,0x4B,0x43,0x37,0x2C,0x20,0x18,0x12,
  285.     0x04,0xF8,0xE8,0xDB,0xCF,0xC6,0xBE,0xB0,0xA8,0xA4,0x9E,0x9A,0x95,0x94,0x8D,0x83,
  286.     0x00,0x00,0x40,0x60,0x7F,0x60,0x40,0x20,0x00,0xE0,0xC0,0xA0,0x80,0xA0,0xC0,0xE0,
  287.     0x00,0x00,0x40,0x60,0x7F,0x60,0x40,0x20,0x00,0xE0,0xC0,0xA0,0x80,0xA0,0xC0,0xE0,
  288.     0x80,0x80,0x90,0x98,0xA0,0xA8,0xB0,0xB8,0xC0,0xC8,0xD0,0xD8,0xE0,0xE8,0xF0,0xF8,
  289.     0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x7F,
  290.     0x80,0x80,0xA0,0xB0,0xC0,0xD0,0xE0,0xF0,0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70
  291. };
  292.  
  293. #ifdef USE_BLEP
  294.  
  295. /* Why this table is not represented as readable float (double) numbers:
  296. ** Accurate float (double) representation in string format requires at least 14 digits and normalized
  297. ** (scientific) notation, notwithstanding compiler issues with precision or rounding error.
  298. ** Also, don't touch this table ever, just keep it exactly identical!
  299. */
  300.  
  301. /* TODO: get a proper double-precision table. This one is converted from float */
  302. static const uint64_t dBlepData[48] =
  303. {
  304.     0x3FEFFC3E20000000, 0x3FEFFAA900000000, 0x3FEFFAD460000000, 0x3FEFFA9C60000000,
  305.     0x3FEFF5B0A0000000, 0x3FEFE42A40000000, 0x3FEFB7F5C0000000, 0x3FEF599BE0000000,
  306.     0x3FEEA5E3C0000000, 0x3FED6E7080000000, 0x3FEB7F7960000000, 0x3FE8AB9E40000000,
  307.     0x3FE4DCA480000000, 0x3FE0251880000000, 0x3FD598FB80000000, 0x3FC53D0D60000000,
  308.     0x3F8383A520000000, 0xBFBC977CC0000000, 0xBFC755C080000000, 0xBFC91BDBA0000000,
  309.     0xBFC455AFC0000000, 0xBFB6461340000000, 0xBF7056C400000000, 0x3FB1028220000000,
  310.     0x3FBB5B7E60000000, 0x3FBC5903A0000000, 0x3FB55403E0000000, 0x3FA3CED340000000,
  311.     0xBF7822DAE0000000, 0xBFA2805D00000000, 0xBFA7140D20000000, 0xBFA18A7760000000,
  312.     0xBF87FF7180000000, 0x3F88CBFA40000000, 0x3F9D4AEC80000000, 0x3FA14A3AC0000000,
  313.     0x3F9D5C5AA0000000, 0x3F92558B40000000, 0x3F7C997EE0000000, 0x0000000000000000,
  314.     0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
  315.     0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000
  316. };
  317. #endif
  318.  
  319. static const uint8_t waveformLengths[47] =
  320. {
  321.     0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  322.     0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  323.     0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
  324.     0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
  325.     0x10, 0x08, 0x10, 0x10, 0x08, 0x08, 0x18
  326. };
  327.  
  328. static bool openMixer(uint32_t audioFreq);
  329. static void closeMixer(void);
  330.  
  331. // CODE START
  332.  
  333. static void paulaStopDMA(uint8_t i)
  334. {
  335.     paula[i].active = false;
  336. }
  337.  
  338. static void paulaStartDMA(uint8_t i)
  339. {
  340.     paulaVoice_t *v;
  341.  
  342.     v = &paula[i];
  343.  
  344.     v->dPhase = 0.0;
  345.     v->pos    = 0;
  346.     v->data   = v->newData;
  347.     v->length = v->newLength;
  348.     v->active = true;
  349. }
  350.  
  351. static void paulaSetPeriod(uint8_t i, uint16_t period)
  352. {
  353.     paulaVoice_t *v;
  354.  
  355.     v = &paula[i];
  356.  
  357.     if (period == 0)
  358.     {
  359.         v->dDelta = 0.0;
  360.         return;
  361.     }
  362.  
  363.     // confirmed Paula behavior
  364.     if (period < 113)
  365.         period = 113;
  366.  
  367.     if (period == oldPeriod)
  368.     {
  369.         v->dDelta = oldVoiceDelta;
  370.     }
  371.     else
  372.     {
  373.         oldPeriod = period;
  374.  
  375.         v->dDelta = ((double)(PAULA_CLK) / period) / dAudioRate;
  376.         oldVoiceDelta = v->dDelta;
  377.     }
  378.  
  379. #ifdef USE_BLEP
  380.     if (v->dLastDelta == 0.0)
  381.         v->dLastDelta = v->dDelta;
  382. #endif
  383. }
  384.  
  385. static void paulaSetVolume(uint8_t i, uint16_t vol)
  386. {
  387.     vol &= 127;
  388.     if (vol > 64)
  389.         vol = 64;
  390.  
  391.     paula[i].dVolume = vol * (1.0 / 64.0);
  392. }
  393.  
  394. static void paulaSetLength(uint8_t i, uint16_t len)
  395. {
  396.     paula[i].newLength = len * 2;
  397. }
  398.  
  399. static void paulaSetData(uint8_t i, const int8_t *src)
  400. {
  401.     paula[i].newData = src;
  402. }
  403.  
  404. #if defined(USE_HIGHPASS) || defined(USE_LOWPASS)
  405. static void calcCoeffLossyIntegrator(double dSr, double dHz, lossyIntegrator_t *filter)
  406. {
  407.     filter->dCoeff[0] = tan((M_PI * dHz) / dSr);
  408.     filter->dCoeff[1] = 1.0 / (1.0 + filter->dCoeff[0]);
  409. }
  410.  
  411. static void clearLossyIntegrator(lossyIntegrator_t *filter)
  412. {
  413.     filter->dBuffer[0] = 0.0;
  414.     filter->dBuffer[1] = 0.0;
  415. }
  416.  
  417. void lossyIntegrator(lossyIntegrator_t *filter, double *dIn, double *dOut)
  418. {
  419.     double dOutput;
  420.  
  421.     // left channel low-pass
  422.     dOutput = (filter->dCoeff[0] * dIn[0] + filter->dBuffer[0]) * filter->dCoeff[1];
  423.     filter->dBuffer[0] = filter->dCoeff[0] * (dIn[0] - dOutput) + dOutput + 1e-10;
  424.     dOut[0] = dOutput;
  425.  
  426.     // right channel low-pass
  427.     dOutput = (filter->dCoeff[0] * dIn[1] + filter->dBuffer[1]) * filter->dCoeff[1];
  428.     filter->dBuffer[1] = filter->dCoeff[0] * (dIn[1] - dOutput) + dOutput + 1e-10;
  429.     dOut[1] = dOutput;
  430. }
  431.  
  432. void lossyIntegratorHighPass(lossyIntegrator_t *filter, double *dIn, double *dOut)
  433. {
  434.     double dLow[2];
  435.  
  436.     lossyIntegrator(filter, dIn, dLow);
  437.  
  438.     dOut[0] = dIn[0] - dLow[0];
  439.     dOut[1] = dIn[1] - dLow[1];
  440. }
  441. #endif
  442.  
  443. #ifdef USE_BLEP
  444. void blepAdd(blep_t *b, double dOffset, double dAmplitude)
  445. {
  446.     int8_t n;
  447.     int32_t i;
  448.     const double *dBlepSrc;
  449.     double f;
  450.  
  451.     f = dOffset * BLEP_SP;
  452.     i = (int32_t)(f); // get integer part of f
  453.     dBlepSrc = (const double *)(dBlepData) + i + BLEP_OS;
  454.     f -= i; // remove integer part from f
  455.  
  456.     i = b->index;
  457.  
  458.     n = BLEP_NS;
  459.     while (n--)
  460.     {
  461.         b->dBuffer[i] += (dAmplitude * LERP(dBlepSrc[0], dBlepSrc[1], f));
  462.         i = (i + 1) & BLEP_RNS;
  463.  
  464.         dBlepSrc += BLEP_SP;
  465.     }
  466.  
  467.     b->samplesLeft = BLEP_NS;
  468. }
  469.  
  470. double blepRun(blep_t *b)
  471. {
  472.     double fBlepOutput;
  473.  
  474.     fBlepOutput = b->dBuffer[b->index];
  475.     b->dBuffer[b->index] = 0.0;
  476.  
  477.     b->index = (b->index + 1) & BLEP_RNS;
  478.  
  479.     b->samplesLeft--;
  480.     return (fBlepOutput);
  481. }
  482. #endif
  483.  
  484. static bool init_music(const uint8_t *moduleData)
  485. {
  486.     uint8_t i;
  487.     soundInfo_t *s;
  488.  
  489.     fc14 = (*PTR2LONG(&moduleData[0]) == 0x34314346); // "FC14"
  490.  
  491.     if ((*PTR2LONG(&moduleData[0]) != 0x444F4D53) && !fc14) // "SMOD"
  492.         return (false);
  493.  
  494.     // setup pointers...
  495.  
  496.     SEQpoint = (uint8_t *)(&moduleData[fc14 ? 180 : 100]);
  497.     PATpoint = (uint8_t *)(&moduleData[SWAP32(*PTR2LONG(&moduleData[8]))]);
  498.     FRQpoint = (uint8_t *)(&moduleData[SWAP32(*PTR2LONG(&moduleData[16]))]);
  499.     VOLpoint = (uint8_t *)(&moduleData[SWAP32(*PTR2LONG(&moduleData[24]))]);
  500.    
  501.     // load samples
  502.  
  503.     ptr8s_1 =  (int8_t *)(&moduleData[SWAP32(*PTR2LONG(&moduleData[32]))]);
  504.     ptr8u_2 = (uint8_t *)(&moduleData[40]);
  505.  
  506.     for (i = 0; i < NUM_SAMPLES; ++i)
  507.     {
  508.         samples[i].data   = ptr8s_1;
  509.         samples[i].length = SWAP16(*PTR2WORD(ptr8u_2));                   ptr8u_2 += 2;
  510.         samples[i].repeat = &samples[i].data[SWAP16(*PTR2WORD(ptr8u_2))]; ptr8u_2 += 2;
  511.         samples[i].replen = SWAP16(*PTR2WORD(ptr8u_2));                   ptr8u_2 += 2;
  512.  
  513.         // fix endless beep on non-looping samples (FC14 doesn't do this)
  514.         if (samples[i].replen <= 1)
  515.         {
  516.             samples[i].replen = 1;
  517.             if (samples[i].length >= 1)
  518.             {
  519.                 if (*PTR2LONG(samples[i].data) != 0x504D5353) // "SSMP"
  520.                     *PTR2WORD(samples[i].data)  = 0;
  521.             }
  522.         }
  523.  
  524.         ptr8s_1 += ((samples[i].length * 2) + 2);
  525.     }
  526.  
  527.     // load waveforms
  528.  
  529.     if (fc14)
  530.     {
  531.         ptr8s_1 =  (int8_t *)(&moduleData[SWAP32(*PTR2LONG(&moduleData[36]))]);
  532.         ptr8u_2 = (uint8_t *)(&moduleData[100]);
  533.  
  534.         for (i = 0; i < NUM_WAVEFORMS; ++i)
  535.         {
  536.             s = &samples[NUM_SAMPLES + i];
  537.  
  538.             s->data   = ptr8s_1;
  539.             s->length = *ptr8u_2++;
  540.             s->repeat = ptr8s_1;
  541.             s->replen = s->length;
  542.  
  543.             ptr8s_1 += (s->length * 2);
  544.         }
  545.     }
  546.     else
  547.     {
  548.         ptr8s_1 = (int8_t *)(waveformDatas);
  549.         for (i = 0; i < NUM_WAVEFORMS; ++i)
  550.         {
  551.             s = &samples[NUM_SAMPLES + i];
  552.  
  553.             if (i < NUM_WAVEFORMS_SMOD)
  554.             {
  555.                 s->data   = ptr8s_1;
  556.                 s->length = waveformLengths[i];
  557.                 s->repeat = s->data;
  558.                 s->replen = s->length;
  559.  
  560.                 ptr8s_1 += (s->length * 2);
  561.             }
  562.             else
  563.             {
  564.                 s->data   = NULL;
  565.                 s->length = 0;
  566.                 s->repeat = NULL;
  567.                 s->replen = 1;
  568.             }
  569.         }
  570.     }
  571.  
  572.     // get number of sequences and make it a multiple of 13 (SEQ_SIZE)
  573.     numSequences = (uint32_t)(SWAP32(*PTR2LONG(&moduleData[4])) / SEQ_SIZE) * SEQ_SIZE;
  574.  
  575.     return (true);
  576. }
  577.  
  578. static void restart_song(void)
  579. {
  580.     uint8_t i;
  581.     fcChannel_t *ch;
  582.  
  583.     memset(Channel, 0, sizeof (Channel));
  584.     for (i = 0; i < PAULA_VOICES; ++i)
  585.     {
  586.         ch = &Channel[i];
  587.  
  588.         ch->voiceIndex      = i;
  589.         ch->volTabPtr       = silentTable;
  590.         ch->freqTabPtr      = silentTable;
  591.         ch->volDelayCounter = 1;
  592.         ch->volDelayLength  = 1;
  593.         ch->pitchBendDelay  = true;
  594.         ch->seqPos          = SEQ_SIZE; // yes
  595.         ch->seqStartPtr     = &SEQpoint[3 * i];
  596.         ch->patPtr          = &PATpoint[ch->seqStartPtr[0] << 6];
  597.         ch->noteTranspose   = (int8_t)(ch->seqStartPtr[1]);
  598.         ch->soundTranspose  = (int8_t)(ch->seqStartPtr[2]);
  599.     }
  600.  
  601.     repspd   = (SEQpoint[12] > 0) ? SEQpoint[12] : 3;
  602.     respcnt  = repspd;
  603.     spdtemp  = 0;
  604.     spdtemp2 = 0;
  605. }
  606.  
  607. static void new_note(fcChannel_t *ch)
  608. {
  609.     uint8_t *tmpSeqPtr, *tmpPatPtr, note, info;
  610.  
  611.     tmpPatPtr = &ch->patPtr[ch->patPos]; // temp pattern pointer
  612.  
  613.     if ((fc14 && ((*tmpPatPtr & 0x7F) == PAT_END_MARKER)) || (ch->patPos == 64))
  614.     {
  615.         ch->patPos = 0;
  616.         if (ch->seqPos >= numSequences)
  617.             ch->seqPos = 0;
  618.  
  619.         tmpSeqPtr  = &ch->seqStartPtr[ch->seqPos];
  620.         ch->patPtr = &PATpoint[tmpSeqPtr[0] << 6];
  621.  
  622.         ch->noteTranspose  = (int8_t)(tmpSeqPtr[1]);
  623.         ch->soundTranspose = (int8_t)(tmpSeqPtr[2]);
  624.  
  625.         if (++spdtemp == 4)
  626.         {
  627.             spdtemp = 0;
  628.  
  629.             // we've read all channels now, let's increase the pos used for RS
  630.             if (++spdtemp2 == (numSequences / SEQ_SIZE)) // numSequences is a multiple of SEQ_SIZE
  631.                 spdtemp2 = 0; // wrap sequence position
  632.         }
  633.  
  634.         // read current RS (replay speed. only update if non-zero)
  635.         if (SEQpoint[(spdtemp2 * 13) + 12] != 0)
  636.         {
  637.             repspd  = SEQpoint[(spdtemp2 * 13) + 12];
  638.             respcnt = repspd;
  639.         }
  640.  
  641.         ch->seqPos += SEQ_SIZE;
  642.         tmpPatPtr = ch->patPtr; // set new temp pattern pointer
  643.     }
  644.  
  645.     note = tmpPatPtr[0];
  646.     info = tmpPatPtr[1];
  647.  
  648.     if (note == 0)
  649.     {
  650.         info &= 0xC0;
  651.         if (info != 0)
  652.         {
  653.             ch->portaParam = 0;
  654.             if (info & (1 << 7))
  655.                 ch->portaParam = tmpPatPtr[3];
  656.         }
  657.     }
  658.     else
  659.     {
  660.         ch->portaValue = 0;
  661.         ch->portaParam = 0;
  662.  
  663.         if (info & (1 << 7))
  664.             ch->portaParam = tmpPatPtr[3];
  665.     }
  666.  
  667.     note &= 0x7F;
  668.     if (note != 0)
  669.     {
  670.         ptr8u_1 = &VOLpoint[((tmpPatPtr[1] & 0x3F) + ch->soundTranspose) << 6];
  671.  
  672.         ch->note            = note;
  673.         ch->volDelayLength  = ptr8u_1[0];
  674.         ch->volDelayCounter = ch->volDelayLength;
  675.         ch->freqTabPtr      = &FRQpoint[ptr8u_1[1] << 6];
  676.         ch->freqTabPos      = 0;
  677.         ch->freqSusCounter  = 0;
  678.         ch->vibratoSpeed    = ptr8u_1[2];
  679.         ch->vibratoDepth    = ptr8u_1[3];
  680.         ch->vibratoDelay    = ptr8u_1[4];
  681.         ch->vibratoCounter  = ch->vibratoDepth;
  682.         ch->vibratoUp       = true; // default initial state on new note
  683.         ch->volTabPtr       = &ptr8u_1[5];
  684.         ch->volTabPos       = 0;
  685.         ch->volSusCounter   = 0;
  686.  
  687.         paulaStopDMA(ch->voiceIndex); // yes, this is important
  688.     }
  689.  
  690.     ch->patPos += 2;
  691. }
  692.  
  693. static void doFreqModulation(fcChannel_t *ch)
  694. {
  695.     bool doTranspose;
  696.     uint8_t *tmpPtr;
  697.     const uint8_t *tabPtr;
  698.     soundInfo_t *sample;
  699.  
  700. testsustain:
  701.     if (ch->freqSusCounter > 0)
  702.     {
  703.         ch->freqSusCounter--;
  704.     }
  705.     else
  706.     {
  707.         tabPtr = &ch->freqTabPtr[ch->freqTabPos];
  708.  
  709. testeffects:
  710.         if (tabPtr[0] != 0xE1)
  711.         {
  712.             doTranspose = true;
  713.  
  714.             if (tabPtr[0] == 0xE0) // freq pos jump
  715.             {
  716.                 ch->freqTabPos = tabPtr[1] & 0x3F;
  717.                 tabPtr = &ch->freqTabPtr[ch->freqTabPos];
  718.             }
  719.  
  720.             if (tabPtr[0] == 0xE2) // set waveform
  721.             {
  722.                 if (tabPtr[1] < (NUM_SAMPLES + NUM_WAVEFORMS))
  723.                 {
  724.                     sample = &samples[tabPtr[1]];
  725.  
  726.                     ch->loopStart  = sample->repeat;
  727.                     ch->loopLength = sample->replen;
  728.  
  729.                     paulaSetData(ch->voiceIndex, sample->data);
  730.                     paulaSetLength(ch->voiceIndex, sample->length);
  731.                     paulaStartDMA(ch->voiceIndex);
  732.  
  733.                     ch->volTabPos = 0;
  734.                     ch->volDelayCounter = 1;
  735.                 }
  736.  
  737.                 ch->freqTabPos += 2;
  738.             }
  739.             else if (tabPtr[0] == 0xE4) // update waveform
  740.             {
  741.                 if (tabPtr[1] < (NUM_SAMPLES + NUM_WAVEFORMS))
  742.                 {
  743.                     sample = &samples[tabPtr[1]];
  744.  
  745.                     ch->loopStart  = sample->repeat;
  746.                     ch->loopLength = sample->replen;
  747.  
  748.                     paulaSetData(ch->voiceIndex, sample->data);
  749.                     paulaSetLength(ch->voiceIndex, sample->length);
  750.                 }
  751.  
  752.                 ch->freqTabPos += 2;
  753.             }
  754.             else if (tabPtr[0] == 0xE9) // set packed waveform
  755.             {
  756.                 if (tabPtr[1] < (NUM_SAMPLES + NUM_WAVEFORMS))
  757.                 {
  758.                     sample = &samples[tabPtr[1]];
  759.                     if (*PTR2LONG(sample->data) == 0x504D5353) // "SSMP"
  760.                     {
  761.                         tmpPtr = (uint8_t *)(&sample->data[4 + (tabPtr[2] * 16)]);
  762.  
  763.                         ch->loopStart  = &sample->data[(4 + 320 + *PTR2LONG(&tmpPtr[0])) + *PTR2WORD(&tmpPtr[6])];
  764.                         ch->loopLength = *PTR2WORD(&tmpPtr[8]);
  765.  
  766.                         // fix endless beep on non-looping samples (FC14 doesn't do this)
  767.                         if (ch->loopLength <= 1)
  768.                         {
  769.                             ch->loopLength = 1;
  770.                             if (*PTR2WORD(&tmpPtr[4]) >= 1) // sample length
  771.                                 *PTR2WORD(&sample->data[4 + 320 + *PTR2LONG(&tmpPtr[0])]) = 0;
  772.                         }
  773.  
  774.                         paulaSetData(ch->voiceIndex,  &sample->data[4 + 320 + *PTR2LONG(&tmpPtr[0])]);
  775.                         paulaSetLength(ch->voiceIndex, *PTR2WORD(&tmpPtr[4]));
  776.                         paulaStartDMA(ch->voiceIndex);
  777.  
  778.                         ch->volTabPos = 0;
  779.                         ch->volDelayCounter = 1;
  780.                     }
  781.                 }
  782.  
  783.                 ch->freqTabPos += 3;
  784.             }
  785.             else if (tabPtr[0] == 0xE7) // new freq pos
  786.             {
  787.                 tabPtr = &FRQpoint[(tabPtr[1] & 0x3F) << 6];
  788.  
  789.                 ch->freqTabPtr = tabPtr;
  790.                 ch->freqTabPos = 0;
  791.  
  792.                 goto testeffects;
  793.             }
  794.             else if (tabPtr[0] == 0xE8) // set freq sustain
  795.             {
  796.                 ch->freqSusCounter = tabPtr[1];
  797.  
  798.                 ch->freqTabPos += 2;
  799.                 goto testsustain;
  800.             }
  801.             else if (tabPtr[0] == 0xE3) // set vibrato
  802.             {
  803.                 ch->freqTabPos += 3;
  804.  
  805.                 ch->vibratoSpeed = tabPtr[1];
  806.                 ch->vibratoDepth = tabPtr[2];
  807.  
  808.                 doTranspose = false; // don't do period transpose here
  809.             }
  810.             else if (tabPtr[0] == 0xEA) // set pitch bend
  811.             {
  812.                 ch->pitchBendValue   = (int8_t)(tabPtr[1]);
  813.                 ch->pitchBendCounter = (int8_t)(tabPtr[2]);
  814.  
  815.                 ch->freqTabPos += 3;
  816.             }
  817.  
  818.             if (doTranspose)
  819.                 ch->periodTranspose = (int8_t)(ch->freqTabPtr[ch->freqTabPos++]);
  820.         }
  821.     }
  822. }
  823.  
  824. static void do_VOLbend(fcChannel_t *ch)
  825. {
  826.     ch->volSlideDelay = !ch->volSlideDelay;
  827.     if (ch->volSlideDelay)
  828.     {
  829.         ch->volSlideCounter--;
  830.  
  831.         ch->volume += ch->volSlideSpeed;
  832.         if (ch->volume < 0)
  833.         {
  834.             ch->volSlideCounter = 0;
  835.             ch->volume = 0;
  836.         }
  837.     }
  838. }
  839.  
  840. static void doVolModulation(fcChannel_t *ch)
  841. {
  842.     const uint8_t *tabPtr;
  843.  
  844. VOLUfx:
  845.     if (ch->volSusCounter > 0)
  846.     {
  847.         ch->volSusCounter--;
  848.     }
  849.     else
  850.     {
  851.         if (ch->volSlideCounter > 0)
  852.         {
  853.             do_VOLbend(ch);
  854.         }
  855.         else
  856.         {
  857.             if (--ch->volDelayCounter == 0)
  858.             {
  859.                 ch->volDelayCounter = ch->volDelayLength;
  860. volu_cmd:
  861.                 tabPtr = &ch->volTabPtr[ch->volTabPos];
  862.  
  863.                 if (tabPtr[0] != 0xE1)
  864.                 {
  865.                     if (tabPtr[0] == 0xE8) // set vol sustain
  866.                     {
  867.                         ch->volSusCounter = tabPtr[1];
  868.                         ch->volTabPos += 2;
  869.  
  870.                         goto VOLUfx;
  871.                     }
  872.                     else if (tabPtr[0] == 0xEA) // set vol slide
  873.                     {
  874.                         ch->volSlideSpeed   = tabPtr[1];
  875.                         ch->volSlideCounter = tabPtr[2];
  876.  
  877.                         ch->volTabPos += 3;
  878.                         do_VOLbend(ch);
  879.                     }
  880.                     else if (tabPtr[0] == 0xE0) // set vol pos
  881.                     {
  882.                         if (((int8_t)(tabPtr[1] & 0x3F) - 5) < 0)
  883.                             ch->volTabPos = 0;
  884.                         else
  885.                             ch->volTabPos = (tabPtr[1] & 0x3F) - 5;
  886.  
  887.                         goto volu_cmd;
  888.                     }
  889.                     else
  890.                     {
  891.                         ch->volume = (int8_t)(ch->volTabPtr[ch->volTabPos++]);
  892.                     }
  893.                 }
  894.             }
  895.         }
  896.     }
  897. }
  898.  
  899. static void effects(fcChannel_t *ch)
  900. {
  901.     int8_t tmpNote;
  902.     int16_t tmpVibPeriod, tmpPeriod;
  903.     uint16_t tmpVibNote;
  904.  
  905.     doFreqModulation(ch);
  906.     doVolModulation(ch);
  907.  
  908.     // get period from note and transposes...
  909.     tmpNote = ch->periodTranspose;
  910.     if (tmpNote >= 0)
  911.     {
  912.         tmpNote += ch->note;
  913.         tmpNote += ch->noteTranspose;
  914.     }
  915.     tmpNote &= 0x7F;
  916.  
  917.     tmpPeriod = periods[tmpNote];
  918.  
  919.     // apply vibrato to period
  920.     if (ch->vibratoDelay > 0)
  921.     {
  922.         ch->vibratoDelay--;
  923.     }
  924.     else
  925.     {
  926.         tmpVibPeriod = ch->vibratoCounter;
  927.         if (!ch->vibratoUp)
  928.         {
  929.             tmpVibPeriod -= ch->vibratoSpeed;
  930.             if (tmpVibPeriod < 0)
  931.             {
  932.                 tmpVibPeriod  = 0;
  933.                 ch->vibratoUp = true;
  934.             }
  935.         }
  936.         else
  937.         {
  938.             tmpVibPeriod += ch->vibratoSpeed;
  939.             if (tmpVibPeriod > (ch->vibratoDepth * 2))
  940.             {
  941.                 tmpVibPeriod  = ch->vibratoDepth * 2;
  942.                 ch->vibratoUp = false;
  943.             }
  944.         }
  945.         ch->vibratoCounter = tmpVibPeriod & 0x00FF;
  946.  
  947.         tmpVibPeriod -= ch->vibratoDepth;
  948.  
  949.         tmpVibNote = tmpNote * 2;
  950.         while (tmpVibNote < (12 * 8))
  951.         {
  952.             tmpVibPeriod *= 2;
  953.             tmpVibNote   += (12 * 2);
  954.         }
  955.  
  956.         tmpPeriod += tmpVibPeriod;
  957.     }
  958.  
  959.     // update portamento value (twice as slow on FC1.4)
  960.     ch->portaDelay = !ch->portaDelay;
  961.     if (!fc14 || ch->portaDelay)
  962.     {
  963.         if (ch->portaParam > 0)
  964.         {
  965.             if (ch->portaParam > 0x1F)
  966.                 ch->portaValue += (ch->portaParam & 0x1F);
  967.             else
  968.                 ch->portaValue -= ch->portaParam;
  969.         }
  970.     }
  971.  
  972.     // apply pitch bend to portamento value
  973.     ch->pitchBendDelay = !ch->pitchBendDelay;
  974.     if (ch->pitchBendDelay)
  975.     {
  976.         if (ch->pitchBendCounter > 0)
  977.         {
  978.             ch->pitchBendCounter--;
  979.             ch->portaValue -= ch->pitchBendValue;
  980.         }
  981.     }
  982.  
  983.     tmpPeriod += ch->portaValue;
  984.     tmpPeriod = CLAMP(tmpPeriod, 0x0071, fc14 ? 0x0D60 : 0x06B0);
  985.  
  986.     paulaSetPeriod(ch->voiceIndex, tmpPeriod);
  987.     paulaSetVolume(ch->voiceIndex, ch->volume);
  988. }
  989.  
  990. static void play_music(void)
  991. {
  992.     uint8_t i;
  993.  
  994.     if (--respcnt == 0)
  995.     {
  996.         respcnt = repspd;
  997.  
  998.         for (i = 0; i < PAULA_VOICES; ++i)
  999.             new_note(&Channel[i]);
  1000.     }
  1001.  
  1002.     for (i = 0; i < PAULA_VOICES; ++i)
  1003.     {
  1004.         effects(&Channel[i]);
  1005.  
  1006.         // these take effect when the current DMA cycle is done
  1007.         paulaSetData(i, Channel[i].loopStart);
  1008.         paulaSetLength(i, Channel[i].loopLength);
  1009.     }
  1010. }
  1011.  
  1012. static void mixAudio(int16_t *stream, int32_t sampleBlockLength)
  1013. {
  1014.     int32_t i, j, smpL, smpR;
  1015.     double dSample, dVolume, dOut[2];
  1016.     paulaVoice_t *v;
  1017. #ifdef USE_BLEP
  1018.     blep_t *bSmp, *bVol;
  1019. #endif
  1020.  
  1021.     memset(dMixerBufferL, 0, sizeof (double) * sampleBlockLength);
  1022.     memset(dMixerBufferR, 0, sizeof (double) * sampleBlockLength);
  1023.  
  1024.     if (musicPaused)
  1025.     {
  1026.         memset(stream, 0, sampleBlockLength * (sizeof (int16_t) * 2));
  1027.         return;
  1028.     }
  1029.  
  1030.     for (i = 0; i < PAULA_VOICES; ++i)
  1031.     {
  1032.         v = &paula[i];
  1033.         if (!v->active || (v->length < 2) || (v->data == NULL))
  1034.             continue;
  1035.  
  1036. #ifdef USE_BLEP
  1037.         bSmp = &blep[i];
  1038.         bVol = &blepVol[i];
  1039. #endif
  1040.         for (j = 0; j < sampleBlockLength; ++j)
  1041.         {
  1042.             dSample = v->data[v->pos] * (1.0 / 128.0);
  1043.             dVolume = v->dVolume;
  1044.  
  1045. #ifdef USE_BLEP
  1046.             if (dSample != bSmp->dLastValue)
  1047.             {
  1048.                 if ((v->dLastDelta > 0.0) && (v->dLastDelta > v->dLastPhase))
  1049.                     blepAdd(bSmp, v->dLastPhase / v->dLastDelta, bSmp->dLastValue - dSample);
  1050.  
  1051.                 bSmp->dLastValue = dSample;
  1052.             }
  1053.  
  1054.             if (dVolume != bVol->dLastValue)
  1055.             {
  1056.                 blepAdd(bVol, 0.0, bVol->dLastValue - dVolume);
  1057.                 bVol->dLastValue = dVolume;
  1058.             }
  1059.  
  1060.             if (bSmp->samplesLeft > 0) dSample += blepRun(bSmp);
  1061.             if (bVol->samplesLeft > 0) dVolume += blepRun(bVol);
  1062. #endif
  1063.             dSample *= dVolume;
  1064.  
  1065.             dMixerBufferL[j] += (dSample * v->dPanL);
  1066.             dMixerBufferR[j] += (dSample * v->dPanR);
  1067.  
  1068.             v->dPhase += v->dDelta;
  1069.             if (v->dPhase >= 1.0)
  1070.             {
  1071.                 v->dPhase -= 1.0;
  1072. #ifdef USE_BLEP
  1073.                 v->dLastPhase = v->dPhase;
  1074.                 v->dLastDelta = v->dDelta;
  1075. #endif
  1076.                 if (++v->pos >= v->length)
  1077.                 {
  1078.                     v->pos = 0;
  1079.  
  1080.                     // re-fetch Paula register values now
  1081.                     v->length = v->newLength;
  1082.                     v->data   = v->newData;
  1083.                 }
  1084.             }
  1085.         }
  1086.     }
  1087.  
  1088.     for (j = 0; j < sampleBlockLength; ++j)
  1089.     {
  1090.         dOut[0] = dMixerBufferL[j];
  1091.         dOut[1] = dMixerBufferR[j];
  1092.  
  1093. #ifdef USE_LOWPASS
  1094.         lossyIntegrator(&filterLo, dOut, dOut);
  1095. #endif
  1096.  
  1097. #ifdef USE_HIGHPASS
  1098.         lossyIntegratorHighPass(&filterHi, dOut, dOut);
  1099. #endif
  1100.  
  1101.         //normalize amplitude
  1102.         dOut[0] *= (32768.0 / 4.0);
  1103.         dOut[1] *= (32768.0 / 4.0);
  1104.  
  1105.         smpL = (int32_t)(dOut[0]);
  1106.         smpR = (int32_t)(dOut[1]);
  1107.  
  1108.         CLAMP16(smpL);
  1109.         CLAMP16(smpR);
  1110.  
  1111.         *stream++ = (int16_t)(smpL);
  1112.         *stream++ = (int16_t)(smpR);
  1113.     }
  1114. }
  1115.  
  1116. // these are used to create an equal powered panning
  1117. static double sinApx(double fX)
  1118. {
  1119.     fX = fX * (2.0 - fX);
  1120.     return (fX * 1.09742972 + fX * fX * 0.31678383);
  1121. }
  1122.  
  1123. static double cosApx(double fX)
  1124. {
  1125.     fX = (1.0 - fX) * (1.0 + fX);
  1126.     return (fX * 1.09742972 + fX * fX * 0.31678383);
  1127. }
  1128. // -------------------------------------------------
  1129.  
  1130. static void calculatePans(uint8_t stereoSeparation)
  1131. {
  1132.     uint8_t scaledPanPos;
  1133.     double p;
  1134.  
  1135.     if (stereoSeparation > 100)
  1136.         stereoSeparation = 100;
  1137.  
  1138.     scaledPanPos = (stereoSeparation * 128) / 100;
  1139.  
  1140.     p = (128 - scaledPanPos) * (1.0 / 256.0);
  1141.     paula[0].dPanL = cosApx(p);
  1142.     paula[0].dPanR = sinApx(p);
  1143.     paula[3].dPanL = cosApx(p);
  1144.     paula[3].dPanR = sinApx(p);
  1145.  
  1146.     p = (128 + scaledPanPos) * (1.0 / 256.0);
  1147.     paula[1].dPanL = cosApx(p);
  1148.     paula[1].dPanR = sinApx(p);
  1149.     paula[2].dPanL = cosApx(p);
  1150.     paula[2].dPanR = sinApx(p);
  1151. }
  1152.  
  1153. void fc14play_FillAudioBuffer(int16_t *buffer, int32_t numSamples)
  1154. {
  1155.     int32_t a, b;
  1156.  
  1157.     a = numSamples;
  1158.     while (a > 0)
  1159.     {
  1160.         if (samplesPerFrameLeft == 0)
  1161.         {
  1162.             if (!musicPaused)
  1163.                 play_music();
  1164.  
  1165.             samplesPerFrameLeft = samplesPerFrame;
  1166.         }
  1167.  
  1168.         b = a;
  1169.         if (b > samplesPerFrameLeft)
  1170.             b = samplesPerFrameLeft;
  1171.  
  1172.         mixAudio(buffer, b);
  1173.         buffer += (b * 2);
  1174.  
  1175.         a -= b;
  1176.         samplesPerFrameLeft -= b;
  1177.     }
  1178.  
  1179.     sampleCounter += numSamples;
  1180. }
  1181.  
  1182. void fc14play_PauseSong(bool flag)
  1183. {
  1184.     musicPaused = flag;
  1185. }
  1186.  
  1187. void fc14play_TogglePause(void)
  1188. {
  1189.     musicPaused ^= 1;
  1190. }
  1191.  
  1192. void fc14play_Close(void)
  1193. {
  1194.     closeMixer();
  1195.  
  1196.     if (dMixerBufferL != NULL)
  1197.     {
  1198.         free(dMixerBufferL);
  1199.         dMixerBufferL = NULL;
  1200.     }
  1201.  
  1202.     if (dMixerBufferR != NULL)
  1203.     {
  1204.         free(dMixerBufferR);
  1205.         dMixerBufferR = NULL;
  1206.     }
  1207.  
  1208.     if (songData != NULL)
  1209.     {
  1210.         free(songData);
  1211.         songData = NULL;
  1212.     }
  1213. }
  1214.  
  1215. void fc14play_SetStereoSep(uint8_t percentage)
  1216. {
  1217.     stereoSep = percentage;
  1218.     if (stereoSep > 100)
  1219.         stereoSep = 100;
  1220.  
  1221.     calculatePans(stereoSep);
  1222. }
  1223.  
  1224. uint32_t fc14play_GetMixerTicks(void)
  1225. {
  1226.     if (audioRate < 1000)
  1227.         return (0);
  1228.  
  1229.     return (sampleCounter / (audioRate / 1000));
  1230. }
  1231.  
  1232. bool fc14play_PlaySong(const uint8_t *moduleData, uint32_t dataLength, uint32_t audioFreq)
  1233. {
  1234.     if (audioFreq == 0)
  1235.         audioFreq = 44100;
  1236.  
  1237.     musicPaused = true;
  1238.  
  1239.     fc14play_Close();
  1240.  
  1241.     oldPeriod = 0;
  1242.     oldVoiceDelta = 0.0;
  1243.     sampleCounter = 0;
  1244.  
  1245.     songData = (uint8_t *)(malloc(dataLength));
  1246.     if (songData == NULL)
  1247.         return (false);
  1248.  
  1249.     memcpy(songData, moduleData, dataLength);
  1250.     if (!init_music(songData))
  1251.     {
  1252.         fc14play_Close();
  1253.         return (false);
  1254.     }
  1255.  
  1256.     dMixerBufferL = (double *)(malloc(MIX_BUF_SAMPLES * sizeof (double)));
  1257.     dMixerBufferR = (double *)(malloc(MIX_BUF_SAMPLES * sizeof (double)));
  1258.  
  1259.     if ((dMixerBufferL == NULL) || (dMixerBufferR == NULL))
  1260.     {
  1261.         fc14play_Close();
  1262.         return (false);
  1263.     }
  1264.  
  1265.     restart_song();
  1266.  
  1267.     // rates below 32kHz will mess up the BLEP synthesis
  1268.     audioFreq = CLAMP(audioFreq, 32000, 96000);
  1269.  
  1270.     if (!openMixer(audioFreq))
  1271.     {
  1272.         fc14play_Close();
  1273.         return (false);
  1274.     }
  1275.  
  1276.     audioRate       = audioFreq;
  1277.     dAudioRate      = (double)(audioFreq);
  1278.     soundBufferSize = MIX_BUF_SAMPLES;
  1279.     samplesPerFrame = (uint32_t)(round(dAudioRate / 50.0));
  1280.  
  1281. #ifdef USE_LOWPASS
  1282.     // Amiga 500 rev6 RC low-pass filter (~4420.97Hz, 500Hz added to better match A500)
  1283.     calcCoeffLossyIntegrator(dAudioRate, 4420.9706414415377 + 500.0, &filterLo);
  1284. #endif
  1285.  
  1286. #ifdef USE_HIGHPASS
  1287.     // Amiga 500 rev6 RC high-pass filter (~5.12Hz)
  1288.     calcCoeffLossyIntegrator(dAudioRate, 5.1276291562435077, &filterHi);
  1289. #endif
  1290.  
  1291.     memset(paula, 0, sizeof (paula));
  1292.     calculatePans(stereoSep);
  1293.  
  1294. #ifdef USE_BLEP
  1295.     memset(blep,    0, sizeof (blep));
  1296.     memset(blepVol, 0, sizeof (blepVol));
  1297. #endif
  1298.  
  1299. #ifdef USE_LOWPASS
  1300.     clearLossyIntegrator(&filterLo);
  1301. #endif
  1302.  
  1303. #ifdef USE_HIGHPASS
  1304.     clearLossyIntegrator(&filterHi);
  1305. #endif
  1306.  
  1307.     musicPaused = false;
  1308.     return (true);
  1309. }
  1310.  
  1311. // the following must be changed if you want to use another audio API than WinMM
  1312.  
  1313. #ifndef WIN32_LEAN_AND_MEAN
  1314. #define WIN32_LEAN_AND_MEAN
  1315. #endif
  1316.  
  1317. #include <windows.h>
  1318. #include <mmsystem.h>
  1319.  
  1320. #define MIX_BUF_NUM 2
  1321.  
  1322. static volatile BOOL audioRunningFlag;
  1323. static uint8_t currBuffer;
  1324. static int16_t *mixBuffer[MIX_BUF_NUM];
  1325. static HANDLE hThread, hAudioSem;
  1326. static WAVEHDR waveBlocks[MIX_BUF_NUM];
  1327. static HWAVEOUT hWave;
  1328.  
  1329. static DWORD WINAPI mixThread(LPVOID lpParam)
  1330. {
  1331.     WAVEHDR *waveBlock;
  1332.  
  1333.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  1334.  
  1335.     while (audioRunningFlag)
  1336.     {
  1337.         waveBlock = &waveBlocks[currBuffer];
  1338.         fc14play_FillAudioBuffer((int16_t *)(waveBlock->lpData), MIX_BUF_SAMPLES);
  1339.         waveOutWrite(hWave, waveBlock, sizeof (WAVEHDR));
  1340.         currBuffer = (currBuffer + 1) % MIX_BUF_NUM;
  1341.  
  1342.         // wait for buffer fill request
  1343.         WaitForSingleObject(hAudioSem, INFINITE);
  1344.     }
  1345.  
  1346.     (void)(lpParam); // make compiler happy!
  1347.  
  1348.     return (0);
  1349. }
  1350.  
  1351. static void CALLBACK waveProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
  1352. {
  1353.     if (uMsg == WOM_DONE)
  1354.         ReleaseSemaphore(hAudioSem, 1, NULL);
  1355.  
  1356.     // make compiler happy!
  1357.     (void)(hWaveOut);
  1358.     (void)(uMsg);
  1359.     (void)(dwInstance);
  1360.     (void)(dwParam1);
  1361.     (void)(dwParam2);
  1362. }
  1363.  
  1364. static void closeMixer(void)
  1365. {
  1366.     int32_t i;
  1367.  
  1368.     audioRunningFlag = false; // make thread end when it's done
  1369.  
  1370.     if (hAudioSem != NULL)
  1371.         ReleaseSemaphore(hAudioSem, 1, NULL);
  1372.  
  1373.     if (hThread != NULL)
  1374.     {
  1375.         WaitForSingleObject(hThread, INFINITE);
  1376.         CloseHandle(hThread);
  1377.         hThread = NULL;
  1378.     }
  1379.  
  1380.     if (hAudioSem != NULL)
  1381.     {
  1382.         CloseHandle(hAudioSem);
  1383.         hAudioSem = NULL;
  1384.     }
  1385.  
  1386.     if (hWave != NULL)
  1387.     {
  1388.         waveOutReset(hWave);
  1389.  
  1390.         for (i = 0; i < MIX_BUF_NUM; ++i)
  1391.         {
  1392.             if (waveBlocks[i].dwUser != 0xFFFF)
  1393.                 waveOutUnprepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR));
  1394.         }
  1395.  
  1396.         waveOutClose(hWave);
  1397.         hWave = NULL;
  1398.     }
  1399.  
  1400.     for (i = 0; i < MIX_BUF_NUM; ++i)
  1401.     {
  1402.         if (mixBuffer[i] != NULL)
  1403.         {
  1404.             free(mixBuffer[i]);
  1405.             mixBuffer[i] = NULL;
  1406.         }
  1407.     }
  1408. }
  1409.  
  1410. static bool openMixer(uint32_t audioFreq)
  1411. {
  1412.     int32_t i;
  1413.     DWORD threadID;
  1414.     WAVEFORMATEX wfx;
  1415.  
  1416.     // don't unprepare headers on error
  1417.     for (i = 0; i < MIX_BUF_NUM; ++i)
  1418.         waveBlocks[i].dwUser = 0xFFFF;
  1419.  
  1420.     closeMixer();
  1421.  
  1422.     ZeroMemory(&wfx, sizeof (wfx));
  1423.     wfx.nSamplesPerSec  = audioFreq;
  1424.     wfx.wBitsPerSample  = 16;
  1425.     wfx.nChannels       = 2;
  1426.     wfx.wFormatTag      = WAVE_FORMAT_PCM;
  1427.     wfx.nBlockAlign     = wfx.nChannels * (wfx.wBitsPerSample / 8);
  1428.     wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
  1429.  
  1430.     samplesPerFrameLeft = 0;
  1431.     currBuffer = 0;
  1432.  
  1433.     if (waveOutOpen(&hWave, WAVE_MAPPER, &wfx, (DWORD_PTR)(&waveProc), 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
  1434.         goto omError;
  1435.  
  1436.     // create semaphore for buffer fill requests
  1437.     hAudioSem = CreateSemaphore(NULL, MIX_BUF_NUM - 1, MIX_BUF_NUM, NULL);
  1438.     if (hAudioSem == NULL)
  1439.         goto omError;
  1440.  
  1441.     // allocate WinMM mix buffers
  1442.     for (i = 0; i < MIX_BUF_NUM; ++i)
  1443.     {
  1444.         mixBuffer[i] = (int16_t *)(calloc(MIX_BUF_SAMPLES, wfx.nBlockAlign));
  1445.         if (mixBuffer[i] == NULL)
  1446.             goto omError;
  1447.     }
  1448.  
  1449.     // initialize WinMM mix headers
  1450.  
  1451.     memset(waveBlocks, 0, sizeof (waveBlocks));
  1452.     for (i = 0; i < MIX_BUF_NUM; ++i)
  1453.     {
  1454.         waveBlocks[i].lpData = (LPSTR)(mixBuffer[i]);
  1455.         waveBlocks[i].dwBufferLength = MIX_BUF_SAMPLES * wfx.nBlockAlign;
  1456.         waveBlocks[i].dwFlags = WHDR_DONE;
  1457.  
  1458.         if (waveOutPrepareHeader(hWave, &waveBlocks[i], sizeof (WAVEHDR)) != MMSYSERR_NOERROR)
  1459.             goto omError;
  1460.     }
  1461.  
  1462.     // create main mixer thread
  1463.     audioRunningFlag = true;
  1464.     hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(mixThread), NULL, 0, &threadID);
  1465.     if (hThread == NULL)
  1466.         goto omError;
  1467.  
  1468.     return (TRUE);
  1469.  
  1470. omError:
  1471.     closeMixer();
  1472.     return (FALSE);
  1473. }
  1474.  
  1475. // ---------------------------------------------------------------------------
  1476.  
  1477. // END OF FILE
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top