# sound.cpp

Go to the documentation of this file.

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