music_mod.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 09 Jan 2012 07:34:10 -0500
changeset 558 7915d5cc775a
parent 542 3de4970b36d4
child 587 56cad6484b04
child 865 3ffb22a2755b
permissions -rw-r--r--
Fixed version of smpeg.dll that doesn't dynamically link to libstdc++-6.dll
     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  */
    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 */