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