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