00001 00002 00003 00004 00005 00006 00007 00008 00009 00010 00011 00012 00013 00014 00015 00016 00017 00018 00019 00020 00021 00022 00023
00024
00025
00026 #include "SoundEngine.h"
00027 #include "Customization.h"
00028
00029 #ifdef NO_SOUND
00030
00031 void SoundEngine::init() {}
00032 void SoundEngine::destroy() {}
00033 void SoundEngine::timeTick() {}
00034
00035
00036
00037 #else
00038
00039
00040
00041
00042 MusicEngine *SoundEngine::musicEngine;
00043 FxEngine *SoundEngine::fxEngine;
00044 UInt16 SoundEngine::baseAmplitude;
00045 Boolean SoundEngine::mute;
00046 UInt32 SoundEngine::nextTimer;
00047
00048
00049 void SoundEngine::playFx(DmResID resID)
00050 {
00051 if (fxEngine == NULL)
00052 fxEngine = new FxEngine(baseAmplitude);
00053
00054 fxEngine->playFx(resID);
00055 }
00056
00057
00058 void SoundEngine::playSong(DmResID resID)
00059 {
00060 if (musicEngine == NULL)
00061 musicEngine = new MusicEngine(baseAmplitude);
00062
00063 musicEngine->playSong(resID);
00064 }
00065
00066
00067 void SoundEngine::stopSong()
00068 {
00069 if (musicEngine != NULL)
00070 musicEngine->stopSong();
00071 }
00072
00073
00074 void SoundEngine::init()
00075 {
00076 baseAmplitude = (UInt16)PrefGetPreference(prefGameSoundVolume);
00077 mute = (baseAmplitude == 0);
00078
00079 musicEngine = NULL;
00080 fxEngine = NULL;
00081
00082 nextTimer = TimGetTicks();
00083 }
00084
00085
00086 void SoundEngine::destroy()
00087 {
00088 if (fxEngine != NULL)
00089 delete fxEngine;
00090 if (musicEngine != NULL)
00091 delete musicEngine;
00092 }
00093
00094
00095 void SoundEngine::timeTick()
00096 {
00097 if (!mute)
00098 {
00099 UInt32 currentTicks = TimGetTicks();
00100
00101 if (currentTicks >= nextTimer)
00102 {
00103 nextTimer = currentTicks + 15;
00104
00105 if ((fxEngine == NULL) || (!(fxEngine->timeTick())))
00106 {
00107 if (musicEngine != NULL)
00108 musicEngine->timeTick();
00109 }
00110 }
00111 }
00112 }
00113
00114
00115
00116
00117
00118 FxEngine::FxEngine(UInt16 baseAmplitude)
00119 {
00120 this->baseAmplitude = baseAmplitude;
00121 playing = false;
00122 }
00123
00124
00125 FxEngine::~FxEngine()
00126 {
00127 }
00128
00129
00130 Boolean FxEngine::timeTick()
00131 {
00132 if (!playing)
00133 return false;
00134
00135 Boolean finished = false;
00136
00137 MemHandle resH = DmGetResource( (DmResType)'Tsfx', fxResID );
00138 ErrNonFatalDisplayIf( !resH, "Effect not found!" );
00139 UInt8* sfxBytes = (UInt8*)MemHandleLock(resH);
00140
00141 do
00142 {
00143 UInt8* sfxPos = sfxBytes + fxPosition * 4;
00144 UInt16 frequency = *((UInt16*)sfxPos);
00145 UInt16 duration = (UInt16)(*((UInt8*)(sfxPos + 2)));
00146 Int16 amplitude = (Int16)(*((Int8*)(sfxPos + 3)));
00147
00148 if (frequency == FREQ_END)
00149 {
00150 playing = false;
00151 finished = true;
00152 continue;
00153 }
00154
00155 SndCommandType snd;
00156 snd.cmd = sndCmdFreqDurationAmp;
00157
00158 snd.param1 = frequency;
00159 snd.param2 = duration;
00160 snd.param3 = baseAmplitude + amplitude;
00161
00162 (void)SndDoCmd(NULL, &snd, 0);
00163
00164 fxPosition++;
00165
00166 if (duration == DURATION_YIELD)
00167 {
00168 finished = true;
00169 }
00170 } while(!finished);
00171
00172 MemPtrUnlock(sfxBytes);
00173 DmReleaseResource( resH );
00174
00175 return true;
00176 }
00177
00178
00179 void FxEngine::playFx(DmResID resID)
00180 {
00181 fxResID = resID;
00182 fxPosition = 0;
00183 playing = true;
00184 }
00185
00186
00187
00188
00189
00190 MusicEngine::MusicEngine(UInt16 baseAmplitude)
00191 {
00192 channel1Playing = channel2Playing = channel2Playing = false;
00193 playing = false;
00194 this->baseAmplitude = baseAmplitude;
00195 }
00196
00197
00198 MusicEngine::~MusicEngine()
00199 {
00200 }
00201
00202
00203 Boolean MusicEngine::timeTick()
00204 {
00205 if (!playing)
00206 return false;
00207
00208
00209 if (channel1Playing) hitIt(patternPosition, channel1Pattern, baseAmplitude, baseDuration, channel2Playing || channel3Playing);
00210 if (channel2Playing) hitIt(patternPosition, channel2Pattern, 5, baseDuration - 3, channel3Playing);
00211 if (channel3Playing) hitIt(patternPosition, channel3Pattern, 2, baseDuration - 5, false);
00212
00213
00214 patternPosition++;
00215 if (patternPosition > 63)
00216 {
00217
00218 patternPosition = 0;
00219 trackPosition++;
00220 setPatternsFromTrack();
00221 }
00222
00223 return true;
00224 }
00225
00226
00227 void MusicEngine::playSong(DmResID resID)
00228 {
00229 trackResID = resID;
00230 patternPosition = 0;
00231 trackPosition = 0;
00232 baseDuration = 22;
00233 playing = true;
00234
00235 setPatternsFromTrack();
00236 }
00237
00238
00239 void MusicEngine::stopSong()
00240 {
00241 playing = false;
00242 }
00243
00244
00245 void MusicEngine::setPatternsFromTrack()
00246 {
00247 MemHandle resH = DmGetResource( (DmResType)'Ttrk', trackResID );
00248 ErrNonFatalDisplayIf( !resH, "Track not found!" );
00249 UInt8* trackBytes = (UInt8*)MemHandleLock(resH);
00250
00251 Boolean finished = false;
00252 do
00253 {
00254 UInt8* trackPos = trackBytes + trackPosition * 3;
00255
00256 UInt8 channel1Byte = *trackPos;
00257 UInt8 channel2Byte = *(trackPos + 1);
00258 UInt8 channel3Byte = *(trackPos + 2);
00259
00260 switch(channel1Byte)
00261 {
00262 case TRACK_REPEAT:
00263 trackPosition = 0;
00264 break;
00265 case TRACK_END:
00266 playing = false;
00267 finished = true;
00268 break;
00269 default:
00270 channel1Pattern = channel1Byte;
00271 channel2Pattern = channel2Byte;
00272 channel3Pattern = channel3Byte;
00273
00274 channel1Playing = (channel1Byte != CHANNEL_MUTE);
00275 channel2Playing = (channel2Byte != CHANNEL_MUTE);
00276 channel3Playing = (channel3Byte != CHANNEL_MUTE);
00277
00278 finished = true;
00279 }
00280 } while(!finished);
00281
00282 MemPtrUnlock(trackBytes);
00283 DmReleaseResource( resH );
00284 }
00285
00286
00287 void MusicEngine::playNote(UInt16 note, UInt16 octave, UInt16 amplitude, UInt16 duration, Boolean wait) const
00288 {
00289 static const Int32 freqHz[] = {66,70,74,78,83,88,93,98,104,110,116,122,
00290 131,139,147,156,165,175,185,196,207,220,233,245,
00291 262,278,294,311,330,349,370,392,415,440,466,494,
00292 523,554,587,622,659,698,740,784,831,880,932,988,
00293 1047,1109,1175,1245,1319,1397,1480,1568,1660,0,0,0};
00294
00295 ErrNonFatalDisplayIf((note < 0) || (note > 11), "Note out of range");
00296 ErrNonFatalDisplayIf(octave > 4, "Invalid octave!");
00297
00298 SndCommandType snd;
00299 if (wait)
00300 snd.cmd = sndCmdFreqDurationAmp;
00301 else
00302 snd.cmd = sndCmdFrqOn;
00303
00304 snd.param1 = freqHz[octave * 12 + note];
00305 snd.param2 = duration;
00306 snd.param3 = amplitude;
00307
00308 (void)SndDoCmd(NULL, &snd, 0);
00309 }
00310
00311
00312
00313 void MusicEngine::playMute(UInt16 duration, Boolean wait) const
00314 {
00315 SndCommandType snd;
00316
00317 if (wait)
00318 {
00319 snd.cmd = sndCmdFreqDurationAmp;
00320 snd.param1 = 0;
00321 snd.param2 = duration;
00322 snd.param3 = 1;
00323 }
00324 else
00325 {
00326 snd.cmd = sndCmdQuiet;
00327 snd.param1 = 0;
00328 snd.param2 = 0;
00329 snd.param3 = 0;
00330 }
00331
00332 (void)SndDoCmd(NULL, &snd, 0);
00333 }
00334
00335
00336 void MusicEngine::hitIt(UInt16 position, UInt16 pattern, UInt16 amplitude, UInt16 duration, Boolean wait) const
00337 {
00338 MemHandle resH = DmGetResource( (DmResType)'Tpat', (DmResID)pattern );
00339 ErrNonFatalDisplayIf( !resH, "Pattern not found!" );
00340 Char* patternString = (Char*)MemHandleLock(resH);
00341 Char* patternPos = patternString + position * 3;
00342 Char noteChar = *patternPos;
00343 Char sharpChar = *(patternPos + 1);
00344 Char octaveChar = *(patternPos + 2);
00345 MemPtrUnlock(patternString);
00346 DmReleaseResource( resH );
00347
00348 UInt16 note = 0xFFFF;
00349 switch (noteChar)
00350 {
00351 case 'c': note = 0; break;
00352 case 'd': note = 2; break;
00353 case 'e': note = 4; break;
00354 case 'f': note = 5; break;
00355 case 'g': note = 7; break;
00356 case 'a': note = 9; break;
00357 case 'b': note = 11; break;
00358 case '-': break;
00359
00360 default:
00361 ErrNonFatalDisplay("Illegal note char!");
00362 break;
00363 }
00364
00365 if (note != 0xFFFF)
00366 {
00367
00368 if (sharpChar == '#') note++;
00369
00370
00371 UInt16 octave = 0;
00372 switch (octaveChar)
00373 {
00374 case '-': octave = 0; break;
00375 case '1': octave = 1; break;
00376 case '2': octave = 2; break;
00377 case '3': octave = 3; break;
00378 case '4': octave = 4; break;
00379
00380 default:
00381 ErrNonFatalDisplay("Illegal octave char!");
00382 break;
00383 }
00384
00385 playNote(note, octave, amplitude, duration, wait);
00386 }
00387 else
00388 {
00389 playMute(duration, wait);
00390 }
00391 }
00392
00393
00394 #endif // !NO_SOUND