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__]));