# sound.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "landscape.h"
    00014 #include "mixer.h"
    00015 #include "newgrf_sound.h"
    00016 #include "fios.h"
    00017 #include "window_gui.h"
    00018 #include "vehicle_base.h"
    00019 #include "zoom_func.h"
    00020
    00021 /* The type of set we're replacing */
    00022 #define SET_TYPE "sounds"
    00023 #include "base_media_func.h"
    00024
    00025 static SoundEntry _original_sounds[ORIGINAL_SAMPLE_COUNT];
    00026 MusicFileSettings _msf;
    00027
    00028 static void OpenBankFile(const char *filename)
    00029 {
    00030   memset(_original_sounds, 0, sizeof(_original_sounds));
    00031
    00032   /* If there is no sound file (nosound set), don't load anything */
    00033   if (filename == NULL) return;
    00034
    00035   FioOpenFile(SOUND_SLOT, filename);
    00036   size_t pos = FioGetPos();
    00037   uint count = FioReadDword();
    00038
    00039   /* The new format has the highest bit always set */
    00040   bool new_format = HasBit(count, 31);
    00041   ClrBit(count, 31);
    00042   count /= 8;
    00043
    00044   /* Simple check for the correct number of original sounds. */
    00045   if (count != ORIGINAL_SAMPLE_COUNT) {
    00046     /* Corrupt sample data? Just leave the allocated memory as those tell
    00047      * there is no sound to play (size = 0 due to calloc). Not allocating
    00048      * the memory disables valid NewGRFs that replace sounds. */
    00049     DEBUG(misc, 6, "Incorrect number of sounds in '%s', ignoring.", filename);
    00050     return;
    00051   }
    00052
    00053   FioSeekTo(pos, SEEK_SET);
    00054
    00055   for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
    00056     _original_sounds[i].file_slot = SOUND_SLOT;
    00057     _original_sounds[i].file_offset = GB(FioReadDword(), 0, 31) + pos;
    00058     _original_sounds[i].file_size = FioReadDword();
    00059   }
    00060
    00061   for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
    00062     SoundEntry *sound = &_original_sounds[i];
    00063     char name[255];
    00064
    00065     FioSeekTo(sound->file_offset, SEEK_SET);
    00066
    00067     /* Check for special case, see else case */
    00068     FioReadBlock(name, FioReadByte()); // Read the name of the sound
    00069     if (new_format || strcmp(name, "Corrupt sound") != 0) {
    00070       FioSeekTo(12, SEEK_CUR); // Skip past RIFF header
    00071
    00072       /* Read riff tags */
    00073       for (;;) {
    00074         uint32 tag = FioReadDword();
    00075         uint32 size = FioReadDword();
    00076
    00077         if (tag == ' tmf') {
    00078           FioReadWord(); // wFormatTag
    00079           sound->channels = FioReadWord();        // wChannels
    00080           sound->rate     = FioReadDword();       // samples per second
    00081           if (!new_format) sound->rate = 11025;   // seems like all old samples should be played at this rate.
    00082           FioReadDword();                         // avg bytes per second
    00083           FioReadWord();                          // alignment
    00084           sound->bits_per_sample = FioReadByte(); // bits per sample
    00085           FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR);
    00086         } else if (tag == 'atad') {
    00087           sound->file_size = size;
    00088           sound->file_slot = SOUND_SLOT;
    00089           sound->file_offset = FioGetPos();
    00090           break;
    00091         } else {
    00092           sound->file_size = 0;
    00093           break;
    00094         }
    00095       }
    00096     } else {
    00097       /*
    00098        * Special case for the jackhammer sound
    00099        * (name in sample.cat is "Corrupt sound")
    00100        * It's no RIFF file, but raw PCM data
    00101        */
    00102       sound->channels = 1;
    00103       sound->rate = 11025;
    00104       sound->bits_per_sample = 8;
    00105       sound->file_slot = SOUND_SLOT;
    00106       sound->file_offset = FioGetPos();
    00107     }
    00108   }
    00109 }
    00110
    00111 static bool SetBankSource(MixerChannel *mc, const SoundEntry *sound)
    00112 {
    00113   assert(sound != NULL);
    00114
    00115   if (sound->file_size == 0) return false;
    00116
    00117   int8 *mem = MallocT<int8>(sound->file_size + 2);
    00118   /* Add two extra bytes so rate conversion can read these
    00119    * without reading out of its input buffer. */
    00120   mem[sound->file_size    ] = 0;
    00121   mem[sound->file_size + 1] = 0;
    00122
    00123   FioSeekToFile(sound->file_slot, sound->file_offset);
    00124   FioReadBlock(mem, sound->file_size);
    00125
    00126   /* 16-bit PCM WAV files should be signed by default */
    00127   if (sound->bits_per_sample == 8) {
    00128     for (uint i = 0; i != sound->file_size; i++) {
    00129       mem[i] += -128; // Convert unsigned sound data to signed
    00130     }
    00131   }
    00132
    00133 #if TTD_ENDIAN == TTD_BIG_ENDIAN
    00134   if (sound->bits_per_sample == 16) {
    00135     uint num_samples = sound->file_size / 2;
    00136     int16 *samples = (int16 *)mem;
    00137     for (uint i = 0; i < num_samples; i++) {
    00138       samples[i] = BSWAP16(samples[i]);
    00139     }
    00140   }
    00141 #endif
    00142
    00143   assert(sound->bits_per_sample == 8 || sound->bits_per_sample == 16);
    00144   assert(sound->channels == 1);
    00145   assert(sound->file_size != 0 && sound->rate != 0);
    00146
    00147   MxSetChannelRawSrc(mc, mem, sound->file_size, sound->rate, sound->bits_per_sample == 16);
    00148
    00149   return true;
    00150 }
    00151
    00152 void InitializeSound()
    00153 {
    00154   DEBUG(misc, 1, "Loading sound effects...");
    00155   OpenBankFile(BaseSounds::GetUsedSet()->files->filename);
    00156 }
    00157
    00158 /* Low level sound player */
    00159 static void StartSound(SoundID sound_id, float pan, uint volume)
    00160 {
    00161   if (volume == 0) return;
    00162
    00163   const SoundEntry *sound = GetSound(sound_id);
    00164   if (sound == NULL) return;
    00165
    00166   /* Empty sound? */
    00167   if (sound->rate == 0) return;
    00168
    00169   MixerChannel *mc = MxAllocateChannel();
    00170   if (mc == NULL) return;
    00171
    00172   if (!SetBankSource(mc, sound)) return;
    00173
    00174   /* Apply the sound effect's own volume. */
    00175   volume = sound->volume * volume;
    00176
    00177   MxSetChannelVolume(mc, volume, pan);
    00178   MxActivateChannel(mc);
    00179 }
    00180
    00181
    00182 static const byte _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87};
    00183 assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_COUNT);
    00184
    00185 static const byte _sound_base_vol[] = {
    00186   128,  90, 128, 128, 128, 128, 128, 128,
    00187   128,  90,  90, 128, 128, 128, 128, 128,
    00188   128, 128, 128,  80, 128, 128, 128, 128,
    00189   128, 128, 128, 128, 128, 128, 128, 128,
    00190   128, 128,  90,  90,  90, 128,  90, 128,
    00191   128,  90, 128, 128, 128,  90, 128, 128,
    00192   128, 128, 128, 128,  90, 128, 128, 128,
    00193   128,  90, 128, 128, 128, 128, 128, 128,
    00194   128, 128,  90,  90,  90, 128, 128, 128,
    00195    90,
    00196 };
    00197
    00198 static const byte _sound_idx[] = {
    00199    2,  3,  4,  5,  6,  7,  8,  9,
    00200   10, 11, 12, 13, 14, 15, 16, 17,
    00201   18, 19, 20, 21, 22, 23, 24, 25,
    00202   26, 27, 28, 29, 30, 31, 32, 33,
    00203   34, 35, 36, 37, 38, 39, 40,  0,
    00204    1, 41, 42, 43, 44, 45, 46, 47,
    00205   48, 49, 50, 51, 52, 53, 54, 55,
    00206   56, 57, 58, 59, 60, 61, 62, 63,
    00207   64, 65, 66, 67, 68, 69, 70, 71,
    00208   72,
    00209 };
    00210
    00211 void SndCopyToPool()
    00212 {
    00213   for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
    00214     SoundEntry *sound = AllocateSound();
    00215     *sound = _original_sounds[_sound_idx[i]];
    00216     sound->volume = _sound_base_vol[i];
    00217     sound->priority = 0;
    00218   }
    00219 }
    00220
    00229 static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
    00230 {
    00231   if (_msf.effect_vol == 0) return;
    00232
    00233   const Window *w;
    00234   FOR_ALL_WINDOWS_FROM_BACK(w) {
    00235     const ViewPort *vp = w->viewport;
    00236
    00237     if (vp != NULL &&
    00238         left < ScaleByZoom(vp->virtual_left, vp->zoom) + vp->virtual_width && right > ScaleByZoom(vp->virtual_left, vp->zoom) &&
    00239         top < ScaleByZoom(vp->virtual_top, vp->zoom) + vp->virtual_height && bottom > ScaleByZoom(vp->virtual_top, vp->zoom)) {
    00240       int screen_x = (left + right) / 2 - ScaleByZoom(vp->virtual_left, vp->zoom);
    00241       int width = (vp->virtual_width == 0 ? 1 : vp->virtual_width);
    00242       float panning = (float)screen_x / width;
    00243
    00244       StartSound(
    00245         sound,
    00246         panning,
    00247         (_msf.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN]) / 256
    00248       );
    00249       return;
    00250     }
    00251   }
    00252 }
    00253
    00254 void SndPlayTileFx(SoundID sound, TileIndex tile)
    00255 {
    00256   /* emits sound from center of the tile */
    00257   int x = min(MapMaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
    00258   int y = min(MapMaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
    00259   uint z = (y < 0 ? 0 : GetSlopeZ(x, y));
    00260   Point pt = RemapCoords(x, y, z);
    00261   y += 2 * TILE_SIZE;
    00262   Point pt2 = RemapCoords(x, y, GetSlopeZ(x, y));
    00263   SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
    00264 }
    00265
    00266 void SndPlayVehicleFx(SoundID sound, const Vehicle *v)
    00267 {
    00268   SndPlayScreenCoordFx(sound,
    00269     v->coord.left, v->coord.right,
    00270     v->coord.top, v->coord.bottom
    00271   );
    00272 }
    00273
    00274 void SndPlayFx(SoundID sound)
    00275 {
    00276   StartSound(sound, 0.5, _msf.effect_vol);
    00277 }
    00278
    00279 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<SoundsSet>, SoundsSet)
    00280
    00281
    00282 static const char * const _sound_file_names[] = { "samples" };
    00283
    00284
    00285 template <class T, size_t Tnum_files, Subdirectory Tsubdir>
    00286 /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _sound_file_names;
    00287
    00288 template <class Tbase_set>
    00289 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
    00290 {
    00291   return ".obs"; // OpenTTD Base Sounds
    00292 }
    00293
    00294 template <class Tbase_set>
    00295 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
    00296 {
    00297   if (BaseMedia<Tbase_set>::used_set != NULL) return true;
    00298
    00299   const Tbase_set *best = NULL;
    00300   for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
    00301     /* Skip unuseable sets */
    00302     if (c->GetNumMissing() != 0) continue;
    00303
    00304     if (best == NULL ||
    00305         (best->fallback && !c->fallback) ||
    00306         best->valid_files < c->valid_files ||
    00307         (best->valid_files == c->valid_files &&
    00308           (best->shortname == c->shortname && best->version < c->version))) {
    00309       best = c;
    00310     }
    00311   }
    00312
    00313   BaseMedia<Tbase_set>::used_set = best;
    00314   return BaseMedia<Tbase_set>::used_set != NULL;
    00315 }
    00316