Main Page   Class Hierarchy   Compound List   File List   Compound Members   File Members

SoundEngine.cpp

Go to the documentation of this file.
00001 /*********************************************************************************
00002  *
00003  * Razor! Engine - A modular C++ presentation engine
00004  *
00005  * $Id$
00006  *
00007  * Copyright (c) 2000 Tilo Christ. All Rights Reserved.
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 // ================================================ SoundEngine =============================================================
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 // ================================================ FxEngine ================================================================
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 // ================================================ MusicEngine =============================================================
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     // Play current chord
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     // Advance to next position in pattern
00214     patternPosition++;
00215     if (patternPosition > 63)
00216     {
00217         // Pattern has been played. Which one do we play next?
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         // Determine sharp
00368         if (sharpChar == '#') note++;
00369 
00370         // Determine octave
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

Razor! Engine Developer's Guide. Copyright © by Tilo Christ. All Rights Reserved. Last updated: 4 Nov 2000