OpenTTD Source: src/sound.cpp Source File

OpenTTD Source 15.0-beta1

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 "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
21/* The type of set we're replacing */
22#define SET_TYPE "sounds"
23#include "base_media_func.h"
24
25#include "safeguards.h"
26
27static std::array<SoundEntry, ORIGINAL_SAMPLE_COUNT> _original_sounds;
28
29 static void OpenBankFile(const std::string &filename)
30{
35 static std::unique_ptr<RandomAccessFile> original_sound_file;
36
37 _original_sounds.fill({});
38
39 /* If there is no sound file (nosound set), don't load anything */
40 if (filename.empty()) return;
41
42 original_sound_file = std::make_unique<RandomAccessFile>(filename, BASESET_DIR);
43 size_t pos = original_sound_file->GetPos();
44 uint count = original_sound_file->ReadDword();
45
46 /* The new format has the highest bit always set */
47 auto source = HasBit(count, 31) ? SoundSource::BasesetNewFormat : SoundSource::BasesetOldFormat;
48 ClrBit(count, 31);
49 count /= 8;
50
51 /* Simple check for the correct number of original sounds. */
52 if (count != ORIGINAL_SAMPLE_COUNT) {
53 /* Corrupt sample data? Just leave the allocated memory as those tell
54 * there is no sound to play (size = 0 due to calloc). Not allocating
55 * the memory disables valid NewGRFs that replace sounds. */
56 Debug(misc, 6, "Incorrect number of sounds in '{}', ignoring.", filename);
57 return;
58 }
59
60 original_sound_file->SeekTo(pos, SEEK_SET);
61
62 /* Read sound file positions. */
63 for (auto &sound : _original_sounds) {
64 sound.file = original_sound_file.get();
65 sound.file_offset = GB(original_sound_file->ReadDword(), 0, 31) + pos;
66 sound.file_size = original_sound_file->ReadDword();
67 sound.source = source;
68 }
69}
70
71static bool SetBankSource(MixerChannel *mc, SoundEntry *sound, SoundID sound_id)
72{
73 assert(sound != nullptr);
74
75 if (sound->file != nullptr) {
76 if (!LoadSound(*sound, sound_id)) {
77 /* Mark as invalid. */
78 sound->file = nullptr;
79 return false;
80 }
81 sound->file = nullptr;
82 }
83
84 /* Check for valid sound. */
85 if (sound->data->empty()) return false;
86
87 MxSetChannelRawSrc(mc, sound->data, sound->rate, sound->bits_per_sample == 16);
88
89 return true;
90}
91
92void InitializeSound()
93{
94 Debug(misc, 1, "Loading sound effects...");
95 OpenBankFile(BaseSounds::GetUsedSet()->files->filename);
96}
97
98
99/* Low level sound player */
100static void StartSound(SoundID sound_id, float pan, uint volume)
101{
102 if (volume == 0) return;
103
104 SoundEntry *sound = GetSound(sound_id);
105 if (sound == nullptr) return;
106
107 if (sound->rate == 0) {
108 /* If the sound's sample rate is not set then the sound needs to be loaded, but if the sound's file pointer
109 * is empty then an attempt was already made to load the sound but it failed. We don't want to try again. */
110 if (sound->file == nullptr) return;
111 }
112
113 MixerChannel *mc = MxAllocateChannel();
114 if (mc == nullptr) return;
115
116 if (!SetBankSource(mc, sound, sound_id)) return;
117
118 /* Apply the sound effect's own volume. */
119 volume = sound->volume * volume;
120
121 MxSetChannelVolume(mc, volume, pan);
122 MxActivateChannel(mc);
123}
124
125
126static const uint8_t _vol_factor_by_zoom[] = {255, 255, 255, 190, 134, 87};
127static_assert(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_END);
128
129static const uint8_t _sound_base_vol[] = {
130 128, 90, 128, 128, 128, 128, 128, 128,
131 128, 90, 90, 128, 128, 128, 128, 128,
132 128, 128, 128, 80, 128, 128, 128, 128,
133 128, 128, 128, 128, 128, 128, 128, 128,
134 128, 128, 90, 90, 90, 128, 90, 128,
135 128, 90, 128, 128, 128, 90, 128, 128,
136 128, 128, 128, 128, 90, 128, 128, 128,
137 128, 90, 128, 128, 128, 128, 128, 128,
138 128, 128, 90, 90, 90, 128, 128, 128,
139 90,
140};
141
142static const uint8_t _sound_idx[] = {
143 2, 3, 4, 5, 6, 7, 8, 9,
144 10, 11, 12, 13, 14, 15, 16, 17,
145 18, 19, 20, 21, 22, 23, 24, 25,
146 26, 27, 28, 29, 30, 31, 32, 33,
147 34, 35, 36, 37, 38, 39, 40, 0,
148 1, 41, 42, 43, 44, 45, 46, 47,
149 48, 49, 50, 51, 52, 53, 54, 55,
150 56, 57, 58, 59, 60, 61, 62, 63,
151 64, 65, 66, 67, 68, 69, 70, 71,
152 72,
153};
154
155void SndCopyToPool()
156{
157 SoundEntry *sound = AllocateSound(ORIGINAL_SAMPLE_COUNT);
158 for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
159 sound[i] = _original_sounds[_sound_idx[i]];
160 sound[i].volume = _sound_base_vol[i];
161 sound[i].priority = 0;
162 }
163}
164
169 void ChangeSoundSet(int index)
170{
171 if (BaseSounds::GetIndexOfUsedSet() == index) return;
172
173 auto set = BaseSounds::GetSet(index);
174 BaseSounds::ini_set = set->name;
175 BaseSounds::SetSet(set);
176
177 MxCloseAllChannels();
178 InitializeSound();
179
180 /* Replace baseset sounds in the pool with the updated original sounds. This is safe to do as
181 * any sound still playing holds its own shared_ptr to the sample data. */
182 for (uint i = 0; i < ORIGINAL_SAMPLE_COUNT; i++) {
183 SoundEntry *sound = GetSound(i);
184 /* GRF Container 0 means the sound comes from the baseset, and isn't overridden by NewGRF. */
185 if (sound == nullptr || sound->grf_container_ver != 0) continue;
186
187 *sound = _original_sounds[_sound_idx[i]];
188 sound->volume = _sound_base_vol[i];
189 sound->priority = 0;
190 }
191
192 InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true);
193}
194
204 static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, int bottom)
205{
206 /* Iterate from back, so that main viewport is checked first */
207 for (const Window *w : Window::IterateFromBack()) {
208 const Viewport *vp = w->viewport;
209
210 if (vp != nullptr &&
211 left < vp->virtual_left + vp->virtual_width && right > vp->virtual_left &&
212 top < vp->virtual_top + vp->virtual_height && bottom > vp->virtual_top) {
213 int screen_x = (left + right) / 2 - vp->virtual_left;
214 int width = (vp->virtual_width == 0 ? 1 : vp->virtual_width);
215 float panning = (float)screen_x / width;
216
217 StartSound(
218 sound,
219 panning,
220 _vol_factor_by_zoom[vp->zoom]
221 );
222 return;
223 }
224 }
225}
226
227void SndPlayTileFx(SoundID sound, TileIndex tile)
228{
229 /* emits sound from center of the tile */
230 int x = std::min(Map::MaxX() - 1, TileX(tile)) * TILE_SIZE + TILE_SIZE / 2;
231 int y = std::min(Map::MaxY() - 1, TileY(tile)) * TILE_SIZE - TILE_SIZE / 2;
232 int z = (y < 0 ? 0 : GetSlopePixelZ(x, y));
233 Point pt = RemapCoords(x, y, z);
234 y += 2 * TILE_SIZE;
235 Point pt2 = RemapCoords(x, y, GetSlopePixelZ(x, y));
236 SndPlayScreenCoordFx(sound, pt.x, pt2.x, pt.y, pt2.y);
237}
238
239void SndPlayVehicleFx(SoundID sound, const Vehicle *v)
240{
241 SndPlayScreenCoordFx(sound,
242 v->coord.left, v->coord.right,
243 v->coord.top, v->coord.bottom
244 );
245}
246
247void SndPlayFx(SoundID sound)
248{
249 StartSound(sound, 0.5, UINT8_MAX);
250}
251
252 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<SoundsSet>, SoundsSet)
253
254
255static const char * const _sound_file_names[] = { "samples" };
256
257
258template <class T, size_t Tnum_files, bool Tsearch_in_tars>
259/* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _sound_file_names;
260
261template <class Tbase_set>
262/* static */ const char *BaseMedia<Tbase_set>::GetExtension()
263{
264 return ".obs"; // OpenTTD Base Sounds
265}
266
267template <class Tbase_set>
268/* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
269{
270 if (BaseMedia<Tbase_set>::used_set != nullptr) return true;
271
272 const Tbase_set *best = nullptr;
273 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
274 /* Skip unusable sets */
275 if (c->GetNumMissing() != 0) continue;
276
277 if (best == nullptr ||
278 (best->fallback && !c->fallback) ||
279 best->valid_files < c->valid_files ||
280 (best->valid_files == c->valid_files &&
281 (best->shortname == c->shortname && best->version < c->version))) {
282 best = c;
283 }
284 }
285
286 BaseMedia<Tbase_set>::used_set = best;
287 return BaseMedia<Tbase_set>::used_set != nullptr;
288}
289
base_media_func.h
Generic function implementations for base data (graphics, sounds).
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
HasBit
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
GB
debug_inline 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
ClrBit
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
Definition bitmath_func.hpp:151
BaseMedia
Base for all base media (graphics, sounds)
Definition base_media_base.h:176
BaseMedia::DetermineBestSet
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
Definition gfxinit.cpp:466
BaseMedia< SoundsSet >::GetUsedSet
static const SoundsSet * GetUsedSet()
Return the used set.
Definition base_media_func.h:394
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
BaseMedia< SoundsSet >::GetIndexOfUsedSet
static int GetIndexOfUsedSet()
Get the index of the currently active graphics set.
Definition base_media_func.h:363
BaseMedia::GetExtension
static const char * GetExtension()
Get the extension that is used to identify this set.
Definition gfxinit.cpp:490
BaseMedia< SoundsSet >::SetSet
static bool SetSet(const SoundsSet *set)
Set the set to be used.
Definition base_media_func.h:241
BaseSounds::ini_set
static std::string ini_set
The set as saved in the config file.
Definition base_media_base.h:301
Debug
#define Debug(category, level, format_string,...)
Ouptut a line of debugging information.
Definition debug.h:37
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition fileio_type.h:123
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
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:79
TileY
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
TileX
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
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.
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:204
_sound_file_names
static const char *const _sound_file_names[]
Names corresponding to the sound set's files.
Definition sound.cpp:255
OpenBankFile
static void OpenBankFile(const std::string &filename)
Definition sound.cpp:29
ChangeSoundSet
void ChangeSoundSet(int index)
Change the configured sound set and reset sounds.
Definition sound.cpp:169
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
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:280
BaseSet
Information about a single base set.
Definition base_media_base.h:49
Map::MaxY
static uint MaxY()
Gets the maximum Y coordinate within the map, including MP_VOID.
Definition map_func.h:306
Map::MaxX
static debug_inline uint MaxX()
Gets the maximum X coordinate within the map, including MP_VOID.
Definition map_func.h:297
MixerChannel
Definition mixer.cpp:20
Point
Coordinates of a point in 2D.
Definition geometry_type.hpp:21
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
SoundsSet
All data of a sounds set.
Definition base_media_base.h:294
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
Vehicle::coord
Rect coord
NOSAVE: Graphical bounding box of the vehicle, i.e. what to redraw on moves.
Definition vehicle_base.h:279
Viewport
Data structure for viewport, display of a part of the world.
Definition viewport_type.h:22
Viewport::zoom
ZoomLevel zoom
The zoom level of the viewport.
Definition viewport_type.h:33
Viewport::virtual_top
int virtual_top
Virtual top coordinate.
Definition viewport_type.h:29
Viewport::virtual_left
int virtual_left
Virtual left coordinate.
Definition viewport_type.h:28
Viewport::virtual_width
int virtual_width
width << zoom
Definition viewport_type.h:30
Viewport::virtual_height
int virtual_height
height << zoom
Definition viewport_type.h:31
Window
Data structure for an opened window.
Definition window_gui.h:273
Window::IterateFromBack
AllWindows< false > IterateFromBack
Iterate all windows in Z order from back to front.
Definition window_gui.h:928
TILE_SIZE
static const 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:3219
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:26
WC_GAME_OPTIONS
@ WC_GAME_OPTIONS
Game options window; Window numbers:
Definition window_type.h:624
ZOOM_LVL_END
@ ZOOM_LVL_END
End for iteration.
Definition zoom_type.h:25