music_mod.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 03:07:33 +0300
branchSDL-1.2
changeset 885 dd251a0ec666
parent 865 3ffb22a2755b
permissions -rw-r--r--
backport fix for bug #2795. (from 2.0 branch commit b28b41b93ba7).
     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 	  mikmod.MikMod_free(list);
   113 	else
   114 	  mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
   115 
   116 	list = mikmod.MikMod_InfoLoader();
   117 	if ( list )
   118 	  mikmod.MikMod_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 	/* struct MREADER in libmikmod <= 3.2.0-beta2
   148 	 * doesn't have iobase members. adding them here
   149 	 * so that if we compile against 3.2.0-beta2, we
   150 	 * can still run OK against 3.2.0b3 and newer. */
   151 	long iobase, prev_iobase;
   152 	long offset;
   153 	long eof;
   154 	SDL_RWops *rw;
   155 } LMM_MREADER;
   156 
   157 int LMM_Seek(struct MREADER *mr,long to,int dir)
   158 {
   159 	LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   160 	if ( dir == SEEK_SET ) {
   161 		to += lmmmr->offset;
   162 		if (to < lmmmr->offset)
   163 			return -1;
   164 	}
   165 	return (SDL_RWseek(lmmmr->rw, to, dir) < lmmmr->offset)? -1 : 0;
   166 }
   167 long LMM_Tell(struct MREADER *mr)
   168 {
   169 	LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   170 	return SDL_RWtell(lmmmr->rw) - lmmmr->offset;
   171 }
   172 BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
   173 {
   174 	LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   175 	return SDL_RWread(lmmmr->rw, buf, sz, 1);
   176 }
   177 int LMM_Get(struct MREADER *mr)
   178 {
   179 	unsigned char c;
   180 	LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   181 	if ( SDL_RWread(lmmmr->rw, &c, 1, 1) ) {
   182 		return c;
   183 	}
   184 	return EOF;
   185 }
   186 BOOL LMM_Eof(struct MREADER *mr)
   187 {
   188 	long offset;
   189 	LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
   190 	offset = LMM_Tell(mr);
   191 	return offset >= lmmmr->eof;
   192 }
   193 MODULE *MikMod_LoadSongRW(SDL_RWops *rw, int maxchan)
   194 {
   195 	LMM_MREADER lmmmr = {
   196 		{ LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
   197 		0,
   198 		0,
   199 		0
   200 	};
   201 	lmmmr.offset = SDL_RWtell(rw);
   202 	SDL_RWseek(rw, 0, RW_SEEK_END);
   203 	lmmmr.eof = SDL_RWtell(rw);
   204 	SDL_RWseek(rw, lmmmr.offset, RW_SEEK_SET);
   205         lmmmr.rw = rw;
   206 	return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
   207 }
   208 
   209 /* Load a MOD stream from an SDL_RWops object */
   210 MODULE *MOD_new_RW(SDL_RWops *rw, int freerw)
   211 {
   212 	MODULE *module;
   213 
   214 	/* Make sure the mikmod library is loaded */
   215 	if ( !Mix_Init(MIX_INIT_MOD) ) {
   216 		if ( freerw ) {
   217 			SDL_RWclose(rw);
   218 		}
   219 		return NULL;
   220 	}
   221 
   222 	module = MikMod_LoadSongRW(rw,64);
   223 	if (!module) {
   224 		Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
   225 		if ( freerw ) {
   226 			SDL_RWclose(rw);
   227 		}
   228 		return NULL;
   229 	}
   230 
   231 	/* Stop implicit looping, fade out and other flags. */
   232 	module->extspd  = 1;
   233 	module->panflag = 1;
   234 	module->wrap    = 0;
   235 	module->loop    = 0;
   236 #if 0 /* Don't set fade out by default - unfortunately there's no real way
   237 to query the status of the song or set trigger actions.  Hum. */
   238 	module->fadeout = 1;
   239 #endif
   240 
   241 	if ( freerw ) {
   242 		SDL_RWclose(rw);
   243 	}
   244 	return module;
   245 }
   246 
   247 /* Start playback of a given MOD stream */
   248 void MOD_play(MODULE *music)
   249 {
   250 	mikmod.Player_Start(music);
   251 }
   252 
   253 /* Return non-zero if a stream is currently playing */
   254 int MOD_playing(MODULE *music)
   255 {
   256 	return mikmod.Player_Active();
   257 }
   258 
   259 /* Play some of a stream previously started with MOD_play() */
   260 int MOD_playAudio(MODULE *music, Uint8 *stream, int len)
   261 {
   262 	if (current_output_channels > 2) {
   263 		int small_len = 2 * len / current_output_channels;
   264 		int i;
   265 		Uint8 *src, *dst;
   266 
   267 		mikmod.VC_WriteBytes((SBYTE *)stream, small_len);
   268 		/* and extend to len by copying channels */
   269 		src = stream + small_len;
   270 		dst = stream + len;
   271 
   272 		switch (current_output_format & 0xFF) {
   273 			case 8:
   274 				for ( i=small_len/2; i; --i ) {
   275 					src -= 2;
   276 					dst -= current_output_channels;
   277 					dst[0] = src[0];
   278 					dst[1] = src[1];
   279 					dst[2] = src[0];
   280 					dst[3] = src[1];
   281 					if (current_output_channels == 6) {
   282 						dst[4] = src[0];
   283 						dst[5] = src[1];
   284 					}
   285 				}
   286 				break;
   287 			case 16:
   288 				for ( i=small_len/4; i; --i ) {
   289 					src -= 4;
   290 					dst -= 2 * current_output_channels;
   291 					dst[0] = src[0];
   292 					dst[1] = src[1];
   293 					dst[2] = src[2];
   294 					dst[3] = src[3];
   295 					dst[4] = src[0];
   296 					dst[5] = src[1];
   297 					dst[6] = src[2];
   298 					dst[7] = src[3];
   299 					if (current_output_channels == 6) {
   300 						dst[8] = src[0];
   301 						dst[9] = src[1];
   302 						dst[10] = src[2];
   303 						dst[11] = src[3];
   304 					}
   305 				}
   306 				break;
   307 		}
   308 	} else {
   309 		mikmod.VC_WriteBytes((SBYTE *)stream, len);
   310 	}
   311 	if ( music_swap8 ) {
   312 		Uint8 *dst;
   313 		int i;
   314 
   315 		dst = stream;
   316 		for ( i=len; i; --i ) {
   317 			*dst++ ^= 0x80;
   318 		}
   319 	} else
   320 	if ( music_swap16 ) {
   321 		Uint8 *dst, tmp;
   322 		int i;
   323 
   324 		dst = stream;
   325 		for ( i=(len/2); i; --i ) {
   326 			tmp = dst[0];
   327 			dst[0] = dst[1];
   328 			dst[1] = tmp;
   329 			dst += 2;
   330 		}
   331 	}
   332 	return 0;
   333 }
   334 
   335 /* Stop playback of a stream previously started with MOD_play() */
   336 void MOD_stop(MODULE *music)
   337 {
   338 	mikmod.Player_Stop();
   339 }
   340 
   341 /* Close the given MOD stream */
   342 void MOD_delete(MODULE *music)
   343 {
   344 	mikmod.Player_Free(music);
   345 }
   346 
   347 /* Jump (seek) to a given position (time is in seconds) */
   348 void MOD_jump_to_time(MODULE *music, double time)
   349 {
   350 	mikmod.Player_SetPosition((UWORD)time);
   351 }
   352 
   353 #endif /* MOD_MUSIC */