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