1 /+ 2 + Copyright 2022 – 2024 Aya Partridge 3 + Copyright 2018 - 2022 Michael D. Parker 4 + Distributed under the Boost Software License, Version 1.0. 5 + (See accompanying file LICENSE_1_0.txt or copy at 6 + http://www.boost.org/LICENSE_1_0.txt) 7 +/ 8 module sdl_mixer; 9 10 import bindbc.sdl.config; 11 static if(bindSDLMixer): 12 import bindbc.sdl.codegen; 13 14 import sdl.audio: AUDIO_S16LSB, AUDIO_S16SYS, SDL_MIX_MAXVOLUME; 15 import sdl.error: SDL_GetError, SDL_SetError, SDL_ClearError, SDL_OutOfMemory; 16 import sdl.rwops: SDL_RWops, SDL_RWFromFile; 17 import sdl.stdinc: SDL_bool; 18 import sdl.version_: SDL_VERSIONNUM; 19 20 enum SDLMixerSupport: SDL_version{ 21 noLibrary = SDL_version(0,0,0), 22 badLibrary = SDL_version(0,0,255), 23 v2_0_0 = SDL_version(2,0,0), 24 v2_0_1 = SDL_version(2,0,1), 25 v2_0_2 = SDL_version(2,0,2), 26 v2_0_4 = SDL_version(2,0,4), 27 v2_6 = SDL_version(2,6,0), 28 v2_8 = SDL_version(2,8,0), 29 30 deprecated("Please use `v2_0_0` instead") sdlMixer200 = SDL_version(2,0,0), 31 deprecated("Please use `v2_0_1` instead") sdlMixer201 = SDL_version(2,0,1), 32 deprecated("Please use `v2_0_2` instead") sdlMixer202 = SDL_version(2,0,2), 33 deprecated("Please use `v2_0_4` instead") sdlMixer204 = SDL_version(2,0,4), 34 deprecated("Please use `v2_6` instead") sdlMixer260 = SDL_version(2,6,0), 35 } 36 37 enum sdlMixerSupport = (){ 38 version(SDL_Mixer_2_8) return SDLMixerSupport.v2_8; 39 else version(SDL_Mixer_2_6) return SDLMixerSupport.v2_6; 40 else version(SDL_Mixer_260) return SDLMixerSupport.v2_6; //NOTE: deprecated, remove this in bindbc-sdl 2.0 41 else version(SDL_Mixer_204) return SDLMixerSupport.v2_0_4; 42 else version(SDL_Mixer_202) return SDLMixerSupport.v2_0_2; 43 else version(SDL_Mixer_201) return SDLMixerSupport.v2_0_1; 44 else return SDLMixerSupport.v2_0_0; 45 }(); 46 47 enum SDL_MIXER_MAJOR_VERSION = sdlMixerSupport.major; 48 enum SDL_MIXER_MINOR_VERSION = sdlMixerSupport.minor; 49 enum SDL_MIXER_PATCHLEVEL = sdlMixerSupport.patch; 50 51 deprecated("Please use `SDL_MIXER_MAJOR_VERSION` instead") alias MIX_MAJOR_VERSION = SDL_MIXER_MAJOR_VERSION; 52 deprecated("Please use `SDL_MIXER_MINOR_VERSION` instead") alias MIX_MINOR_VERSION = SDL_MIXER_MINOR_VERSION; 53 deprecated("Please use `SDL_MIXER_PATCHLEVEL` instead") alias MIX_PATCH_LEVEL = SDL_MIXER_PATCHLEVEL; 54 55 pragma(inline, true) void SDL_MIXER_VERSION(SDL_version* x) nothrow @nogc pure @safe{ 56 x.major = SDL_MIXER_MAJOR_VERSION; 57 x.minor = SDL_MIXER_MINOR_VERSION; 58 x.patch = SDL_MIXER_PATCHLEVEL; 59 } 60 61 // These were implemented in SDL_mixer 2.0.2, but are fine for all versions. 62 deprecated("Please use SDL_MIXER_VERSION_ATLEAST or SDL_MIXER_VERSION instead") 63 enum SDL_MIXER_COMPILEDVERSION = SDL_version(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL); 64 65 pragma(inline, true) nothrow @nogc{ 66 bool SDL_MIXER_VERSION_ATLEAST(ubyte x, ubyte y, ubyte z){ return SDL_version(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL) >= SDL_version(x, y, z); } 67 } 68 deprecated("Please use the non-template variant instead"){ 69 enum SDL_MIXER_VERSION_ATLEAST(ubyte x, ubyte y, ubyte z) = SDL_version(SDL_MIXER_MAJOR_VERSION, SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL) >= SDL_version(x, y, z); 70 } 71 72 alias Mix_InitFlags = int; 73 enum: Mix_InitFlags{ 74 MIX_INIT_FLAC = 0x0000_0001, 75 MIX_INIT_MOD = 0x0000_0002, 76 MIX_INIT_MP3 = 0x0000_0008, 77 MIX_INIT_OGG = 0x0000_0010, 78 } 79 static if(sdlMixerSupport < SDLMixerSupport.v2_0_2) 80 enum: Mix_InitFlags{ 81 MIX_INIT_MODPLUG = 0x0000_0004, 82 MIX_INIT_FLUIDSYNTH = 0x0000_0020, 83 } 84 else //sdlMixerSupport >= SDLMixerSupport.v2_0_2 85 enum: Mix_InitFlags{ 86 MIX_INIT_MID = 0x0000_0020, 87 } 88 static if(sdlMixerSupport >= SDLMixerSupport.v2_0_4) 89 enum: Mix_InitFlags{ 90 MIX_INIT_OPUS = 0x0000_0040, 91 } 92 93 enum MIX_CHANNELS = 8; 94 95 enum MIX_DEFAULT_FREQUENCY = (){ 96 static if(sdlMixerSupport >= SDLMixerSupport.v2_6) 97 return 44100; 98 else 99 return 22050; 100 }(); 101 102 enum MIX_DEFAULT_FORMAT = (){ 103 static if(sdlMixerSupport >= SDLMixerSupport.v2_8){ 104 return AUDIO_S16SYS; 105 }else{ 106 version(LittleEndian) return AUDIO_S16LSB; 107 else return AUDIO_S16MSB; 108 } 109 }(); 110 111 enum MIX_DEFAULT_CHANNELS = 2; 112 113 alias MIX_MAX_VOLUME = SDL_MIX_MAXVOLUME; 114 115 struct Mix_Chunk{ 116 int allocated; 117 ubyte* abuf; 118 uint alen; 119 ubyte volume; 120 } 121 122 alias Mix_Fading = int; 123 enum: Mix_Fading{ 124 MIX_NO_FADING, 125 MIX_FADING_OUT, 126 MIX_FADING_IN, 127 } 128 129 alias Mix_MusicType = int; 130 enum: Mix_MusicType{ 131 MUS_NONE = 0, 132 MUS_CMD = 1, 133 MUS_WAV = 2, 134 MUS_MOD = 3, 135 MUS_MID = 4, 136 MUS_OGG = 5, 137 MUS_MP3 = 6, 138 139 MUS_FLAC = 8, 140 } 141 static if(sdlMixerSupport < SDLMixerSupport.v2_0_2) 142 enum: Mix_MusicType{ 143 MUS_MP3_MAD = 7, 144 MUS_MODPLUG = 9, 145 } 146 else //sdlMixerSupport >= SDLMixerSupport.v2_0_2 147 enum: Mix_MusicType{ 148 MUS_MP3_MAD_UNUSED = 7, 149 MUS_MODPLUG_UNUSED = 9, 150 } 151 static if(sdlMixerSupport >= SDLMixerSupport.v2_0_4) 152 enum: Mix_MusicType{ 153 MUS_OPUS = 10, 154 } 155 static if(sdlMixerSupport >= SDLMixerSupport.v2_8) 156 enum: Mix_MusicType{ 157 MUS_WAVPACK = 11, 158 MUS_GME = 12, 159 } 160 161 struct Mix_Music; 162 163 enum MIX_CHANNEL_POST = -2; 164 165 enum MIX_EFFECTSMAXSPEED = "MIX_EFFECTSMAXSPEED"; 166 167 extern(C) nothrow{ 168 alias Mix_EffectFunc_t = void function(int, void*, int, void*); 169 alias Mix_EffectDone_t = void function(int, void*); 170 171 //These aren't in SDL_mixer.h and are just here as a convenient and visible means to add the proper attributes to these callbacks. 172 alias callbackVI = void function(int); 173 alias callbackVPVPUbI = void function(void*, ubyte*, int); 174 alias callbackV = void function(); 175 alias callbackIPCPV = int function(const(char*), void*); 176 } 177 178 alias Mix_SetError = SDL_SetError; 179 180 alias Mix_GetError = SDL_GetError; 181 182 alias Mix_ClearError = SDL_ClearError; 183 184 alias Mix_OutOfMemory = SDL_OutOfMemory; 185 186 static if(sdlMixerSupport < SDLMixerSupport.v2_6){ 187 pragma(inline, true) nothrow @nogc{ 188 Mix_Chunk* Mix_LoadWAV(const(char)* file){ 189 return Mix_LoadWAV_RW(SDL_RWFromFile(file, "rb"), 1); 190 } 191 192 int Mix_PlayChannel(int channel, Mix_Chunk* chunk, int loops){ 193 return Mix_PlayChannelTimed(channel, chunk, loops, -1); 194 } 195 196 int Mix_FadeInChannel(int channel, Mix_Chunk* chunk, int loops, int ms){ 197 return Mix_FadeInChannelTimed(channel, chunk, loops, ms, -1); 198 } 199 } 200 } 201 202 mixin(joinFnBinds((){ 203 FnBind[] ret = [ 204 {q{const(SDL_version)*}, q{Mix_Linked_Version}, q{}}, 205 {q{int}, q{Mix_Init}, q{int flags}}, 206 {q{void}, q{Mix_Quit}, q{}}, 207 {q{int}, q{Mix_OpenAudio}, q{int frequency, ushort format, int channels, int chunkSize}}, 208 {q{int}, q{Mix_AllocateChannels}, q{int numChans}}, 209 {q{int}, q{Mix_QuerySpec}, q{int* frequency, ushort* format, int* channels}}, 210 {q{Mix_Chunk*}, q{Mix_LoadWAV_RW}, q{SDL_RWops* src, int freeSrc}}, 211 {q{Mix_Music*}, q{Mix_LoadMUS}, q{const(char)* file}}, 212 {q{Mix_Music*}, q{Mix_LoadMUS_RW}, q{SDL_RWops* src, int freeSrc}}, 213 {q{Mix_Music*}, q{Mix_LoadMUSType_RW}, q{SDL_RWops* src, Mix_MusicType type, int freeSrc}}, 214 {q{Mix_Chunk*}, q{Mix_QuickLoad_WAV}, q{ubyte* mem}}, 215 {q{Mix_Chunk*}, q{Mix_QuickLoad_RAW}, q{ubyte* mem, uint len}}, 216 {q{void}, q{Mix_FreeChunk}, q{Mix_Chunk* chunk}}, 217 {q{void}, q{Mix_FreeMusic}, q{Mix_Music* music}}, 218 {q{int}, q{Mix_GetNumChunkDecoders}, q{}}, 219 {q{const(char)*}, q{Mix_GetChunkDecoder}, q{int index}}, 220 {q{int}, q{Mix_GetNumMusicDecoders}, q{}}, 221 {q{const(char)*}, q{Mix_GetMusicDecoder}, q{int index}}, 222 {q{Mix_MusicType}, q{Mix_GetMusicType}, q{const(Mix_Music)* music}}, 223 {q{void}, q{Mix_SetPostMix}, q{callbackVPVPUbI mixFunc, void* arg}}, 224 {q{void}, q{Mix_HookMusic}, q{callbackVPVPUbI mixFunc, void* arg}}, 225 {q{void}, q{Mix_HookMusicFinished}, q{callbackV musicFinished}}, 226 {q{void*}, q{Mix_GetMusicHookData}, q{}}, 227 {q{void}, q{Mix_ChannelFinished}, q{callbackVI channelFinished}}, 228 {q{int}, q{Mix_RegisterEffect}, q{int chan, Mix_EffectFunc_t f, Mix_EffectDone_t d, void* arg}}, 229 {q{int}, q{Mix_UnregisterEffect}, q{int channel, Mix_EffectFunc_t f}}, 230 {q{int}, q{Mix_UnregisterAllEffects}, q{int channel}}, 231 {q{int}, q{Mix_SetPanning}, q{int channel, ubyte left, ubyte right}}, 232 {q{int}, q{Mix_SetPosition}, q{int channel, short angle, ubyte distance}}, 233 {q{int}, q{Mix_SetDistance}, q{int channel, ubyte distance}}, 234 {q{int}, q{Mix_SetReverseStereo}, q{int channel, int flip}}, 235 {q{int}, q{Mix_ReserveChannels}, q{int num}}, 236 {q{int}, q{Mix_GroupChannel}, q{int which, int tag}}, 237 {q{int}, q{Mix_GroupChannels}, q{int from, int to, int tag}}, 238 {q{int}, q{Mix_GroupAvailable}, q{int tag}}, 239 {q{int}, q{Mix_GroupCount}, q{int tag}}, 240 {q{int}, q{Mix_GroupOldest}, q{int tag}}, 241 {q{int}, q{Mix_GroupNewer}, q{int tag}}, 242 {q{int}, q{Mix_PlayChannelTimed}, q{int channel, Mix_Chunk* chunk, int loops, int ticks}}, 243 {q{int}, q{Mix_PlayMusic}, q{Mix_Music* music, int loops}}, 244 {q{int}, q{Mix_FadeInMusic}, q{Mix_Music* music, int loops, int ms}}, 245 {q{int}, q{Mix_FadeInMusicPos}, q{Mix_Music* music, int loops, int ms, double position}}, 246 {q{int}, q{Mix_FadeInChannelTimed}, q{int channel, Mix_Chunk* chunk, int loops, int ms, int ticks}}, 247 {q{int}, q{Mix_Volume}, q{int channel, int volume}}, 248 {q{int}, q{Mix_VolumeChunk}, q{Mix_Chunk* chunk, int volume}}, 249 {q{int}, q{Mix_VolumeMusic}, q{int volume}}, 250 {q{int}, q{Mix_HaltChannel}, q{int channel}}, 251 {q{int}, q{Mix_HaltGroup}, q{int tag}}, 252 {q{int}, q{Mix_HaltMusic}, q{}}, 253 {q{int}, q{Mix_ExpireChannel}, q{int channel, int ticks}}, 254 {q{int}, q{Mix_FadeOutChannel}, q{int which, int ms}}, 255 {q{int}, q{Mix_FadeOutGroup}, q{int tag, int ms}}, 256 {q{int}, q{Mix_FadeOutMusic}, q{int ms}}, 257 {q{Mix_Fading}, q{Mix_FadingMusic}, q{}}, 258 {q{Mix_Fading}, q{Mix_FadingChannel}, q{int which}}, 259 {q{void}, q{Mix_Pause}, q{int channel}}, 260 {q{void}, q{Mix_Resume}, q{int channel}}, 261 {q{int}, q{Mix_Paused}, q{int channel}}, 262 {q{void}, q{Mix_PauseMusic}, q{}}, 263 {q{void}, q{Mix_ResumeMusic}, q{}}, 264 {q{void}, q{Mix_RewindMusic}, q{}}, 265 {q{int}, q{Mix_PausedMusic}, q{}}, 266 {q{int}, q{Mix_SetMusicPosition}, q{double position}}, 267 {q{int}, q{Mix_Playing}, q{int channel}}, 268 {q{int}, q{Mix_PlayingMusic}, q{}}, 269 {q{int}, q{Mix_SetMusicCMD}, q{const(char)* command}}, 270 {q{int}, q{Mix_SetSynchroValue}, q{int value}}, 271 {q{int}, q{Mix_GetSynchroValue}, q{}}, 272 {q{int}, q{Mix_SetSoundFonts}, q{const(char)* paths}}, 273 {q{const(char)*}, q{Mix_GetSoundFonts}, q{}}, 274 {q{int}, q{Mix_EachSoundFont}, q{callbackIPCPV function_, void* data}}, 275 {q{Mix_Chunk*}, q{Mix_GetChunk}, q{int channel}}, 276 {q{void}, q{Mix_CloseAudio}, q{}}, 277 ]; 278 if(sdlMixerSupport >= SDLMixerSupport.v2_0_2){ 279 FnBind[] add = [ 280 {q{int}, q{Mix_OpenAudioDevice}, q{int frequency, ushort format, int channels, int chunkSize, const(char)* device, int allowedChanges}}, 281 {q{SDL_bool}, q{Mix_HasChunkDecoder}, q{const(char)* name}}, 282 //[q{SDL_bool}, q{Mix_HasMusicDecoder}, q{const(char)*}], //Declared in SDL_mixer.h, but not implemented 283 ]; 284 ret ~= add; 285 } 286 if(sdlMixerSupport >= SDLMixerSupport.v2_6){ 287 FnBind[] add = [ 288 {q{Mix_Chunk*}, q{Mix_LoadWAV}, q{const(char)* file}}, 289 {q{int}, q{Mix_PlayChannel}, q{int channel, Mix_Chunk* chunk, int loops}}, 290 {q{int}, q{Mix_FadeInChannel}, q{int channel, Mix_Chunk* chunk, int loops, int ms}}, 291 {q{SDL_bool}, q{Mix_HasMusicDecoder}, q{const(char)* name}}, 292 {q{const(char)*}, q{Mix_GetMusicTitle}, q{const(Mix_Music)* music}}, 293 {q{const(char)*}, q{Mix_GetMusicTitleTag}, q{const(Mix_Music)* music}}, 294 {q{const(char)*}, q{Mix_GetMusicArtistTag}, q{const(Mix_Music)* music}}, 295 {q{const(char)*}, q{Mix_GetMusicAlbumTag}, q{const(Mix_Music)* music}}, 296 {q{const(char)*}, q{Mix_GetMusicCopyrightTag}, q{const(Mix_Music)* music}}, 297 {q{int}, q{Mix_GetMusicVolume}, q{Mix_Music* music}}, 298 {q{int}, q{Mix_MasterVolume}, q{int volume}}, 299 {q{int}, q{Mix_ModMusicJumpToOrder}, q{int order}}, 300 {q{double}, q{Mix_GetMusicPosition}, q{Mix_Music* music}}, 301 {q{double}, q{Mix_MusicDuration}, q{Mix_Music* music}}, 302 {q{double}, q{Mix_GetMusicLoopStartTime}, q{Mix_Music* music}}, 303 {q{double}, q{Mix_GetMusicLoopEndTime}, q{Mix_Music* music}}, 304 {q{double}, q{Mix_GetMusicLoopLengthTime}, q{Mix_Music* music}}, 305 {q{int}, q{Mix_SetTimidityCfg}, q{const(char)* path}}, 306 {q{const(char)*}, q{Mix_GetTimidityCfg}, q{}}, 307 ]; 308 ret ~= add; 309 } 310 if(sdlMixerSupport >= SDLMixerSupport.v2_8){ 311 FnBind[] add = [ 312 {q{void}, q{Mix_PauseAudio}, q{int pauseOn}}, 313 {q{int}, q{Mix_StartTrack}, q{Mix_Music* music, int track}}, 314 {q{int}, q{Mix_GetNumTracks}, q{Mix_Music* music}}, 315 ]; 316 ret ~= add; 317 } 318 return ret; 319 }())); 320 321 static if(!staticBinding): 322 import bindbc.loader; 323 324 private{ 325 SharedLib lib; 326 SDLMixerSupport loadedVersion; 327 enum libNamesCT = (){ 328 version(Windows){ 329 return [ 330 `SDL2_mixer.dll`, 331 ]; 332 }else version(OSX){ 333 return [ 334 `libSDL2_mixer.dylib`, 335 `/opt/homebrew/lib/libSDL2_mixer.dylib`, 336 `SDL2_mixer`, 337 `/Library/Frameworks/SDL2_mixer.framework/SDL2_mixer`, 338 `/System/Library/Frameworks/SDL2_mixer.framework/SDL2_mixer`, 339 ]; 340 }else version(Posix){ 341 return [ 342 `libSDL2_mixer.so`, 343 `libSDL2-2.0_mixer.so`, 344 `libSDL2-2.0_mixer.so.0`, 345 ]; 346 }else static assert(0, "BindBC-SDL_mixer does not have library search paths set up for this platform"); 347 }(); 348 } 349 350 nothrow @nogc: 351 deprecated("Please use `Mix_Linked_Version` instead") 352 SDLMixerSupport loadedSDLMixerVersion(){ return loadedVersion; } 353 354 mixin(bindbc.sdl.codegen.makeDynloadFns("Mixer", [__MODULE__]));