Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

XFuXMPlayer.cpp

Go to the documentation of this file.
00001 /*! \file 
00002  * X-Forge Util <br>
00003  * Copyright 2000-2003 Fathammer Ltd
00004  * 
00005  * \brief XM player.
00006  * \todo doxygen comments
00007  * \todo Panning envelope support on "key off"
00008  * \todo Random waveform support for vibrato and tremolo
00009  * \todo Set glissando control -command
00010  * 
00011  * $Id: XFuXMPlayer.cpp,v 1.55 2003/10/10 11:34:51 toni Exp $
00012  * $Date: 2003/10/10 11:34:51 $
00013  * $Revision: 1.55 $
00014  */
00015 
00016 #include <stdlib.h> // \ needed for
00017 #include <stdarg.h> // / va_args
00018 #include <xfcore/XFcCore.h>
00019 #include <xfcore/XFcAudioFlags.h>
00020 #include <xfcore/XFcLinkedList.h>
00021 #include <xfutil/XFuXMPlayer.h>
00022 #include <xfutil/XFuXMPlayer_internal.h>
00023 #include <xfutil/XFuXMPlayerEventHandler.h>
00024 
00025 
00026 const static INT32 linearFrequencyTable[768] =
00027 {
00028     535232,534749,534266,533784,533303,532822,532341,531861,
00029     531381,530902,530423,529944,529466,528988,528511,528034,
00030     527558,527082,526607,526131,525657,525183,524709,524236,
00031     523763,523290,522818,522346,521875,521404,520934,520464,
00032     519994,519525,519057,518588,518121,517653,517186,516720,
00033     516253,515788,515322,514858,514393,513929,513465,513002,
00034     512539,512077,511615,511154,510692,510232,509771,509312,
00035     508852,508393,507934,507476,507018,506561,506104,505647,
00036     505191,504735,504280,503825,503371,502917,502463,502010,
00037     501557,501104,500652,500201,499749,499298,498848,498398,
00038     497948,497499,497050,496602,496154,495706,495259,494812,
00039     494366,493920,493474,493029,492585,492140,491696,491253,
00040     490809,490367,489924,489482,489041,488600,488159,487718,
00041     487278,486839,486400,485961,485522,485084,484647,484210,
00042     483773,483336,482900,482465,482029,481595,481160,480726,
00043     480292,479859,479426,478994,478562,478130,477699,477268,
00044     476837,476407,475977,475548,475119,474690,474262,473834,
00045     473407,472979,472553,472126,471701,471275,470850,470425,
00046     470001,469577,469153,468730,468307,467884,467462,467041,
00047     466619,466198,465778,465358,464938,464518,464099,463681,
00048     463262,462844,462427,462010,461593,461177,460760,460345,
00049     459930,459515,459100,458686,458272,457859,457446,457033,
00050     456621,456209,455797,455386,454975,454565,454155,453745,
00051     453336,452927,452518,452110,451702,451294,450887,450481,
00052     450074,449668,449262,448857,448452,448048,447644,447240,
00053     446836,446433,446030,445628,445226,444824,444423,444022,
00054     443622,443221,442821,442422,442023,441624,441226,440828,
00055     440430,440033,439636,439239,438843,438447,438051,437656,
00056     437261,436867,436473,436079,435686,435293,434900,434508,
00057     434116,433724,433333,432942,432551,432161,431771,431382,
00058     430992,430604,430215,429827,429439,429052,428665,428278,
00059     427892,427506,427120,426735,426350,425965,425581,425197,
00060     424813,424430,424047,423665,423283,422901,422519,422138,
00061     421757,421377,420997,420617,420237,419858,419479,419101,
00062     418723,418345,417968,417591,417214,416838,416462,416086,
00063     415711,415336,414961,414586,414212,413839,413465,413092,
00064     412720,412347,411975,411604,411232,410862,410491,410121,
00065     409751,409381,409012,408643,408274,407906,407538,407170,
00066     406803,406436,406069,405703,405337,404971,404606,404241,
00067     403876,403512,403148,402784,402421,402058,401695,401333,
00068     400970,400609,400247,399886,399525,399165,398805,398445,
00069     398086,397727,397368,397009,396651,396293,395936,395579,
00070     395222,394865,394509,394153,393798,393442,393087,392733,
00071     392378,392024,391671,391317,390964,390612,390259,389907,
00072     389556,389204,388853,388502,388152,387802,387452,387102,
00073     386753,386404,386056,385707,385359,385012,384664,384317,
00074     383971,383624,383278,382932,382587,382242,381897,381552,
00075     381208,380864,380521,380177,379834,379492,379149,378807,
00076     378466,378124,377783,377442,377102,376762,376422,376082,
00077     375743,375404,375065,374727,374389,374051,373714,373377,
00078     373040,372703,372367,372031,371695,371360,371025,370690,
00079     370356,370022,369688,369355,369021,368688,368356,368023,
00080     367691,367360,367028,366697,366366,366036,365706,365376,
00081     365046,364717,364388,364059,363731,363403,363075,362747,
00082     362420,362093,361766,361440,361114,360788,360463,360137,
00083     359813,359488,359164,358840,358516,358193,357869,357547,
00084     357224,356902,356580,356258,355937,355616,355295,354974,
00085     354654,354334,354014,353695,353376,353057,352739,352420,
00086     352103,351785,351468,351150,350834,350517,350201,349885,
00087     349569,349254,348939,348624,348310,347995,347682,347368,
00088     347055,346741,346429,346116,345804,345492,345180,344869,
00089     344558,344247,343936,343626,343316,343006,342697,342388,
00090     342079,341770,341462,341154,340846,340539,340231,339924,
00091     339618,339311,339005,338700,338394,338089,337784,337479,
00092     337175,336870,336566,336263,335959,335656,335354,335051,
00093     334749,334447,334145,333844,333542,333242,332941,332641,
00094     332341,332041,331741,331442,331143,330844,330546,330247,
00095     329950,329652,329355,329057,328761,328464,328168,327872,
00096     327576,327280,326985,326690,326395,326101,325807,325513,
00097     325219,324926,324633,324340,324047,323755,323463,323171,
00098     322879,322588,322297,322006,321716,321426,321136,320846,
00099     320557,320267,319978,319690,319401,319113,318825,318538,
00100     318250,317963,317676,317390,317103,316817,316532,316246,
00101     315961,315676,315391,315106,314822,314538,314254,313971,
00102     313688,313405,313122,312839,312557,312275,311994,311712,
00103     311431,311150,310869,310589,310309,310029,309749,309470,
00104     309190,308911,308633,308354,308076,307798,307521,307243,
00105     306966,306689,306412,306136,305860,305584,305308,305033,
00106     304758,304483,304208,303934,303659,303385,303112,302838,
00107     302565,302292,302019,301747,301475,301203,300931,300660,
00108     300388,300117,299847,299576,299306,299036,298766,298497,
00109     298227,297958,297689,297421,297153,296884,296617,296349,
00110     296082,295815,295548,295281,295015,294749,294483,294217,
00111     293952,293686,293421,293157,292892,292628,292364,292100,
00112     291837,291574,291311,291048,290785,290523,290261,289999,
00113     289737,289476,289215,288954,288693,288433,288173,287913,
00114     287653,287393,287134,286875,286616,286358,286099,285841,
00115     285583,285326,285068,284811,284554,284298,284041,283785,
00116     283529,283273,283017,282762,282507,282252,281998,281743,
00117     281489,281235,280981,280728,280475,280222,279969,279716,
00118     279464,279212,278960,278708,278457,278206,277955,277704,
00119     277453,277203,276953,276703,276453,276204,275955,275706,
00120     275457,275209,274960,274712,274465,274217,273970,273722,
00121     273476,273229,272982,272736,272490,272244,271999,271753,
00122     271508,271263,271018,270774,270530,270286,270042,269798,
00123     269555,269312,269069,268826,268583,268341,268099,267857
00124 };
00125 
00126 
00127 #define ABS(x) (((x) < 0) ? -(x) : (x))
00128 
00129 #define interpolateLinear8(ch, val1, val2) \
00130     val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00131     \
00132     if ((ch.mPointer + FP_VALUE) < ch.mLength) \
00133         val2 = *((INT8 *)ch.mOffset + ((ch.mPointer + FP_VALUE) >> FP_BITS)); \
00134     else \
00135         val2 = 0;
00136 
00137 
00138 #define interpolateLinear16(ch, val1, val2) \
00139     val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00140     \
00141     if ((ch.mPointer + FP_VALUE) < ch.mLength) \
00142         val2 = *((INT16 *)ch.mOffset + ((ch.mPointer + FP_VALUE) >> FP_BITS)); \
00143     else \
00144         val2 = 0;
00145 
00146 
00147 #define interpolateLinearForwardLoop8(ch, val1, val2, temp) \
00148     val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00149     \
00150     temp = (ch.mPointer + FP_VALUE); \
00151     if ((temp >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00152         temp = ch.mLoopStart + (temp - (ch.mLoopEnd + FP_VALUE)); \
00153     \
00154     val2 = *((INT8 *)ch.mOffset + (temp >> FP_BITS));
00155 
00156 
00157 #define interpolateLinearForwardLoop16(ch, val1, val2, temp) \
00158     val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00159     \
00160     temp = (ch.mPointer + FP_VALUE); \
00161     if ((temp >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00162         temp = ch.mLoopStart + (temp - (ch.mLoopEnd + FP_VALUE)); \
00163     \
00164     val2 = *((INT16 *)ch.mOffset + (temp >> FP_BITS));
00165 
00166 
00167 #define interpolateLinearBidirectionalLoop8(ch, val1, val2, temp) \
00168     val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00169     \
00170     temp = (ch.mPointer + FP_VALUE); \
00171     if ((temp >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00172         temp = ch.mLoopEnd - (temp - (ch.mLoopEnd + FP_VALUE)); \
00173     \
00174     val2 = *((INT8 *)ch.mOffset + (temp >> FP_BITS)); \
00175 
00176 
00177 #define interpolateLinearBidirectionalLoop16(ch, val1, val2, temp) \
00178     val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS)); \
00179     \
00180     temp = (ch.mPointer + FP_VALUE); \
00181     if ((temp >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00182         temp = ch.mLoopEnd - (temp - (ch.mLoopEnd + FP_VALUE)); \
00183     \
00184     val2 = *((INT16 *)ch.mOffset + (temp >> FP_BITS));
00185 
00186 
00187 #define addPointer(ch) \
00188     ch.mPointer += ch.mSpeed; \
00189     if (ch.mPointer > (ch.mLength - FP_VALUE)) \
00190         ch.mIsSample = 0;
00191 
00192 
00193 #define addPointerForwardLoop(ch) \
00194     ch.mPointer += ch.mSpeed; \
00195     if ((ch.mPointer >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00196         ch.mPointer = ch.mLoopStart + (ch.mPointer - (ch.mLoopEnd + FP_VALUE));
00197 
00198 
00199 #define addPointerBidirectionalLoop(ch) \
00200     if (ch.mDirection == 1) \
00201     { \
00202         ch.mPointer += ch.mSpeed; \
00203         \
00204         if ((ch.mPointer >> FP_BITS) > (ch.mLoopEnd >> FP_BITS)) \
00205         { \
00206             ch.mDirection = -1; \
00207             ch.mPointer = ch.mLoopEnd - (ch.mPointer - (ch.mLoopEnd + FP_VALUE)); \
00208         } \
00209     } \
00210     else \
00211     { \
00212         ch.mPointer -= ch.mSpeed; \
00213         \
00214         if ((ch.mPointer >> FP_BITS) < (ch.mLoopStart >> FP_BITS)) \
00215         { \
00216             ch.mDirection = 1; \
00217             ch.mPointer = ch.mLoopStart + (ch.mLoopStart - ch.mPointer); \
00218         } \
00219     }
00220 
00221 
00222 //#define OUTPUT_XM_INFO
00223 
00224 #ifdef OUTPUT_XM_INFO
00225 static const CHAR *notes[12] =
00226     {"C-","C#","D-","D#","E-","F-","F#","G-","G#","A-","A#","B-"};
00227 #endif // OUTPUT_XM_INFO
00228 
00229 
00230 void XFuXMPlayer::debugPrint(XFcFile *aTextout, CHAR *aFmt, ...)
00231 {
00232 #if !defined(XFC_PLATFORM_EPOC) && !defined(XFC_PLATFORM_PALM)
00233     if (aTextout != NULL)
00234     {
00235         va_list ap; 
00236         static CHAR buffer[10240];  
00237         va_start(ap, aFmt);  
00238         vsprintf(buffer, aFmt, ap);  
00239         aTextout->write(buffer, 1, XFcStringToolkit::getLength(buffer));
00240         va_end(ap);
00241     }
00242 #endif // XFC_PLATFORM_EPOC
00243 }
00244 
00245 
00246 void XFuXMPlayer::dumpSongParameters(XFcFile *aTextout)
00247 {
00248     INT i = 0, x;
00249 
00250     debugPrint(aTextout, "\n\nParameters for current mSong. \n\n");
00251     debugPrint(aTextout, "song length: %d\n", mSong.mSongLength);
00252     debugPrint(aTextout, "song restart: %d\n", mSong.mRestartPosition);
00253     debugPrint(aTextout, "order: ");
00254 
00255     for (i = 0; i < mSong.mSongLength; i++)
00256         debugPrint(aTextout, "%d ", mSong.mOrderTable[i]);
00257 
00258     debugPrint(aTextout, "\n");
00259     debugPrint(aTextout, "nof patterns: %d\n", mSong.mNbPatterns);
00260     debugPrint(aTextout, "nof channels: %d\n", mSong.mNbChannels);
00261     debugPrint(aTextout, "nof instrument: %d\n", mSong.mNbInstruments);
00262     debugPrint(aTextout, "tempo: %d\n", mSong.mTempo);
00263     debugPrint(aTextout, "bpm: %d\n", mSong.mBpm);
00264     debugPrint(aTextout, "\nmInstruments: \n");
00265 
00266     for (i = 0; i < mSong.mNbInstruments; i++)
00267     {
00268         XFuXMInstrument &ins = mInstruments[i];
00269 
00270         debugPrint(aTextout, "\ninstr %02X \n", i);
00271 
00272         debugPrint(aTextout, "is vibrato? %d \n", ins.mIsVibrato);
00273         debugPrint(aTextout, "vibratoType: %d \n", ins.mVibratoType);
00274         debugPrint(aTextout, "vibratoSweep: %d \n", ins.mVibratoSweep);
00275         debugPrint(aTextout, "vibratoDepth: %d \n", ins.mVibratoDepth);
00276         debugPrint(aTextout, "vibratoRate: %d \n", ins.mVibratoRate);
00277 
00278         debugPrint(aTextout, "\nvolume envelope: \n");
00279         debugPrint(aTextout, "  fadeout: %d \n", ins.mVolumeFadeout);
00280         debugPrint(aTextout, "  end point: %d\n", ins.mVolEnvEnd);  
00281         debugPrint(aTextout, "  type: %d\n", ins.mVolEnvType);
00282         debugPrint(aTextout, "  on? %d\n", (ins.mVolEnvType & ENVELOPE_ON));
00283         debugPrint(aTextout, "  sustain? %d\n",
00284                    (ins.mVolEnvType & ENVELOPE_SUSTAIN));
00285         debugPrint(aTextout, "  sustain point: %d\n", (ins.mVolEnvSustain));
00286         debugPrint(aTextout, "  loop? %d\n", (ins.mVolEnvType & ENVELOPE_LOOP));
00287         debugPrint(aTextout, "  loop start: %d\n", ins.mVolEnvLoopStart);
00288         debugPrint(aTextout, "  loop end: %d\n", ins.mVolEnvLoopEnd);
00289         debugPrint(aTextout, "  envelope: ");
00290 
00291         for (x = 0; x < ins.mVolEnvEnd; x++)
00292             debugPrint(aTextout,"%2.2f ", ins.mVolumeEnvelope[x]);
00293 
00294         debugPrint(aTextout, "\n"); 
00295         debugPrint(aTextout, "\npanning envelope: \n");
00296         debugPrint(aTextout, "  on? %d\n", (ins.mPanEnvType & ENVELOPE_ON));
00297         debugPrint(aTextout, "  loop? %d\n", (ins.mPanEnvType & ENVELOPE_LOOP));
00298         debugPrint(aTextout, "  loop start: %d\n", ins.mPanEnvLoopStart);
00299         debugPrint(aTextout, "  loop end: %d\n", ins.mPanEnvLoopEnd);
00300         debugPrint(aTextout, "  envelope: ");
00301 
00302         for (x = 0; x < ins.mPanEnvEnd; x++)
00303             debugPrint(aTextout, "%d ", ins.mPanningEnvelope[x]);
00304 
00305         debugPrint(aTextout,"\n-----\n");   
00306     }
00307 }
00308 
00309 
00310 INT XFuXMPlayer::loadXM(const CHAR *aFilename, XFcFile *aTextout)
00311 {
00312     XFcFile *f;
00313 
00314     INT i, j, k, ii;
00315     FLOAT32 y1, y2, dx;
00316     INT xx, x1, x2;
00317 
00318     XFuXMFormatInstrumentHeader xmInstrumentHeader;
00319     XFuXMFormatInstrument xmInstrument;
00320     XFuXMFormatSample xmSample;
00321 
00322     debugPrint(aTextout, "Fathammer XM-player debug file.\n");
00323     debugPrint(aTextout, "Opening %s.\n", aFilename);
00324 
00325     f = XFcFile::open(aFilename, "rb");
00326     if (f == 0) {
00327         debugPrint(aTextout, "error opening file.\n");
00328         if (aTextout != NULL) aTextout->close();
00329         return 1;
00330     }
00331 
00332     debugPrint(aTextout, "opened.\n");
00333 
00334     debugPrint(aTextout, "reading header...\n\n");
00335     f->read(mXMHeader.mIdString, 17, sizeof(UINT8));
00336     mXMHeader.mIdString[17] = 0;
00337 
00338     // Check id string
00339     INT wrongId = XFcStringToolkit::compare((const CHAR *)mXMHeader.mIdString, "Extended Module: ");
00340     if (wrongId) return 1;
00341 
00342     f->read(mXMHeader.mModuleName, 20, sizeof(UINT8));
00343     mXMHeader.mModuleName[20] = 0;
00344 
00345     // Check $1A byte
00346     f->readUINT8(mXMHeader.mH1A);
00347     if (mXMHeader.mH1A != 0x1A) return 1;
00348 
00349     f->read(mXMHeader.mTrackerName, 20, sizeof(UINT8));
00350     mXMHeader.mTrackerName[20] = 0;
00351 
00352     f->readUINT16(mXMHeader.mVersion);
00353 
00354     f->readUINT32(mXMHeader.mHeaderSize);
00355 
00356     f->readUINT16(mXMHeader.mSongLength);
00357     f->readUINT16(mXMHeader.mRestartPosition);
00358     f->readUINT16(mXMHeader.mNbChannels);
00359     f->readUINT16(mXMHeader.mNbPatterns);
00360     f->readUINT16(mXMHeader.mNbInstruments);
00361 
00362     f->readUINT16(mXMHeader.mFlags);
00363 
00364     f->readUINT16(mXMHeader.mTempo);
00365     f->readUINT16(mXMHeader.mBpm);
00366 
00367     f->read(mXMHeader.mOrderTable, 256, sizeof(UINT8));
00368 
00369     debugPrint(aTextout, "id string: %s\n", mXMHeader.mIdString);
00370     debugPrint(aTextout, "module name: %s\n", mXMHeader.mModuleName);
00371     debugPrint(aTextout, "h1A: %d\n", mXMHeader.mH1A);
00372     debugPrint(aTextout, "tracker name: %s\n", mXMHeader.mTrackerName);
00373     debugPrint(aTextout, "tracker version: %d\n", mXMHeader.mVersion);
00374     debugPrint(aTextout, "header size: %d\n", mXMHeader.mHeaderSize);
00375     debugPrint(aTextout, "song length: %d\n", mXMHeader.mSongLength);
00376     debugPrint(aTextout, "restart pos: %d\n", mXMHeader.mRestartPosition);
00377     debugPrint(aTextout, "nof mChannels: %d\n", mXMHeader.mNbChannels);
00378     debugPrint(aTextout, "nof patterns: %d\n", mXMHeader.mNbPatterns);
00379     debugPrint(aTextout, "nof instrument: %d\n", mXMHeader.mNbInstruments);
00380     debugPrint(aTextout, "flags: %d\n", mXMHeader.mFlags);
00381     debugPrint(aTextout, "tempo: %d\n", mXMHeader.mTempo);
00382     debugPrint(aTextout, "bpm: %d\n", mXMHeader.mBpm);
00383     debugPrint(aTextout, "ordertable: \n");
00384 
00385     // Allocate memory for order list
00386     mSong.mOrderTable = new UINT8[mXMHeader.mSongLength];
00387     memset(mSong.mOrderTable, 0, mXMHeader.mSongLength * sizeof(UINT8));
00388 
00389     for (i = 0; i < mXMHeader.mSongLength; i++)
00390     {
00391         debugPrint(aTextout, "%2X->%2X\n", i, mXMHeader.mOrderTable[i]);
00392         mSong.mOrderTable[i] = mXMHeader.mOrderTable[i];
00393     }
00394 
00395     // Save song header
00396     mSong.mNbChannels = mXMHeader.mNbChannels;
00397     mSong.mNbPatterns = mXMHeader.mNbPatterns;
00398     mSong.mNbInstruments = mXMHeader.mNbInstruments;
00399     mSong.mSongLength = mXMHeader.mSongLength;
00400     mSong.mRestartPosition = mXMHeader.mRestartPosition;
00401     mSong.mBpm = mXMHeader.mBpm;
00402     mSong.mTempo = mXMHeader.mTempo;
00403     
00404     // Allocate memory for mChannels
00405     mChannels = new XFuXMChannel[mSong.mNbChannels];
00406     memset(mChannels, 0, mSong.mNbChannels * sizeof(XFuXMChannel));
00407 
00408     // Read pattern headers and pattern data
00409     debugPrint(aTextout, "\npatterns:\n\n");
00410     
00411     // Allocate memory for pattern headers
00412     mXMPatternHeaders = new XFuXMFormatPatternHeader[mXMHeader.mNbPatterns];
00413     memset(mXMPatternHeaders, 0, mXMHeader.mNbPatterns * sizeof(XFuXMFormatPatternHeader));
00414 
00415     // Allocate memory for pattern data blocks
00416     mPatternData = new XFuXMPattern[mXMHeader.mNbPatterns];
00417     memset(mPatternData, 0, mXMHeader.mNbPatterns * sizeof(XFuXMPattern));
00418 
00419     for (i = 0; i < mXMHeader.mNbPatterns; i++)
00420     {
00421         debugPrint(aTextout, "pattern %d \n", i);
00422 
00423         f->readUINT32(mXMPatternHeaders[i].mHeaderLength);
00424         f->readUINT8(mXMPatternHeaders[i].mPackingType);
00425         f->readUINT16(mXMPatternHeaders[i].mNbRows);
00426         f->readUINT16(mXMPatternHeaders[i].mSize);
00427 
00428         debugPrint(aTextout, "header length: %d \n", mXMPatternHeaders[i].mHeaderLength);
00429         debugPrint(aTextout, "packing type: %d \n", mXMPatternHeaders[i].mPackingType);
00430         debugPrint(aTextout, "nof rows: %d \n", mXMPatternHeaders[i].mNbRows);
00431         debugPrint(aTextout, "size: %d \n\n", mXMPatternHeaders[i].mSize);
00432 
00433         if (mXMPatternHeaders[i].mSize > 0)
00434         {
00435             // Allocate memory for actual pattern data
00436             mPatternData[i].mData = new UINT8[mXMPatternHeaders[i].mSize];
00437             mPatternData[i].mRows = new UINT32[mXMPatternHeaders[i].mNbRows];
00438             f->read(mPatternData[i].mData, mXMPatternHeaders[i].mSize, 1);
00439 
00440             UINT8 b = 0;
00441             UINT32 p = 0;
00442             for (j = 0; j < mXMPatternHeaders[i].mNbRows; j++)
00443             {
00444                 mPatternData[i].mRows[j] = p;
00445 
00446                 for (k = 0; k < mSong.mNbChannels; k++)
00447                 {
00448                     b = *(mPatternData[i].mData + p++);
00449 
00450                     if ((b & 128) == 128) {
00451                         // Packed atom
00452                         if ((b & 1) == 1) p++;
00453                         if ((b & 2) == 2) p++;
00454                         if ((b & 4) == 4) p++;
00455                         if ((b & 8) == 8) p++;
00456                         if ((b & 16) == 16) p++;
00457                     }
00458                     else
00459                     {
00460                         // Normal atom
00461                         p += 4;
00462                     }
00463                 }
00464             }
00465         }
00466     }
00467 
00468     // Allocate memory for mInstruments
00469     mInstruments = new XFuXMInstrument[mXMHeader.mNbInstruments];
00470     memset(mInstruments, 0, mXMHeader.mNbInstruments * sizeof(XFuXMInstrument));
00471 
00472     // Read mInstruments
00473     debugPrint(aTextout, "mInstruments \n\n");
00474     for (i = 0; i < mXMHeader.mNbInstruments; i++)
00475     {
00476         debugPrint(aTextout, "instrument %02x \n", i);
00477 
00478         XFuXMInstrument &ins = mInstruments[i];
00479 
00480         memset(&xmInstrumentHeader, 0, sizeof(XFuXMFormatInstrumentHeader));
00481 
00482         f->readUINT32(xmInstrumentHeader.mSize);
00483 
00484         f->read(xmInstrumentHeader.mInstrumentName, 22, sizeof(UINT8));
00485         xmInstrumentHeader.mInstrumentName[22] = 0;
00486 
00487         f->readUINT8(xmInstrumentHeader.mType);
00488         f->readUINT16(xmInstrumentHeader.mNbSamples);
00489 
00490         ins.mNbSamples = xmInstrumentHeader.mNbSamples;
00491 
00492         debugPrint(aTextout, "size: %d \n", xmInstrumentHeader.mSize);
00493         debugPrint(aTextout, "name: %s \n", xmInstrumentHeader.mInstrumentName);
00494         debugPrint(aTextout, "type: %d \n", xmInstrumentHeader.mType);
00495         debugPrint(aTextout, "nof samples: %d \n",
00496                    xmInstrumentHeader.mNbSamples);
00497 
00498         // sizeof(xmInstrumentHeader) could return an ALIGNED value
00499         INT32 skip = xmInstrumentHeader.mSize - 29;
00500 
00501         if (xmInstrumentHeader.mNbSamples > 0)
00502         {
00503             memset(&xmInstrument, 0, sizeof(XFuXMFormatInstrument));
00504 
00505             f->readUINT32(xmInstrument.mSampleHeaderSize);
00506             f->read(xmInstrument.mKeyboard, 96, sizeof(UINT8));
00507             f->read(xmInstrument.mVolumeEnvelope, 24, sizeof(UINT16));
00508             f->read(xmInstrument.mPanningEnvelope, 24, sizeof(UINT16));
00509             f->readUINT8(xmInstrument.mNbVolEnvPoints);
00510             f->readUINT8(xmInstrument.mNbPanEnvPoints);
00511             f->readUINT8(xmInstrument.mVolEnvSustain);
00512             f->readUINT8(xmInstrument.mVolEnvLoopStart);
00513             f->readUINT8(xmInstrument.mVolEnvLoopEnd);
00514             f->readUINT8(xmInstrument.mPanEnvSustain);
00515             f->readUINT8(xmInstrument.mPanEnvLoopStart);
00516             f->readUINT8(xmInstrument.mPanEnvLoopEnd);
00517             f->readUINT8(xmInstrument.mVolEnvType);
00518             f->readUINT8(xmInstrument.mPanEnvType);
00519 
00520             f->readUINT8(xmInstrument.mVibratoType);
00521             f->readUINT8(xmInstrument.mVibratoSweep);
00522             f->readUINT8(xmInstrument.mVibratoDepth);
00523             f->readUINT8(xmInstrument.mVibratoRate);
00524 
00525             f->readUINT16(xmInstrument.mVolumeFadeout);
00526 
00527             f->readUINT16(xmInstrument.mReserved);
00528 
00529             // sizeof(xmInstrument) could return an ALIGNED value
00530             skip -= 214;
00531             f->seek(skip, SEEK_CUR);
00532 
00533             // Allocate memory for samples in instrument
00534             ins.mSamples = new XFuXMSample[xmInstrumentHeader.mNbSamples];
00535             memset(ins.mSamples, 0, xmInstrumentHeader.mNbSamples * sizeof(XFuXMSample));
00536 
00537             for (j = 0; j < xmInstrumentHeader.mNbSamples; j++)
00538             {
00539                 XFuXMSample &smp = ins.mSamples[j];
00540 
00541                 memset(&xmSample, 0, sizeof(XFuXMFormatSample));
00542 
00543                 f->readUINT32(xmSample.mSampleLength);
00544                 f->readUINT32(xmSample.mLoopStart);
00545                 f->readUINT32(xmSample.mLoopLength);
00546                 f->readUINT8(xmSample.mVolume);
00547                 f->readINT8(xmSample.mFinetune);
00548                 f->readUINT8(xmSample.mType);
00549                 f->readUINT8(xmSample.mPan);
00550                 f->readINT8(xmSample.mRelativeNote);
00551                 f->readUINT8(xmSample.mReserved);
00552 
00553                 f->read(xmSample.mSampleName, 22, sizeof(UINT8));
00554                 xmSample.mSampleName[22] = 0;
00555 
00556                 debugPrint(aTextout, "Allocating %d bytes for sample %02X in "
00557                            "instrument %02X\n", xmSample.mSampleLength, j, i);
00558                 debugPrint(aTextout, "sample length %d \n", xmSample.mSampleLength);
00559                 debugPrint(aTextout, "loop start %d \n", xmSample.mLoopStart);
00560                 debugPrint(aTextout, "loop length %d \n", xmSample.mLoopLength);
00561                 debugPrint(aTextout, "volume %d \n", xmSample.mVolume);
00562                 debugPrint(aTextout, "finetune %d \n", xmSample.mFinetune);
00563                 debugPrint(aTextout, "type %d \n", xmSample.mType);
00564                 debugPrint(aTextout, "  loop fwd? %d\n", (xmSample.mType & LOOP_FORWARD) ? 1 : 0);
00565                 debugPrint(aTextout, "  loop pingpong? %d\n", 
00566                     (xmSample.mType & LOOP_PINGPONG) ? 1 : 0);
00567                 debugPrint(aTextout, "  16 bit? %d\n", 
00568                     (xmSample.mType & XMFORMAT_SAMPLE_16BIT) ? 1 : 0);
00569                 debugPrint(aTextout, "pan %d \n", xmSample.mPan);
00570                 debugPrint(aTextout, "relative note %d \n", xmSample.mRelativeNote);
00571                 debugPrint(aTextout, "sample name %s \n", xmSample.mSampleName);
00572 
00573                 smp.mSize = xmSample.mSampleLength;
00574                 smp.mVolume = xmSample.mVolume;
00575                 smp.mPan = xmSample.mPan;
00576                 smp.mLoopForward = (UINT8)((xmSample.mType & LOOP_FORWARD) ? 1 : 0);
00577                 smp.mLoopPingpong = (UINT8)((xmSample.mType & LOOP_PINGPONG) ? 1 : 0);
00578                 smp.m16Bit = (UINT8)((xmSample.mType & XMFORMAT_SAMPLE_16BIT) ? 1 : 0);
00579                 smp.mLoopStart = xmSample.mLoopStart;
00580                 smp.mLoopEnd = xmSample.mLoopStart + xmSample.mLoopLength - 1;
00581 
00582                 if (smp.mLoopStart == smp.mLoopEnd)
00583                 {
00584                     smp.mLoopPingpong = 0;
00585                     smp.mLoopForward = 0;
00586                 }
00587 
00588                 smp.mRelativeNote = xmSample.mRelativeNote;
00589                 smp.mFinetune = xmSample.mFinetune;
00590             }
00591 
00592             // Read samples
00593             for (j = 0; j < xmInstrumentHeader.mNbSamples; j++)
00594             {
00595                 XFuXMSample &smp = ins.mSamples[j];
00596 
00597                 if (smp.mSize > 0)
00598                 {
00599                     smp.mOffset = new INT8[smp.mSize];
00600                     memset((INT8 *)smp.mOffset, 0, smp.mSize * sizeof(INT8));
00601 
00602                     if (smp.mOffset == NULL)
00603                     {
00604                         debugPrint(aTextout, "! Memory allocation for sample "
00605                                    "%02X in instrument %02X failed \n", j, i);
00606                         return 1;
00607                     }
00608 
00609                     debugPrint(aTextout, "Reading sample \n");
00610                     f->read((INT8 *)smp.mOffset, smp.mSize, 1);
00611 
00612                     if (smp.m16Bit)
00613                     {
00614                         INT16 os, ns;
00615                         INT16 *temp = (INT16 *)smp.mOffset;
00616                         os = 0;
00617 
00618                         smp.mSize >>= 1;
00619                         smp.mLoopStart >>= 1;
00620                         smp.mLoopEnd >>= 1;
00621 
00622                         if (mFlags & XFCAUDIO_16BIT)
00623                         {
00624                             UINT32 a;
00625                             for (a = 0; a < smp.mSize; ++a)
00626                             {
00627                                 ns = (INT16)(os + *(temp + a));
00628                                 *((INT16 *)smp.mOffset + a) = ns;
00629                                 os = ns;
00630                             }
00631                         }
00632                         else
00633                         {
00634                             UINT32 a;
00635                             for (a = 0; a < smp.mSize; ++a)
00636                             {
00637                                 ns = (INT16)(os + *(temp + a));
00638                                 *((INT8 *)smp.mOffset + a) = (INT8)(ns >> 8);
00639                                 os = ns;
00640                             }
00641                             
00642                             smp.m16Bit = 0;
00643 
00644                             INT8 *temp = new INT8[smp.mSize];
00645                             memcpy(temp, smp.mOffset, smp.mSize);
00646                             delete[] (INT8 *)smp.mOffset;
00647                             smp.mOffset = temp;
00648                         }
00649                     }
00650                     else
00651                     {
00652                         INT8 os, ns;
00653                         os = 0;
00654                         UINT32 a;
00655                         for (a = 0; a < smp.mSize; a++)
00656                         {
00657                             ns = (INT8)(os + *((INT8 *)smp.mOffset + a));
00658                             *((INT8 *)smp.mOffset + a) = ns;
00659                             os = ns;
00660                         }
00661                     }
00662                 }
00663             }
00664         
00665             ins.mVolumeFadeout = REALf(xmInstrument.mVolumeFadeout / 65536.0f);
00666 
00667             if (xmInstrument.mVibratoRate) 
00668                 ins.mIsVibrato = 1;
00669             else
00670                 ins.mIsVibrato = 0;
00671 
00672             ins.mVibratoType = xmInstrument.mVibratoType;
00673             ins.mVibratoSweep = xmInstrument.mVibratoSweep;
00674             ins.mVibratoDepth = xmInstrument.mVibratoDepth;
00675             ins.mVibratoRate = xmInstrument.mVibratoRate;
00676 
00677             debugPrint(aTextout, "sampleheader size: %d \n", xmInstrument.mSampleHeaderSize);
00678 
00679             debugPrint(aTextout, "keyboard: ");
00680 
00681             for (ii = 0; ii < 96; ii++)
00682             {
00683                 debugPrint(aTextout, "%d, ", xmInstrument.mKeyboard[ii]);
00684                 if (((ii + 1) % 20) == 0)
00685                     debugPrint(aTextout, "\n    ");
00686             }
00687 
00688             memcpy(ins.mKeyboard, xmInstrument.mKeyboard, 96);
00689 
00690             debugPrint(aTextout, "\n");
00691             debugPrint(aTextout, "volume. envelope: ");
00692 
00693 //            if (xmInstrument.mVolEnvType & ENVELOPE_LOOP)
00694 //                xmInstrument.mNbVolEnvPoints =
00695 //                    xmInstrument.mVolEnvLoopEnd + 1;
00696 
00697             for (ii = 0; ii < xmInstrument.mNbVolEnvPoints * 2; ii++)
00698             {
00699                 debugPrint(aTextout, "%d, ", xmInstrument.mVolumeEnvelope[ii]);
00700 
00701                 if (((ii + 1) % 20) == 0)
00702                     debugPrint(aTextout, "\n    ");
00703             }
00704 
00705             debugPrint(aTextout, "\n");
00706             debugPrint(aTextout, "pan. envelope: ");
00707 
00708 //            if (xmInstrument.mPanEnvType & ENVELOPE_LOOP)
00709 //                xmInstrument.mNbPanEnvPoints =
00710 //                    xmInstrument.mPanEnvLoopEnd + 1;
00711 
00712             for (ii = 0; ii < xmInstrument.mNbPanEnvPoints * 2; ii++)
00713             {
00714                 debugPrint(aTextout, "%d, ", xmInstrument.mPanningEnvelope[ii]);
00715 
00716                 if (((ii + 1) % 20) == 0)
00717                     debugPrint(aTextout, "\n    ");
00718             }
00719 
00720             // Volume envelope
00721             ins.mVolEnvType = xmInstrument.mVolEnvType;
00722 
00723             if (xmInstrument.mVolEnvLoopStart != xmInstrument.mVolEnvLoopEnd)
00724             {
00725                 ins.mVolEnvLoopStart = xmInstrument.mVolumeEnvelope[
00726                     xmInstrument.mVolEnvLoopStart * 2];
00727                 ins.mVolEnvLoopEnd = xmInstrument.mVolumeEnvelope[
00728                     xmInstrument.mVolEnvLoopEnd * 2];
00729             }
00730             memset(ins.mVolumeEnvelope, 0, XMFORMAT_SIZEOF_ENVELOPE);
00731 
00732             if (xmInstrument.mNbVolEnvPoints == 1)
00733             {
00734                 // Volume envelope fix for trackers that don't follow FT2 standard
00735                 ins.mVolumeEnvelope[0] = REALf(xmInstrument.mVolumeEnvelope[1] / 64.0f);
00736             }
00737             else
00738             {
00739                 for (ii = 0; ii < xmInstrument.mNbVolEnvPoints - 1; ii++)
00740                 {
00741                     x1 = xmInstrument.mVolumeEnvelope[(ii * 2)];
00742                     y1 = xmInstrument.mVolumeEnvelope[(ii * 2) + 1];
00743                     x2 = xmInstrument.mVolumeEnvelope[((ii + 1) * 2)];
00744                     y2 = xmInstrument.mVolumeEnvelope[((ii + 1) * 2) + 1];
00745 
00746                     dx = (y2 - y1) / (x2 - x1);
00747 
00748                     for (xx = x1; xx <= x2; xx++)
00749                     {
00750                         ins.mVolumeEnvelope[xx] = REALf(y1 / 64.0f);
00751                         y1 += dx;
00752                     }
00753                 }
00754             }
00755 
00756             if (xmInstrument.mVolEnvType & ENVELOPE_SUSTAIN)
00757                 ins.mVolEnvSustain = xmInstrument.mVolumeEnvelope[
00758                     xmInstrument.mVolEnvSustain * 2];
00759 
00760             if (xmInstrument.mNbVolEnvPoints > 0)
00761                 ins.mVolEnvEnd = xmInstrument.mVolumeEnvelope[
00762                     (xmInstrument.mNbVolEnvPoints - 1) * 2];
00763 
00764             // Panning envelope
00765             ins.mPanEnvType = xmInstrument.mPanEnvType;
00766 
00767             if (xmInstrument.mPanEnvLoopStart != xmInstrument.mPanEnvLoopEnd)
00768             {
00769                 ins.mPanEnvLoopStart = xmInstrument.mPanningEnvelope[
00770                     xmInstrument.mPanEnvLoopStart * 2];
00771                 ins.mPanEnvLoopEnd = xmInstrument.mPanningEnvelope[
00772                     xmInstrument.mPanEnvLoopEnd * 2];
00773             }
00774             memset(ins.mPanningEnvelope, 0, XMFORMAT_SIZEOF_ENVELOPE);
00775 
00776             if (xmInstrument.mNbPanEnvPoints == 1)
00777             {
00778                 // Panning envelope fix for trackers that don't follow FT2 standard
00779                 ins.mPanningEnvelope[0] = (UINT8)xmInstrument.mPanningEnvelope[1];
00780             }
00781             else
00782             {
00783                 for (ii = 0; ii < xmInstrument.mNbPanEnvPoints - 1; ii++)
00784                 {
00785                     x1 = xmInstrument.mPanningEnvelope[(ii * 2)];
00786                     y1 = xmInstrument.mPanningEnvelope[(ii * 2) + 1];
00787                     x2 = xmInstrument.mPanningEnvelope[((ii + 1) * 2)];
00788                     y2 = xmInstrument.mPanningEnvelope[((ii + 1) * 2) + 1];
00789 
00790                     dx = (y2 - y1) / (x2 - x1);
00791 
00792                     for (xx = x1; xx < x2; xx++)
00793                     {
00794                         ins.mPanningEnvelope[xx] = (UINT8)((INT8)y1);
00795                         y1 += dx;
00796                     }
00797                 }
00798             }
00799 
00800             ins.mPanEnvSustain = xmInstrument.mPanningEnvelope[
00801                 xmInstrument.mPanEnvSustain * 2];
00802 
00803             if (xmInstrument.mNbPanEnvPoints > 0)
00804                 ins.mPanEnvEnd = xmInstrument.mPanningEnvelope[
00805                     (xmInstrument.mNbPanEnvPoints - 1) * 2];
00806         
00807             debugPrint(aTextout, "\n");
00808 
00809             debugPrint(aTextout, "nof volpoints %d\n",
00810                        xmInstrument.mNbVolEnvPoints);
00811             debugPrint(aTextout, "nof panpoints %d\n",
00812                        xmInstrument.mNbPanEnvPoints);
00813             debugPrint(aTextout, "volume. sustain %d\n",
00814                        xmInstrument.mVolEnvSustain);
00815             debugPrint(aTextout, "volume. loop start %d\n",
00816                        xmInstrument.mVolEnvLoopStart);
00817             debugPrint(aTextout, "volume. loop end %d\n",
00818                        xmInstrument.mVolEnvLoopEnd);
00819             debugPrint(aTextout, "pan. sustain %d\n",
00820                        xmInstrument.mPanEnvSustain);
00821             debugPrint(aTextout, "pan. loop start %d\n",
00822                        xmInstrument.mPanEnvLoopStart);
00823             debugPrint(aTextout, "pan. loop end %d\n",
00824                        xmInstrument.mPanEnvLoopEnd);
00825             debugPrint(aTextout, "volume. type %d\n", xmInstrument.mVolEnvType);
00826             debugPrint(aTextout, "  env. on? %d\n",
00827                        (xmInstrument.mVolEnvType & ENVELOPE_ON) ? 1 : 0);
00828             debugPrint(aTextout, "  env. sustain? %d\n",
00829                        (xmInstrument.mVolEnvType & ENVELOPE_SUSTAIN) ? 1 : 0);
00830             debugPrint(aTextout, "  env. loop? %d \n",
00831                        (xmInstrument.mVolEnvType & ENVELOPE_LOOP) ? 1 : 0);
00832             debugPrint(aTextout, "pan. type %d\n",
00833                        xmInstrument.mPanEnvType);
00834 
00835             debugPrint(aTextout, "  env. on? %d\n",
00836                        (xmInstrument.mPanEnvType & ENVELOPE_ON) ? 1 : 0);
00837             debugPrint(aTextout, "  env. sustain? %d\n",
00838                        (xmInstrument.mPanEnvType & ENVELOPE_SUSTAIN) ? 1 : 0);
00839             debugPrint(aTextout, "  env. loop? %d \n",
00840                        (xmInstrument.mPanEnvType & ENVELOPE_LOOP) ? 1 : 0);
00841             
00842             debugPrint(aTextout, "vibr. type %d \n",
00843                        xmInstrument.mVibratoType);
00844             debugPrint(aTextout, "vibr. sweep %d \n",
00845                        xmInstrument.mVibratoSweep);
00846             debugPrint(aTextout, "vibr. depth %d \n",
00847                        xmInstrument.mVibratoDepth);
00848             debugPrint(aTextout, "vibr. rate %d \n",
00849                        xmInstrument.mVibratoRate);
00850             debugPrint(aTextout, "volume. fadeout %d \n\n",
00851                        xmInstrument.mVolumeFadeout);
00852         }
00853         else
00854         {
00855             f->seek(skip, SEEK_CUR);
00856         }
00857 
00858         debugPrint(aTextout, "-----\n\n");
00859     }
00860 
00861     mSong.mNbInstruments = mXMHeader.mNbInstruments;
00862 
00863     debugPrint(aTextout, "XM read. \n");
00864     debugPrint(aTextout, "Not changing pattern structure yet.. \n");
00865 
00866     f->close();
00867 
00868     return 0;
00869 }
00870 
00871 
00872 UINT32 XFuXMPlayer::getPeriod(INT8 aNote, INT8 aFinetune)
00873 {
00874     if (aNote < 0) aNote = 0;
00875     if (aNote > 96) aNote = 96;
00876 
00877     return (7680 - (aNote << 6) - (aFinetune >> 1));
00878 }
00879 
00880 
00881 UINT32 XFuXMPlayer::getSpeed(UINT32 aPeriod, FLOAT32 aSamplingRate)
00882 {
00883     INT32 freq;
00884     INT32 shift = (INT32)(aPeriod / 768);
00885     
00886     if (shift >= 0)
00887         freq = linearFrequencyTable[aPeriod - (shift << 9) - (shift << 8)] >> shift;
00888     //return linearFrequencyTable[aPeriod % 768] >> shift;
00889     else
00890         freq = linearFrequencyTable[aPeriod - (shift << 9) - (shift << 8)] << (-shift);
00891     //return linearFrequencyTable[aPeriod % 768] << (-shift);
00892     
00893 /*
00894     FLOAT32 freq = (FLOAT32)(8363.0f * pow(2.0f, (4608.f - (FLOAT32)period) / 768.0f));
00895 */
00896     
00897     return (INT32)((freq / aSamplingRate) * FP_VALUE);
00898 }
00899 
00900 
00901 XFuXMFormatAtom XFuXMPlayer::getAtom()
00902 {
00903     UINT8 *XMFORMAT_pa = mPatternData[mPatternNb].mData;
00904     XFuXMFormatAtom ta;
00905     UINT8 b;
00906     ta.mNote = 0;
00907     ta.mVolume = 0;
00908     ta.mInstrumentNb = 0;
00909     ta.mEffectType = 0;
00910     ta.mEffectValue = 0;
00911 
00912     b = *(XMFORMAT_pa + mPpoint++);
00913     if (b & 0x80)
00914     {
00915         // Packed atom
00916         if (b & 0x1)
00917             ta.mNote = *(XMFORMAT_pa + mPpoint++);
00918         if (b & 0x2)
00919             ta.mInstrumentNb = *(XMFORMAT_pa + mPpoint++);
00920         if (b & 0x4)
00921             ta.mVolume = *(XMFORMAT_pa + mPpoint++);
00922         if (b & 0x8)
00923             ta.mEffectType = *(XMFORMAT_pa + mPpoint++);
00924         if (b & 0x10)
00925             ta.mEffectValue = *(XMFORMAT_pa + mPpoint++);
00926     }
00927     else
00928     {
00929         // Normal atom
00930         ta.mNote = b;
00931         ta.mInstrumentNb = *(XMFORMAT_pa + mPpoint++);
00932         ta.mVolume = *(XMFORMAT_pa + mPpoint++);
00933         ta.mEffectType = *(XMFORMAT_pa + mPpoint++);
00934         ta.mEffectValue = *(XMFORMAT_pa + mPpoint++);
00935     }
00936 
00937     return ta;
00938 }
00939 
00940 
00941 void XFuXMPlayer::initChannel(XFuXMChannel &aCh)
00942 {
00943     aCh.mIsValid = 1;
00944     aCh.mInitSample = 0;
00945 
00946     // Initialize sample if valid note
00947     if ((aCh.mTa.mNote > 0) && (aCh.mTa.mNote < 97))
00948     {
00949         aCh.mNote = (INT8)(aCh.mTa.mNote - 1);
00950         aCh.mInitSample = 1;
00951 
00952         aCh.mTremorTicker = 0;
00953         aCh.mMultiRetrigTicker = 0;        
00954     }
00955     else if (aCh.mTa.mNote != 0)
00956         aCh.mIsValid = 0;
00957 
00958     aCh.mVolumeColumn = aCh.mTa.mVolume;
00959 
00960     if (aCh.mTa.mInstrumentNb != 0)
00961     {
00962         // New instrument
00963         aCh.mInstrumentNb = (INT16)(aCh.mTa.mInstrumentNb - 1);
00964 
00965         if (aCh.mInstrumentNb < mSong.mNbInstruments)
00966         {
00967             aCh.mCurrentInstrument = mInstruments[aCh.mInstrumentNb];
00968 
00969             if (aCh.mCurrentInstrument.mKeyboard[aCh.mNote] < aCh.mCurrentInstrument.mNbSamples)
00970             {
00971                 if (aCh.mCurrentInstrument.mSamples[aCh.mCurrentInstrument.mKeyboard[aCh.mNote]].mOffset != NULL)
00972                 {
00973                     aCh.mCurrentSample = aCh.mCurrentInstrument.mSamples[aCh.mCurrentInstrument.mKeyboard[aCh.mNote]];
00974                 }
00975                 else aCh.mIsValid = 0;
00976             }
00977             else aCh.mIsValid = 0;
00978         }
00979         else aCh.mIsValid = 0;
00980         
00981         aCh.mVolume = aCh.mCurrentSample.mVolume;
00982         aCh.mBaseVolume = aCh.mVolume;
00983 
00984         aCh.mPan = aCh.mCurrentSample.mPan;
00985     }
00986     else
00987     {
00988         if ((aCh.mInstrumentNb != -1) && (aCh.mInstrumentNb < mSong.mNbInstruments))
00989         {            
00990             aCh.mCurrentInstrument = mInstruments[aCh.mInstrumentNb];
00991 
00992             if (aCh.mCurrentInstrument.mKeyboard[aCh.mNote] < aCh.mCurrentInstrument.mNbSamples)
00993             {
00994                 if (aCh.mCurrentInstrument.mSamples[aCh.mCurrentInstrument.mKeyboard[aCh.mNote]].mOffset != NULL)
00995                 {
00996                     aCh.mCurrentSample = aCh.mCurrentInstrument.mSamples[aCh.mCurrentInstrument.mKeyboard[aCh.mNote]];
00997                 }
00998                 else aCh.mIsValid = 0;
00999             }
01000             else aCh.mIsValid = 0;
01001         }
01002         else aCh.mIsValid = 0;
01003     }
01004 
01005     aCh.mOldPeriod = aCh.mPeriod;
01006 
01007     // Do not initialize sound if note delay or pattern delay effect on
01008     if ((aCh.mInitSample) || (mPatternDelayCounter != 0))
01009         initSound(aCh);
01010 }
01011 
01012 
01013 void XFuXMPlayer::initSound(XFuXMChannel &aCh)
01014 {
01015     if (aCh.mIsValid)
01016     {
01017         // Initialize sample
01018         if (aCh.mInitSample) aCh.mIsSample = 1;
01019 
01020         // New note
01021         aCh.mDirection = 1;
01022 
01023         // Init vibrato
01024         /* Instrument vibrato is independent
01025         aCh.mIsVibrato = aCh.mCurrentInstrument.mIsVibrato;
01026         if (aCh.mCurrentInstrument.mVibratoDepth != 0)
01027             aCh.mVibratoDepth = aCh.mCurrentInstrument.mVibratoDepth;
01028         if (aCh.mCurrentInstrument.mVibratoRate != 0)
01029             aCh.mVibratoRate = aCh.mCurrentInstrument.mVibratoRate;
01030         */
01031         aCh.mIsVibrato = 0;
01032 
01033         if ((aCh.mVibratoWaveform & WAVEFORM_NO_RETRIG) == 0) aCh.mVibratoPointer = 0;
01034         if ((aCh.mTremoloWaveform & WAVEFORM_NO_RETRIG) == 0) aCh.mTremoloPointer = 0;
01035 
01036         // Init volume envelope
01037         aCh.mVolEnvType = aCh.mCurrentInstrument.mVolEnvType;
01038         aCh.mVolEnvPointer = 0;
01039         aCh.mVolEnvSpeed = 1;
01040         aCh.mVolEnvLoopStart = aCh.mCurrentInstrument.mVolEnvLoopStart;
01041         aCh.mVolEnvLoopEnd = aCh.mCurrentInstrument.mVolEnvLoopEnd;
01042         aCh.mVolEnvSustain = aCh.mCurrentInstrument.mVolEnvSustain;
01043 
01044         aCh.mVolumeFadeout = aCh.mCurrentInstrument.mVolumeFadeout;
01045         aCh.mVolumeFadeoutValue = REAL(1);
01046 
01047         // Init panning envelope
01048         aCh.mPanEnvType = aCh.mCurrentInstrument.mPanEnvType;
01049         aCh.mPanEnvPointer = 0;
01050         aCh.mPanEnvSpeed = 1;
01051         aCh.mPanEnvLoopStart = aCh.mCurrentInstrument.mPanEnvLoopStart;
01052         aCh.mPanEnvLoopEnd = aCh.mCurrentInstrument.mPanEnvLoopEnd;
01053         aCh.mPanEnvSustain = aCh.mCurrentInstrument.mPanEnvSustain;
01054 
01055         aCh.mSustainReleased = 0;
01056 
01057         aCh.mOffset = aCh.mCurrentSample.mOffset;
01058         aCh.mFinetune = aCh.mCurrentSample.mFinetune;
01059 
01060         if ((aCh.mEffectType != 0x3) &&   // No tone portamento
01061             (aCh.mEffectType != 0x5) &&   // No tone portamento + volume slide
01062             (aCh.mVolumeColumn < 0xF0)) // No volume column tone portamento
01063             aCh.mPointer = 0; // Reset pointer if no tone effect
01064 
01065         aCh.mLength = (aCh.mCurrentSample.mSize << FP_BITS);
01066         aCh.mPeriod = getPeriod((INT8)(aCh.mNote + aCh.mCurrentSample.mRelativeNote), aCh.mFinetune);
01067         aCh.mBasePeriod = aCh.mPeriod;
01068 
01069         aCh.mLoop = 0;
01070         if (aCh.mCurrentSample.mLoopForward) aCh.mLoop = LOOP_FORWARD;
01071         if (aCh.mCurrentSample.mLoopPingpong) aCh.mLoop = LOOP_PINGPONG;
01072         aCh.mLoopStart = (aCh.mCurrentSample.mLoopStart << FP_BITS);
01073         aCh.mLoopEnd = (aCh.mCurrentSample.mLoopEnd << FP_BITS);
01074     }
01075     else
01076         aCh.mIsSample = 0;
01077 }
01078 
01079 
01080 void XFuXMPlayer::notifyHandlers(XFuXMChannel &aCh)
01081 {
01082     XFuXMPlayerEvent event;
01083 
01084     event.mNote = aCh.mNote;
01085     event.mInstrumentNb = aCh.mInstrumentNb;
01086     event.mVolume = aCh.mVolume;
01087 
01088     if ((aCh.mEffectType != 0xE) && (aCh.mEffectType != 0x21))
01089     {
01090         event.mEffectType = aCh.mEffectType;
01091         event.mEffectValue = aCh.mEffectValue;
01092     }
01093     else
01094     {
01095         event.mEffectType = (INT16)((aCh.mEffectType << 4) | (aCh.mEffectValue >> 4));
01096         event.mEffectValue = (INT16)(aCh.mEffectValue & 0xF);
01097     }
01098 
01099     event.mPlayer = this;
01100 
01101     XFcLinkedList<XFuXMPlayerEventHandlerSlot>::forwardIterator i;
01102     XFuXMPlayerEventHandlerSlot temp;
01103 
01104     for (i = mEventHandlers->forwardBegin(); i != mEventHandlers->forwardEnd(); ++i)
01105     {
01106         temp = i.getData();
01107 
01108         if (((temp.mEvent.mNote != -1) && (temp.mEvent.mNote == event.mNote)) ||
01109             ((temp.mEvent.mInstrumentNb != -1) && (temp.mEvent.mInstrumentNb == event.mInstrumentNb)) ||
01110             ((temp.mEvent.mEffectType != -1) && (temp.mEvent.mEffectType == event.mEffectType)))
01111         {
01112             temp.mHandler->handleXMPlayerEvent(event);
01113         }
01114     }
01115 }
01116 
01117 
01118 void XFuXMPlayer::initSong(INT16 aStartOrder)
01119 {
01120     INT i;
01121 
01122     if ((aStartOrder < 0) || (aStartOrder >= mSong.mSongLength)) aStartOrder = 0;
01123     mCurrentOrder = (INT16)(aStartOrder - 1);
01124 
01125     mPatternNb = mSong.mOrderTable[aStartOrder];
01126     if (mPatternNb >= mSong.mNbPatterns) mPatternNb = mSong.mOrderTable[0];
01127 
01128     mCurrentRow = 1024;
01129 
01130     mPatternDelayCounter = 0;
01131     mPatternDelayCounterTemp = 0;
01132     mPatternDelaySkip = 0;
01133     mJumpFlag = 0;
01134     mIsRead = 1;
01135 
01136     mTickRate = (2.0f * mSong.mBpm / 5.0f);
01137 
01138     mSamplesPerTick = (INT32)(mSamplingRate / mTickRate);
01139 
01140     mCurrentTick = (UINT8)(mSong.mTempo - 1); // Playing starts from last tick
01141     mSamplePointer = mSamplesPerTick;         // Playing starts from last sample of last tick
01142 
01143     mTotalTicks = 0;
01144 
01145     mSong.mGlobalVolume = 64;
01146 
01147     for (i = 0; i < mSong.mNbChannels; i++)
01148     {
01149         XFuXMChannel &ch = mChannels[i];
01150 
01151         ch.mOffset = NULL;
01152         ch.mPointer = 0;
01153         ch.mLength = 0;
01154         ch.mInstrumentNb = -1;
01155         ch.mSpeed = 0;
01156         ch.mPeriod = 0;
01157 
01158         ch.mLoop = 0;
01159         ch.mLoopStart = 0;
01160         ch.mLoopEnd = 0;
01161 
01162         ch.mIsSample = 0;
01163 
01164         ch.mVolume = 0;
01165         ch.mBaseVolume = 0;
01166         ch.mFinalOldVolume = 0;
01167         ch.mFinalVolumeSpeed = 0;
01168 
01169         ch.mNote = 0;
01170 
01171         ch.mDirection = 1;
01172 
01173         ch.mVolEnvType = 0;
01174         ch.mVolEnvPointer = 0;
01175         ch.mVolEnvSpeed = 0;
01176         ch.mVolEnvLoopStart = 0;
01177         ch.mVolEnvLoopEnd = 0;
01178         ch.mVolEnvSustain = 0;
01179         ch.mVolEnvValue = 0;
01180 
01181         ch.mPanEnvType = 0;
01182         ch.mPanEnvPointer = 0;
01183         ch.mPanEnvSpeed = 0;
01184         ch.mPanEnvLoopStart = 0;
01185         ch.mPanEnvLoopEnd = 0;
01186         ch.mPanEnvSustain = 0;
01187         ch.mPanEnvValue = 0;
01188 
01189         ch.mVolumeFadeout = 0;
01190         ch.mVolumeFadeoutValue = 0;
01191 
01192         ch.mSustainReleased = 0;
01193 
01194         ch.mVolumeColumn = 0;
01195 
01196         ch.mEffectType = 0;
01197         ch.mEffectValue = 0;
01198         ch.mTonePortamentoV = 0;
01199         ch.mVolumeSlideV = 0;
01200         ch.mGlobalVolumeSlideV = 0;
01201         ch.mFineVolumeSlideUpV = 0;
01202         ch.mFineVolumeSlideDownV = 0;
01203         ch.mFinePortamentoUpV = 0;
01204         ch.mFinePortamentoDownV = 0;
01205         ch.mFineVolumeSlideUpV = 0;
01206         ch.mFineVolumeSlideDownV = 0;
01207         ch.mGlobalVolumeSlideV = 0;
01208         ch.mMultiRetrigVolumeV = 0;
01209         ch.mMultiRetrigRateV = 0;
01210         ch.mTremorV = 0;
01211         ch.mExtraFinePortamentoUpV = 0;
01212         ch.mExtraFinePortamentoDownV = 0;
01213 
01214         ch.mMultiRetrigTicker = 0;
01215         ch.mTremorTicker = 0;
01216 
01217         ch.mPeriod = 0;
01218         ch.mBasePeriod = 0;
01219         ch.mOldPeriod = 0;
01220         ch.mDestPeriod = 0;
01221 
01222         ch.mIsVibrato = 0;
01223         ch.mVibratoDepth = 0;
01224         ch.mVibratoRate = 0;
01225         ch.mVibratoPointer = 0;
01226         ch.mVibratoWaveform = WAVEFORM_SINEWAVE;
01227 
01228         ch.mTremoloDepth = 0;
01229         ch.mTremoloRate = 0;
01230         ch.mTremoloPointer = 0;
01231         ch.mTremoloWaveform = WAVEFORM_SINEWAVE;
01232     }
01233 }
01234 
01235 
01236 INT32 XFuXMPlayer::getTick()
01237 {
01238     return (INT32)((mTotalTicks * 1000) / mSamplingRate);
01239 }
01240 
01241 
01242 INT16 XFuXMPlayer::getCurrentOrder()
01243 {
01244     return mCurrentOrder;
01245 }
01246 
01247 
01248 void XFuXMPlayer::addHandler(XFuXMPlayerEvent aEvent, XFuXMPlayerEventHandler *aHandler)
01249 {
01250     if (aHandler != NULL)
01251     {
01252         XFuXMPlayerEventHandlerSlot temp;
01253         temp.mEvent = aEvent;
01254         temp.mHandler = aHandler;
01255 
01256         mEventHandlers->addLast(temp);
01257     }
01258 }
01259 
01260 
01261 INT XFuXMPlayer::removeHandler(XFuXMPlayerEventHandler *aHandler)
01262 {
01263     if (aHandler != NULL)
01264     {
01265         XFuXMPlayerEventHandlerSlot temp;
01266         temp.mHandler = aHandler;
01267 
01268         return mEventHandlers->remove(temp);
01269     }
01270 
01271     return 0;
01272 }
01273 
01274 
01275 void XFuXMPlayer::removeHandlers()
01276 {
01277     XFcLinkedList<XFuXMPlayerEventHandlerSlot>::forwardIterator i = mEventHandlers->forwardBegin();
01278     XFuXMPlayerEventHandlerSlot temp;
01279 
01280     while (i.isValid())
01281     {
01282         mEventHandlers->remove(i);
01283     }
01284 }
01285 
01286 
01287 void XFuXMPlayer::stop()
01288 {
01289     initSong(0);
01290 }
01291 
01292 // For MSVC
01293 #pragma warning(disable:4244)
01294 
01295 UINT32 XFuXMPlayer::stream(void *aBuffer, INT32 aTargetSamples, INT32 aOffset, 
01296                            INT32 aSamples)
01297 {
01298     INT32 cLeft, cRight;
01299 
01300     INT32 chan;
01301 
01302     INT32 index;
01303     INT32 targetOffset = aOffset;
01304     INT32 counter;
01305     INT32 indicesLeft;
01306     INT32 ticksLeft;
01307     INT32 samplesToRender;
01308 
01309     INT32 delta;
01310     INT32 val1, val2;
01311     INT32 value;
01312     INT32 temp;
01313 
01314     XFuXMFormatAtom ta;
01315     INT16 *vibratoTable = mSineWaveTable;
01316     INT16 *tremoloTable = mSineWaveTable;
01317 
01318     for (index = 0; index < aSamples; )
01319     {
01320         if (mSamplePointer == mSamplesPerTick)
01321         {
01322             // Tick finished
01323             mSamplePointer = 0;
01324 
01325             mRamp = 0;
01326 
01327             if (mIsRead && (++mCurrentTick == mSong.mTempo))
01328             {
01329                 // Row finished
01330                 mCurrentTick = 0;
01331 
01332                 if (mPatternDelayCounter == 0)
01333                 {
01334                     mCurrentRow++;
01335                     mPatternDelaySkip = 0;
01336                 }
01337                 else 
01338                     mPatternDelayCounter--;
01339 
01340                 if ((mCurrentRow >= mXMPatternHeaders[mPatternNb].mNbRows) || (mJumpFlag != 0))
01341                 {
01342                     // Pattern finished
01343                     mCurrentOrder++;
01344                     if (mCurrentOrder >= mSong.mSongLength) mCurrentOrder = mSong.mRestartPosition;
01345 
01346                     mPatternNb = mSong.mOrderTable[mCurrentOrder];
01347                     if (mPatternNb >= mSong.mNbPatterns) mPatternNb = mSong.mOrderTable[0];
01348 
01349                     if (mJumpFlag == 0) mCurrentRow = 0;
01350 
01351                     mJumpFlag = 0;
01352                 }
01353 
01354                 mPpoint = mPatternData[mPatternNb].mRows[mCurrentRow];
01355 
01356                 for (chan = 0; chan < mSong.mNbChannels; chan++)
01357                 {
01358                     XFuXMChannel &ch = mChannels[chan];
01359 
01360                     if (mPatternDelayCounter == 0)
01361                     {
01362                         ta = getAtom(); // Get track data
01363 
01364                         ch.mTa = ta;
01365 
01366                         ch.mEffectType = ta.mEffectType;
01367                         ch.mEffectValue = ta.mEffectValue;
01368 
01369                         if (!((ch.mEffectType == 0xE) &&
01370                             // No change if note delay effect
01371                             (((ch.mEffectValue >> 4) & 0xF) == 0xD)))
01372                         {
01373                             initChannel(ch);
01374                         }
01375                     }
01376 
01377                     // Volume column effects
01378                     if (ch.mVolumeColumn != 0)
01379                     {
01380                         // Set volume
01381                         if ((ch.mVolumeColumn >= 0x10) && (ch.mVolumeColumn <= 0x50))
01382                         {
01383                             ch.mVolume = (INT8)(ch.mVolumeColumn - 0x10);
01384                             if (ch.mVolume > 0x40) ch.mVolume = 0x40;
01385                             ch.mBaseVolume = ch.mVolume;
01386                         }
01387                         else
01388                         {
01389                             switch (ch.mVolumeColumn >> 4)
01390                             {
01391                             case 0x8:   // Fine volume slide down (arrow down)
01392                                 if (ch.mVolume > 0) ch.mVolume -= (ch.mVolumeColumn & 0xF);
01393                                 ch.mBaseVolume = ch.mVolume;
01394                                 break;
01395 
01396                             case 0x9:    // Fine volume slide up (arrow up)
01397                                 if (ch.mVolume < 0x40) ch.mVolume += (ch.mVolumeColumn & 0xF);
01398                                 ch.mBaseVolume = ch.mVolume;
01399                                 break;
01400 
01401                             case 0xA:   // Set vibrato speed (Sxy)
01402                                 if ((ch.mVolumeColumn & 0xF) != 0) 
01403                                     ch.mVibratoRate = (UINT8)(ch.mVolumeColumn & 0xF);
01404                                 break;
01405 
01406                             case 0xB:   // Set vibrato depth (Vxy)
01407                                 if ((ch.mVolumeColumn & 0xF) != 0)
01408                                 {
01409                                     ch.mVibratoDepth = (UINT8)((ch.mVolumeColumn & 0xF) * 4);
01410                                     ch.mIsVibrato = 1;
01411                                 }
01412                                 break;
01413 
01414                             case 0xC:   // Set panning (Pxy)
01415                                 ch.mPan = (UINT8)((ch.mVolumeColumn & 0xF) << 4);
01416                                 break;
01417 
01418                             case 0xD:   // Panning slide left (left arrow)
01419                                 if (ch.mPan > 0) ch.mPan -= (ch.mVolumeColumn & 0xF);
01420                                 break;
01421 
01422                             case 0xE:   // Panning slide right (right arrow)
01423                                 if (ch.mPan < 0xFF) ch.mPan += (ch.mVolumeColumn & 0xF);
01424                                 break;
01425 
01426                             case 0xF:   // Tone portamento (Mxy)
01427                                 if ((ch.mVolumeColumn & 0xF) != 0)
01428                                     ch.mTonePortamentoV = (UINT8)((ch.mVolumeColumn & 0xF) << 4);
01429                         
01430                                 if ((ch.mTa.mNote != 0) && (ch.mTa.mNote != 97))
01431                                 {
01432                                     ch.mDestPeriod = ch.mPeriod;
01433                                     ch.mPeriod = ch.mOldPeriod;
01434                                     ch.mBasePeriod = ch.mPeriod;
01435                                 }
01436                                 break;
01437                             }
01438                         }
01439                     }
01440 
01441                     // Row effects
01442                     switch (ch.mEffectType)
01443                     {
01444                         case 0x0:   // Arpeggio (0xy)
01445                             ch.mPeriod = ch.mBasePeriod;
01446                             break;
01447 
01448                         case 0x1:   // Portamento up (1xy)
01449                             if (ch.mEffectValue != 0) ch.mPortamentoUpV = ch.mEffectValue;
01450                             break;
01451 
01452                         case 0x2:   // Portamento down (2xy)
01453                             if (ch.mEffectValue != 0) ch.mPortamentoDownV = ch.mEffectValue;
01454                             break;
01455 
01456                         case 0x3:   // Tone portamento (3xy)
01457                             if (ch.mOldPeriod != 0)
01458                             {
01459                                 if (ch.mEffectValue != 0) ch.mTonePortamentoV = ch.mEffectValue;
01460 
01461                                 if ((ch.mTa.mNote != 0) && (ch.mTa.mNote != 97))
01462                                 {
01463                                     ch.mDestPeriod = ch.mPeriod;
01464                                     ch.mPeriod = ch.mOldPeriod;
01465                                     ch.mBasePeriod = ch.mPeriod;
01466                                 }
01467                             }
01468                             break;
01469                     
01470                         case 0x4:   // Vibrato (4xy)
01471                             if ((ch.mEffectValue & 0xF) != 0)
01472                                 ch.mVibratoDepth = (UINT8)((ch.mEffectValue & 0xF) * 4);
01473 
01474                             if ((ch.mEffectValue >> 4) != 0)
01475                                 ch.mVibratoRate = (UINT8)(ch.mEffectValue >> 4);
01476 
01477                             ch.mIsVibrato = 1;
01478                             break;
01479                     
01480                         case 0x5:   // Tone portamento + volume slide (5xy)
01481                             if (ch.mEffectValue != 0) ch.mVolumeSlideV = ch.mEffectValue;
01482                             break;
01483 
01484                         case 0x6:   // Vibrato + volume slide (6xy)
01485                             if (ch.mEffectValue != 0) ch.mVolumeSlideV = ch.mEffectValue;
01486 
01487                             ch.mIsVibrato = 1;
01488                             break;
01489 
01490                         case 0x7:   // Tremolo (7xy)
01491                             if ((ch.mEffectValue & 0xF) != 0)
01492                                 ch.mTremoloDepth = (UINT8)(ch.mEffectValue & 0xF);
01493 
01494                             if ((ch.mEffectValue >> 4) != 0)
01495                                 ch.mTremoloRate = (UINT8)(ch.mEffectValue >> 4);
01496                             break;
01497 
01498                         case 0x8:   // Set panning (8xy)
01499                             ch.mPan = ch.mEffectValue;
01500                             break;
01501 
01502                         case 0x9:   // Sample offset (9xy)
01503                             ch.mPointer = ch.mEffectValue << (8 + FP_BITS);
01504                             XFCASSERT(ch.mPointer < ch.mLength);
01505                             // FastTracker2 behaviour when not in debug mode,
01506                             // if offset is beyond sample length, stop sample
01507                             if (ch.mPointer >= ch.mLength) ch.mIsSample = 0;
01508                             break;
01509 
01510                         case 0xA:   // Volume slide (Axy)
01511                             if (ch.mEffectValue != 0) ch.mVolumeSlideV = ch.mEffectValue;
01512                             break;
01513 
01514                         case 0xB:   // Jump to pattern (Bxy)
01515                             if (mJumpFlag == 0)
01516                             {
01517                                 mCurrentRow = -1;
01518                                 mCurrentOrder = (INT16)(ch.mEffectValue - 1);
01519                                 mJumpFlag = 1;
01520                             }
01521                             break;
01522 
01523                         case 0xC:   // Volume (Cxy)
01524                             ch.mVolume = ch.mEffectValue;
01525                             if (ch.mVolume > 0x40) ch.mVolume = 0x40;
01526                             ch.mBaseVolume = ch.mVolume;
01527                             break;
01528                     
01529                         case 0xD:   // Pattern break (Dxy)
01530                             if (mJumpFlag == 0)
01531                             {
01532                                 mCurrentRow = (INT16)(ch.mEffectValue - 1);
01533                                 mJumpFlag = 1;
01534                             }
01535                             break;
01536 
01537                         case 0xE:   // Extra effects (Exy)
01538                             switch ((ch.mEffectValue >> 4))
01539                             {
01540                                 case 0x1:   // Fine portamento up (E1y)
01541                                     if ((ch.mEffectValue & 0xF) != 0)
01542                                         ch.mFinePortamentoUpV = (UINT8)(ch.mEffectValue & 0xF);
01543 
01544                                     ch.mPeriod -= ch.mFinePortamentoUpV * 4;
01545                                     ch.mBasePeriod = ch.mPeriod;
01546                                     break;
01547 
01548                                 case 0x2:   // Fine portamento down (E2y)
01549                                     if ((ch.mEffectValue & 0xF) != 0)
01550                                         ch.mFinePortamentoUpV = (UINT8)(ch.mEffectValue & 0xF);
01551 
01552                                     ch.mPeriod += ch.mFinePortamentoUpV * 4;
01553                                     ch.mBasePeriod = ch.mPeriod;
01554                                     break;
01555 
01556                                 case 0x3:   // Glissando control (E3y), FIX ME!
01557                                     break;
01558 
01559                                 case 0x4:   // Vibrato waveform (E4y)
01560                                     if ((ch.mEffectValue & 0xF) < 7)
01561                                         ch.mVibratoWaveform = (UINT8)(ch.mEffectValue & 0xF);
01562                                     break;
01563 
01564                                 case 0x5:   // Set finetune (E5y)
01565                                     ch.mFinetune = (INT8)((ch.mEffectValue & 0xF) << 1);
01566                                     break;
01567 
01568                                 case 0x6:   // Pattern loop (E6y), FIX ME!
01569                                     break;
01570 
01571                                 case 0x7:   // Tremolo waveform (E7y)
01572                                     if ((ch.mEffectValue & 0xF) < 7)
01573                                         ch.mTremoloWaveform = (UINT8)(ch.mEffectValue & 0xF);
01574                                     break;
01575 
01576                                 case 0xA:   // Fine volume slide up (EAy)
01577                                     if ((ch.mEffectValue & 0xF) != 0)
01578                                         ch.mFineVolumeSlideUpV = (UINT8)(ch.mEffectValue & 0xF);
01579 
01580                                     if (ch.mVolume < 0x40) ch.mVolume += ch.mFineVolumeSlideUpV;
01581                                     ch.mBaseVolume = ch.mVolume;
01582                                     break;
01583 
01584                                 case 0xB:   // Fine volume slide down (EBy)
01585                                     if ((ch.mEffectValue & 0xF) != 0)
01586                                         ch.mFineVolumeSlideDownV = (UINT8)(ch.mEffectValue & 0xF);
01587 
01588                                     if (ch.mVolume > 0) ch.mVolume -= ch.mFineVolumeSlideDownV;
01589                                     ch.mBaseVolume = ch.mVolume;
01590                                     break;
01591 
01592                                 case 0xE:   // Pattern delay (EEy)
01593                                     if ((mPatternDelayCounter == 0) && (mPatternDelaySkip == 0))
01594                                         mPatternDelayCounterTemp = (UINT8)(ch.mEffectValue & 0xF);
01595                                     break;
01596 
01597                                 }
01598                             break;
01599                     
01600                         case 0xF:   // Tempo or BPM change (Fxy)
01601                             if (ch.mEffectValue < 32) 
01602                             {
01603                                 mSong.mTempo = ch.mEffectValue;
01604                                 if (mSong.mTempo == 0) mIsRead = 0;
01605                             }
01606                             else
01607                             { // BPM
01608                                 mSong.mBpm = ch.mEffectValue;
01609                                 mTickRate = 2.0f * mSong.mBpm / 5.0f;
01610 
01611                                 mSamplesPerTick = (INT)(mSamplingRate / mTickRate);
01612                             }
01613                             break;
01614 
01615                         case 0x10:  // Global volume (Gxy)
01616                             mSong.mGlobalVolume = ch.mEffectValue;
01617 
01618                             if (mSong.mGlobalVolume > 0x40) mSong.mGlobalVolume = 0x40;
01619                             break;
01620 
01621                         case 0x11:  // Global volume slide (Hxy)
01622                             if (ch.mEffectValue != 0) ch.mGlobalVolumeSlideV = ch.mEffectValue;
01623 
01624                             if (mSong.mGlobalVolume < 0x40) 
01625                                 mSong.mGlobalVolume += (ch.mGlobalVolumeSlideV >> 4) << 1;
01626                             if (mSong.mGlobalVolume > 0) 
01627                                 mSong.mGlobalVolume -= (ch.mGlobalVolumeSlideV & 0xF) << 1;
01628                             break;
01629 
01630                         case 0x15:  // Set envelope position (Lxy)
01631                             ch.mVolEnvPointer = ch.mPanEnvPointer = ch.mEffectValue;
01632                             break;
01633 
01634                         case 0x19:  // Panning slide (Pxy)
01635                             if (ch.mEffectValue != 0) ch.mPanningSlideV = ch.mEffectValue;
01636                             break;
01637 
01638                         case 0x1B:  // Multi retrig note (Rxy)
01639                             if ((ch.mEffectValue >> 4) != 0)
01640                                 ch.mMultiRetrigVolumeV = (UINT8)(ch.mEffectValue >> 4);
01641 
01642                             if ((ch.mEffectValue & 0xF) != 0)
01643                                 ch.mMultiRetrigRateV = (UINT8)(ch.mEffectValue & 0xF);
01644 
01645                             if (ch.mMultiRetrigRateV != 0)
01646                             {
01647                                 if ((ch.mMultiRetrigTicker % ch.mMultiRetrigRateV) == 0)
01648                                 {
01649                                     ch.mInitSample = 1;
01650                                     initSound(ch);
01651 
01652                                     if ((ch.mMultiRetrigVolumeV != 8) && (ch.mMultiRetrigTicker != 0))
01653                                     {
01654                                         // Don't affect volume on first tick
01655                                         switch (ch.mMultiRetrigVolumeV)
01656                                         {
01657                                             case 0x1:
01658                                                 ch.mVolume -= 1;
01659                                                 break;
01660                                             case 0x2:
01661                                                 ch.mVolume -= 2;
01662                                                 break;
01663                                             case 0x3:
01664                                                 ch.mVolume -= 4;
01665                                                 break;
01666                                             case 0x4:
01667                                                 ch.mVolume -= 8;
01668                                                 break;
01669                                             case 0x5:
01670                                                 ch.mVolume -= 16;
01671                                                 break;
01672                                             case 0x6:
01673                                                 ch.mVolume = (INT8)((ch.mVolume * 2) / 3);
01674                                                 break;
01675                                             case 0x7:
01676                                                 ch.mVolume >>= 1;
01677                                                 break;
01678                                             case 0x9:
01679                                                 ch.mVolume += 1;
01680                                                 break;
01681                                             case 0xA:
01682                                                 ch.mVolume += 2;
01683                                                 break;
01684                                             case 0xB:
01685                                                 ch.mVolume += 4;
01686                                                 break;
01687                                             case 0xC:
01688                                                 ch.mVolume += 8;
01689                                                 break;
01690                                             case 0xD:
01691                                                 ch.mVolume += 16;
01692                                                 break;
01693                                             case 0xE:
01694                                                 ch.mVolume = (INT8)((ch.mVolume * 3) / 2);
01695                                                 break;
01696                                             case 0xF:
01697                                                 ch.mVolume <<= 1;
01698                                                 break;
01699                                         }
01700 
01701                                         ch.mBaseVolume = ch.mVolume;
01702                                     }
01703                                 }
01704                             }
01705 
01706                             ch.mMultiRetrigTicker++;
01707                             break;
01708 
01709                         case 0x1D:  // Tremor (Txy)
01710                             if (ch.mEffectValue != 0) ch.mTremorV = ch.mEffectValue;
01711                         
01712                             if (ch.mTremorV != 0)
01713                             {
01714                                 if ((UINT8)(ch.mTremorTicker % 
01715                                     ((ch.mTremorV >> 4) + (ch.mTremorV & 0xF) + 2)) < 
01716                                     (ch.mTremorV >> 4) + 1)
01717                                     ch.mVolume = ch.mBaseVolume;
01718                                 else
01719                                     ch.mVolume = 0;
01720                             }
01721                             break;
01722 
01723                         case 0x21:  // Extra fine portamento up/down (X1y/X2y)
01724                             switch(ch.mEffectValue >> 4)
01725                             {
01726                                 case 0x1:   // Extra fine portamento up (X1y)
01727                                     if ((ch.mEffectValue & 0xF) != 0)
01728                                         ch.mExtraFinePortamentoUpV = (UINT8)(ch.mEffectValue & 0xF);
01729 
01730                                     ch.mPeriod -= ch.mExtraFinePortamentoUpV;
01731                                     ch.mBasePeriod = ch.mPeriod;
01732                                     break;
01733                                 case 0x2:   // Extra fine portamento down (X2y)
01734                                     if ((ch.mEffectValue & 0xF) != 0)
01735                                         ch.mExtraFinePortamentoDownV = (UINT8)(ch.mEffectValue & 0xF);
01736 
01737                                     ch.mPeriod -= ch.mExtraFinePortamentoUpV;
01738                                     ch.mBasePeriod = ch.mPeriod;
01739                                     break;
01740                             }
01741                             break;
01742                     }
01743 
01744                     notifyHandlers(ch);
01745 
01746                     // Key off (Kxy)
01747                     if ((ch.mEffectType == 0x14) || (ch.mTa.mNote == 97))
01748                     {
01749                         if (ch.mSustainReleased == 0)
01750                         {
01751                             ch.mSustainReleased = 1;
01752                             if (ch.mVolEnvType & ENVELOPE_SUSTAIN)
01753                                 ch.mVolEnvSpeed = 1; // Start envelope
01754 
01755                             if (ch.mVolEnvType == 0)
01756                                 ch.mVolume = 0;
01757 
01758                             // FIX ME!!! PANNING ENVELOPE STUFF MISSING!!!
01759                         }
01760                      }
01761                 }
01762 
01763                 if (mPatternDelayCounterTemp != 0)
01764                 {
01765                     mPatternDelayCounter = mPatternDelayCounterTemp;
01766                     mPatternDelayCounterTemp = 0;
01767                     mPatternDelaySkip = 1;
01768                 }
01769             }
01770             else if (mSong.mTempo != 0)
01771             {
01772                 // Tick effects
01773                 for (chan = 0; chan < mSong.mNbChannels; chan++)
01774                 {
01775                     XFuXMChannel &ch = mChannels[chan];
01776 
01777                     if (ch.mVolumeColumn != 0)
01778                     {
01779                         switch(ch.mVolumeColumn >> 4)
01780                         {
01781                         case 0x6:   // Volume slide down (-xy)
01782                             ch.mVolume -= (ch.mVolumeColumn & 0xF);
01783                             ch.mBaseVolume = ch.mVolume;
01784                             break;
01785 
01786                         case 0x7:   // Volume slide up (+xy)
01787                             ch.mVolume += (ch.mVolumeColumn & 0xF);
01788                             ch.mBaseVolume = ch.mVolume;
01789                             break;
01790 
01791                         case 0xF:   // Tone portamento (Mxy)
01792                             if (ch.mOldPeriod != 0)
01793                             {
01794                                 if (ch.mDestPeriod > ch.mPeriod)
01795                                 {
01796                                     ch.mPeriod += (ch.mTonePortamentoV * 4);
01797                                     if (ch.mPeriod > ch.mDestPeriod)
01798                                         ch.mPeriod = ch.mDestPeriod;
01799                                 }
01800                                 else if (ch.mDestPeriod < ch.mPeriod)
01801                                 {
01802                                     ch.mPeriod -= (ch.mTonePortamentoV * 4);
01803                                     if (ch.mPeriod < ch.mDestPeriod)
01804                                         ch.mPeriod = ch.mDestPeriod;
01805                                 }
01806 
01807                                 ch.mBasePeriod = ch.mPeriod;
01808                             }
01809                             break;
01810                         }
01811                     }
01812 
01813                     switch (ch.mEffectType)
01814                     {
01815                         case 0x0:   // Arpeggio (0xy)
01816                             if (ch.mEffectValue != 0)
01817                             {
01818                                 switch (mCurrentTick % 3)
01819                                 {
01820                                     case 0:
01821                                         ch.mPeriod = ch.mBasePeriod;
01822                                         break;
01823                                     case 1:
01824                                         ch.mPeriod = ch.mBasePeriod - (ch.mEffectValue >> 4) * 64;
01825                                         break;
01826                                     case 2:
01827                                         ch.mPeriod = ch.mBasePeriod - (ch.mEffectValue & 0xF) * 64;
01828                                         break;
01829                                 }
01830                             }
01831                             break;
01832 
01833                         case 0x1:   // Portamento up (1xy)
01834                             ch.mPeriod -= (ch.mPortamentoUpV * 4);
01835                             ch.mBasePeriod = ch.mPeriod;
01836                             break;
01837 
01838                         case 0x2:   // Portamento down (2xy)
01839                             ch.mPeriod += (ch.mPortamentoDownV * 4);
01840                             ch.mBasePeriod = ch.mPeriod;
01841                             break;
01842 
01843                         case 0x7:   // Tremolo (7xy), FIX ME!
01844                             if (ch.mTremoloWaveform & WAVEFORM_SINEWAVE)
01845                                 tremoloTable = mSineWaveTable;
01846                             else if (ch.mTremoloWaveform & WAVEFORM_SQUAREWAVE)
01847                                 tremoloTable = mSquareWaveTable;
01848                             else if (ch.mTremoloWaveform & WAVEFORM_RAMPDOWN)
01849                                 tremoloTable = mRampDownTable;
01850                             // FIX ME! Random waveform not supported
01851 
01852                             ch.mVolume = (INT8)(ch.mBaseVolume + 
01853                                 ((tremoloTable[ch.mTremoloPointer] * ch.mTremoloDepth) >> 6));
01854                             ch.mTremoloPointer = (UINT8)(
01855                                 (ch.mTremoloPointer + ch.mTremoloRate) % XMFORMAT_SIZEOF_WAVEFORM);
01856                             break;
01857 
01858                         case 0xE:   // Extra effects (Exy)
01859                             switch ((ch.mEffectValue >> 4))
01860                             {
01861                                 case 0x9:   // Retrig note (E9y)
01862                                     if (((ch.mEffectValue & 0xF) != 0) &&
01863                                         ((mCurrentTick % (ch.mEffectValue & 0xF)) == 0))
01864                                     {
01865                                         ch.mInitSample = 1;
01866                                         initSound(ch);
01867                                     }
01868                                     break;
01869 
01870                                 case 0xC:   // Note cut (ECy)
01871                                     if (mCurrentTick == (ch.mEffectValue & 0xF))
01872                                     {
01873                                         ch.mVolume = 0;
01874                                         ch.mBaseVolume = ch.mVolume;
01875                                     }
01876                                     break;
01877 
01878                                 case 0xD:   // Note delay (EDy)
01879                                     if (mCurrentTick == (ch.mEffectValue & 0xF))
01880                                     {
01881                                         initChannel(ch);
01882                                     }
01883                                     break;
01884                             }
01885                             break;
01886 
01887                         case 0x19:  // Panning slide (Pxy)
01888                             ch.mPan += (ch.mPanningSlideV >> 4);
01889                             if (ch.mPan > 0xFF) ch.mPan = 0xFF;
01890 
01891                             ch.mPan -= (ch.mPanningSlideV & 0xF);
01892                             if (ch.mPan < 0) ch.mPan = 0;
01893 
01894                             break;
01895 
01896                         case 0x1B:  // Multi retrig note (Rxy)
01897                             if (ch.mMultiRetrigRateV != 0)
01898                             {
01899                                 if ((ch.mMultiRetrigTicker % ch.mMultiRetrigRateV) == 0)
01900                                 {
01901                                     ch.mInitSample = 1;
01902                                     initSound(ch);
01903 
01904                                     if ((ch.mMultiRetrigVolumeV != 8) && (ch.mMultiRetrigTicker != 0))
01905                                     {
01906                                         // Don't affect volume on first tick
01907                                         switch (ch.mMultiRetrigVolumeV)
01908                                         {
01909                                             case 0x1:
01910                                                 ch.mVolume -= 1;
01911                                                 break;
01912                                             case 0x2:
01913                                                 ch.mVolume -= 2;
01914                                                 break;
01915                                             case 0x3:
01916                                                 ch.mVolume -= 4;
01917                                                 break;
01918                                             case 0x4:
01919                                                 ch.mVolume -= 8;
01920                                                 break;
01921                                             case 0x5:
01922                                                 ch.mVolume -= 16;
01923                                                 break;
01924                                             case 0x6:
01925                                                 ch.mVolume = (INT8)(ch.mVolume * 2.0f / 3.0f);
01926                                                 break;
01927                                             case 0x7:
01928                                                 ch.mVolume >>= 1;
01929                                                 break;
01930                                             case 0x9:
01931                                                 ch.mVolume += 1;
01932                                                 break;
01933                                             case 0xA:
01934                                                 ch.mVolume += 2;
01935                                                 break;
01936                                             case 0xB:
01937                                                 ch.mVolume += 4;
01938                                                 break;
01939                                             case 0xC:
01940                                                 ch.mVolume += 8;
01941                                                 break;
01942                                             case 0xD:
01943                                                 ch.mVolume += 16;
01944                                                 break;
01945                                             case 0xE:
01946                                                 ch.mVolume = (INT8)(ch.mVolume * 3.0f / 2.0f);
01947                                                 break;
01948                                             case 0xF:
01949                                                 ch.mVolume <<= 1;
01950                                                 break;
01951                                         }
01952 
01953                                         if (ch.mVolume > 0x40) ch.mVolume = 0x40;
01954                                         else if (ch.mVolume < 0) ch.mVolume = 0;
01955 
01956                                         ch.mBaseVolume = ch.mVolume;
01957                                     }
01958                                 }
01959                             }
01960 
01961                             ch.mMultiRetrigTicker++;
01962                             break;
01963 
01964                         case 0x1D:  // Tremor (Txy)
01965                             if (ch.mTremorV != 0)
01966                             {
01967                                 if ((UINT8)(ch.mTremorTicker % 
01968                                     ((ch.mTremorV >> 4) + (ch.mTremorV & 0xF) + 2)) < 
01969                                     (ch.mTremorV >> 4) + 1)
01970                                     ch.mVolume = ch.mBaseVolume;
01971                                 else
01972                                     ch.mVolume = 0;
01973                             }
01974 
01975                             ch.mTremorTicker++;
01976                             break;
01977 
01978                     }
01979 
01980                     // Tone portamento (3xy) /
01981                     // tone portamento + volume slide (5xy) /
01982                     // volume column tone portamento (Mxy)
01983                     if ((ch.mEffectType == 0x3) || (ch.mEffectType == 0x5))
01984                     {
01985                         if (ch.mOldPeriod != 0)
01986                         {
01987                             if (ch.mDestPeriod > ch.mPeriod)
01988                             {
01989                                 ch.mPeriod += (ch.mTonePortamentoV * 4);
01990                                 if (ch.mPeriod > ch.mDestPeriod) ch.mPeriod = ch.mDestPeriod;
01991                             }
01992                             else if (ch.mDestPeriod < ch.mPeriod)
01993                             {
01994                                 ch.mPeriod -= (ch.mTonePortamentoV * 4);
01995                                 if (ch.mPeriod < ch.mDestPeriod) ch.mPeriod = ch.mDestPeriod;
01996                             }
01997 
01998                             ch.mBasePeriod = ch.mPeriod;
01999                         }
02000                     }
02001 
02002                     // Volume slide (Axy) / 
02003                     // tone portamento + volume slide (5xy) / 
02004                     // vibrato + volume slide (6xy)
02005                     if ((ch.mEffectType == 0xA) || (ch.mEffectType == 0x5) || (ch.mEffectType == 0x6))
02006                     {
02007                         ch.mVolume += (ch.mVolumeSlideV >> 4);
02008                         if (ch.mVolume > 0x40) ch.mVolume = 0x40;
02009 
02010                         ch.mVolume -= (ch.mVolumeSlideV & 0xF);
02011                         if (ch.mVolume < 0) ch.mVolume = 0;
02012 
02013                         ch.mBaseVolume = ch.mVolume;
02014                     }
02015 
02016                     // Vibrato
02017                     if (ch.mIsVibrato)
02018                     {
02019                         if (ch.mVibratoWaveform & WAVEFORM_SINEWAVE)
02020                             vibratoTable = mSineWaveTable;
02021                         else if (ch.mVibratoWaveform & WAVEFORM_SQUAREWAVE)
02022                             vibratoTable = mSquareWaveTable;
02023                         else if (ch.mVibratoWaveform & WAVEFORM_RAMPDOWN)
02024                             vibratoTable = mRampDownTable;
02025                         // FIX ME! Random waveform not supported
02026 
02027                         ch.mPeriod = ch.mBasePeriod + 
02028                             ((vibratoTable[ch.mVibratoPointer] * ch.mVibratoDepth) >> 7);
02029                         ch.mVibratoPointer = (UINT8)(
02030                             (ch.mVibratoPointer + ch.mVibratoRate) % XMFORMAT_SIZEOF_WAVEFORM);
02031                     }
02032                 }
02033             }
02034 
02035             for (chan = 0; chan < mSong.mNbChannels; chan++)
02036             {
02037                 XFuXMChannel &ch = mChannels[chan];
02038                 XFuXMInstrument *in = NULL;
02039 
02040                 if ((ch.mInstrumentNb != -1) && (ch.mInstrumentNb < mSong.mNbInstruments))
02041                 {
02042                     in = &mInstruments[ch.mInstrumentNb];
02043 
02044                     ch.mFinalOldVolume = ch.mFinalVolume;
02045 
02046                     // Volume envelope
02047                     if (ch.mVolEnvType & ENVELOPE_ON)
02048                     {
02049                         if (ch.mVolEnvPointer >= in->mVolEnvEnd)
02050                         {
02051                             // Not normal behavior but can happen when using
02052                             // set envelope position (Lxy) command with a value
02053                             // which is bigger than the last envelope point
02054                             ch.mVolEnvPointer = in->mVolEnvEnd;
02055                             ch.mVolEnvSpeed = 0;
02056                         }
02057 
02058                         ch.mVolEnvValue = in->mVolumeEnvelope[ch.mVolEnvPointer];
02059 
02060                         if ((ch.mVolEnvType & ENVELOPE_SUSTAIN) && 
02061                             (ch.mVolEnvPointer == ch.mVolEnvSustain) &&
02062                             (ch.mSustainReleased == 0))
02063                         {
02064                             // Sustain point, stop envelope
02065                             ch.mVolEnvSpeed = 0;
02066                         }
02067 
02068                         // Fix for trackers which don't follow FT2 standard in envelopes
02069                         if (in->mVolEnvEnd != 0) ch.mVolEnvPointer += ch.mVolEnvSpeed;
02070 
02071                         if (ch.mVolEnvType & ENVELOPE_LOOP)
02072                         {
02073                             // Envelope loop
02074                             // The loop check is faulty and should check whether
02075                             // the pointer has passed the end point instead, but 
02076                             // as the bug is in FT2, it is also here for the sake 
02077                             // of compatibility
02078                             if ((ch.mVolEnvSpeed) && (ch.mVolEnvPointer == ch.mVolEnvLoopEnd))
02079                                 ch.mVolEnvPointer = ch.mVolEnvLoopStart;
02080                         }
02081                         else
02082                         {
02083                             if (ch.mVolEnvPointer == in->mVolEnvEnd)
02084                                 ch.mVolEnvSpeed = 0;
02085                         }
02086 
02087                         if (ch.mSustainReleased)
02088                         {
02089                             ch.mVolumeFadeoutValue -= (ch.mVolumeFadeout * 2);
02090                             if (ch.mVolumeFadeoutValue < 0) ch.mVolumeFadeoutValue = 0;
02091                         }
02092                     } else ch.mVolEnvValue = REALf(1.0f); // No volume envelope
02093 
02094                     // Panning envelope
02095                     if (ch.mPanEnvType & ENVELOPE_ON)
02096                     {
02097                         if (ch.mPanEnvPointer >= in->mPanEnvEnd)
02098                         {
02099                             // Not normal behavior but can happen when using
02100                             // set envelope position (Lxy) command with a value
02101                             // which is bigger than the last envelope point
02102                             ch.mPanEnvPointer = in->mPanEnvEnd;
02103                             ch.mPanEnvSpeed = 0;
02104                         }
02105 
02106                         ch.mPanEnvValue = in->mPanningEnvelope[ch.mPanEnvPointer];
02107 
02108                         if ((ch.mPanEnvType & ENVELOPE_SUSTAIN) && 
02109                             (ch.mPanEnvPointer == ch.mPanEnvSustain) &&
02110                             (ch.mSustainReleased == 0))
02111                         {
02112                             // Sustain point, stop envelope
02113                             ch.mPanEnvSpeed = 0;
02114                         }
02115 
02116                         // Fix for trackers which don't follow FT2 standard in envelopes
02117                         if (in->mPanEnvEnd != 0) ch.mPanEnvPointer += ch.mPanEnvSpeed;
02118 
02119                         if (ch.mPanEnvType & ENVELOPE_LOOP)
02120                         {
02121                             // Envelope loop
02122                             // The loop check is faulty and should check whether
02123                             // the pointer has passed the end point instead, but 
02124                             // as the bug is in FT2, it is also here for the sake 
02125                             // of compatibility
02126                             if ((ch.mPanEnvSpeed) && (ch.mPanEnvPointer == ch.mPanEnvLoopEnd))
02127                                 ch.mPanEnvPointer = ch.mPanEnvLoopStart;
02128                         }
02129                         else
02130                         {
02131                             if (ch.mPanEnvPointer == in->mPanEnvEnd) ch.mPanEnvSpeed = 0;
02132                         }
02133                     } else ch.mPanEnvValue = 0x20;  // No panning envelope
02134 
02135                     if (ch.mVolume > 0x40) ch.mVolume = 0x40;
02136                     if (ch.mVolume < 0) ch.mVolume = 0;
02137 
02138                     INT32 intVolume = (ch.mVolume * mSong.mGlobalVolume);
02139                     REAL volumeTemp;
02140                     volumeTemp.mValue = (intVolume << 3) + (intVolume << 1);
02141                     ch.mFinalVolume = (volumeTemp * ch.mVolEnvValue * ch.mVolumeFadeoutValue);
02142                     
02143                     volumeTemp = (ch.mFinalVolume - ch.mFinalOldVolume);
02144                     ch.mFinalVolumeSpeed = (volumeTemp * mVolumeRampDivOpt);
02145                     
02146 /*
02147                     REAL volumeTemp;
02148                     volumeTemp.mValue = (ch.mVolume * mSong.mGlobalVolume * ch.mVolumeFadeoutValue) >> 13;
02149                     ch.mFinalVolume = (volumeTemp * ch.mVolEnvValue);
02150 
02151                     volumeTemp = (ch.mFinalVolume - ch.mFinalOldVolume);
02152                     ch.mFinalVolumeSpeed = (volumeTemp * mVolumeRampDivOpt);
02153 */
02154 /*
02155                     FLOAT32 volumeReal;
02156                     volumeReal = (((FLOAT32)ch.mVolume / 65.0f) * (FLOAT32)ch.mVolEnvValue * 
02157                         (FLOAT32)ch.mVolumeFadeoutValue * ((FLOAT32)mSong.mGlobalVolume / 65.0f));
02158                     
02159                     ch.mFinalVolume = REALf(volumeReal);
02160                     
02161                     ch.mFinalVolumeSpeed = (ch.mFinalVolume - ch.mFinalOldVolume) / REALf(32);
02162 */                    
02163 /*
02164                     volumeReal = (ch.mVolume * (FLOAT32)ch.mVolEnvValue *
02165                         ch.mVolumeFadeoutValue * mSong.mGlobalVolume) *
02166                         VOLUME_DIV_OPT;
02167 
02168                     ch.mFinalVolume = (INT32)volumeReal;
02169 
02170                     ch.mFinalVolumeSpeed = (INT32)((ch.mFinalVolume - ch.mFinalOldVolume) *
02171                         VOLUME_RAMP_WIDTH_OPT);
02172 */
02173                     
02174                     ch.mFinalPan = (INT16)(ch.mPan + (((ch.mPanEnvValue - 0x20) *
02175                           (0x80 - ABS(ch.mPan - 0x80))) >> 5));
02176                     if (ch.mFinalPan > 0xFF) ch.mFinalPan = 0xFF;
02177                     else if (ch.mFinalPan < 0) ch.mFinalPan = 0;
02178 
02179                     ch.mSpeed = getSpeed(ch.mPeriod, mSamplingRate);
02180                 }
02181             }
02182         }
02183 
02184         ticksLeft = (mSamplesPerTick - mSamplePointer);
02185         indicesLeft = (aSamples - index);
02186 
02187         if (ticksLeft < indicesLeft) samplesToRender = ticksLeft;
02188         else samplesToRender = indicesLeft;
02189 
02190         INT startChan = 0;
02191         INT endChan = mSong.mNbChannels;
02192         if (mFlags & XFCAUDIO_16BIT)    // 16bit
02193         {
02194             if (mFlags & XFCAUDIO_STEREO)   // 16bit stereo
02195             {
02196                 for (counter = 0; counter < samplesToRender; ++counter, ++index)
02197                 {
02198                     cLeft = cRight = 0;
02199 
02200                     for (chan = startChan; chan < endChan; chan++)
02201                     {
02202                         XFuXMChannel &ch = mChannels[chan];
02203 
02204                         if (ch.mIsSample)
02205                         {
02206                             delta = ch.mPointer & (FP_VALUE - 1);
02207 
02208                             val1 = 0;
02209                             val2 = 0;
02210 
02211                             if (ch.mCurrentSample.m16Bit)
02212                             {
02213                                 val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02214 
02215                                 if (ch.mLoop == 0)
02216                                 {
02217                                     interpolateLinear16(ch, val1, val2);
02218                                     addPointer(ch);
02219                                 }
02220                                 else
02221                                 {
02222                                     temp = (ch.mPointer + FP_VALUE);
02223 
02224                                     if (ch.mLoop == LOOP_FORWARD)
02225                                     {
02226                                         interpolateLinearForwardLoop16(ch, val1, val2, temp);
02227                                         addPointerForwardLoop(ch);
02228                                     }
02229                                     else if (ch.mLoop == LOOP_PINGPONG)
02230                                     {
02231                                         interpolateLinearBidirectionalLoop16(ch, val1, val2, temp);
02232                                         addPointerBidirectionalLoop(ch);
02233                                     }
02234                                 }
02235                             }
02236                             else
02237                             {
02238                                 val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02239 
02240                                 if (ch.mLoop == 0)
02241                                 {
02242                                     interpolateLinear8(ch, val1, val2);
02243                                     addPointer(ch);
02244                                 }
02245                                 else
02246                                 {
02247                                     temp = (ch.mPointer + FP_VALUE);
02248 
02249                                     if (ch.mLoop == LOOP_FORWARD)
02250                                     {
02251                                         interpolateLinearForwardLoop8(ch, val1, val2, temp);
02252                                         addPointerForwardLoop(ch);
02253                                     }
02254                                     else if (ch.mLoop == LOOP_PINGPONG)
02255                                     {
02256                                         interpolateLinearBidirectionalLoop8(ch, val1, val2, temp);
02257                                         addPointerBidirectionalLoop(ch);
02258                                     }
02259                                 }
02260 
02261                                 val1 <<= 8;
02262                                 val2 <<= 8;
02263                             }
02264 
02265                             value = (((val2 * delta) + (val1 * (FP_VALUE - delta))) >> FP_BITS);
02266 
02267                             value = (INT16)(value * ch.mFinalOldVolume);
02268                             cLeft += ((value * (0xFF - (UINT8)ch.mFinalPan)) >> 8);
02269                             cRight += ((value * (UINT8)ch.mFinalPan) >> 8);
02270 
02271                             if (mRamp < VOLUME_RAMP_WIDTH) ch.mFinalOldVolume += ch.mFinalVolumeSpeed;
02272                         }
02273                     }
02274 
02275                     if (cLeft < -32768) cLeft = -32768;
02276                     else if (cLeft > 32767) cLeft = 32767;
02277                     if (cRight < -32768) cRight = -32768;
02278                     else if (cRight > 32767) cRight = 32767;
02279 
02280                     *((INT16 *)aBuffer + (targetOffset << 1)) = (INT16)(cLeft ^ mTgtXor);
02281                     *((INT16 *)aBuffer + (targetOffset << 1) + 1) = (INT16)(cRight ^ mTgtXor);
02282 
02283                     targetOffset++;
02284                     if (targetOffset == aTargetSamples) targetOffset = 0;
02285 
02286                     if (mRamp < VOLUME_RAMP_WIDTH) mRamp++;
02287                 }
02288             }
02289             else    // 16bit mono
02290             {
02291                 for (counter = 0; counter < samplesToRender; ++counter, ++index)
02292                 {
02293                     cLeft = 0;
02294 
02295                     for (chan = startChan; chan < endChan; chan++)
02296                     {
02297                         XFuXMChannel &ch = mChannels[chan];
02298 
02299                         if (ch.mIsSample)
02300                         {
02301                             delta = ch.mPointer & (FP_VALUE - 1);
02302 
02303                             val1 = 0;
02304                             val2 = 0;
02305 
02306                             if (ch.mCurrentSample.m16Bit)
02307                             {
02308                                 val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02309 
02310                                 if (ch.mLoop == 0)
02311                                 {
02312                                     interpolateLinear16(ch, val1, val2);
02313                                     addPointer(ch);
02314                                 }
02315                                 else
02316                                 {
02317                                     temp = (ch.mPointer + FP_VALUE);
02318 
02319                                     if (ch.mLoop == LOOP_FORWARD)
02320                                     {
02321                                         interpolateLinearForwardLoop16(ch, val1, val2, temp);
02322                                         addPointerForwardLoop(ch);
02323                                     }
02324                                     else if (ch.mLoop == LOOP_PINGPONG)
02325                                     {
02326                                         interpolateLinearBidirectionalLoop16(ch, val1, val2, temp);
02327                                         addPointerBidirectionalLoop(ch);
02328                                     }
02329                                 }
02330                             }
02331                             else
02332                             {
02333                                 val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02334 
02335                                 if (ch.mLoop == 0)
02336                                 {
02337                                     interpolateLinear8(ch, val1, val2);
02338                                     addPointer(ch);
02339                                 }
02340                                 else
02341                                 {
02342                                     temp = (ch.mPointer + FP_VALUE);
02343 
02344                                     if (ch.mLoop == LOOP_FORWARD)
02345                                     {
02346                                         interpolateLinearForwardLoop8(ch, val1, val2, temp);
02347                                         addPointerForwardLoop(ch);
02348                                     }
02349                                     else if (ch.mLoop == LOOP_PINGPONG)
02350                                     {
02351                                         interpolateLinearBidirectionalLoop8(ch, val1, val2, temp);
02352                                         addPointerBidirectionalLoop(ch);
02353                                     }
02354                                 }
02355 
02356                                 val1 <<= 8;
02357                                 val2 <<= 8;
02358                             }
02359 
02360                             value = (((val2 * delta) + (val1 * (FP_VALUE - delta))) >> FP_BITS);
02361 
02362                             cLeft += (INT16)(value * ch.mFinalOldVolume);
02363 
02364                             if (mRamp < VOLUME_RAMP_WIDTH) ch.mFinalOldVolume += ch.mFinalVolumeSpeed;
02365                         }
02366                     }
02367 
02368                     if (cLeft > 32767) cLeft = 32767;
02369                     else if (cLeft < -32768) cLeft = -32768;
02370 
02371                     *((INT16 *)aBuffer + targetOffset) = (INT16)(cLeft ^ mTgtXor);
02372 
02373                     targetOffset++;
02374                     if (targetOffset == aTargetSamples) targetOffset = 0;
02375 
02376                     if (mRamp < VOLUME_RAMP_WIDTH) mRamp++;
02377                 }
02378             }
02379         }
02380         else    // 8bit
02381         {
02382             if (mFlags & XFCAUDIO_STEREO)   // 8bit stereo
02383             {
02384                 for (counter = 0; counter < samplesToRender; ++counter, ++index)
02385                 {
02386                     cLeft = cRight = 0;
02387 
02388                     for (chan = startChan; chan < endChan; chan++)
02389                     {
02390                         XFuXMChannel &ch = mChannels[chan];
02391 
02392                         if (ch.mIsSample)
02393                         {
02394                             delta = ch.mPointer & (FP_VALUE - 1);
02395 
02396                             val1 = 0;
02397                             val2 = 0;
02398 
02399                             if (ch.mCurrentSample.m16Bit)
02400                             {
02401                                 val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02402 
02403                                 if (ch.mLoop == 0)
02404                                 {
02405                                     interpolateLinear16(ch, val1, val2);
02406                                     addPointer(ch);
02407                                 }
02408                                 else
02409                                 {
02410                                     temp = (ch.mPointer + FP_VALUE);
02411 
02412                                     if (ch.mLoop == LOOP_FORWARD)
02413                                     {
02414                                         interpolateLinearForwardLoop16(ch, val1, val2, temp);
02415                                         addPointerForwardLoop(ch);
02416                                     }
02417                                     else if (ch.mLoop == LOOP_PINGPONG)
02418                                     {
02419                                         interpolateLinearBidirectionalLoop16(ch, val1, val2, temp);
02420                                         addPointerBidirectionalLoop(ch);
02421                                     }
02422                                 }
02423 
02424                                 val1 >>= 8;
02425                                 val2 >>= 8;
02426                             }
02427                             else
02428                             {
02429                                 val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02430 
02431                                 if (ch.mLoop == 0)
02432                                 {
02433                                     interpolateLinear8(ch, val1, val2);
02434                                     addPointer(ch);
02435                                 }
02436                                 else
02437                                 {
02438                                     temp = (ch.mPointer + FP_VALUE);
02439 
02440                                     if (ch.mLoop == LOOP_FORWARD)
02441                                     {
02442                                         interpolateLinearForwardLoop8(ch, val1, val2, temp);
02443                                         addPointerForwardLoop(ch);
02444                                     }
02445                                     else if (ch.mLoop == LOOP_PINGPONG)
02446                                     {
02447                                         interpolateLinearBidirectionalLoop8(ch, val1, val2, temp);
02448                                         addPointerBidirectionalLoop(ch);
02449                                     }
02450                                 }
02451                             }
02452 
02453                             value = (((val2 * delta) + (val1 * (FP_VALUE - delta))) >> FP_BITS);
02454 
02455                             value = (INT8)(value * ch.mFinalOldVolume);
02456                             cLeft += ((value * (0xFF - (UINT8)ch.mFinalPan)) >> 8);
02457                             cRight += ((value * (UINT8)ch.mFinalPan) >> 8);
02458 
02459                             if (mRamp < VOLUME_RAMP_WIDTH) ch.mFinalOldVolume += ch.mFinalVolumeSpeed;
02460                         }
02461                     }
02462 
02463                     if (cLeft < -128) cLeft = -128;
02464                     else if (cLeft > 127) cLeft = 127;
02465                     if (cRight < -128) cRight = -128;
02466                     else if (cRight > 127) cRight = 127;
02467 
02468                     *((INT8 *)aBuffer + (targetOffset << 1)) = (INT8)(cLeft ^ mTgtXor);
02469                     *((INT8 *)aBuffer + (targetOffset << 1) + 1) = (INT8)(cRight ^ mTgtXor);
02470 
02471                     targetOffset++;
02472                     if (targetOffset == aTargetSamples) targetOffset = 0;
02473                     
02474                     if (mRamp < VOLUME_RAMP_WIDTH) mRamp++;
02475                 }
02476             }
02477             else    // 8bit mono
02478             {
02479                 for (counter = 0; counter < samplesToRender; ++counter, ++index)
02480                 {
02481                     cLeft = 0;
02482 
02483                     for (chan = startChan; chan < endChan; chan++)
02484                     {
02485                         XFuXMChannel &ch = mChannels[chan];
02486 
02487                         if (ch.mIsSample)
02488                         {
02489                             delta = ch.mPointer & (FP_VALUE - 1);
02490 
02491                             val1 = 0;
02492                             val2 = 0;
02493 
02494                             if (ch.mCurrentSample.m16Bit)
02495                             {
02496                                 val1 = *((INT16 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02497 
02498                                 if (ch.mLoop == 0)
02499                                 {
02500                                     interpolateLinear16(ch, val1, val2);
02501                                     addPointer(ch);
02502                                 }
02503                                 else
02504                                 {
02505                                     temp = (ch.mPointer + FP_VALUE);
02506 
02507                                     if (ch.mLoop == LOOP_FORWARD)
02508                                     {
02509                                         interpolateLinearForwardLoop16(ch, val1, val2, temp);
02510                                         addPointerForwardLoop(ch);
02511                                     }
02512                                     else if (ch.mLoop == LOOP_PINGPONG)
02513                                     {
02514                                         interpolateLinearBidirectionalLoop16(ch, val1, val2, temp);
02515                                         addPointerBidirectionalLoop(ch);
02516                                     }
02517                                 }
02518 
02519                                 val1 >>= 8;
02520                                 val2 >>= 8;
02521                             }
02522                             else
02523                             {
02524                                 val1 = *((INT8 *)ch.mOffset + (ch.mPointer >> FP_BITS));
02525 
02526                                 if (ch.mLoop == 0)
02527                                 {
02528                                     interpolateLinear8(ch, val1, val2);
02529                                     addPointer(ch);
02530                                 }
02531                                 else
02532                                 {
02533                                     temp = (ch.mPointer + FP_VALUE);
02534 
02535                                     if (ch.mLoop == LOOP_FORWARD)
02536                                     {
02537                                         interpolateLinearForwardLoop8(ch, val1, val2, temp);
02538                                         addPointerForwardLoop(ch);
02539                                     }
02540                                     else if (ch.mLoop == LOOP_PINGPONG)
02541                                     {
02542                                         interpolateLinearBidirectionalLoop8(ch, val1, val2, temp);
02543                                         addPointerBidirectionalLoop(ch);
02544                                     }
02545                                 }
02546                             }
02547 
02548                             value = (((val2 * delta) + (val1 * (FP_VALUE - delta))) >> FP_BITS);
02549 
02550                             cLeft += (INT8)(value * ch.mFinalOldVolume);
02551 
02552                             if (mRamp < VOLUME_RAMP_WIDTH) ch.mFinalOldVolume += ch.mFinalVolumeSpeed;
02553                         }
02554                     }
02555 
02556                     if (cLeft > 127) cLeft = 127;
02557                     else if (cLeft < -128) cLeft = -128;
02558 
02559                     *((INT8 *)aBuffer + targetOffset) = (INT8)(cLeft ^ mTgtXor);
02560 
02561                     targetOffset++;
02562                     if (targetOffset == aTargetSamples) targetOffset = 0;
02563                     
02564                     if (mRamp < VOLUME_RAMP_WIDTH) mRamp++;
02565                 }
02566             }
02567         }
02568 
02569         mSamplePointer += samplesToRender;
02570     }
02571 
02572     mTotalTicks += aSamples;
02573 
02574     return 1;
02575 }
02576 
02577 // For MSVC
02578 #pragma warning(default:4244)
02579 
02580 
02581 XFuXMPlayer::XFuXMPlayer(FLOAT32 aSamplingRate, UINT32 aFlags)
02582 {
02583     mSamplingRate = aSamplingRate;
02584     mFlags = aFlags;
02585 
02586     mTgtXor = 0;
02587     if (!(mFlags & XFCAUDIO_SIGNED))
02588     {
02589         if (mFlags & XFCAUDIO_16BIT) mTgtXor = -32768;
02590         else mTgtXor = -128;
02591     }
02592 
02593     /* Sinewave table */
02594     mSineWaveTable[0] = 0;
02595     mSineWaveTable[1] = 24;
02596     mSineWaveTable[2] = 49;
02597     mSineWaveTable[3] = 74;
02598     mSineWaveTable[4] = 97;
02599     mSineWaveTable[5] = 120;
02600     mSineWaveTable[6] = 141;
02601     mSineWaveTable[7] = 161;
02602     mSineWaveTable[8] = 180;
02603     mSineWaveTable[9] = 197;
02604     mSineWaveTable[10] = 212;
02605     mSineWaveTable[11] = 224;
02606     mSineWaveTable[12] = 235;
02607     mSineWaveTable[13] = 244;
02608     mSineWaveTable[14] = 250;
02609     mSineWaveTable[15] = 253;
02610     mSineWaveTable[16] = 255;
02611     mSineWaveTable[17] = 253;
02612     mSineWaveTable[18] = 250;
02613     mSineWaveTable[19] = 244;
02614     mSineWaveTable[20] = 235;
02615     mSineWaveTable[21] = 224;
02616     mSineWaveTable[22] = 212;
02617     mSineWaveTable[23] = 197;
02618     mSineWaveTable[24] = 180;
02619     mSineWaveTable[25] = 161;
02620     mSineWaveTable[26] = 141;
02621     mSineWaveTable[27] = 120;
02622     mSineWaveTable[28] = 97;
02623     mSineWaveTable[29] = 74;
02624     mSineWaveTable[30] = 49;
02625     mSineWaveTable[31] = 24;
02626 
02627     INT i;
02628 
02629     for (i = 32; i < 64; i++)
02630     {
02631         mSineWaveTable[i] = (INT16)-mSineWaveTable[i - 32];
02632     }
02633 
02634     /* Squarewave table */
02635     for (i = 0; i < 32; i++) mSquareWaveTable[i] = -255;
02636     for (i = 32; i < 64; i++) mSquareWaveTable[i] = 255;
02637 
02638     /* Ramp up/down tables */
02639     for (i = 0; i < 32; i++)
02640     {
02641         mRampUpTable[i] = (INT16)(255.0f - (FLOAT32)i * (510.0f / 32.0f));
02642         mRampUpTable[i + 32] = (INT16)(255.0f - (FLOAT32)i * (510.0f / 32.0f));
02643 
02644         mRampDownTable[i] = (INT16)(-255.0f + (FLOAT32)i * (510.0f / 32.0f));
02645         mRampDownTable[i + 32] =
02646             (INT16)(-255.0f + (FLOAT32)i * (510.0f / 32.0f));
02647     }
02648 
02649     mSong.mOrderTable = NULL;
02650     mXMPatternHeaders = NULL;
02651     mPatternData = NULL;
02652     mInstruments = NULL;
02653     mChannels = NULL;
02654 
02655     mEventHandlers = NULL;
02656 }
02657 
02658 
02659 XFuXMPlayer::~XFuXMPlayer()
02660 {
02661     INT i, j;
02662 
02663     delete[] mSong.mOrderTable;
02664 
02665     delete[] mXMPatternHeaders;
02666 
02667     if (mPatternData != NULL)
02668     {
02669         for (i = 0; i < mSong.mNbPatterns; i++)
02670         {
02671             delete[] mPatternData[i].mData;
02672             delete[] mPatternData[i].mRows;
02673         }
02674 
02675         delete[] mPatternData;
02676     }
02677 
02678     if (mInstruments != NULL)
02679     {
02680         for (i = 0; i < mSong.mNbInstruments; i++)
02681         {
02682             XFuXMInstrument &ins = mInstruments[i];
02683 
02684             if (ins.mSamples != NULL)
02685             {
02686                 for (j = 0; j < ins.mNbSamples; j++)
02687                 {
02688                     delete[] (INT8 *)ins.mSamples[j].mOffset;
02689                 }
02690 
02691                 delete[] ins.mSamples;
02692             }
02693         }
02694 
02695         delete[] mInstruments;
02696     }
02697 
02698     delete[] mChannels;
02699 
02700     delete mEventHandlers;
02701 }
02702 
02703 
02704 XFuXMPlayer * XFuXMPlayer::create(const CHAR *aTunename, FLOAT32 aSampleRate, UINT32 aFlags)
02705 {
02706     XFcFile *textout = NULL;
02707 
02708     XFuXMPlayer *pl = new XFuXMPlayer(aSampleRate, aFlags);
02709 
02710     if (pl != NULL)
02711     {
02712         pl->initialize(aSampleRate, aFlags, 1.0, 0.5, 0);
02713 
02714         pl->mEventHandlers = new XFcLinkedList<XFuXMPlayerEventHandlerSlot>();
02715 
02716         if (pl->mEventHandlers == NULL)
02717         {
02718             delete pl;
02719             return NULL;
02720         }
02721 
02722 #ifdef OUTPUT_XM_INFO
02723         XFuXMFormatAtom ta;
02724 
02725         textout = XFcFile::open("xmoutput.txt","wt");
02726 #endif
02727 
02728         if (pl->loadXM(aTunename, textout) != 0)
02729         {
02730             // file not found
02731             delete pl;
02732             return NULL;
02733         }
02734 
02735 #ifdef OUTPUT_XM_INFO
02736         pl->dumpSongParameters(textout);
02737 
02738         pl->mPatternNb = -1;
02739         INT i;
02740         for (i = 0; i < pl->mXMHeader.mNbPatterns; i++)
02741         {
02742             pl->mPatternNb++;
02743             if (pl->mXMPatternHeaders[i].mSize > 0)
02744             {
02745                 debugPrint(textout, "* Pattern: %02X\n", pl->mPatternNb);
02746                 {
02747                     INT row, channel;
02748                     pl->mPpoint = 0; 
02749                     for (row = 0; row < pl->mXMPatternHeaders[i].mNbRows; row++)
02750                     {
02751                         debugPrint(textout, "%02X: ", row);
02752                         for (channel = 0; channel < pl->mXMHeader.mNbChannels; channel++)
02753                         {
02754                             ta = pl->getAtom();
02755                         
02756                             if (ta.mNote == 0)
02757                             {
02758                                 if (ta.mVolume != 0)
02759                                     debugPrint(textout, "--- %02X %02X %1X%02X ; ",
02760                                                ta.mInstrumentNb, ta.mVolume - 0x10,
02761                                                ta.mEffectType, ta.mEffectValue);
02762                                 else
02763                                     debugPrint(textout, "--- %02X -- %1X%02X ; ",
02764                                                ta.mInstrumentNb, ta.mEffectType,
02765                                                ta.mEffectValue);
02766                             }
02767                             else
02768                             {
02769                                 if (ta.mVolume != 0)
02770                                     debugPrint(textout,
02771                                                "%s%1d %02X %02X %1X%02X ; ",
02772                                                notes[(ta.mNote - 1) % 12],
02773                                                (INT)((ta.mNote - 1) / 12),
02774                                                ta.mInstrumentNb, ta.mVolume - 0x10,
02775                                                ta.mEffectType, ta.mEffectValue);
02776                                 else
02777                                     debugPrint(textout, "%s%1d %02X -- %1X%02X ; ",
02778                                                notes[(ta.mNote - 1) % 12],
02779                                                (INT)((ta.mNote - 1) / 12),
02780                                                ta.mInstrumentNb, ta.mEffectType,
02781                                                ta.mEffectValue);
02782                             }
02783                         }
02784 
02785                         debugPrint(textout, "\n");
02786                     }
02787                 }
02788             }
02789         }
02790 
02791         if (textout != NULL) textout->close();
02792 #endif
02793 
02794         pl->initSong(0);
02795         pl->mVolumeRampDivOpt = REALf(1.0f / VOLUME_RAMP_WIDTH);
02796     }
02797 
02798     return pl;
02799 }
02800 

   
X-Forge Documentation
Confidential
Copyright © 2002-2003 Fathammer
   
Documentation generated
with doxygen
by Dimitri van Heesch