OpenTTD Source  20241121-master-g67a0fccfad
sound.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
10 #include "stdafx.h"
11 #include "landscape.h"
12 #include "mixer.h"
13 #include "newgrf_sound.h"
15 #include "window_func.h"
16 #include "window_gui.h"
17 #include "vehicle_base.h"
18 
19 /* The type of set we're replacing */
20 #define SET_TYPE "sounds"
21 #include "base_media_func.h"
22 
23 #include "safeguards.h"
24 
25 static SoundEntry _original_sounds[ORIGINAL_SAMPLE_COUNT];
26 
27 static void OpenBankFile(const std::string &filename)
28 {
33  static std::unique_ptr<RandomAccessFile> original_sound_file;
34 
35  memset(_original_sounds, 0, sizeof(_original_sounds));
36 
37  /* If there is no sound file (nosound set), don't load anything */
38  if (filename.empty()) return;
39 
40  original_sound_file.reset(new RandomAccessFile(filename, BASESET_DIR));
41  size_t pos = original_sound_file->GetPos();
42  uint count = original_sound_file->ReadDword();
43 
44  /* The new format has the highest bit always set */
45  bool new_format = HasBit(count, 31);
46  ClrBit(count, 31);
47  count /= 8;
48 
49  /* Simple check for the correct number of original sounds. */
50  if (count != ORIGINAL_SAMPLE_COUNT) {
51  /* Corrupt sample data? Just leave the allocated memory as those tell
52  * there is no sound to play (size = 0 due to calloc). Not allocating
53  * the memory disables valid NewGRFs that replace sounds. */
54  Debug(misc, 6, "Incorrect number of sounds in '{}', ignoring.", filename);
55  return;
56  }
57 
58  original_sound_file->SeekTo(pos, SEEK_SET);
59 
60  for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
61  _original_sounds[i].file = original_sound_file.get();
62  _original_sounds[i].file_offset = GB(original_sound_file->ReadDword(), 0, 31) + pos;
63  _original_sounds[i].file_size = original_sound_file->ReadDword();
64  }
65 
66  for (uint i = 0; i != ORIGINAL_SAMPLE_COUNT; i++) {
67  SoundEntry *sound = &_original_sounds[i];
68  char name[255];
69 
70  original_sound_file->SeekTo(sound->file_offset, SEEK_SET);
71 
72  /* Check for special case, see else case */
73  original_sound_file->ReadBlock(name, original_sound_file->ReadByte()); // Read the name of the sound
74  if (new_format || strcmp(name, "Corrupt sound") != 0) {
75  original_sound_file->SeekTo(12, SEEK_CUR); // Skip past RIFF header
76 
77  /* Read riff tags */
78  for (;;) {
79  uint32_t tag = original_sound_file->ReadDword();
80  uint32_t size = original_sound_file->ReadDword();
81 
82  if (tag == ' tmf') {
83  original_sound_file->ReadWord(); // wFormatTag
84  sound->channels = original_sound_file->ReadWord(); // wChannels
85  sound->rate = original_sound_file->ReadDword(); // samples per second
86  if (!new_format) sound->rate = 11025; // seems like all old samples should be played at this rate.
87  original_sound_file->ReadDword(); // avg bytes per second
88  original_sound_file->ReadWord(); // alignment
89  sound->bits_per_sample = original_sound_file->ReadByte(); // bits per sample
90  original_sound_file->SeekTo(size - (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR);
91  } else if (tag == 'atad') {
92  sound->file_size = size;
93  sound->file = original_sound_file.get();
94  sound->file_offset = original_sound_file->GetPos();
95  break;
96  } else {
97  sound->file_size = 0;
98  break;
99  }
100  }
101  } else {
102  /*
103  * Special case for the jackhammer sound
104  * (name in sample.cat is "Corrupt sound")
105  * It's no RIFF file, but raw PCM data
106  */
107  sound->channels = 1;
108  sound->rate = 11025;
109  sound->bits_per_sample = 8;
110  sound->file = original_sound_file.get();
111  sound->file_offset = original_sound_file->GetPos();
112  }
113  }
114 }
115 
116 static bool SetBankSource(MixerChannel *mc, const SoundEntry *sound)
117 {
118  assert(sound != nullptr);
119 
120  /* Check for valid sound size. */
121  if (sound->file_size == 0 || sound->file_size > SIZE_MAX - 2) return false;
122 
123  int8_t *mem = MallocT<int8_t>(sound->file_size + 2);
124  /* Add two extra bytes so rate conversion can read these
125  * without reading out of its input buffer. */
126  mem[sound->file_size ] = 0;
127  mem[sound->file_size + 1] = 0;
128 
129  RandomAccessFile *file = sound->file;
130  file->SeekTo(sound->file_offset, SEEK_SET);
131  file->ReadBlock(mem, sound->file_size);
132 
133  /* 16-bit PCM WAV files should be signed by default */
134  if (sound->bits_per_sample == 8) {
135  for (uint i = 0; i != sound->file_size; i++) {
136  mem[i] += -128; // Convert unsigned sound data to signed
137  }
138  }
139 
140  if constexpr (std::endian::native == std::endian::big) {
141  if (sound->bits_per_sample == 16) {
142  size_t num_samples = sound->file_size / 2;
143  int16_t *samples = reinterpret_cast<int16_t *>(mem);
144  for (size_t i = 0; i < num_samples; i++) {
145  samples[i] = BSWAP16(samples[i]);
146  }
147  }
148  }
149 
150  assert(sound->bits_per_sample == 8 || sound->bits_per_sample == 16);
151  assert(sound->channels == 1);
152  assert(sound->file_size != 0 && sound->rate != 0);
153 
154  MxSetChannelRawSrc(mc, mem, sound->file_size, sound->rate, sound->bits_per_sample == 16);
155 
156  return true;
157 }
158 
159 void InitializeSound()
160 {
161  Debug(misc, 1, "Loading sound effects...");
162  OpenBankFile(BaseSounds::GetUsedSet()->files->filename);
163 }
164 
165 /* Low level sound player */
166 static void StartSound(SoundID sound_id, float pan, uint volume)
167 {
168  if (volume == 0) return;
169 
170  SoundEntry *sound = GetSound(sound_id);
171  if (sound == nullptr) return;
172 
173  /* NewGRF sound that wasn't loaded yet? */
174  if (sound->rate == 0 && sound->file != nullptr) {
175  if (!LoadNewGRFSound(sound)) {
176  /* Mark as invalid. */
177  sound->file = nullptr;
178  return;
179  }
180  }
181 
182  /* Empty sound? */
183  if (sound->rate == 0) return;
184 
185  MixerChannel *mc = MxAllocateChannel();
186  if (mc == nullptr) return;
187 
188  if (!SetBankSource(mc, sound)) return;
189 
190  /* Apply the sound effect's own volume. */
191  volume = sound->volume * volume;
192 
193  MxSetChannelVolume(mc, volume, pan);
194  MxActivateChannel(mc);
195 }
196 
197 
198 static const uint8_t _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87};
199 static_assert(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_END);
200 
201 static const uint8_t _sound_base_vol[] = {
202  128, 90, 128, 128, 128, 128, 128, 128,
203  128, 90, 90, 128, 128, 128, 128, 128,
204  128, 128, 128, 80, 128, 128, 128, 128,
205  128, 128, 128, 128, 128, 128, 128, 128,
206  128, 128, 90, 90, 90, 128, 90, 128,
207  128, 90, 128, 128, 128, 90, 128, 128,
208  128, 128, 128, 128, 90, 128, 128, 128,
209  128, 90, 128, 128, 128, 128, 128, 128,
210  128, 128, 90, 90, 90, 128, 128, 128,
211  90,
212 };
213 
214 static const uint8_t _sound_idx[] = {
215  2, 3, 4, 5, 6, 7, 8, 9,
216  10, 11, 12, 13, 14, 15, 16, 17,
217  18, 19, 20, 21, 22, 23, 24, 25,
218  26, 27, 28, 29, 30, 31, 32, 33,
219  34, 35, 36, 37, 38, 39, 40, 0,
220  1, 41, 42, 43, 44, 45, 46, 47,
221  48, 49, 50, 51, 52, 53, 54, 55,
222  56, 57, 58, 59, 60, 61, 62, 63,
223  64, 65, 66, 67, 68, 69, 70, 71,
224  72,
225 };
226 
227 void SndCopyToPool()
228 {
230  for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
231  sound[i] = _original_sounds[_sound_idx[i]];
232  sound[i].volume = _sound_base_vol[i];
233  sound[i].priority = 0;
234  }
235 }
236 
241 void ChangeSoundSet(int index)
242 {
243  if (BaseSounds::GetIndexOfUsedSet() == index) return;
244 
245  auto set = BaseSounds::GetSet(index);
246  BaseSounds::ini_set = set->name;
247  BaseSounds::SetSet(set);
248 
250  InitializeSound();
251 
252  /* Replace baseset sounds in the pool with the updated original sounds. This is safe to do as
253  * any sound still playing owns its sample data. */
254  for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
255  SoundEntry *sound = GetSound(i);
256  /* GRF Container 0 means the sound comes from the baseset, and isn't overridden by NewGRF. */
257  if (sound == nullptr || sound->grf_container_ver != 0) continue;
258 
259  *sound = _original_sounds[_sound_idx[i]];
260  sound->volume = _sound_base_vol[i];
261  sound->priority = 0;
262  }
263 
265 }
266 
276 static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
277 {
278  /* Iterate from back, so that main viewport is checked first */
279  for (const Window *w : Window::IterateFromBack()) {
280  const Viewport *vp = w->viewport;
281 
282  if (vp != nullptr &&
283  left < vp->virtual_left + vp->virtual_width && right > vp->virtual_left &&
284  top < vp->virtual_top + vp->virtual_height && bottom > vp->virtual_top) {
285  int screen_x = (left + right) / 2 - vp->virtual_left;
286  int width = (vp->virtual_width == 0 ? 1 : vp->virtual_width);
287  float panning = (float)screen_x / width;
288 
289  StartSound(
290  sound,
291  panning,
292  _vol_factor_by_zoom[vp->zoom]
293  );
294  return;
295  }
296  }
297 }
298 
299 void SndPlayTileFx(SoundID sound, TileIndex tile)
300 {
301  /* emits sound from center of the tile */
302  int x = std::min(Map::MaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
303  int y = std::min(Map::MaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
304  int z = (y < 0 ? 0 : GetSlopePixelZ(x, y));
305  Point pt = RemapCoords(x, y, z);
306  y += 2 * TILE_SIZE;
307  Point pt2 = RemapCoords(x, y, GetSlopePixelZ(x, y));
308  SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
309 }
310 
311 void SndPlayVehicleFx(SoundID sound, const Vehicle *v)
312 {
313  SndPlayScreenCoordFx(sound,
314  v->coord.left, v->coord.right,
315  v->coord.top, v->coord.bottom
316  );
317 }
318 
319 void SndPlayFx(SoundID sound)
320 {
321  StartSound(sound, 0.5, UINT8_MAX);
322 }
323 
325 
326 
327 static const char * const _sound_file_names[] = { "samples" };
328 
329 
330 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
332 
333 template <class Tbase_set>
334 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
335 {
336  return ".obs"; // OpenTTD Base Sounds
337 }
338 
339 template <class Tbase_set>
340 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
341 {
342  if (BaseMedia<Tbase_set>::used_set != nullptr) return true;
343 
344  const Tbase_set *best = nullptr;
345  for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
346  /* Skip unusable sets */
347  if (c->GetNumMissing() != 0) continue;
348 
349  if (best == nullptr ||
350  (best->fallback && !c->fallback) ||
351  best->valid_files < c->valid_files ||
352  (best->valid_files == c->valid_files &&
353  (best->shortname == c->shortname && best->version < c->version))) {
354  best = c;
355  }
356  }
357 
359  return BaseMedia<Tbase_set>::used_set != nullptr;
360 }
361 
Generic function implementations for base data (graphics, sounds).
#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type)
Force instantiation of methods so we don't get linker errors.
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr static debug_inline uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
static uint16_t BSWAP16(uint16_t x)
Perform a 16 bits endianness bitswap on x.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
Definition: gfxinit.cpp:466
static const SoundsSet * GetUsedSet()
Return the used set.
static const SoundsSet * GetSet(int index)
Get the name of the graphics set at the specified index.
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
static const char * GetExtension()
Get the extension that is used to identify this set.
Definition: gfxinit.cpp:490
static bool SetSet(const SoundsSet *set)
Set the set to be used.
static std::string ini_set
The set as saved in the config file.
A file from which bytes, words and double words are read in (potentially) a random order.
void ReadBlock(void *ptr, size_t size)
Read a block.
void SeekTo(size_t pos, int mode)
Seek in the current file.
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:123
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition: landscape.cpp:303
Functions related to OTTD's landscape.
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:79
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:415
void MxCloseAllChannels()
Close all mixer channels.
Definition: mixer.cpp:117
void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
Set volume and pan parameters for a sound.
Definition: mixer.cpp:208
Functions to mix sound samples.
bool LoadNewGRFSound(SoundEntry *sound)
Extract meta data from a NewGRF sound.
SoundEntry * AllocateSound(uint num)
Allocate sound slots.
Functions related to NewGRF provided sounds.
Class related to random access to files.
A number of safeguards to prevent using unsafe methods.
static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
Decide 'where' (between left and right speaker) to play the sound effect.
Definition: sound.cpp:276
static const char *const _sound_file_names[]
Names corresponding to the sound set's files.
Definition: sound.cpp:327
static void OpenBankFile(const std::string &filename)
Definition: sound.cpp:27
void ChangeSoundSet(int index)
Change the configured sound set and reset sounds.
Definition: sound.cpp:241
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition: sound_type.h:116
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280
Information about a single base set.
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition: map_func.h:306
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition: map_func.h:297
Coordinates of a point in 2D.
uint8_t grf_container_ver
NewGRF container version if the sound is from a NewGRF.
Definition: sound_type.h:22
All data of a sounds set.
Vehicle data structure.
Definition: vehicle_base.h:244
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Definition: vehicle_base.h:279
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
int virtual_top
Virtual top coordinate.
Definition: viewport_type.h:29
int virtual_left
Virtual left coordinate.
Definition: viewport_type.h:28
int virtual_width
width << zoom
Definition: viewport_type.h:30
int virtual_height
height << zoom
Definition: viewport_type.h:31
Iterable ensemble of all valid Windows.
Definition: window_gui.h:913
Data structure for an opened window.
Definition: window_gui.h:273
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
Base class for all vehicles.
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition: window.cpp:3211
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition: window_type.h:26
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Definition: window_type.h:624
@ ZOOM_LVL_END
End for iteration.
Definition: zoom_type.h:25