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/>.
8 /** @file sound.cpp Handling of playing sounds. */
11 #include "landscape.h"
13 #include "newgrf_sound.h"
15 #include "window_gui.h"
16 #include "vehicle_base.h"
18 /* The type of set we're replacing */
19 #define SET_TYPE "sounds"
20 #include "base_media_func.h"
22 #include "safeguards.h"
24 static SoundEntry _original_sounds
[ORIGINAL_SAMPLE_COUNT
];
26 static void OpenBankFile(const char *filename
)
28 memset(_original_sounds
, 0, sizeof(_original_sounds
));
30 /* If there is no sound file (nosound set), don't load anything */
31 if (filename
== nullptr) return;
33 FioOpenFile(SOUND_SLOT
, filename
, BASESET_DIR
);
34 size_t pos
= FioGetPos();
35 uint count
= FioReadDword();
37 /* The new format has the highest bit always set */
38 bool new_format
= HasBit(count
, 31);
42 /* Simple check for the correct number of original sounds. */
43 if (count
!= ORIGINAL_SAMPLE_COUNT
) {
44 /* Corrupt sample data? Just leave the allocated memory as those tell
45 * there is no sound to play (size = 0 due to calloc). Not allocating
46 * the memory disables valid NewGRFs that replace sounds. */
47 DEBUG(misc
, 6, "Incorrect number of sounds in '%s', ignoring.", filename
);
51 FioSeekTo(pos
, SEEK_SET
);
53 for (uint i
= 0; i
!= ORIGINAL_SAMPLE_COUNT
; i
++) {
54 _original_sounds
[i
].file_slot
= SOUND_SLOT
;
55 _original_sounds
[i
].file_offset
= GB(FioReadDword(), 0, 31) + pos
;
56 _original_sounds
[i
].file_size
= FioReadDword();
59 for (uint i
= 0; i
!= ORIGINAL_SAMPLE_COUNT
; i
++) {
60 SoundEntry
*sound
= &_original_sounds
[i
];
63 FioSeekTo(sound
->file_offset
, SEEK_SET
);
65 /* Check for special case, see else case */
66 FioReadBlock(name
, FioReadByte()); // Read the name of the sound
67 if (new_format
|| strcmp(name
, "Corrupt sound") != 0) {
68 FioSeekTo(12, SEEK_CUR
); // Skip past RIFF header
72 uint32 tag
= FioReadDword();
73 uint32 size
= FioReadDword();
76 FioReadWord(); // wFormatTag
77 sound
->channels
= FioReadWord(); // wChannels
78 sound
->rate
= FioReadDword(); // samples per second
79 if (!new_format
) sound
->rate
= 11025; // seems like all old samples should be played at this rate.
80 FioReadDword(); // avg bytes per second
81 FioReadWord(); // alignment
82 sound
->bits_per_sample
= FioReadByte(); // bits per sample
83 FioSeekTo(size
- (2 + 2 + 4 + 4 + 2 + 1), SEEK_CUR
);
84 } else if (tag
== 'atad') {
85 sound
->file_size
= size
;
86 sound
->file_slot
= SOUND_SLOT
;
87 sound
->file_offset
= FioGetPos();
96 * Special case for the jackhammer sound
97 * (name in sample.cat is "Corrupt sound")
98 * It's no RIFF file, but raw PCM data
102 sound
->bits_per_sample
= 8;
103 sound
->file_slot
= SOUND_SLOT
;
104 sound
->file_offset
= FioGetPos();
109 static bool SetBankSource(MixerChannel
*mc
, const SoundEntry
*sound
)
111 assert(sound
!= nullptr);
113 /* Check for valid sound size. */
114 if (sound
->file_size
== 0 || sound
->file_size
> ((size_t)-1) - 2) return false;
116 int8
*mem
= MallocT
<int8
>(sound
->file_size
+ 2);
117 /* Add two extra bytes so rate conversion can read these
118 * without reading out of its input buffer. */
119 mem
[sound
->file_size
] = 0;
120 mem
[sound
->file_size
+ 1] = 0;
122 FioSeekToFile(sound
->file_slot
, sound
->file_offset
);
123 FioReadBlock(mem
, sound
->file_size
);
125 /* 16-bit PCM WAV files should be signed by default */
126 if (sound
->bits_per_sample
== 8) {
127 for (uint i
= 0; i
!= sound
->file_size
; i
++) {
128 mem
[i
] += -128; // Convert unsigned sound data to signed
132 #if TTD_ENDIAN == TTD_BIG_ENDIAN
133 if (sound
->bits_per_sample
== 16) {
134 uint num_samples
= sound
->file_size
/ 2;
135 int16
*samples
= (int16
*)mem
;
136 for (uint i
= 0; i
< num_samples
; i
++) {
137 samples
[i
] = BSWAP16(samples
[i
]);
142 assert(sound
->bits_per_sample
== 8 || sound
->bits_per_sample
== 16);
143 assert(sound
->channels
== 1);
144 assert(sound
->file_size
!= 0 && sound
->rate
!= 0);
146 MxSetChannelRawSrc(mc
, mem
, sound
->file_size
, sound
->rate
, sound
->bits_per_sample
== 16);
151 void InitializeSound()
153 DEBUG(misc
, 1, "Loading sound effects...");
154 OpenBankFile(BaseSounds::GetUsedSet()->files
->filename
);
157 /* Low level sound player */
158 static void StartSound(SoundID sound_id
, float pan
, uint volume
)
160 if (volume
== 0) return;
162 SoundEntry
*sound
= GetSound(sound_id
);
163 if (sound
== nullptr) return;
165 /* NewGRF sound that wasn't loaded yet? */
166 if (sound
->rate
== 0 && sound
->file_slot
!= 0) {
167 if (!LoadNewGRFSound(sound
)) {
168 /* Mark as invalid. */
169 sound
->file_slot
= 0;
175 if (sound
->rate
== 0) return;
177 MixerChannel
*mc
= MxAllocateChannel();
178 if (mc
== nullptr) return;
180 if (!SetBankSource(mc
, sound
)) return;
182 /* Apply the sound effect's own volume. */
183 volume
= sound
->volume
* volume
;
185 MxSetChannelVolume(mc
, volume
, pan
);
186 MxActivateChannel(mc
);
190 static const byte _vol_factor_by_zoom
[] = {255, 255, 255, 190, 134, 87};
191 assert_compile(lengthof(_vol_factor_by_zoom
) == ZOOM_LVL_COUNT
);
193 static const byte _sound_base_vol
[] = {
194 128, 90, 128, 128, 128, 128, 128, 128,
195 128, 90, 90, 128, 128, 128, 128, 128,
196 128, 128, 128, 80, 128, 128, 128, 128,
197 128, 128, 128, 128, 128, 128, 128, 128,
198 128, 128, 90, 90, 90, 128, 90, 128,
199 128, 90, 128, 128, 128, 90, 128, 128,
200 128, 128, 128, 128, 90, 128, 128, 128,
201 128, 90, 128, 128, 128, 128, 128, 128,
202 128, 128, 90, 90, 90, 128, 128, 128,
206 static const byte _sound_idx
[] = {
207 2, 3, 4, 5, 6, 7, 8, 9,
208 10, 11, 12, 13, 14, 15, 16, 17,
209 18, 19, 20, 21, 22, 23, 24, 25,
210 26, 27, 28, 29, 30, 31, 32, 33,
211 34, 35, 36, 37, 38, 39, 40, 0,
212 1, 41, 42, 43, 44, 45, 46, 47,
213 48, 49, 50, 51, 52, 53, 54, 55,
214 56, 57, 58, 59, 60, 61, 62, 63,
215 64, 65, 66, 67, 68, 69, 70, 71,
221 SoundEntry
*sound
= AllocateSound(ORIGINAL_SAMPLE_COUNT
);
222 for (uint i
= 0; i
< ORIGINAL_SAMPLE_COUNT
; i
++) {
223 sound
[i
] = _original_sounds
[_sound_idx
[i
]];
224 sound
[i
].volume
= _sound_base_vol
[i
];
225 sound
[i
].priority
= 0;
230 * Decide 'where' (between left and right speaker) to play the sound effect.
231 * @param sound Sound effect to play
232 * @param left Left edge of virtual coordinates where the sound is produced
233 * @param right Right edge of virtual coordinates where the sound is produced
234 * @param top Top edge of virtual coordinates where the sound is produced
235 * @param bottom Bottom edge of virtual coordinates where the sound is produced
237 static void SndPlayScreenCoordFx(SoundID sound
, int left
, int right
, int top
, int bottom
)
239 if (_settings_client
.music
.effect_vol
== 0) return;
242 FOR_ALL_WINDOWS_FROM_BACK(w
) {
243 const ViewPort
*vp
= w
->viewport
;
246 left
< vp
->virtual_left
+ vp
->virtual_width
&& right
> vp
->virtual_left
&&
247 top
< vp
->virtual_top
+ vp
->virtual_height
&& bottom
> vp
->virtual_top
) {
248 int screen_x
= (left
+ right
) / 2 - vp
->virtual_left
;
249 int width
= (vp
->virtual_width
== 0 ? 1 : vp
->virtual_width
);
250 float panning
= (float)screen_x
/ width
;
255 (_settings_client
.music
.effect_vol
* _vol_factor_by_zoom
[vp
->zoom
- ZOOM_LVL_BEGIN
]) / 256
262 void SndPlayTileFx(SoundID sound
, TileIndex tile
)
264 /* emits sound from center of the tile */
265 int x
= min(MapMaxX() - 1, TileX(tile
)) * TILE_SIZE
+ TILE_SIZE
/ 2;
266 int y
= min(MapMaxY() - 1, TileY(tile
)) * TILE_SIZE
- TILE_SIZE
/ 2;
267 int z
= (y
< 0 ? 0 : GetSlopePixelZ(x
, y
));
268 Point pt
= RemapCoords(x
, y
, z
);
270 Point pt2
= RemapCoords(x
, y
, GetSlopePixelZ(x
, y
));
271 SndPlayScreenCoordFx(sound
, pt
.x
, pt2
.x
, pt
.y
, pt2
.y
);
274 void SndPlayVehicleFx(SoundID sound
, const Vehicle
*v
)
276 SndPlayScreenCoordFx(sound
,
277 v
->coord
.left
, v
->coord
.right
,
278 v
->coord
.top
, v
->coord
.bottom
282 void SndPlayFx(SoundID sound
)
284 StartSound(sound
, 0.5, _settings_client
.music
.effect_vol
);
287 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia
<SoundsSet
>, SoundsSet
)
289 /** Names corresponding to the sound set's files */
290 static const char * const _sound_file_names
[] = { "samples" };
293 template <class T
, size_t Tnum_files
, bool Tsearch_in_tars
>
294 /* static */ const char * const *BaseSet
<T
, Tnum_files
, Tsearch_in_tars
>::file_names
= _sound_file_names
;
296 template <class Tbase_set
>
297 /* static */ const char *BaseMedia
<Tbase_set
>::GetExtension()
299 return ".obs"; // OpenTTD Base Sounds
302 template <class Tbase_set
>
303 /* static */ bool BaseMedia
<Tbase_set
>::DetermineBestSet()
305 if (BaseMedia
<Tbase_set
>::used_set
!= nullptr) return true;
307 const Tbase_set
*best
= nullptr;
308 for (const Tbase_set
*c
= BaseMedia
<Tbase_set
>::available_sets
; c
!= nullptr; c
= c
->next
) {
309 /* Skip unusable sets */
310 if (c
->GetNumMissing() != 0) continue;
312 if (best
== nullptr ||
313 (best
->fallback
&& !c
->fallback
) ||
314 best
->valid_files
< c
->valid_files
||
315 (best
->valid_files
== c
->valid_files
&&
316 (best
->shortname
== c
->shortname
&& best
->version
< c
->version
))) {
321 BaseMedia
<Tbase_set
>::used_set
= best
;
322 return BaseMedia
<Tbase_set
>::used_set
!= nullptr;