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