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