Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
| 3 | * It is copyright by its individual contributors, as recorded in the |
||
| 4 | * project's Git history. See COPYING.txt at the top level for license |
||
| 5 | * terms and a link to the Git history. |
||
| 6 | */ |
||
| 7 | /* |
||
| 8 | * Digital audio support |
||
| 9 | * Library-independent stub for dynamic selection of sound system |
||
| 10 | */ |
||
| 11 | |||
| 12 | #include "dxxerror.h" |
||
| 13 | #include "vecmat.h" |
||
| 14 | #include "piggy.h" |
||
| 15 | #include "config.h" |
||
| 16 | #include "digi.h" |
||
| 17 | #include "sounds.h" |
||
| 18 | #include "console.h" |
||
| 19 | #include "rbaudio.h" |
||
| 20 | #include "jukebox.h" |
||
| 21 | #include "args.h" |
||
| 22 | #include <stdlib.h> |
||
| 23 | #include <stdio.h> |
||
| 24 | #include <string.h> |
||
| 25 | #include <digi.h> |
||
| 26 | #include <digi_audio.h> |
||
| 27 | |||
| 28 | #if DXX_USE_SDLMIXER |
||
| 29 | #include <digi_mixer.h> |
||
| 30 | #include <SDL_mixer.h> |
||
| 31 | #endif |
||
| 32 | #ifdef _WIN32 |
||
| 33 | #include "hmp.h" |
||
| 34 | #endif |
||
| 35 | |||
| 36 | namespace dcx { |
||
| 37 | |||
| 38 | int digi_volume = SOUND_MAX_VOLUME; |
||
| 39 | |||
| 40 | /* The values for these three defines are arbitrary and can be changed, |
||
| 41 | * provided that they remain unique with respect to each other. |
||
| 42 | */ |
||
| 43 | #define DXX_STS_MIXER_WITH_POINTER 0 |
||
| 44 | #define DXX_STS_MIXER_WITH_COPY 1 |
||
| 45 | #define DXX_STS_NO_MIXER 2 |
||
| 46 | |||
| 47 | #if DXX_USE_SDLMIXER |
||
| 48 | #ifndef DXX_SOUND_TABLE_STYLE |
||
| 49 | #ifdef __PIE__ |
||
| 50 | /* PIE -> paranoid checks |
||
| 51 | * No PIE -> prefer speed |
||
| 52 | */ |
||
| 53 | #define DXX_SOUND_TABLE_STYLE DXX_STS_MIXER_WITH_POINTER |
||
| 54 | #else |
||
| 55 | #define DXX_SOUND_TABLE_STYLE DXX_STS_MIXER_WITH_COPY |
||
| 56 | #endif |
||
| 57 | #endif |
||
| 58 | #else |
||
| 59 | #define DXX_SOUND_TABLE_STYLE DXX_STS_NO_MIXER |
||
| 60 | #endif |
||
| 61 | |||
| 62 | /* Sound system function pointers */ |
||
| 63 | |||
| 64 | namespace { |
||
| 65 | |||
| 66 | struct sound_function_table_t |
||
| 67 | { |
||
| 68 | int (*init)(); |
||
| 69 | void (*close)(); |
||
| 70 | #ifndef RELEASE |
||
| 71 | void (*reset)(); |
||
| 72 | #endif |
||
| 73 | void (*set_channel_volume)(int, int); |
||
| 74 | void (*set_channel_pan)(int, int); |
||
| 75 | int (*start_sound)(short, fix, int, int, int, int, sound_object *); |
||
| 76 | void (*stop_sound)(int); |
||
| 77 | void (*end_sound)(int); |
||
| 78 | int (*is_channel_playing)(int); |
||
| 79 | void (*stop_all_channels)(); |
||
| 80 | void (*set_digi_volume)(int); |
||
| 81 | }; |
||
| 82 | |||
| 83 | #if DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_POINTER |
||
| 84 | [[noreturn]] |
||
| 85 | __attribute_cold |
||
| 86 | void report_invalid_table() |
||
| 87 | { |
||
| 88 | /* Out of line due to length of generated code */ |
||
| 89 | throw std::logic_error("invalid sound table pointer"); |
||
| 90 | } |
||
| 91 | #endif |
||
| 92 | |||
| 93 | } |
||
| 94 | |||
| 95 | } |
||
| 96 | |||
| 97 | #if DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_POINTER |
||
| 98 | namespace dsx |
||
| 99 | #else |
||
| 100 | namespace dcx |
||
| 101 | #endif |
||
| 102 | { |
||
| 103 | |||
| 104 | namespace { |
||
| 105 | |||
| 106 | class sound_function_pointers_t |
||
| 107 | { |
||
| 108 | #if DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_COPY |
||
| 109 | sound_function_table_t table; |
||
| 110 | #elif DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_POINTER |
||
| 111 | const sound_function_table_t *table; |
||
| 112 | #endif |
||
| 113 | public: |
||
| 114 | inline const sound_function_table_t *operator->(); |
||
| 115 | inline sound_function_pointers_t &operator=(const sound_function_table_t &t); |
||
| 116 | }; |
||
| 117 | |||
| 118 | #if DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_COPY |
||
| 119 | const sound_function_table_t *sound_function_pointers_t::operator->() |
||
| 120 | { |
||
| 121 | return &table; |
||
| 122 | } |
||
| 123 | |||
| 124 | sound_function_pointers_t &sound_function_pointers_t::operator=(const sound_function_table_t &t) |
||
| 125 | { |
||
| 126 | table = t; |
||
| 127 | return *this; |
||
| 128 | } |
||
| 129 | #elif DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_POINTER |
||
| 130 | |||
| 131 | sound_function_pointers_t &sound_function_pointers_t::operator=(const sound_function_table_t &t) |
||
| 132 | { |
||
| 133 | table = &t; |
||
| 134 | /* Trap bad initial assignment */ |
||
| 135 | operator->(); |
||
| 136 | return *this; |
||
| 137 | } |
||
| 138 | #elif DXX_SOUND_TABLE_STYLE == DXX_STS_NO_MIXER |
||
| 139 | const sound_function_table_t *sound_function_pointers_t::operator->() |
||
| 140 | { |
||
| 141 | return &digi_audio_table; |
||
| 142 | } |
||
| 143 | |||
| 144 | sound_function_pointers_t &sound_function_pointers_t::operator=(const sound_function_table_t &) |
||
| 145 | { |
||
| 146 | return *this; |
||
| 147 | } |
||
| 148 | #endif |
||
| 149 | |||
| 150 | } |
||
| 151 | |||
| 152 | static sound_function_pointers_t fptr; |
||
| 153 | |||
| 154 | } |
||
| 155 | |||
| 156 | namespace dsx { |
||
| 157 | |||
| 158 | namespace { |
||
| 159 | |||
| 160 | /* Some of the functions are in dsx, so the definition and |
||
| 161 | * initializer must be in dsx. |
||
| 162 | */ |
||
| 163 | #if DXX_USE_SDLMIXER |
||
| 164 | constexpr sound_function_table_t digi_mixer_table{ |
||
| 165 | &digi_mixer_init, |
||
| 166 | &digi_mixer_close, |
||
| 167 | #ifndef RELEASE |
||
| 168 | &digi_mixer_reset, |
||
| 169 | #endif |
||
| 170 | &digi_mixer_set_channel_volume, |
||
| 171 | &digi_mixer_set_channel_pan, |
||
| 172 | &digi_mixer_start_sound, |
||
| 173 | &digi_mixer_stop_sound, |
||
| 174 | &digi_mixer_end_sound, |
||
| 175 | &digi_mixer_is_channel_playing, |
||
| 176 | &digi_mixer_stop_all_channels, |
||
| 177 | &digi_mixer_set_digi_volume, |
||
| 178 | }; |
||
| 179 | #endif |
||
| 180 | |||
| 181 | constexpr sound_function_table_t digi_audio_table{ |
||
| 182 | &digi_audio_init, |
||
| 183 | &digi_audio_close, |
||
| 184 | #ifndef RELEASE |
||
| 185 | &digi_audio_reset, |
||
| 186 | #endif |
||
| 187 | &digi_audio_set_channel_volume, |
||
| 188 | &digi_audio_set_channel_pan, |
||
| 189 | &digi_audio_start_sound, |
||
| 190 | &digi_audio_stop_sound, |
||
| 191 | &digi_audio_end_sound, |
||
| 192 | &digi_audio_is_channel_playing, |
||
| 193 | &digi_audio_stop_all_channels, |
||
| 194 | &digi_audio_set_digi_volume, |
||
| 195 | }; |
||
| 196 | |||
| 197 | #if DXX_SOUND_TABLE_STYLE == DXX_STS_MIXER_WITH_POINTER |
||
| 198 | const sound_function_table_t *sound_function_pointers_t::operator->() |
||
| 199 | { |
||
| 200 | if (table != &digi_audio_table && table != &digi_mixer_table) |
||
| 201 | report_invalid_table(); |
||
| 202 | return table; |
||
| 203 | } |
||
| 204 | #endif |
||
| 205 | |||
| 206 | } |
||
| 207 | |||
| 208 | void digi_select_system() |
||
| 209 | { |
||
| 210 | #if DXX_USE_SDLMIXER |
||
| 211 | if (!CGameArg.SndDisableSdlMixer) |
||
| 212 | { |
||
| 213 | const auto vl = Mix_Linked_Version(); |
||
| 214 | con_printf(CON_NORMAL, "Using SDL_mixer library v%u.%u.%u", vl->major, vl->minor, vl->patch); |
||
| 215 | fptr = digi_mixer_table; |
||
| 216 | return; |
||
| 217 | } |
||
| 218 | #endif |
||
| 219 | con_puts(CON_NORMAL,"Using plain old SDL audio"); |
||
| 220 | fptr = digi_audio_table; |
||
| 221 | } |
||
| 222 | |||
| 223 | /* Common digi functions */ |
||
| 224 | #if defined(DXX_BUILD_DESCENT_I) |
||
| 225 | int digi_sample_rate = SAMPLE_RATE_11K; |
||
| 226 | #endif |
||
| 227 | |||
| 228 | /* Stub functions */ |
||
| 229 | |||
| 230 | int digi_init() |
||
| 231 | { |
||
| 232 | digi_init_sounds(); |
||
| 233 | return fptr->init(); |
||
| 234 | } |
||
| 235 | |||
| 236 | void digi_close() { fptr->close(); } |
||
| 237 | #ifndef RELEASE |
||
| 238 | void digi_reset() { fptr->reset(); } |
||
| 239 | #endif |
||
| 240 | |||
| 241 | void digi_set_channel_volume(int channel, int volume) { fptr->set_channel_volume(channel, volume); } |
||
| 242 | void digi_set_channel_pan(int channel, int pan) { fptr->set_channel_pan(channel, pan); } |
||
| 243 | |||
| 244 | int digi_start_sound(short soundnum, fix volume, int pan, int looping, int loop_start, int loop_end, sound_object *soundobj) |
||
| 245 | { |
||
| 246 | return fptr->start_sound(soundnum, volume, pan, looping, loop_start, loop_end, soundobj); |
||
| 247 | } |
||
| 248 | |||
| 249 | void digi_stop_sound(int channel) { fptr->stop_sound(channel); } |
||
| 250 | void digi_end_sound(int channel) { fptr->end_sound(channel); } |
||
| 251 | |||
| 252 | int digi_is_channel_playing(int channel) { return fptr->is_channel_playing(channel); } |
||
| 253 | void digi_stop_all_channels() { fptr->stop_all_channels(); } |
||
| 254 | void digi_set_digi_volume(int dvolume) { fptr->set_digi_volume(dvolume); } |
||
| 255 | |||
| 256 | #ifdef _WIN32 |
||
| 257 | // Windows native-MIDI stuff. |
||
| 258 | static uint8_t digi_win32_midi_song_playing; |
||
| 259 | static uint8_t already_playing; |
||
| 260 | static std::unique_ptr<hmp_file> cur_hmp; |
||
| 261 | |||
| 262 | void digi_win32_set_midi_volume( int mvolume ) |
||
| 263 | { |
||
| 264 | hmp_setvolume(cur_hmp.get(), mvolume*MIDI_VOLUME_SCALE/8); |
||
| 265 | } |
||
| 266 | |||
| 267 | int digi_win32_play_midi_song( const char * filename, int loop ) |
||
| 268 | { |
||
| 269 | if (!already_playing) |
||
| 270 | { |
||
| 271 | hmp_reset(); |
||
| 272 | already_playing = 1; |
||
| 273 | } |
||
| 274 | digi_win32_stop_midi_song(); |
||
| 275 | |||
| 276 | if (filename == NULL) |
||
| 277 | return 0; |
||
| 278 | |||
| 279 | if ((cur_hmp = hmp_open(filename))) |
||
| 280 | { |
||
| 281 | /* |
||
| 282 | * FIXME: to be implemented as soon as we have some kind or checksum function - replacement for ugly hack in hmp.c for descent.hmp |
||
| 283 | * if (***filesize check*** && ***CRC32 or MD5 check***) |
||
| 284 | * (((*cur_hmp).trks)[1]).data[6] = 0x6C; |
||
| 285 | */ |
||
| 286 | if (hmp_play(cur_hmp.get(),loop) != 0) |
||
| 287 | return 0; // error |
||
| 288 | digi_win32_midi_song_playing = 1; |
||
| 289 | digi_win32_set_midi_volume(GameCfg.MusicVolume); |
||
| 290 | return 1; |
||
| 291 | } |
||
| 292 | |||
| 293 | return 0; |
||
| 294 | } |
||
| 295 | |||
| 296 | void digi_win32_pause_midi_song() |
||
| 297 | { |
||
| 298 | hmp_pause(cur_hmp.get()); |
||
| 299 | } |
||
| 300 | |||
| 301 | void digi_win32_resume_midi_song() |
||
| 302 | { |
||
| 303 | hmp_resume(cur_hmp.get()); |
||
| 304 | } |
||
| 305 | |||
| 306 | void digi_win32_stop_midi_song() |
||
| 307 | { |
||
| 308 | if (!digi_win32_midi_song_playing) |
||
| 309 | return; |
||
| 310 | digi_win32_midi_song_playing = 0; |
||
| 311 | cur_hmp.reset(); |
||
| 312 | hmp_reset(); |
||
| 313 | } |
||
| 314 | #endif |
||
| 315 | |||
| 316 | } |