00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00031
00032
00033
00034
00035
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039
00040 #include "stratagus.h"
00041
00042 #include "SDL.h"
00043
00044 #include "sound.h"
00045 #include "video.h"
00046 #include "sound.h"
00047 #include "unitsound.h"
00048 #include "unittype.h"
00049 #include "player.h"
00050 #include "unit.h"
00051 #include "sound_server.h"
00052 #include "missile.h"
00053 #include "map.h"
00054 #include "ui.h"
00055 #include "widgets.h"
00056
00057
00058
00059
00060
00066 GameSound GameSounds
00067 #ifndef laterUSE_CCL
00068 ={
00069 SoundConfig("placement error"),
00070 SoundConfig("placement success"),
00071 SoundConfig("click"),
00072 SoundConfig("transport docking"),
00073 SoundConfig("building construction"),
00074 SoundConfig("basic human voices work complete"),
00075 SoundConfig("rescue (human) UNUSED"),
00076 }
00077 #endif
00078 ;
00079
00083 struct SelectionHandling {
00084 Origin Source;
00085 CSound *Sound;
00086 unsigned char HowMany;
00087 };
00088
00089 SelectionHandling SelectionHandler;
00090
00091 static int ViewPointOffset;
00092 int DistanceSilent;
00093
00094
00095
00096
00097
00101 static CSample *SimpleChooseSample(const CSound *sound)
00102 {
00103 if (sound->Number == ONE_SOUND) {
00104 return sound->Sound.OneSound;
00105 } else {
00106 return sound->Sound.OneGroup[FrameCounter % sound->Number];
00107 }
00108 }
00109
00113 static CSample *ChooseSample(CSound *sound, bool selection, Origin &source)
00114 {
00115 CSample *result = NULL;
00116
00117 if (!sound || !SoundEnabled()) {
00118 return NULL;
00119 }
00120
00121 if (sound->Number == TWO_GROUPS) {
00122
00123 if (SelectionHandler.Source.Base == source.Base &&
00124 SelectionHandler.Source.Id == source.Id) {
00125 if (SelectionHandler.Sound == sound->Sound.TwoGroups.First) {
00126 result = SimpleChooseSample(SelectionHandler.Sound);
00127 SelectionHandler.HowMany++;
00128 if (SelectionHandler.HowMany >= 3) {
00129 SelectionHandler.HowMany = 0;
00130 SelectionHandler.Sound = sound->Sound.TwoGroups.Second;
00131 }
00132 } else {
00133
00134 if (SelectionHandler.Sound->Number > 1) {
00135 result = SelectionHandler.Sound->Sound.OneGroup[SelectionHandler.HowMany];
00136 SelectionHandler.HowMany++;
00137 if (SelectionHandler.HowMany >= SelectionHandler.Sound->Number) {
00138 SelectionHandler.HowMany = 0;
00139 SelectionHandler.Sound = sound->Sound.TwoGroups.First;
00140 }
00141 } else {
00142 result = SelectionHandler.Sound->Sound.OneSound;
00143 SelectionHandler.HowMany = 0;
00144 SelectionHandler.Sound = sound->Sound.TwoGroups.First;
00145 }
00146 }
00147 } else {
00148 SelectionHandler.Source = source;
00149 SelectionHandler.Sound = sound->Sound.TwoGroups.First;
00150 result = SimpleChooseSample(SelectionHandler.Sound);
00151 SelectionHandler.HowMany = 1;
00152 }
00153 } else {
00154
00155 result = SimpleChooseSample(sound);
00156 if (selection) {
00157 SelectionHandler.Source = source;
00158 }
00159 }
00160
00161 return result;
00162 }
00163
00172 static CSound *ChooseUnitVoiceSound(const CUnit *unit, UnitVoiceGroup voice)
00173 {
00174 switch (voice) {
00175 case VoiceAcknowledging:
00176 return unit->Type->Sound.Acknowledgement.Sound;
00177 case VoiceReady:
00178 return unit->Type->Sound.Ready.Sound;
00179 case VoiceSelected:
00180 return unit->Type->Sound.Selected.Sound;
00181 case VoiceHelpMe:
00182 return unit->Type->Sound.Help.Sound;
00183 case VoiceDying:
00184 return unit->Type->Sound.Dead.Sound;
00185 case VoiceWorkCompleted:
00186 return GameSounds.WorkComplete.Sound;
00187 case VoiceBuilding:
00188 return GameSounds.BuildingConstruction.Sound;
00189 case VoiceDocking:
00190 return GameSounds.Docking.Sound;
00191 case VoiceRepairing:
00192 return unit->Type->Sound.Repair.Sound;
00193 case VoiceHarvesting:
00194 return unit->Type->Sound.Harvest.Sound;
00195 }
00196
00197 return NO_SOUND;
00198 }
00199
00209 static unsigned char VolumeForDistance(unsigned short d, unsigned char range)
00210 {
00211 int d_tmp;
00212 int range_tmp;
00213
00214
00215 if (d <= ViewPointOffset || range == INFINITE_SOUND_RANGE) {
00216 return MaxVolume;
00217 } else {
00218 if (range) {
00219 d -= ViewPointOffset;
00220 d_tmp = d * MAX_SOUND_RANGE;
00221 range_tmp = DistanceSilent * range;
00222 if (d_tmp > range_tmp) {
00223 return 0;
00224 } else {
00225 return (unsigned char)((range_tmp - d_tmp) *
00226 MAX_SOUND_RANGE / range_tmp);
00227 }
00228 } else {
00229 return 0;
00230 }
00231 }
00232 }
00233
00238 static unsigned char CalculateVolume(bool isVolume, int power,
00239 unsigned char range)
00240 {
00241 if (isVolume) {
00242 if (power > MaxVolume) {
00243 return MaxVolume;
00244 } else {
00245 return (unsigned char)power;
00246 }
00247 } else {
00248
00249 return VolumeForDistance(power, range);
00250 }
00251 }
00252
00256 static char CalculateStereo(const CUnit *unit)
00257 {
00258 int stereo;
00259
00260 stereo = ((unit->X * TileSizeX + unit->Type->TileWidth * TileSizeX / 2 +
00261 unit->IX - UI.SelectedViewport->MapX * TileSizeX) * 256 /
00262 ((UI.SelectedViewport->MapWidth - 1) * TileSizeX)) - 128;
00263 if (stereo < -128) {
00264 stereo = -128;
00265 } else if (stereo > 127) {
00266 stereo = 127;
00267 }
00268
00269 return stereo;
00270 }
00271
00280 void PlayUnitSound(const CUnit *unit, UnitVoiceGroup voice)
00281 {
00282 CSound *sound = ChooseUnitVoiceSound(unit, voice);
00283 if (!sound) {
00284 return;
00285 }
00286
00287 bool selection = (voice == VoiceSelected || voice == VoiceBuilding);
00288 Origin source = {unit, unit->Slot};
00289
00290 int channel = PlaySample(ChooseSample(sound, selection, source));
00291 if (channel == -1) {
00292 return;
00293 }
00294 SetChannelVolume(channel, CalculateVolume(false, ViewPointDistanceToUnit(unit), sound->Range));
00295 SetChannelStereo(channel, CalculateStereo(unit));
00296 }
00297
00306 void PlayUnitSound(const CUnit *unit, CSound *sound)
00307 {
00308 Origin source = {unit, unit->Slot};
00309 int channel = PlaySample(ChooseSample(sound, false, source));
00310 if (channel == -1) {
00311 return;
00312 }
00313 SetChannelVolume(channel, CalculateVolume(false, ViewPointDistanceToUnit(unit), sound->Range));
00314 SetChannelStereo(channel, CalculateStereo(unit));
00315 }
00316
00323 void PlayMissileSound(const Missile *missile, CSound *sound)
00324 {
00325 int stereo;
00326
00327 stereo = ((missile->X + missile->Type->G->Width / 2 -
00328 UI.SelectedViewport->MapX * TileSizeX) * 256 /
00329 ((UI.SelectedViewport->MapWidth - 1) * TileSizeX)) - 128;
00330 if (stereo < -128) {
00331 stereo = -128;
00332 } else if (stereo > 127) {
00333 stereo = 127;
00334 }
00335
00336 Origin source = {NULL, 0};
00337
00338 int channel = PlaySample(ChooseSample(sound, false, source));
00339 if (channel == -1) {
00340 return;
00341 }
00342 SetChannelVolume(channel, CalculateVolume(false, ViewPointDistanceToMissile(missile), sound->Range));
00343 SetChannelStereo(channel, stereo);
00344 }
00345
00352 void PlayGameSound(CSound *sound, unsigned char volume)
00353 {
00354 Origin source = {NULL, 0};
00355
00356 int channel = PlaySample(ChooseSample(sound, false, source));
00357 if (channel == -1) {
00358 return;
00359 }
00360 SetChannelVolume(channel, CalculateVolume(true, volume, sound->Range));
00361 }
00362
00363 static std::map<int, LuaActionListener *> ChannelMap;
00364
00368 static void PlaySoundFileCallback(int channel)
00369 {
00370 LuaActionListener *listener = ChannelMap[channel];
00371 if (listener != NULL) {
00372 listener->action("");
00373 ChannelMap[channel] = NULL;
00374 }
00375 delete GetChannelSample(channel);
00376 }
00377
00386 int PlayFile(const std::string &name, LuaActionListener *listener)
00387 {
00388 int channel = -1;
00389 CSample *sample;
00390
00391 sample = LoadSample(name);
00392 if (sample) {
00393 channel = PlaySample(sample);
00394 if (channel != -1) {
00395 SetChannelVolume(channel, MaxVolume);
00396 SetChannelFinishedCallback(channel, PlaySoundFileCallback);
00397 ChannelMap[channel] = listener;
00398 }
00399 }
00400
00401 return channel;
00402 }
00403
00410 void SetSoundRange(CSound *sound, unsigned char range)
00411 {
00412 if (sound != NO_SOUND) {
00413 sound->Range = range;
00414 }
00415 }
00416
00427 CSound *RegisterSound(const char *files[], unsigned number)
00428 {
00429 unsigned i;
00430 CSound *id;
00431
00432 id = new CSound;
00433 if (number > 1) {
00434 id->Sound.OneGroup = new CSample *[number];
00435 id->Number = number;
00436 for (i = 0; i < number; ++i) {
00437 id->Sound.OneGroup[i] = LoadSample(files[i]);
00438 if (!id->Sound.OneGroup[i]) {
00439 delete id;
00440 return NO_SOUND;
00441 }
00442 }
00443 } else {
00444 id->Sound.OneSound = LoadSample(files[0]);
00445 if (!id->Sound.OneSound) {
00446 delete id;
00447 return NO_SOUND;
00448 }
00449 id->Number = ONE_SOUND;
00450 }
00451 id->Range = MAX_SOUND_RANGE;
00452 return id;
00453 }
00454
00463 CSound *RegisterTwoGroups(CSound *first, CSound *second)
00464 {
00465 CSound *id;
00466
00467 if (first == NO_SOUND || second == NO_SOUND) {
00468 return NO_SOUND;
00469 }
00470 id = new CSound;
00471 id->Number = TWO_GROUPS;
00472 id->Sound.TwoGroups.First = first;
00473 id->Sound.TwoGroups.Second = second;
00474 id->Range = MAX_SOUND_RANGE;
00475
00476 return id;
00477 }
00478
00482 void InitSoundClient(void)
00483 {
00484 if (!SoundEnabled()) {
00485 return;
00486 }
00487
00488
00489 if (!GameSounds.PlacementError.Sound) {
00490 GameSounds.PlacementError.Sound =
00491 SoundForName(GameSounds.PlacementError.Name);
00492 }
00493 if (!GameSounds.PlacementSuccess.Sound) {
00494 GameSounds.PlacementSuccess.Sound =
00495 SoundForName(GameSounds.PlacementSuccess.Name);
00496 }
00497 if (!GameSounds.Click.Sound) {
00498 GameSounds.Click.Sound = SoundForName(GameSounds.Click.Name);
00499 }
00500 if (!GameSounds.Docking.Sound) {
00501 GameSounds.Docking.Sound =
00502 SoundForName(GameSounds.Docking.Name);
00503 }
00504 if (!GameSounds.BuildingConstruction.Sound) {
00505 GameSounds.BuildingConstruction.Sound =
00506 SoundForName(GameSounds.BuildingConstruction.Name);
00507 }
00508 if (!GameSounds.WorkComplete.Sound &&
00509 !GameSounds.WorkComplete.Name.empty()) {
00510 GameSounds.WorkComplete.Sound =
00511 SoundForName(GameSounds.WorkComplete.Name);
00512 }
00513 if (!GameSounds.Rescue.Sound && !GameSounds.Rescue.Name.empty()) {
00514 GameSounds.Rescue.Sound =
00515 SoundForName(GameSounds.Rescue.Name);
00516 }
00517 if (!GameSounds.ChatMessage.Sound && !GameSounds.ChatMessage.Name.empty()) {
00518 GameSounds.ChatMessage.Sound =
00519 SoundForName(GameSounds.ChatMessage.Name);
00520 }
00521
00522 int MapWidth = (UI.MapArea.EndX - UI.MapArea.X + TileSizeX) / TileSizeX;
00523 int MapHeight = (UI.MapArea.EndY - UI.MapArea.Y + TileSizeY) / TileSizeY;
00524 DistanceSilent = 3 * std::max(MapWidth, MapHeight);
00525 ViewPointOffset = std::max(MapWidth / 2, MapHeight / 2);
00526 }
00527
00528
00529 CSound::~CSound()
00530 {
00531 if (this->Number == ONE_SOUND) {
00532 delete Sound.OneSound;
00533 } else if (this->Number == TWO_GROUPS) {
00534 } else {
00535 for (int i = 0; i < this->Number; ++i) {
00536 delete this->Sound.OneGroup[i];
00537 }
00538 delete[] this->Sound.OneGroup;
00539 }
00540 }
00541
00542