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