OpenTTD Source: src/sound.cpp Source File

OpenTTD Source 15.0-RC2

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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "stdafx.h"
11#include "landscape.h"
12#include "sound_type.h"
13#include "soundloader_func.h"
14#include "mixer.h"
15#include "newgrf_sound.h"
16#include "random_access_file_type.h"
17#include "window_func.h"
18#include "window_gui.h"
19#include "vehicle_base.h"
20#include "base_media_func.h"
21#include "base_media_sounds.h"
22
23#include "safeguards.h"
24
25static std::array<SoundEntry, ORIGINAL_SAMPLE_COUNT> _original_sounds;
26
27 static void OpenBankFile(const std::string &filename)
28{
33 static std::unique_ptr<RandomAccessFile> original_sound_file;
34
35 _original_sounds.fill({});
36
37 /* If there is no sound file (nosound set), don't load anything */
38 if (filename.empty()) return;
39
40 original_sound_file = std::make_unique<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 auto source = HasBit(count, 31) ? SoundSource::BasesetNewFormat : SoundSource::BasesetOldFormat;
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 /* Read sound file positions. */
61 for (auto &sound : _original_sounds) {
62 sound.file = original_sound_file.get();
63 sound.file_offset = GB(original_sound_file->ReadDword(), 0, 31) + pos;
64 sound.file_size = original_sound_file->ReadDword();
65 sound.source = source;
66 }
67}
68
69static bool SetBankSource(MixerChannel *mc, SoundEntry *sound, SoundID sound_id)
70{
71 assert(sound != nullptr);
72
73 if (sound->file != nullptr) {
74 if (!LoadSound(*sound, sound_id)) {
75 /* Mark as invalid. */
76 sound->file = nullptr;
77 return false;
78 }
79 sound->file = nullptr;
80 }
81
82 /* Check for valid sound. */
83 if (sound->data->empty()) return false;
84
85 MxSetChannelRawSrc(mc, sound->data, sound->rate, sound->bits_per_sample == 16);
86
87 return true;
88}
89
90void InitializeSound()
91{
92 Debug(misc, 1, "Loading sound effects...");
93 OpenBankFile(BaseSounds::GetUsedSet()->files[0].filename);
94}
95
96
97/* Low level sound player */
98static void StartSound(SoundID sound_id, float pan, uint volume)
99{
100 if (volume == 0) return;
101
102 SoundEntry *sound = GetSound(sound_id);
103 if (sound == nullptr) return;
104
105 if (sound->rate == 0) {
106 /* If the sound's sample rate is not set then the sound needs to be loaded, but if the sound's file pointer
107 * is empty then an attempt was already made to load the sound but it failed. We don't want to try again. */
108 if (sound->file == nullptr) return;
109 }
110
111 MixerChannel *mc = MxAllocateChannel();
112 if (mc == nullptr) return;
113
114 if (!SetBankSource(mc, sound, sound_id)) return;
115
116 /* Apply the sound effect's own volume. */
117 volume = sound->volume * volume;
118
119 MxSetChannelVolume(mc, volume, pan);
120 MxActivateChannel(mc);
121}
122
123
124static const uint8_t _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87};
125static_assert(lengthof(_vol_factor_by_zoom) == to_underlying(ZoomLevel::End));
126
127static const uint8_t _sound_base_vol[] = {
128 128, 90, 128, 128, 128, 128, 128, 128,
129 128, 90, 90, 128, 128, 128, 128, 128,
130 128, 128, 128, 80, 128, 128, 128, 128,
131 128, 128, 128, 128, 128, 128, 128, 128,
132 128, 128, 90, 90, 90, 128, 90, 128,
133 128, 90, 128, 128, 128, 90, 128, 128,
134 128, 128, 128, 128, 90, 128, 128, 128,
135 128, 90, 128, 128, 128, 128, 128, 128,
136 128, 128, 90, 90, 90, 128, 128, 128,
137 90,
138};
139
140static const uint8_t _sound_idx[] = {
141 2, 3, 4, 5, 6, 7, 8, 9,
142 10, 11, 12, 13, 14, 15, 16, 17,
143 18, 19, 20, 21, 22, 23, 24, 25,
144 26, 27, 28, 29, 30, 31, 32, 33,
145 34, 35, 36, 37, 38, 39, 40, 0,
146 1, 41, 42, 43, 44, 45, 46, 47,
147 48, 49, 50, 51, 52, 53, 54, 55,
148 56, 57, 58, 59, 60, 61, 62, 63,
149 64, 65, 66, 67, 68, 69, 70, 71,
150 72,
151};
152
153void SndCopyToPool()
154{
155 SoundEntry *sound = AllocateSound(ORIGINAL_SAMPLE_COUNT);
156 for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
157 sound[i] = _original_sounds[_sound_idx[i]];
158 sound[i].volume = _sound_base_vol[i];
159 sound[i].priority = 0;
160 }
161}
162
167 void ChangeSoundSet(int index)
168{
169 if (BaseSounds::GetIndexOfUsedSet() == index) return;
170
171 auto set = BaseSounds::GetSet(index);
172 BaseSounds::ini_set = set->name;
173 BaseSounds::SetSet(set);
174
175 MxCloseAllChannels();
176 InitializeSound();
177
178 /* Replace baseset sounds in the pool with the updated original sounds. This is safe to do as
179 * any sound still playing holds its own shared_ptr to the sample data. */
180 for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
181 SoundEntry *sound = GetSound(i);
182 /* GRF Container 0 means the sound comes from the baseset, and isn't overridden by NewGRF. */
183 if (sound == nullptr || sound->grf_container_ver != 0) continue;
184
185 *sound = _original_sounds[_sound_idx[i]];
186 sound->volume = _sound_base_vol[i];
187 sound->priority = 0;
188 }
189
190 InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
191}
192
202 static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
203{
204 /* Iterate from back, so that main viewport is checked first */
205 for (const Window *w : Window::IterateFromBack()) {
206 if (w->viewport == nullptr) continue;
207
208 const Viewport &vp = *w->viewport;
209 if (left < vp.virtual_left + vp.virtual_width && right > vp.virtual_left &&
210 top < vp.virtual_top + vp.virtual_height && bottom > vp.virtual_top) {
211 int screen_x = (left + right) / 2 - vp.virtual_left;
212 int width = (vp.virtual_width == 0 ? 1 : vp.virtual_width);
213 float panning = (float)screen_x / width;
214
215 StartSound(
216 sound,
217 panning,
218 _vol_factor_by_zoom[to_underlying(vp.zoom)]
219 );
220 return;
221 }
222 }
223}
224
225void SndPlayTileFx(SoundID sound, TileIndex tile)
226{
227 /* emits sound from center of the tile */
228 int x = std::min(Map::MaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
229 int y = std::min(Map::MaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
230 int z = (y < 0 ? 0 : GetSlopePixelZ(x, y));
231 Point pt = RemapCoords(x, y, z);
232 y += 2 * TILE_SIZE;
233 Point pt2 = RemapCoords(x, y, GetSlopePixelZ(x, y));
234 SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
235}
236
237void SndPlayVehicleFx(SoundID sound, const Vehicle *v)
238{
239 SndPlayScreenCoordFx(sound,
240 v->coord.left, v->coord.right,
241 v->coord.top, v->coord.bottom
242 );
243}
244
245void SndPlayFx(SoundID sound)
246{
247 StartSound(sound, 0.5, UINT8_MAX);
248}
249
253 void SndClickBeep()
254{
255 if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
256}
257
261 void SndConfirmBeep()
262{
263 if (_settings_client.sound.confirm) SndPlayFx(SND_15_BEEP);
264}
265
267 static const std::string_view _sound_file_names[] = { "samples" };
268
269template <>
270/* static */ std::span<const std::string_view> BaseSet<SoundsSet>::GetFilenames()
271{
272 return _sound_file_names;
273}
274
275template <>
276/* static */ std::string_view BaseMedia<SoundsSet>::GetExtension()
277{
278 return ".obs"; // OpenTTD Base Sounds
279}
280
281template <>
282/* static */ bool BaseMedia<SoundsSet>::DetermineBestSet()
283{
284 if (BaseMedia<SoundsSet>::used_set != nullptr) return true;
285
286 const SoundsSet *best = nullptr;
287 for (const auto &c : BaseMedia<SoundsSet>::available_sets) {
288 /* Skip unusable sets */
289 if (c->GetNumMissing() != 0) continue;
290
291 if (best == nullptr ||
292 (best->fallback && !c->fallback) ||
293 best->valid_files < c->valid_files ||
294 (best->valid_files == c->valid_files &&
295 (best->shortname == c->shortname && best->version < c->version))) {
296 best = c.get();
297 }
298 }
299
300 BaseMedia<SoundsSet>::used_set = best;
301 return BaseMedia<SoundsSet>::used_set != nullptr;
302}
303
304template class BaseMedia<SoundsSet>;
base_media_func.h
Generic function implementations for base data (graphics, sounds).
base_media_sounds.h
Generic functions for replacing base sounds data.
GB
static constexpr 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
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
ClrBit
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Definition bitmath_func.hpp:139
BaseMedia
Base for all base media (graphics, sounds)
Definition base_media_base.h:163
BaseMedia< SoundsSet >::GetUsedSet
static const SoundsSet * GetUsedSet()
Return the used set.
Definition base_media_func.h:400
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:385
BaseMedia< SoundsSet >::GetIndexOfUsedSet
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
Definition base_media_func.h:369
BaseMedia::DetermineBestSet
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
BaseMedia< SoundsSet >::SetSet
static bool SetSet(const SoundsSet *set)
Set the set to be used.
Definition base_media_func.h:250
BaseMedia::GetExtension
static std::string_view GetExtension()
Get the extension that is used to identify this set.
BaseSounds::ini_set
static std::string ini_set
The set as saved in the config file.
Definition base_media_sounds.h:28
Debug
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Definition debug.h:37
to_underlying
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23)
Definition enum_type.hpp:17
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition fileio_type.h:96
GetSlopePixelZ
int GetSlopePixelZ(int x, int y, bool ground_vehicle)
Return world Z coordinate of a given point of a tile.
Definition landscape.cpp:306
landscape.h
Functions related to OTTD's landscape.
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:81
TileY
static uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:437
TileX
static uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:427
MxCloseAllChannels
void MxCloseAllChannels()
Close all mixer channels.
Definition mixer.cpp:119
MxSetChannelVolume
void MxSetChannelVolume(MixerChannel *mc, uint volume, float pan)
Set volume and pan parameters for a sound.
Definition mixer.cpp:213
mixer.h
Functions to mix sound samples.
AllocateSound
SoundEntry * AllocateSound(uint num)
Allocate sound slots.
Definition newgrf_sound.cpp:33
newgrf_sound.h
Functions related to NewGRF provided sounds.
random_access_file_type.h
Class related to random access to files.
safeguards.h
A number of safeguards to prevent using unsafe methods.
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
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:202
_sound_file_names
static const std::string_view _sound_file_names[]
Names corresponding to the sound set's files.
Definition sound.cpp:267
SndConfirmBeep
void SndConfirmBeep()
Play a beep sound for a confirm event if enabled in settings.
Definition sound.cpp:261
OpenBankFile
static void OpenBankFile(const std::string &filename)
Definition sound.cpp:27
SndClickBeep
void SndClickBeep()
Play a beep sound for a click event if enabled in settings.
Definition sound.cpp:253
ChangeSoundSet
void ChangeSoundSet(int index)
Change the configured sound set and reset sounds.
Definition sound.cpp:167
sound_type.h
Types related to sounds.
ORIGINAL_SAMPLE_COUNT
static const uint ORIGINAL_SAMPLE_COUNT
The number of sounds in the original sample.cat.
Definition sound_type.h:124
SND_15_BEEP
@ SND_15_BEEP
19 == 0x13 GUI button click
Definition sound_type.h:66
soundloader_func.h
Functions related to sound loaders.
stdafx.h
Definition of base types and functions in a cross-platform compatible way.
lengthof
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
BaseSet::valid_files
uint valid_files
Number of the files that could be found and are valid.
Definition base_media_base.h:71
BaseSet::GetFilenames
static std::span< const std::string_view > GetFilenames()
Get the internal names of the files in this set.
BaseSet::fallback
bool fallback
This set is a fallback set, i.e. it should be used only as last resort.
Definition base_media_base.h:67
BaseSet::version
std::vector< uint32_t > version
The version of this base set.
Definition base_media_base.h:66
BaseSet::shortname
uint32_t shortname
Four letter short variant of the name.
Definition base_media_base.h:65
ClientSettings::sound
SoundSettings sound
sound effect settings
Definition settings_type.h:653
Coord2D< int >
Coord2D::y
T y
Y coordinate.
Definition geometry_type.hpp:35
Coord2D::x
T x
X coordinate.
Definition geometry_type.hpp:34
Map::MaxY
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:308
Map::MaxX
static uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:299
MixerChannel
Definition mixer.cpp:20
SoundEntry
Definition sound_type.h:19
SoundEntry::grf_container_ver
uint8_t grf_container_ver
NewGRF container version if the sound is from a NewGRF.
Definition sound_type.h:30
SoundSettings::click_beep
bool click_beep
Beep on a random selection of buttons.
Definition settings_type.h:276
SoundSettings::confirm
bool confirm
Play sound effect on successful constructions or other actions.
Definition settings_type.h:275
SoundsSet
All data of a sounds set.
Definition base_media_sounds.h:22
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:205
Vehicle::coord
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Definition vehicle_base.h:240
Viewport
Data structure for viewport, display of a part of the world.
Definition viewport_type.h:31
Viewport::zoom
ZoomLevel zoom
The zoom level of the viewport.
Definition viewport_type.h:42
Viewport::virtual_top
int virtual_top
Virtual top coordinate.
Definition viewport_type.h:38
Viewport::virtual_left
int virtual_left
Virtual left coordinate.
Definition viewport_type.h:37
Viewport::virtual_width
int virtual_width
width << zoom
Definition viewport_type.h:39
Window
Data structure for an opened window.
Definition window_gui.h:274
Window::IterateFromBack
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:934
TILE_SIZE
static constexpr uint TILE_SIZE
Tile size in world coordinates.
Definition tile_type.h:15
vehicle_base.h
Base class for all vehicles.
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:3294
window_func.h
Window functions not directly related to making/drawing windows.
window_gui.h
Functions, definitions and such used only by the GUI.
WN_GAME_OPTIONS_GAME_OPTIONS
@ WN_GAME_OPTIONS_GAME_OPTIONS
Game options.
Definition window_type.h:31
WC_GAME_OPTIONS
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Definition window_type.h:635
ZoomLevel::End
@ End
End for iteration.