1 /*
2 SDL_mixer: An audio mixer library based on the SDL library
3 Copyright (C) 1997-2012 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 /* $Id: music_mod.c 4211 2008-12-08 00:27:32Z slouken $ */
23
24 #ifdef MOD_MUSIC
25
26 /* This file supports MOD tracker music streams */
27
28 #include "SDL_mixer.h"
29 #include "dynamic_mod.h"
30 #include "music_mod.h"
31
32 #include "mikmod.h"
33
34 #define SDL_SURROUND
35 #ifdef SDL_SURROUND
36 #define MAX_OUTPUT_CHANNELS 6
37 #else
38 #define MAX_OUTPUT_CHANNELS 2
39 #endif
40
41 /* Reference for converting mikmod output to 4/6 channels */
42 static int current_output_channels;
43 static Uint16 current_output_format;
44
45 static int music_swap8;
46 static int music_swap16;
47
48 /* Initialize the MOD player, with the given mixer settings
49 This function returns 0, or -1 if there was an error.
50 */
MOD_init(SDL_AudioSpec * mixerfmt)51 int MOD_init(SDL_AudioSpec *mixerfmt)
52 {
53 CHAR *list;
54
55 if ( !Mix_Init(MIX_INIT_MOD) ) {
56 return -1;
57 }
58
59 /* Set the MikMod music format */
60 music_swap8 = 0;
61 music_swap16 = 0;
62 switch (mixerfmt->format) {
63
64 case AUDIO_U8:
65 case AUDIO_S8: {
66 if ( mixerfmt->format == AUDIO_S8 ) {
67 music_swap8 = 1;
68 }
69 *mikmod.md_mode = 0;
70 }
71 break;
72
73 case AUDIO_S16LSB:
74 case AUDIO_S16MSB: {
75 /* See if we need to correct MikMod mixing */
76 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
77 if ( mixerfmt->format == AUDIO_S16MSB ) {
78 #else
79 if ( mixerfmt->format == AUDIO_S16LSB ) {
80 #endif
81 music_swap16 = 1;
82 }
83 *mikmod.md_mode = DMODE_16BITS;
84 }
85 break;
86
87 default: {
88 Mix_SetError("Unknown hardware audio format");
89 return -1;
90 }
91 }
92 current_output_channels = mixerfmt->channels;
93 current_output_format = mixerfmt->format;
94 if ( mixerfmt->channels > 1 ) {
95 if ( mixerfmt->channels > MAX_OUTPUT_CHANNELS ) {
96 Mix_SetError("Hardware uses more channels than mixerfmt");
97 return -1;
98 }
99 *mikmod.md_mode |= DMODE_STEREO;
100 }
101 *mikmod.md_mixfreq = mixerfmt->freq;
102 *mikmod.md_device = 0;
103 *mikmod.md_volume = 96;
104 *mikmod.md_musicvolume = 128;
105 *mikmod.md_sndfxvolume = 128;
106 *mikmod.md_pansep = 128;
107 *mikmod.md_reverb = 0;
108 *mikmod.md_mode |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;
109
110 list = mikmod.MikMod_InfoDriver();
111 if ( list )
112 free(list);
113 else
114 mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
115
116 list = mikmod.MikMod_InfoLoader();
117 if ( list )
118 free(list);
119 else
120 mikmod.MikMod_RegisterAllLoaders();
121
122 if ( mikmod.MikMod_Init(NULL) ) {
123 Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
124 return -1;
125 }
126
127 return 0;
128 }
129
130 /* Uninitialize the music players */
131 void MOD_exit(void)
132 {
133 if (mikmod.MikMod_Exit) {
134 mikmod.MikMod_Exit();
135 }
136 }
137
138 /* Set the volume for a MOD stream */
139 void MOD_setvolume(MODULE *music, int volume)
140 {
141 mikmod.Player_SetVolume((SWORD)volume);
142 }
143
144 typedef struct
145 {
146 MREADER mr;
147 long offset;
148 long eof;
149 SDL_RWops *rw;
150 } LMM_MREADER;
151
152 BOOL LMM_Seek(struct MREADER *mr,long to,int dir)
153 {
154 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
155 if ( dir == SEEK_SET ) {
156 to += lmmmr->offset;
157 }
158 return (SDL_RWseek(lmmmr->rw, to, dir) < lmmmr->offset);
159 }
160 long LMM_Tell(struct MREADER *mr)
161 {
162 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
163 return SDL_RWtell(lmmmr->rw) - lmmmr->offset;
164 }
165 BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
166 {
167 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
168 return SDL_RWread(lmmmr->rw, buf, sz, 1);
169 }
170 int LMM_Get(struct MREADER *mr)
171 {
172 unsigned char c;
173 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
174 if ( SDL_RWread(lmmmr->rw, &c, 1, 1) ) {
175 return c;
176 }
177 return EOF;
178 }
179 BOOL LMM_Eof(struct MREADER *mr)
180 {
181 long offset;
182 LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
183 offset = LMM_Tell(mr);
184 return offset >= lmmmr->eof;
185 }
186 MODULE *MikMod_LoadSongRW(SDL_RWops *rw, int maxchan)
187 {
188 LMM_MREADER lmmmr = {
189 { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
190 0,
191 0,
192 0
193 };
194 lmmmr.offset = SDL_RWtell(rw);
195 SDL_RWseek(rw, 0, RW_SEEK_END);
196 lmmmr.eof = SDL_RWtell(rw);
197 SDL_RWseek(rw, lmmmr.offset, RW_SEEK_SET);
198 lmmmr.rw = rw;
199 return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
200 }
201
202 /* Load a MOD stream from an SDL_RWops object */
203 MODULE *MOD_new_RW(SDL_RWops *rw, int freerw)
204 {
205 MODULE *module;
206
207 /* Make sure the mikmod library is loaded */
208 if ( !Mix_Init(MIX_INIT_MOD) ) {
209 if ( freerw ) {
210 SDL_RWclose(rw);
211 }
212 return NULL;
213 }
214
215 module = MikMod_LoadSongRW(rw,64);
216 if (!module) {
217 Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
218 if ( freerw ) {
219 SDL_RWclose(rw);
220 }
221 return NULL;
222 }
223
224 /* Stop implicit looping, fade out and other flags. */
225 module->extspd = 1;
226 module->panflag = 1;
227 module->wrap = 0;
228 module->loop = 0;
229 #if 0 /* Don't set fade out by default - unfortunately there's no real way
230 to query the status of the song or set trigger actions. Hum. */
231 module->fadeout = 1;
232 #endif
233
234 if ( freerw ) {
235 SDL_RWclose(rw);
236 }
237 return module;
238 }
239
240 /* Start playback of a given MOD stream */
241 void MOD_play(MODULE *music)
242 {
243 mikmod.Player_Start(music);
244 }
245
246 /* Return non-zero if a stream is currently playing */
247 int MOD_playing(MODULE *music)
248 {
249 return mikmod.Player_Active();
250 }
251
252 /* Play some of a stream previously started with MOD_play() */
253 int MOD_playAudio(MODULE *music, Uint8 *stream, int len)
254 {
255 if (current_output_channels > 2) {
256 int small_len = 2 * len / current_output_channels;
257 int i;
258 Uint8 *src, *dst;
259
260 mikmod.VC_WriteBytes((SBYTE *)stream, small_len);
261 /* and extend to len by copying channels */
262 src = stream + small_len;
263 dst = stream + len;
264
265 switch (current_output_format & 0xFF) {
266 case 8:
267 for ( i=small_len/2; i; --i ) {
268 src -= 2;
269 dst -= current_output_channels;
270 dst[0] = src[0];
271 dst[1] = src[1];
272 dst[2] = src[0];
273 dst[3] = src[1];
274 if (current_output_channels == 6) {
275 dst[4] = src[0];
276 dst[5] = src[1];
277 }
278 }
279 break;
280 case 16:
281 for ( i=small_len/4; i; --i ) {
282 src -= 4;
283 dst -= 2 * current_output_channels;
284 dst[0] = src[0];
285 dst[1] = src[1];
286 dst[2] = src[2];
287 dst[3] = src[3];
288 dst[4] = src[0];
289 dst[5] = src[1];
290 dst[6] = src[2];
291 dst[7] = src[3];
292 if (current_output_channels == 6) {
293 dst[8] = src[0];
294 dst[9] = src[1];
295 dst[10] = src[2];
296 dst[11] = src[3];
297 }
298 }
299 break;
300 }
301 } else {
302 mikmod.VC_WriteBytes((SBYTE *)stream, len);
303 }
304 if ( music_swap8 ) {
305 Uint8 *dst;
306 int i;
307
308 dst = stream;
309 for ( i=len; i; --i ) {
310 *dst++ ^= 0x80;
311 }
312 } else
313 if ( music_swap16 ) {
314 Uint8 *dst, tmp;
315 int i;
316
317 dst = stream;
318 for ( i=(len/2); i; --i ) {
319 tmp = dst[0];
320 dst[0] = dst[1];
321 dst[1] = tmp;
322 dst += 2;
323 }
324 }
325 return 0;
326 }
327
328 /* Stop playback of a stream previously started with MOD_play() */
329 void MOD_stop(MODULE *music)
330 {
331 mikmod.Player_Stop();
332 }
333
334 /* Close the given MOD stream */
335 void MOD_delete(MODULE *music)
336 {
337 mikmod.Player_Free(music);
338 }
339
340 /* Jump (seek) to a given position (time is in seconds) */
341 void MOD_jump_to_time(MODULE *music, double time)
342 {
343 mikmod.Player_SetPosition((UWORD)time);
344 }
345
346 #endif /* MOD_MUSIC */
347