1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #ifdef MUSIC_MOD_MODPLUG
23
24 #include "SDL_loadso.h"
25
26 #include "music_modplug.h"
27
28 #ifdef MODPLUG_HEADER
29 #include MODPLUG_HEADER
30 #else
31 #include <libmodplug/modplug.h>
32 #endif
33
34 typedef struct {
35 int loaded;
36 void *handle;
37
38 ModPlugFile* (*ModPlug_Load)(const void* data, int size);
39 void (*ModPlug_Unload)(ModPlugFile* file);
40 int (*ModPlug_Read)(ModPlugFile* file, void* buffer, int size);
41 void (*ModPlug_Seek)(ModPlugFile* file, int millisecond);
42 void (*ModPlug_GetSettings)(ModPlug_Settings* settings);
43 void (*ModPlug_SetSettings)(const ModPlug_Settings* settings);
44 void (*ModPlug_SetMasterVolume)(ModPlugFile* file,unsigned int cvol) ;
45 } modplug_loader;
46
47 static modplug_loader modplug = {
48 0, NULL
49 };
50
51
52 static ModPlug_Settings settings;
53
54 #ifdef MODPLUG_DYNAMIC
55 #define FUNCTION_LOADER(FUNC, SIG) \
56 modplug.FUNC = (SIG) SDL_LoadFunction(modplug.handle, #FUNC); \
57 if (modplug.FUNC == NULL) { SDL_UnloadObject(modplug.handle); return -1; }
58 #else
59 #define FUNCTION_LOADER(FUNC, SIG) \
60 modplug.FUNC = FUNC;
61 #endif
62
MODPLUG_Load(void)63 static int MODPLUG_Load(void)
64 {
65 if (modplug.loaded == 0) {
66 #ifdef MODPLUG_DYNAMIC
67 modplug.handle = SDL_LoadObject(MODPLUG_DYNAMIC);
68 if (modplug.handle == NULL) {
69 return -1;
70 }
71 #elif defined(__MACOSX__)
72 extern ModPlugFile* ModPlug_Load(const void* data, int size) __attribute__((weak_import));
73 if (ModPlug_Load == NULL)
74 {
75 /* Missing weakly linked framework */
76 Mix_SetError("Missing modplug.framework");
77 return -1;
78 }
79 #endif
80 FUNCTION_LOADER(ModPlug_Load, ModPlugFile* (*)(const void* data, int size))
81 FUNCTION_LOADER(ModPlug_Unload, void (*)(ModPlugFile* file))
82 FUNCTION_LOADER(ModPlug_Read, int (*)(ModPlugFile* file, void* buffer, int size))
83 FUNCTION_LOADER(ModPlug_Seek, void (*)(ModPlugFile* file, int millisecond))
84 FUNCTION_LOADER(ModPlug_GetSettings, void (*)(ModPlug_Settings* settings))
85 FUNCTION_LOADER(ModPlug_SetSettings, void (*)(const ModPlug_Settings* settings))
86 FUNCTION_LOADER(ModPlug_SetMasterVolume, void (*)(ModPlugFile* file,unsigned int cvol))
87 }
88 ++modplug.loaded;
89
90 return 0;
91 }
92
MODPLUG_Unload(void)93 static void MODPLUG_Unload(void)
94 {
95 if (modplug.loaded == 0) {
96 return;
97 }
98 if (modplug.loaded == 1) {
99 #ifdef MODPLUG_DYNAMIC
100 SDL_UnloadObject(modplug.handle);
101 #endif
102 }
103 --modplug.loaded;
104 }
105
106
107 typedef struct
108 {
109 int play_count;
110 ModPlugFile *file;
111 SDL_AudioStream *stream;
112 void *buffer;
113 int buffer_size;
114 } MODPLUG_Music;
115
116
117 static int MODPLUG_Seek(void *context, double position);
118 static void MODPLUG_Delete(void *context);
119
MODPLUG_Open(const SDL_AudioSpec * spec)120 static int MODPLUG_Open(const SDL_AudioSpec *spec)
121 {
122 /* ModPlug supports U8 or S16 audio output */
123 modplug.ModPlug_GetSettings(&settings);
124 settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING;
125 if (spec->channels == 1) {
126 settings.mChannels = 1;
127 } else {
128 settings.mChannels = 2;
129 }
130 if (SDL_AUDIO_BITSIZE(spec->format) == 8) {
131 settings.mBits = 8;
132 } else {
133 settings.mBits = 16;
134 }
135 if (spec->freq >= 44100) {
136 settings.mFrequency = 44100;
137 } else if (spec->freq >= 22050) {
138 settings.mFrequency = 22050;
139 } else {
140 settings.mFrequency = 11025;
141 }
142 settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
143 settings.mReverbDepth = 0;
144 settings.mReverbDelay = 100;
145 settings.mBassAmount = 0;
146 settings.mBassRange = 50;
147 settings.mSurroundDepth = 0;
148 settings.mSurroundDelay = 10;
149 settings.mLoopCount = 0;
150 modplug.ModPlug_SetSettings(&settings);
151 return 0;
152 }
153
154 /* Load a modplug stream from an SDL_RWops object */
MODPLUG_CreateFromRW(SDL_RWops * src,int freesrc)155 void *MODPLUG_CreateFromRW(SDL_RWops *src, int freesrc)
156 {
157 MODPLUG_Music *music;
158 void *buffer;
159 size_t size;
160
161 music = (MODPLUG_Music *)SDL_calloc(1, sizeof(*music));
162 if (!music) {
163 SDL_OutOfMemory();
164 return NULL;
165 }
166
167 music->stream = SDL_NewAudioStream((settings.mBits == 8) ? AUDIO_U8 : AUDIO_S16SYS, settings.mChannels, settings.mFrequency,
168 music_spec.format, music_spec.channels, music_spec.freq);
169 if (!music->stream) {
170 MODPLUG_Delete(music);
171 return NULL;
172 }
173
174 music->buffer_size = music_spec.samples * (settings.mBits / 8) * settings.mChannels;
175 music->buffer = SDL_malloc(music->buffer_size);
176 if (!music->buffer) {
177 MODPLUG_Delete(music);
178 return NULL;
179 }
180
181 buffer = SDL_LoadFile_RW(src, &size, SDL_FALSE);
182 if (buffer) {
183 music->file = modplug.ModPlug_Load(buffer, (int)size);
184 if (!music->file) {
185 Mix_SetError("ModPlug_Load failed");
186 }
187 SDL_free(buffer);
188 }
189
190 if (!music->file) {
191 MODPLUG_Delete(music);
192 return NULL;
193 }
194
195 if (freesrc) {
196 SDL_RWclose(src);
197 }
198 return music;
199 }
200
201 /* Set the volume for a modplug stream */
MODPLUG_SetVolume(void * context,int volume)202 static void MODPLUG_SetVolume(void *context, int volume)
203 {
204 MODPLUG_Music *music = (MODPLUG_Music *)context;
205 modplug.ModPlug_SetMasterVolume(music->file, volume*4);
206 }
207
208 /* Start playback of a given modplug stream */
MODPLUG_Play(void * context,int play_count)209 static int MODPLUG_Play(void *context, int play_count)
210 {
211 MODPLUG_Music *music = (MODPLUG_Music *)context;
212 music->play_count = play_count;
213 return MODPLUG_Seek(music, 0.0);
214 }
215
216 /* Play some of a stream previously started with modplug_play() */
MODPLUG_GetSome(void * context,void * data,int bytes,SDL_bool * done)217 static int MODPLUG_GetSome(void *context, void *data, int bytes, SDL_bool *done)
218 {
219 MODPLUG_Music *music = (MODPLUG_Music *)context;
220 int filled, amount;
221
222 filled = SDL_AudioStreamGet(music->stream, data, bytes);
223 if (filled != 0) {
224 return filled;
225 }
226
227 if (!music->play_count) {
228 /* All done */
229 *done = SDL_TRUE;
230 return 0;
231 }
232
233 amount = modplug.ModPlug_Read(music->file, music->buffer, music->buffer_size);
234 if (amount > 0) {
235 if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) {
236 return -1;
237 }
238 } else {
239 if (music->play_count == 1) {
240 music->play_count = 0;
241 SDL_AudioStreamFlush(music->stream);
242 } else {
243 int play_count = -1;
244 if (music->play_count > 0) {
245 play_count = (music->play_count - 1);
246 }
247 if (MODPLUG_Play(music, play_count) < 0) {
248 return -1;
249 }
250 }
251 }
252 return 0;
253 }
MODPLUG_GetAudio(void * context,void * data,int bytes)254 static int MODPLUG_GetAudio(void *context, void *data, int bytes)
255 {
256 return music_pcm_getaudio(context, data, bytes, MIX_MAX_VOLUME, MODPLUG_GetSome);
257 }
258
259 /* Jump (seek) to a given position */
MODPLUG_Seek(void * context,double position)260 static int MODPLUG_Seek(void *context, double position)
261 {
262 MODPLUG_Music *music = (MODPLUG_Music *)context;
263 modplug.ModPlug_Seek(music->file, (int)(position*1000));
264 return 0;
265 }
266
267 /* Close the given modplug stream */
MODPLUG_Delete(void * context)268 static void MODPLUG_Delete(void *context)
269 {
270 MODPLUG_Music *music = (MODPLUG_Music *)context;
271 if (music->file) {
272 modplug.ModPlug_Unload(music->file);
273 }
274 if (music->stream) {
275 SDL_FreeAudioStream(music->stream);
276 }
277 if (music->buffer) {
278 SDL_free(music->buffer);
279 }
280 SDL_free(music);
281 }
282
283 Mix_MusicInterface Mix_MusicInterface_MODPLUG =
284 {
285 "MODPLUG",
286 MIX_MUSIC_MODPLUG,
287 MUS_MOD,
288 SDL_FALSE,
289 SDL_FALSE,
290
291 MODPLUG_Load,
292 MODPLUG_Open,
293 MODPLUG_CreateFromRW,
294 NULL, /* CreateFromFile */
295 MODPLUG_SetVolume,
296 MODPLUG_Play,
297 NULL, /* IsPlaying */
298 MODPLUG_GetAudio,
299 MODPLUG_Seek,
300 NULL, /* Pause */
301 NULL, /* Resume */
302 NULL, /* Stop */
303 MODPLUG_Delete,
304 NULL, /* Close */
305 MODPLUG_Unload,
306 };
307
308 #endif /* MUSIC_MOD_MODPLUG */
309
310 /* vi: set ts=4 sw=4 expandtab: */
311