# sound.cpp

Go to the documentation of this file.

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