OpenTTD Source  20240917-master-g9ab0a47812
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 
TileY
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition: map_func.h:437
AllocateSound
SoundEntry * AllocateSound(uint num)
Allocate sound slots.
Definition: newgrf_sound.cpp:31
InvalidateWindowData
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:3208
base_media_func.h
BaseMedia::DetermineBestSet
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
Definition: gfxinit.cpp:469
SoundsSet
All data of a sounds set.
Definition: base_media_base.h:294
BaseMedia< SoundsSet >::GetIndexOfUsedSet
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
Definition: base_media_func.h:363
BaseMedia< SoundsSet >::SetSet
static bool SetSet(const SoundsSet *set)
Set the set to be used.
Definition: base_media_func.h:241
Map::MaxX
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition: map_func.h:297
ChangeSoundSet
void ChangeSoundSet(int index)
Change the configured sound set and reset sounds.
Definition: sound.cpp:241
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:123
ZOOM_LVL_END
@ ZOOM_LVL_END
End for iteration.
Definition: zoom_type.h:25
GB
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.
Definition: bitmath_func.hpp:32
vehicle_base.h
TILE_SIZE
static const uint TILE_SIZE
Tile size in world coordinates.
Definition: tile_type.h:15
StrongType::Typedef< uint32_t, struct TileIndexTag, StrongType::Compare, StrongType::Integer, StrongType::Compatible< int32_t >, StrongType::Compatible< int64_t > >
Vehicle
Vehicle data structure.
Definition: vehicle_base.h:244
Viewport::virtual_top
int virtual_top
Virtual top coordinate.
Definition: viewport_type.h:29
RandomAccessFile::ReadBlock
void ReadBlock(void *ptr, size_t size)
Read a block.
Definition: random_access_file.cpp:145
GetSlopePixelZ
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition: landscape.cpp:303
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
OpenBankFile
static void OpenBankFile(const std::string &filename)
Definition: sound.cpp:27
random_access_file_type.h
WN_GAME_OPTIONS_GAME_OPTIONS
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition: window_type.h:26
SoundEntry::grf_container_ver
uint8_t grf_container_ver
NewGRF container version if the sound is from a NewGRF.
Definition: sound_type.h:22
window_gui.h
Viewport
Data structure for viewport, display of a part of the world.
Definition: viewport_type.h:22
LoadNewGRFSound
bool LoadNewGRFSound(SoundEntry *sound)
Extract meta data from a NewGRF sound.
Definition: newgrf_sound.cpp:66
Viewport::virtual_left
int virtual_left
Virtual left coordinate.
Definition: viewport_type.h:28
BaseMedia< SoundsSet >
safeguards.h
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition: stdafx.h:280
MxCloseAllChannels
void MxCloseAllChannels()
Close all mixer channels.
Definition: mixer.cpp:117
Viewport::virtual_width
int virtual_width
width << zoom
Definition: viewport_type.h:30
Point
Coordinates of a point in 2D.
Definition: geometry_type.hpp:21
SoundEntry
Definition: sound_type.h:13
stdafx.h
landscape.h
Window::AllWindows
Iterable ensemble of all valid Windows.
Definition: window_gui.h:916
INSTANTIATE_BASE_MEDIA_METHODS
#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type)
Force instantiation of methods so we don't get linker errors.
Definition: base_media_func.h:414
SndPlayScreenCoordFx
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
WC_GAME_OPTIONS
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Definition: window_type.h:618
BSWAP16
static uint16_t BSWAP16(uint16_t x)
Perform a 16 bits endianness bitswap on x.
Definition: bitmath_func.hpp:379
newgrf_sound.h
Map::MaxY
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition: map_func.h:306
MixerChannel
Definition: mixer.cpp:20
BaseSet
Information about a single base set.
Definition: base_media_base.h:49
MxSetChannelVolume
void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
Set volume and pan parameters for a sound.
Definition: mixer.cpp:208
ORIGINAL_SAMPLE_COUNT
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition: sound_type.h:116
Vehicle::coord
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Definition: vehicle_base.h:279
RandomAccessFile::SeekTo
void SeekTo(size_t pos, int mode)
Seek in the current file.
Definition: random_access_file.cpp:90
window_func.h
BaseMedia< SoundsSet >::GetSet
static const SoundsSet * GetSet(int index)
Get the name of the graphics set at the specified index.
Definition: base_media_func.h:379
Viewport::zoom
ZoomLevel zoom
The zoom level of the viewport.
Definition: viewport_type.h:33
_sound_file_names
static const char *const _sound_file_names[]
Names corresponding to the sound set's files.
Definition: sound.cpp:327
Window
Data structure for an opened window.
Definition: window_gui.h:276
BaseSounds::ini_set
static std::string ini_set
The set as saved in the config file.
Definition: base_media_base.h:301
BaseMedia< SoundsSet >::GetUsedSet
static const SoundsSet * GetUsedSet()
Return the used set.
Definition: base_media_func.h:394
TileX
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition: map_func.h:427
Viewport::virtual_height
int virtual_height
height << zoom
Definition: viewport_type.h:31
ClrBit
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Definition: bitmath_func.hpp:151
BaseMedia::GetExtension
static const char * GetExtension()
Get the extension that is used to identify this set.
Definition: gfxinit.cpp:493
RemapCoords
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
mixer.h
RandomAccessFile
A file from which bytes, words and double words are read in (potentially) a random order.
Definition: random_access_file_type.h:22
HasBit
constexpr debug_inline bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition: bitmath_func.hpp:103