sound.cpp

Go to the documentation of this file.
00001 /* $Id: sound.cpp 22886 2011-09-03 18:56:34Z frosch $ */
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 static void OpenBankFile(const char *filename)
00028 {
00029   memset(_original_sounds, 0, sizeof(_original_sounds));
00030 
00031   /* If there is no sound file (nosound set), don't load anything */
00032   if (filename == NULL) return;
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   /* Check for valid sound size. */
00115   if (sound->file_size == 0 || sound->file_size > ((size_t)-1) - 2) 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, 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 < vp->virtual_left + vp->virtual_width && right > vp->virtual_left &&
00239         top < vp->virtual_top + vp->virtual_height && bottom > vp->virtual_top) {
00240       int screen_x = (left + right) / 2 - vp->virtual_left;
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