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