OpenTTD
sound.cpp
Go to the documentation of this file.
1 /* $Id: sound.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "stdafx.h"
13 #include "landscape.h"
14 #include "mixer.h"
15 #include "newgrf_sound.h"
16 #include "fios.h"
17 #include "window_gui.h"
18 #include "vehicle_base.h"
19 
20 /* The type of set we're replacing */
21 #define SET_TYPE "sounds"
22 #include "base_media_func.h"
23 
24 #include "safeguards.h"
25 
26 static SoundEntry _original_sounds[ORIGINAL_SAMPLE_COUNT];
27 
28 static void OpenBankFile(const char *filename)
29 {
30  memset(_original_sounds, 0, sizeof(_original_sounds));
31 
32  /* If there is no sound file (nosound set), don't load anything */
33  if (filename == NULL) return;
34 
36  size_t pos = FioGetPos();
37  uint count = FioReadDword();
38 
39  /* The new format has the highest bit always set */
40  bool new_format = HasBit(count, 31);
41  ClrBit(count, 31);
42  count /= 8;
43 
44  /* Simple check for the correct number of original sounds. */
45  if (count != ORIGINAL_SAMPLE_COUNT) {
46  /* Corrupt sample data? Just leave the allocated memory as those tell
47  * there is no sound to play (size = 0 due to calloc). Not allocating
48  * the memory disables valid NewGRFs that replace sounds. */
49  DEBUG(misc, 6, "Incorrect number of sounds in '%s', ignoring.", filename);
50  return;
51  }
52 
53  FioSeekTo(pos, SEEK_SET);
54 
55  for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
56  _original_sounds[i].file_slot = SOUND_SLOT;
57  _original_sounds[i].file_offset = GB(FioReadDword(), 0, 31) + pos;
58  _original_sounds[i].file_size = FioReadDword();
59  }
60 
61  for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
62  SoundEntry *sound = &_original_sounds[i];
63  char name[255];
64 
65  FioSeekTo(sound->file_offset, SEEK_SET);
66 
67  /* Check for special case, see else case */
68  FioReadBlock(name, FioReadByte()); // Read the name of the sound
69  if (new_format || strcmp(name, "Corrupt sound") != 0) {
70  FioSeekTo(12, SEEK_CUR); // Skip past RIFF header
71 
72  /* Read riff tags */
73  for (;;) {
74  uint32 tag = FioReadDword();
75  uint32 size = FioReadDword();
76 
77  if (tag == ' tmf') {
78  FioReadWord(); // wFormatTag
79  sound->channels = FioReadWord(); // wChannels
80  sound->rate = FioReadDword(); // samples per second
81  if (!new_format) sound->rate = 11025; // seems like all old samples should be played at this rate.
82  FioReadDword(); // avg bytes per second
83  FioReadWord(); // alignment
84  sound->bits_per_sample = FioReadByte(); // bits per sample
85  FioSeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR);
86  } else if (tag == 'atad') {
87  sound->file_size = size;
88  sound->file_slot = SOUND_SLOT;
89  sound->file_offset = FioGetPos();
90  break;
91  } else {
92  sound->file_size = 0;
93  break;
94  }
95  }
96  } else {
97  /*
98  * Special case for the jackhammer sound
99  * (name in sample.cat is "Corrupt sound")
100  * It's no RIFF file, but raw PCM data
101  */
102  sound->channels = 1;
103  sound->rate = 11025;
104  sound->bits_per_sample = 8;
105  sound->file_slot = SOUND_SLOT;
106  sound->file_offset = FioGetPos();
107  }
108  }
109 }
110 
111 static bool SetBankSource(MixerChannel *mc, const SoundEntry *sound)
112 {
113  assert(sound != NULL);
114 
115  /* Check for valid sound size. */
116  if (sound->file_size == 0 || sound->file_size > ((size_t)-1) - 2) return false;
117 
118  int8 *mem = MallocT<int8>(sound->file_size + 2);
119  /* Add two extra bytes so rate conversion can read these
120  * without reading out of its input buffer. */
121  mem[sound->file_size ] = 0;
122  mem[sound->file_size + 1] = 0;
123 
124  FioSeekToFile(sound->file_slot, sound->file_offset);
125  FioReadBlock(mem, sound->file_size);
126 
127  /* 16-bit PCM WAV files should be signed by default */
128  if (sound->bits_per_sample == 8) {
129  for (uint i = 0; i != sound->file_size; i++) {
130  mem[i] += -128; // Convert unsigned sound data to signed
131  }
132  }
133 
134 #if TTD_ENDIAN == TTD_BIG_ENDIAN
135  if (sound->bits_per_sample == 16) {
136  uint num_samples = sound->file_size / 2;
137  int16 *samples = (int16 *)mem;
138  for (uint i = 0; i < num_samples; i++) {
139  samples[i] = BSWAP16(samples[i]);
140  }
141  }
142 #endif
143 
144  assert(sound->bits_per_sample == 8 || sound->bits_per_sample == 16);
145  assert(sound->channels == 1);
146  assert(sound->file_size != 0 && sound->rate != 0);
147 
148  MxSetChannelRawSrc(mc, mem, sound->file_size, sound->rate, sound->bits_per_sample == 16);
149 
150  return true;
151 }
152 
153 void InitializeSound()
154 {
155  DEBUG(misc, 1, "Loading sound effects...");
156  OpenBankFile(BaseSounds::GetUsedSet()->files->filename);
157 }
158 
159 /* Low level sound player */
160 static void StartSound(SoundID sound_id, float pan, uint volume)
161 {
162  if (volume == 0) return;
163 
164  SoundEntry *sound = GetSound(sound_id);
165  if (sound == NULL) return;
166 
167  /* NewGRF sound that wasn't loaded yet? */
168  if (sound->rate == 0 && sound->file_slot != 0) {
169  if (!LoadNewGRFSound(sound)) {
170  /* Mark as invalid. */
171  sound->file_slot = 0;
172  return;
173  }
174  }
175 
176  /* Empty sound? */
177  if (sound->rate == 0) return;
178 
179  MixerChannel *mc = MxAllocateChannel();
180  if (mc == NULL) return;
181 
182  if (!SetBankSource(mc, sound)) return;
183 
184  /* Apply the sound effect's own volume. */
185  volume = sound->volume * volume;
186 
187  MxSetChannelVolume(mc, volume, pan);
188  MxActivateChannel(mc);
189 }
190 
191 
192 static const byte _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87};
193 assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_COUNT);
194 
195 static const byte _sound_base_vol[] = {
196  128, 90, 128, 128, 128, 128, 128, 128,
197  128, 90, 90, 128, 128, 128, 128, 128,
198  128, 128, 128, 80, 128, 128, 128, 128,
199  128, 128, 128, 128, 128, 128, 128, 128,
200  128, 128, 90, 90, 90, 128, 90, 128,
201  128, 90, 128, 128, 128, 90, 128, 128,
202  128, 128, 128, 128, 90, 128, 128, 128,
203  128, 90, 128, 128, 128, 128, 128, 128,
204  128, 128, 90, 90, 90, 128, 128, 128,
205  90,
206 };
207 
208 static const byte _sound_idx[] = {
209  2, 3, 4, 5, 6, 7, 8, 9,
210  10, 11, 12, 13, 14, 15, 16, 17,
211  18, 19, 20, 21, 22, 23, 24, 25,
212  26, 27, 28, 29, 30, 31, 32, 33,
213  34, 35, 36, 37, 38, 39, 40, 0,
214  1, 41, 42, 43, 44, 45, 46, 47,
215  48, 49, 50, 51, 52, 53, 54, 55,
216  56, 57, 58, 59, 60, 61, 62, 63,
217  64, 65, 66, 67, 68, 69, 70, 71,
218  72,
219 };
220 
221 void SndCopyToPool()
222 {
224  for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
225  sound[i] = _original_sounds[_sound_idx[i]];
226  sound[i].volume = _sound_base_vol[i];
227  sound[i].priority = 0;
228  }
229 }
230 
239 static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
240 {
241  if (_settings_client.music.effect_vol == 0) return;
242 
243  const Window *w;
244  FOR_ALL_WINDOWS_FROM_BACK(w) {
245  const ViewPort *vp = w->viewport;
246 
247  if (vp != NULL &&
248  left < vp->virtual_left + vp->virtual_width && right > vp->virtual_left &&
249  top < vp->virtual_top + vp->virtual_height && bottom > vp->virtual_top) {
250  int screen_x = (left + right) / 2 - vp->virtual_left;
251  int width = (vp->virtual_width == 0 ? 1 : vp->virtual_width);
252  float panning = (float)screen_x / width;
253 
254  StartSound(
255  sound,
256  panning,
257  (_settings_client.music.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN]) / 256
258  );
259  return;
260  }
261  }
262 }
263 
264 void SndPlayTileFx(SoundID sound, TileIndex tile)
265 {
266  /* emits sound from center of the tile */
267  int x = min(MapMaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
268  int y = min(MapMaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
269  int z = (y < 0 ? 0 : GetSlopePixelZ(x, y));
270  Point pt = RemapCoords(x, y, z);
271  y += 2 * TILE_SIZE;
272  Point pt2 = RemapCoords(x, y, GetSlopePixelZ(x, y));
273  SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
274 }
275 
276 void SndPlayVehicleFx(SoundID sound, const Vehicle *v)
277 {
278  SndPlayScreenCoordFx(sound,
279  v->coord.left, v->coord.right,
280  v->coord.top, v->coord.bottom
281  );
282 }
283 
284 void SndPlayFx(SoundID sound)
285 {
286  StartSound(sound, 0.5, _settings_client.music.effect_vol);
287 }
288 
290 
291 
292 static const char * const _sound_file_names[] = { "samples" };
293 
294 
295 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
297 
298 template <class Tbase_set>
299 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
300 {
301  return ".obs"; // OpenTTD Base Sounds
302 }
303 
304 template <class Tbase_set>
305 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
306 {
307  if (BaseMedia<Tbase_set>::used_set != NULL) return true;
308 
309  const Tbase_set *best = NULL;
310  for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
311  /* Skip unusable sets */
312  if (c->GetNumMissing() != 0) continue;
313 
314  if (best == NULL ||
315  (best->fallback && !c->fallback) ||
316  best->valid_files < c->valid_files ||
317  (best->valid_files == c->valid_files &&
318  (best->shortname == c->shortname && best->version < c->version))) {
319  best = c;
320  }
321  }
322 
324  return BaseMedia<Tbase_set>::used_set != NULL;
325 }
326 
Slot for the sound.
Definition: fios.h:90
int virtual_left
Virtual left coordinate.
Definition: viewport_type.h:30
Begin for iteration.
Definition: zoom_type.h:23
Generic function implementations for base data (graphics, sounds).
static Point RemapCoords(int x, int y, int z)
Map 3D world or tile coordinate to equivalent 2D coordinate as used in the viewports and smallmap...
Definition: landscape.h:83
static const char * GetExtension()
Get the extension that is used to identify this set.
Definition: gfxinit.cpp:456
uint16 FioReadWord()
Read a word (16 bits) from the file (in low endian format).
Definition: fileio.cpp:166
static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
Decide &#39;where&#39; (between left and right speaker) to play the sound effect.
Definition: sound.cpp:239
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition: sound_type.h:118
int virtual_height
height << zoom
Definition: viewport_type.h:33
MusicSettings music
settings related to music/sound
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:207
Vehicle data structure.
Definition: vehicle_base.h:212
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:118
byte FioReadByte()
Read a byte from the file.
Definition: fileio.cpp:133
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
Functions, definitions and such used only by the GUI.
Data structure for an opened window.
Definition: window_gui.h:271
Number of zoom levels.
Definition: zoom_type.h:32
bool LoadNewGRFSound(SoundEntry *sound)
Extract meta data from a NewGRF sound.
static const char *const _sound_file_names[]
Names corresponding to the sound set&#39;s files.
Definition: sound.cpp:292
ClientSettings _settings_client
The current settings for this game.
Definition: settings.cpp:76
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
void FioSeekTo(size_t pos, int mode)
Seek in the current file.
Definition: fileio.cpp:88
void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
Set volume and pan parameters for a sound.
Definition: mixer.cpp:198
void FioSeekToFile(uint8 slot, size_t pos)
Switch to a different file and seek to a position.
Definition: fileio.cpp:115
int virtual_width
width << zoom
Definition: viewport_type.h:32
void FioReadBlock(void *ptr, size_t size)
Read a block.
Definition: fileio.cpp:187
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:42
SoundEntry * AllocateSound(uint num)
Allocate sound slots.
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:39
void FioOpenFile(int slot, const char *filename, Subdirectory subdir)
Open a slotted file.
Definition: fileio.cpp:250
uint32 FioReadDword()
Read a double word (32 bits) from the file (in low endian format).
Definition: fileio.cpp:176
static T ClrBit(T &x, const uint8 y)
Clears a bit in a variable.
Base class for all vehicles.
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:24
Declarations for savegames operations.
uint32 TileIndex
The index/ID of a Tile.
Definition: tile_type.h:80
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:217
All data of a sounds set.
#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type)
Force instantiation of methods so we don&#39;t get linker errors.
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
static uint MapMaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition: map_func.h:113
Functions related to OTTD&#39;s landscape.
static uint16 BSWAP16(uint16 x)
Perform a 16 bits endianness bitswap on x.
Information about a single base set.
Coordinates of a point in 2D.
Functions to mix sound samples.
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:35
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
Functions related to NewGRF provided sounds.
int virtual_top
Virtual top coordinate.
Definition: viewport_type.h:31
static uint MapMaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition: map_func.h:104
size_t FioGetPos()
Get position in the current file.
Definition: fileio.cpp:68
ViewportData * viewport
Pointer to viewport data, if present.
Definition: window_gui.h:321
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Definition: vehicle_base.h:245
byte effect_vol
The requested effects volume.
static const SoundsSet * GetUsedSet()
Return the used set.
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
Definition: gfxinit.cpp:432