hedgewars/uSound.pas
branchphysfslayer
changeset 8520 1dedcc37bfe8
parent 8471 7681d14b9f01
child 8678 fb62d23c70f8
equal deleted inserted replaced
8056:d5d5e1698554 8520:1dedcc37bfe8
    19 {$INCLUDE "options.inc"}
    19 {$INCLUDE "options.inc"}
    20 
    20 
    21 unit uSound;
    21 unit uSound;
    22 (*
    22 (*
    23  * This unit controls the sounds and music of the game.
    23  * This unit controls the sounds and music of the game.
    24  * Doesn't really do anything if isSoundEnabled = false.
    24  * Doesn't really do anything if isSoundEnabled = false and isMusicEnabled = false
    25  *
    25  *
    26  * There are three basic types of sound controls:
    26  * There are three basic types of sound controls:
    27  *    Music        - The background music of the game:
    27  *    Music        - The background music of the game:
    28  *                   * will only be played if isMusicEnabled = true
    28  *                   * will only be played if isMusicEnabled = true
    29  *                   * can be started, changed, paused and resumed
    29  *                   * can be started, changed, paused and resumed
   103 
   103 
   104 // Returns a pointer to the voicepack with the given name.
   104 // Returns a pointer to the voicepack with the given name.
   105 function  AskForVoicepack(name: shortstring): Pointer;
   105 function  AskForVoicepack(name: shortstring): Pointer;
   106 
   106 
   107 
   107 
       
   108 var Volume: LongInt;
       
   109     SoundTimerTicks: Longword;
   108 implementation
   110 implementation
   109 uses uVariables, uConsole, uUtils, uCommands, uDebug, uPhysFSLayer;
   111 uses uVariables, uConsole, uCommands, uDebug, uPhysFSLayer;
   110 
   112 
   111 const chanTPU = 32;
   113 const chanTPU = 32;
   112 var Volume: LongInt;
   114 var cInitVolume: LongInt;
   113     cInitVolume: LongInt;
       
   114     previousVolume: LongInt; // cached volume value
   115     previousVolume: LongInt; // cached volume value
   115     lastChan: array [TSound] of LongInt;
   116     lastChan: array [TSound] of LongInt;
   116     voicepacks: array[0..cMaxTeams] of TVoicepack;
   117     voicepacks: array[0..cMaxTeams] of TVoicepack;
   117     defVoicepack: PVoicepack;
   118     defVoicepack: PVoicepack;
   118     Mus: PMixMusic; // music pointer
   119     Mus: PMixMusic; // music pointer
   119     MusicFN: shortstring; // music file name
   120     MusicFN: shortstring; // music file name
   120     isMusicEnabled: boolean;
   121     isMusicEnabled: boolean;
   121     isSoundEnabled: boolean;
   122     isSoundEnabled: boolean;
   122     isSEBackup: boolean;
   123     isSEBackup: boolean;
       
   124     VoiceList : array[0..7] of TVoice =  (
       
   125                     ( snd: sndNone; voicepack: nil),
       
   126                     ( snd: sndNone; voicepack: nil),
       
   127                     ( snd: sndNone; voicepack: nil),
       
   128                     ( snd: sndNone; voicepack: nil),
       
   129                     ( snd: sndNone; voicepack: nil),
       
   130                     ( snd: sndNone; voicepack: nil),
       
   131                     ( snd: sndNone; voicepack: nil),
       
   132                     ( snd: sndNone; voicepack: nil));
       
   133     Soundz: array[TSound] of record
       
   134             FileName: string[31];
       
   135             Path    : TPathType;
       
   136             end = (
       
   137             (FileName:                         ''; Path: ptNone  ),// sndNone
       
   138             (FileName:        'grenadeimpact.ogg'; Path: ptSounds),// sndGrenadeImpact
       
   139             (FileName:            'explosion.ogg'; Path: ptSounds),// sndExplosion
       
   140             (FileName:         'throwpowerup.ogg'; Path: ptSounds),// sndThrowPowerUp
       
   141             (FileName:         'throwrelease.ogg'; Path: ptSounds),// sndThrowRelease
       
   142             (FileName:               'splash.ogg'; Path: ptSounds),// sndSplash
       
   143             (FileName:        'shotgunreload.ogg'; Path: ptSounds),// sndShotgunReload
       
   144             (FileName:          'shotgunfire.ogg'; Path: ptSounds),// sndShotgunFire
       
   145             (FileName:          'graveimpact.ogg'; Path: ptSounds),// sndGraveImpact
       
   146             (FileName:           'mineimpact.ogg'; Path: ptSounds),// sndMineImpact
       
   147             (FileName:             'minetick.ogg'; Path: ptSounds),// sndMineTicks
       
   148             (FileName:             'Droplet1.ogg'; Path: ptSounds),// sndMudballImpact
       
   149             (FileName:           'pickhammer.ogg'; Path: ptSounds),// sndPickhammer
       
   150             (FileName:                  'gun.ogg'; Path: ptSounds),// sndGun
       
   151             (FileName:                  'bee.ogg'; Path: ptSounds),// sndBee
       
   152             (FileName:                'Jump1.ogg'; Path: ptVoices),// sndJump1
       
   153             (FileName:                'Jump2.ogg'; Path: ptVoices),// sndJump2
       
   154             (FileName:                'Jump3.ogg'; Path: ptVoices),// sndJump3
       
   155             (FileName:               'Yessir.ogg'; Path: ptVoices),// sndYesSir
       
   156             (FileName:                'Laugh.ogg'; Path: ptVoices),// sndLaugh
       
   157             (FileName:            'Illgetyou.ogg'; Path: ptVoices),// sndIllGetYou
       
   158             (FileName:          'JustYouWait.ogg'; Path: ptVoices),// sndJustYouWait
       
   159             (FileName:             'Incoming.ogg'; Path: ptVoices),// sndIncoming
       
   160             (FileName:               'Missed.ogg'; Path: ptVoices),// sndMissed
       
   161             (FileName:               'Stupid.ogg'; Path: ptVoices),// sndStupid
       
   162             (FileName:           'Firstblood.ogg'; Path: ptVoices),// sndFirstBlood
       
   163             (FileName:               'Boring.ogg'; Path: ptVoices),// sndBoring
       
   164             (FileName:               'Byebye.ogg'; Path: ptVoices),// sndByeBye
       
   165             (FileName:             'Sameteam.ogg'; Path: ptVoices),// sndSameTeam
       
   166             (FileName:               'Nutter.ogg'; Path: ptVoices),// sndNutter
       
   167             (FileName:       'Reinforcements.ogg'; Path: ptVoices),// sndReinforce
       
   168             (FileName:              'Traitor.ogg'; Path: ptVoices),// sndTraitor
       
   169             (FileName:      'Youllregretthat.ogg'; Path: ptVoices),// sndRegret
       
   170             (FileName:            'Enemydown.ogg'; Path: ptVoices),// sndEnemyDown
       
   171             (FileName:               'Coward.ogg'; Path: ptVoices),// sndCoward
       
   172             (FileName:                'Hurry.ogg'; Path: ptVoices),// sndHurry
       
   173             (FileName:              'Watchit.ogg'; Path: ptVoices),// sndWatchIt
       
   174             (FileName:             'Kamikaze.ogg'; Path: ptVoices),// sndKamikaze
       
   175             (FileName:                'cake2.ogg'; Path: ptSounds),// sndCake
       
   176             (FileName:                  'Ow1.ogg'; Path: ptVoices),// sndOw1
       
   177             (FileName:                  'Ow2.ogg'; Path: ptVoices),// sndOw2
       
   178             (FileName:                  'Ow3.ogg'; Path: ptVoices),// sndOw3
       
   179             (FileName:                  'Ow4.ogg'; Path: ptVoices),// sndOw4
       
   180             (FileName:           'Firepunch1.ogg'; Path: ptVoices),// sndFirepunch1
       
   181             (FileName:           'Firepunch2.ogg'; Path: ptVoices),// sndFirepunch2
       
   182             (FileName:           'Firepunch3.ogg'; Path: ptVoices),// sndFirepunch3
       
   183             (FileName:           'Firepunch4.ogg'; Path: ptVoices),// sndFirepunch4
       
   184             (FileName:           'Firepunch5.ogg'; Path: ptVoices),// sndFirepunch5
       
   185             (FileName:           'Firepunch6.ogg'; Path: ptVoices),// sndFirepunch6
       
   186             (FileName:                'Melon.ogg'; Path: ptVoices),// sndMelon
       
   187             (FileName:              'Hellish.ogg'; Path: ptSounds),// sndHellish
       
   188             (FileName:               'Yoohoo.ogg'; Path: ptSounds),// sndYoohoo
       
   189             (FileName:              'rcplane.ogg'; Path: ptSounds),// sndRCPlane
       
   190             (FileName:            'whipcrack.ogg'; Path: ptSounds),// sndWhipCrack
       
   191             (FileName:'ride_of_the_valkyries.ogg'; Path: ptSounds),// sndRideOfTheValkyries
       
   192             (FileName:               'denied.ogg'; Path: ptSounds),// sndDenied
       
   193             (FileName:               'placed.ogg'; Path: ptSounds),// sndPlaced
       
   194             (FileName:          'baseballbat.ogg'; Path: ptSounds),// sndBaseballBat
       
   195             (FileName:                'steam.ogg'; Path: ptSounds),// sndVaporize
       
   196             (FileName:                 'warp.ogg'; Path: ptSounds),// sndWarp
       
   197             (FileName:          'suddendeath.ogg'; Path: ptSounds),// sndSuddenDeath
       
   198             (FileName:               'mortar.ogg'; Path: ptSounds),// sndMortar
       
   199             (FileName:         'shutterclick.ogg'; Path: ptSounds),// sndShutter
       
   200             (FileName:              'homerun.ogg'; Path: ptSounds),// sndHomerun
       
   201             (FileName:              'molotov.ogg'; Path: ptSounds),// sndMolotov
       
   202             (FileName:            'Takecover.ogg'; Path: ptVoices),// sndCover
       
   203             (FileName:                'Uh-oh.ogg'; Path: ptVoices),// sndUhOh
       
   204             (FileName:                 'Oops.ogg'; Path: ptVoices),// sndOops
       
   205             (FileName:                 'Nooo.ogg'; Path: ptVoices),// sndNooo
       
   206             (FileName:                'Hello.ogg'; Path: ptVoices),// sndHello
       
   207             (FileName:             'ropeshot.ogg'; Path: ptSounds),// sndRopeShot
       
   208             (FileName:           'ropeattach.ogg'; Path: ptSounds),// sndRopeAttach
       
   209             (FileName:          'roperelease.ogg'; Path: ptSounds),// sndRopeRelease
       
   210             (FileName:            'switchhog.ogg'; Path: ptSounds),// sndSwitchHog
       
   211             (FileName:              'Victory.ogg'; Path: ptVoices),// sndVictory
       
   212             (FileName:             'Flawless.ogg'; Path: ptVoices),// sndFlawless
       
   213             (FileName:         'sniperreload.ogg'; Path: ptSounds),// sndSniperReload
       
   214             (FileName:                'steps.ogg'; Path: ptSounds),// sndSteps
       
   215             (FileName:           'lowgravity.ogg'; Path: ptSounds),// sndLowGravity
       
   216             (FileName:           'hell_growl.ogg'; Path: ptSounds),// sndHellishImpact1
       
   217             (FileName:            'hell_ooff.ogg'; Path: ptSounds),// sndHellishImpact2
       
   218             (FileName:              'hell_ow.ogg'; Path: ptSounds),// sndHellishImpact3
       
   219             (FileName:             'hell_ugh.ogg'; Path: ptSounds),// sndHellishImpact4
       
   220             (FileName:          'melonimpact.ogg'; Path: ptSounds),// sndMelonImpact
       
   221             (FileName:             'Droplet1.ogg'; Path: ptSounds),// sndDroplet1
       
   222             (FileName:             'Droplet2.ogg'; Path: ptSounds),// sndDroplet2
       
   223             (FileName:             'Droplet3.ogg'; Path: ptSounds),// sndDroplet3
       
   224             (FileName:                  'egg.ogg'; Path: ptSounds),// sndEggBreak
       
   225             (FileName:             'drillgun.ogg'; Path: ptSounds),// sndDrillRocket
       
   226             (FileName:          'PoisonCough.ogg'; Path: ptVoices),// sndPoisonCough
       
   227             (FileName:           'PoisonMoan.ogg'; Path: ptVoices),// sndPoisonMoan
       
   228             (FileName:             'BirdyLay.ogg'; Path: ptSounds),// sndBirdyLay
       
   229             (FileName:              'Whistle.ogg'; Path: ptSounds),// sndWhistle
       
   230             (FileName:             'beewater.ogg'; Path: ptSounds),// sndBeeWater
       
   231             (FileName:                   '1C.ogg'; Path: ptSounds),// sndPiano0
       
   232             (FileName:                   '2D.ogg'; Path: ptSounds),// sndPiano1
       
   233             (FileName:                   '3E.ogg'; Path: ptSounds),// sndPiano2
       
   234             (FileName:                   '4F.ogg'; Path: ptSounds),// sndPiano3
       
   235             (FileName:                   '5G.ogg'; Path: ptSounds),// sndPiano4
       
   236             (FileName:                   '6A.ogg'; Path: ptSounds),// sndPiano5
       
   237             (FileName:                   '7B.ogg'; Path: ptSounds),// sndPiano6
       
   238             (FileName:                   '8C.ogg'; Path: ptSounds),// sndPiano7
       
   239             (FileName:                   '9D.ogg'; Path: ptSounds),// sndPiano8
       
   240             (FileName:                 'skip.ogg'; Path: ptSounds),// sndSkip
       
   241             (FileName:              'sinegun.ogg'; Path: ptSounds),// sndSineGun
       
   242             (FileName:                'Ooff1.ogg'; Path: ptVoices),// sndOoff1
       
   243             (FileName:                'Ooff2.ogg'; Path: ptVoices),// sndOoff2
       
   244             (FileName:                'Ooff3.ogg'; Path: ptVoices),// sndOoff3
       
   245             (FileName:               'hammer.ogg'; Path: ptSounds),// sndWhack
       
   246             (FileName:           'Comeonthen.ogg'; Path: ptVoices),// sndComeonthen
       
   247             (FileName:            'parachute.ogg'; Path: ptSounds),// sndParachute
       
   248             (FileName:                 'bump.ogg'; Path: ptSounds),// sndBump
       
   249             (FileName:            'hogchant3.ogg'; Path: ptSounds),// sndResurrector
       
   250             (FileName:                'plane.ogg'; Path: ptSounds),// sndPlane
       
   251             (FileName:               'TARDIS.ogg'; Path: ptSounds) // sndTardis
       
   252             );
       
   253 
   123 
   254 
   124 
   255 
   125 function  AskForVoicepack(name: shortstring): Pointer;
   256 function  AskForVoicepack(name: shortstring): Pointer;
   126 var i: Longword;
   257 var i: Longword;
   127     locName, path: shortstring;
   258     locName, path: shortstring;
   155     AskForVoicepack:= @voicepacks[i]
   286     AskForVoicepack:= @voicepacks[i]
   156 end;
   287 end;
   157 
   288 
   158 procedure InitSound;
   289 procedure InitSound;
   159 const channels: LongInt = {$IFDEF MOBILE}1{$ELSE}2{$ENDIF};
   290 const channels: LongInt = {$IFDEF MOBILE}1{$ELSE}2{$ENDIF};
   160 begin
   291 var success: boolean;
   161     if not isSoundEnabled then
   292 begin
       
   293     if not (isSoundEnabled or isMusicEnabled) then
   162         exit;
   294         exit;
   163     WriteToConsole('Init sound...');
   295     WriteToConsole('Init sound...');
   164     isSoundEnabled:= SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0;
   296     success:= SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0;
   165 
   297 
   166     if isSoundEnabled then
   298     if success then
   167         isSoundEnabled:= Mix_OpenAudio(44100, $8010, channels, 1024) = 0;
   299         success:= Mix_OpenAudio(44100, $8010, channels, 1024) = 0;
   168 
   300 
   169     if isSoundEnabled then
   301     if success then
   170         WriteLnToConsole(msgOK)
   302         WriteLnToConsole(msgOK)
   171     else
   303     else
       
   304     begin
   172         WriteLnToConsole(msgFailed);
   305         WriteLnToConsole(msgFailed);
       
   306         isSoundEnabled:= false;
       
   307         isMusicEnabled:= false;
       
   308     end;
   173 
   309 
   174     WriteToConsole('Init SDL_mixer... ');
   310     WriteToConsole('Init SDL_mixer... ');
   175     SDLTry(Mix_Init(MIX_INIT_OGG) <> 0, true);
   311     SDLTry(Mix_Init(MIX_INIT_OGG) <> 0, true);
   176     WriteLnToConsole(msgOK);
   312     WriteLnToConsole(msgOK);
   177 
   313 
   254     if (voicepack <> nil) then
   390     if (voicepack <> nil) then
   255         begin
   391         begin
   256         if (voicepack^.chunks[snd] = nil) and (Soundz[snd].Path = ptVoices) and (Soundz[snd].FileName <> '') then
   392         if (voicepack^.chunks[snd] = nil) and (Soundz[snd].Path = ptVoices) and (Soundz[snd].FileName <> '') then
   257             begin
   393             begin
   258             s:= cPathz[Soundz[snd].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
   394             s:= cPathz[Soundz[snd].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
   259             if (not FileExists(s)) and (snd in [sndFirePunch2, sndFirePunch3, sndFirePunch4, sndFirePunch5, sndFirePunch6]) then
   395             if (not pfsExists(s)) and (snd in [sndFirePunch2, sndFirePunch3, sndFirePunch4, sndFirePunch5, sndFirePunch6]) then
   260                 s:= cPathz[Soundz[sndFirePunch1].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
   396                 s:= cPathz[Soundz[sndFirePunch1].Path] + '/' + voicepack^.name + '/' + Soundz[snd].FileName;
   261             WriteToConsole(msgLoading + s + ' ');
   397             WriteToConsole(msgLoading + s + ' ');
   262             voicepack^.chunks[snd]:= Mix_LoadWAV_RW(rwopsOpenRead(s), 1);
   398             voicepack^.chunks[snd]:= Mix_LoadWAV_RW(rwopsOpenRead(s), 1);
   263             if voicepack^.chunks[snd] = nil then
   399             if voicepack^.chunks[snd] = nil then
   264                 WriteLnToConsole(msgFailed)
   400                 WriteLnToConsole(msgFailed)
   287     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd = snd) and  (LastVoice.voicepack = voicepack)) then
   423     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd = snd) and  (LastVoice.voicepack = voicepack)) then
   288         exit;
   424         exit;
   289     if (snd = sndVictory) or (snd = sndFlawless) then
   425     if (snd = sndVictory) or (snd = sndFlawless) then
   290         begin
   426         begin
   291         Mix_FadeOutChannel(-1, 800);
   427         Mix_FadeOutChannel(-1, 800);
   292         for i:= 0 to 7 do
   428         for i:= 0 to High(VoiceList) do
   293             VoiceList[i].snd:= sndNone;
   429             VoiceList[i].snd:= sndNone;
   294         LastVoice.snd:= sndNone;
   430         LastVoice.snd:= sndNone;
   295         end;
   431         end;
   296 
   432 
   297     i:= 0;
   433     i:= 0;
   309 var i : LongInt;
   445 var i : LongInt;
   310 begin
   446 begin
   311     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd <> sndNone) and (lastChan[LastVoice.snd] <> -1) and (Mix_Playing(lastChan[LastVoice.snd]) <> 0)) then
   447     if (not isSoundEnabled) or fastUntilLag or ((LastVoice.snd <> sndNone) and (lastChan[LastVoice.snd] <> -1) and (Mix_Playing(lastChan[LastVoice.snd]) <> 0)) then
   312         exit;
   448         exit;
   313     i:= 0;
   449     i:= 0;
   314     while (i<8) and (VoiceList[i].snd = sndNone) do
   450     while (i<High(VoiceList)) and (VoiceList[i].snd = sndNone) do
   315         inc(i);
   451         inc(i);
   316     
   452     
   317     if (VoiceList[i].snd <> sndNone) then
   453     if (VoiceList[i].snd <> sndNone) then
   318         begin
   454         begin
   319         LastVoice.snd:= VoiceList[i].snd;
   455         LastVoice.snd:= VoiceList[i].snd;
   411 end;
   547 end;
   412 
   548 
   413 procedure PlayMusic;
   549 procedure PlayMusic;
   414 var s: shortstring;
   550 var s: shortstring;
   415 begin
   551 begin
   416     if (not isSoundEnabled) or (MusicFN = '') or (not isMusicEnabled) then
   552     if (MusicFN = '') or (not isMusicEnabled) then
   417         exit;
   553         exit;
   418 
   554 
   419     s:= '/Music/' + MusicFN;
   555     s:= '/Music/' + MusicFN;
   420     WriteToConsole(msgLoading + s + ' ');
   556     WriteToConsole(msgLoading + s + ' ');
   421 
   557 
   432 end;
   568 end;
   433 
   569 
   434 function ChangeVolume(voldelta: LongInt): LongInt;
   570 function ChangeVolume(voldelta: LongInt): LongInt;
   435 begin
   571 begin
   436     ChangeVolume:= 0;
   572     ChangeVolume:= 0;
   437     if (not isSoundEnabled) or ((voldelta = 0) and (not (cInitVolume = 0))) then
   573     if not (isSoundEnabled or isMusicEnabled) or ((voldelta = 0) and (not (cInitVolume = 0))) then
   438         exit;
   574         exit;
   439 
   575 
   440     inc(Volume, voldelta);
   576     inc(Volume, voldelta);
   441     if Volume < 0 then
   577     if Volume < 0 then
   442         Volume:= 0;
   578         Volume:= 0;
   472     ChangeVolume(previousVolume - Volume);
   608     ChangeVolume(previousVolume - Volume);
   473 end;
   609 end;
   474 
   610 
   475 procedure MuteAudio;
   611 procedure MuteAudio;
   476 begin
   612 begin
   477     if not isSoundEnabled then
   613     if not (isSoundEnabled or isMusicEnabled) then
   478         exit;
   614         exit;
   479 
   615 
   480     if (isAudioMuted) then
   616     if (isAudioMuted) then
   481     begin
   617     begin
   482         ResumeMusic;
   618         ResumeMusic;
   575     MusicFN:='';
   711     MusicFN:='';
   576     Mus:= nil;
   712     Mus:= nil;
   577     isAudioMuted:= false;
   713     isAudioMuted:= false;
   578     isSEBackup:= isSoundEnabled;
   714     isSEBackup:= isSoundEnabled;
   579     Volume:= 0;
   715     Volume:= 0;
       
   716     SoundTimerTicks:= 0;
   580     defVoicepack:= AskForVoicepack('Default');
   717     defVoicepack:= AskForVoicepack('Default');
   581 
   718 
   582     for i:= Low(TSound) to High(TSound) do
   719     for i:= Low(TSound) to High(TSound) do
   583         lastChan[i]:= -1;
   720         lastChan[i]:= -1;
   584 
   721 
   587         if voicepacks[t].name <> '' then
   724         if voicepacks[t].name <> '' then
   588             for i:= Low(TSound) to High(TSound) do
   725             for i:= Low(TSound) to High(TSound) do
   589                 voicepacks[t].chunks[i]:= nil;
   726                 voicepacks[t].chunks[i]:= nil;
   590 
   727 
   591     (* on MOBILE SDL_mixer has to be compiled against Tremor (USE_OGG_TREMOR)
   728     (* on MOBILE SDL_mixer has to be compiled against Tremor (USE_OGG_TREMOR)
   592        or sound files bigger than 32k will lockup the game *)
   729        or sound files bigger than 32k will lockup the game on slow cpu *)
   593     for i:= Low(TSound) to High(TSound) do
   730     for i:= Low(TSound) to High(TSound) do
   594         defVoicepack^.chunks[i]:= nil;
   731         defVoicepack^.chunks[i]:= nil;
   595 
   732 
   596 end;
   733 end;
   597 
   734 
   598 procedure freeModule;
   735 procedure freeModule;
   599 begin
   736 begin
   600     if isSoundEnabled then
   737     if isSoundEnabled or isMusicEnabled then
   601         ReleaseSound(true);
   738         ReleaseSound(true);
   602 end;
   739 end;
   603 
   740 
   604 end.
   741 end.
   605 
   742