music_mod.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 21 May 2013 21:21:23 -0700
changeset 617 87116a42526e
parent 601 05123263dab3
child 621 944412baab72
permissions -rw-r--r--
Cleaned up whitespace for the 2.0.0 release
slouken@411
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@601
     3
  Copyright (C) 1997-2013 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@617
    53
    CHAR *list;
slouken@419
    54
slouken@617
    55
    if ( !Mix_Init(MIX_INIT_MOD) ) {
slouken@617
    56
        return -1;
slouken@617
    57
    }
slouken@411
    58
slouken@617
    59
    /* Set the MikMod music format */
slouken@617
    60
    music_swap8 = 0;
slouken@617
    61
    music_swap16 = 0;
slouken@617
    62
    switch (mixerfmt->format) {
slouken@411
    63
slouken@617
    64
        case AUDIO_U8:
slouken@617
    65
        case AUDIO_S8: {
slouken@617
    66
            if ( mixerfmt->format == AUDIO_S8 ) {
slouken@617
    67
                music_swap8 = 1;
slouken@617
    68
            }
slouken@617
    69
            *mikmod.md_mode = 0;
slouken@617
    70
        }
slouken@617
    71
        break;
slouken@411
    72
slouken@617
    73
        case AUDIO_S16LSB:
slouken@617
    74
        case AUDIO_S16MSB: {
slouken@617
    75
            /* See if we need to correct MikMod mixing */
slouken@411
    76
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@617
    77
            if ( mixerfmt->format == AUDIO_S16MSB ) {
slouken@411
    78
#else
slouken@617
    79
            if ( mixerfmt->format == AUDIO_S16LSB ) {
slouken@411
    80
#endif
slouken@617
    81
                music_swap16 = 1;
slouken@617
    82
            }
slouken@617
    83
            *mikmod.md_mode = DMODE_16BITS;
slouken@617
    84
        }
slouken@617
    85
        break;
slouken@411
    86
slouken@617
    87
        default: {
slouken@617
    88
            Mix_SetError("Unknown hardware audio format");
slouken@617
    89
            return -1;
slouken@617
    90
        }
slouken@617
    91
    }
slouken@617
    92
    current_output_channels = mixerfmt->channels;
slouken@617
    93
    current_output_format = mixerfmt->format;
slouken@617
    94
    if ( mixerfmt->channels > 1 ) {
slouken@617
    95
        if ( mixerfmt->channels > MAX_OUTPUT_CHANNELS ) {
slouken@617
    96
            Mix_SetError("Hardware uses more channels than mixerfmt");
slouken@617
    97
            return -1;
slouken@617
    98
        }
slouken@617
    99
        *mikmod.md_mode |= DMODE_STEREO;
slouken@617
   100
    }
slouken@617
   101
    *mikmod.md_mixfreq = mixerfmt->freq;
slouken@617
   102
    *mikmod.md_device  = 0;
slouken@617
   103
    *mikmod.md_volume  = 96;
slouken@617
   104
    *mikmod.md_musicvolume = 128;
slouken@617
   105
    *mikmod.md_sndfxvolume = 128;
slouken@617
   106
    *mikmod.md_pansep  = 128;
slouken@617
   107
    *mikmod.md_reverb  = 0;
slouken@617
   108
    *mikmod.md_mode    |= DMODE_HQMIXER|DMODE_SOFT_MUSIC|DMODE_SURROUND;
slouken@419
   109
slouken@617
   110
    list = mikmod.MikMod_InfoDriver();
slouken@617
   111
    if ( list )
slouken@617
   112
      mikmod.MikMod_free(list);
slouken@617
   113
    else
slouken@617
   114
      mikmod.MikMod_RegisterDriver(mikmod.drv_nos);
slouken@419
   115
slouken@617
   116
    list = mikmod.MikMod_InfoLoader();
slouken@617
   117
    if ( list )
slouken@617
   118
      mikmod.MikMod_free(list);
slouken@617
   119
    else
slouken@617
   120
      mikmod.MikMod_RegisterAllLoaders();
slouken@419
   121
slouken@617
   122
    if ( mikmod.MikMod_Init(NULL) ) {
slouken@617
   123
        Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
slouken@617
   124
        return -1;
slouken@617
   125
    }
slouken@411
   126
slouken@617
   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@617
   133
    if (mikmod.MikMod_Exit) {
slouken@617
   134
        mikmod.MikMod_Exit();
slouken@617
   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@617
   141
    mikmod.Player_SetVolume((SWORD)volume);
slouken@411
   142
}
slouken@411
   143
slouken@411
   144
typedef struct
slouken@411
   145
{
slouken@617
   146
    MREADER mr;
slouken@617
   147
    long offset;
slouken@617
   148
    long eof;
slouken@617
   149
    SDL_RWops *rw;
slouken@411
   150
} LMM_MREADER;
slouken@411
   151
slouken@411
   152
BOOL LMM_Seek(struct MREADER *mr,long to,int dir)
slouken@411
   153
{
slouken@617
   154
    LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
slouken@617
   155
    if ( dir == SEEK_SET ) {
slouken@617
   156
        to += lmmmr->offset;
slouken@617
   157
    }
slouken@617
   158
    return (SDL_RWseek(lmmmr->rw, to, dir) < lmmmr->offset);
slouken@411
   159
}
slouken@411
   160
long LMM_Tell(struct MREADER *mr)
slouken@411
   161
{
slouken@617
   162
    LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
slouken@617
   163
    return SDL_RWtell(lmmmr->rw) - lmmmr->offset;
slouken@411
   164
}
slouken@411
   165
BOOL LMM_Read(struct MREADER *mr,void *buf,size_t sz)
slouken@411
   166
{
slouken@617
   167
    LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
slouken@617
   168
    return SDL_RWread(lmmmr->rw, buf, sz, 1);
slouken@411
   169
}
slouken@411
   170
int LMM_Get(struct MREADER *mr)
slouken@411
   171
{
slouken@617
   172
    unsigned char c;
slouken@617
   173
    LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
slouken@617
   174
    if ( SDL_RWread(lmmmr->rw, &c, 1, 1) ) {
slouken@617
   175
        return c;
slouken@617
   176
    }
slouken@617
   177
    return EOF;
slouken@411
   178
}
slouken@411
   179
BOOL LMM_Eof(struct MREADER *mr)
slouken@411
   180
{
slouken@617
   181
    long offset;
slouken@617
   182
    LMM_MREADER* lmmmr = (LMM_MREADER*)mr;
slouken@617
   183
    offset = LMM_Tell(mr);
slouken@617
   184
    return offset >= lmmmr->eof;
slouken@411
   185
}
slouken@411
   186
MODULE *MikMod_LoadSongRW(SDL_RWops *rw, int maxchan)
slouken@411
   187
{
slouken@617
   188
    LMM_MREADER lmmmr = {
slouken@617
   189
        { LMM_Seek, LMM_Tell, LMM_Read, LMM_Get, LMM_Eof },
slouken@617
   190
        0,
slouken@617
   191
        0,
slouken@617
   192
        0
slouken@617
   193
    };
slouken@617
   194
    lmmmr.offset = SDL_RWtell(rw);
slouken@617
   195
    SDL_RWseek(rw, 0, RW_SEEK_END);
slouken@617
   196
    lmmmr.eof = SDL_RWtell(rw);
slouken@617
   197
    SDL_RWseek(rw, lmmmr.offset, RW_SEEK_SET);
slouken@411
   198
        lmmmr.rw = rw;
slouken@617
   199
    return mikmod.Player_LoadGeneric((MREADER*)&lmmmr, maxchan, 0);
slouken@411
   200
}
slouken@411
   201
slouken@411
   202
/* Load a MOD stream from an SDL_RWops object */
slouken@521
   203
MODULE *MOD_new_RW(SDL_RWops *rw, int freerw)
slouken@411
   204
{
slouken@617
   205
    MODULE *module;
slouken@419
   206
slouken@617
   207
    /* Make sure the mikmod library is loaded */
slouken@617
   208
    if ( !Mix_Init(MIX_INIT_MOD) ) {
slouken@617
   209
        if ( freerw ) {
slouken@617
   210
            SDL_RWclose(rw);
slouken@617
   211
        }
slouken@617
   212
        return NULL;
slouken@617
   213
    }
slouken@479
   214
slouken@617
   215
    module = MikMod_LoadSongRW(rw,64);
slouken@617
   216
    if (!module) {
slouken@617
   217
        Mix_SetError("%s", mikmod.MikMod_strerror(*mikmod.MikMod_errno));
slouken@617
   218
        if ( freerw ) {
slouken@617
   219
            SDL_RWclose(rw);
slouken@617
   220
        }
slouken@617
   221
        return NULL;
slouken@617
   222
    }
slouken@411
   223
slouken@617
   224
    /* Stop implicit looping, fade out and other flags. */
slouken@617
   225
    module->extspd  = 1;
slouken@617
   226
    module->panflag = 1;
slouken@617
   227
    module->wrap    = 0;
slouken@617
   228
    module->loop    = 0;
slouken@411
   229
#if 0 /* Don't set fade out by default - unfortunately there's no real way
slouken@411
   230
to query the status of the song or set trigger actions.  Hum. */
slouken@617
   231
    module->fadeout = 1;
slouken@411
   232
#endif
slouken@521
   233
slouken@617
   234
    if ( freerw ) {
slouken@617
   235
        SDL_RWclose(rw);
slouken@617
   236
    }
slouken@617
   237
    return module;
slouken@411
   238
}
slouken@411
   239
slouken@411
   240
/* Start playback of a given MOD stream */
slouken@411
   241
void MOD_play(MODULE *music)
slouken@411
   242
{
slouken@617
   243
    mikmod.Player_Start(music);
slouken@411
   244
}
slouken@411
   245
slouken@411
   246
/* Return non-zero if a stream is currently playing */
slouken@411
   247
int MOD_playing(MODULE *music)
slouken@411
   248
{
slouken@617
   249
    return mikmod.Player_Active();
slouken@411
   250
}
slouken@411
   251
slouken@411
   252
/* Play some of a stream previously started with MOD_play() */
slouken@411
   253
int MOD_playAudio(MODULE *music, Uint8 *stream, int len)
slouken@411
   254
{
slouken@617
   255
    if (current_output_channels > 2) {
slouken@617
   256
        int small_len = 2 * len / current_output_channels;
slouken@617
   257
        int i;
slouken@617
   258
        Uint8 *src, *dst;
slouken@411
   259
slouken@617
   260
        mikmod.VC_WriteBytes((SBYTE *)stream, small_len);
slouken@617
   261
        /* and extend to len by copying channels */
slouken@617
   262
        src = stream + small_len;
slouken@617
   263
        dst = stream + len;
slouken@411
   264
slouken@617
   265
        switch (current_output_format & 0xFF) {
slouken@617
   266
            case 8:
slouken@617
   267
                for ( i=small_len/2; i; --i ) {
slouken@617
   268
                    src -= 2;
slouken@617
   269
                    dst -= current_output_channels;
slouken@617
   270
                    dst[0] = src[0];
slouken@617
   271
                    dst[1] = src[1];
slouken@617
   272
                    dst[2] = src[0];
slouken@617
   273
                    dst[3] = src[1];
slouken@617
   274
                    if (current_output_channels == 6) {
slouken@617
   275
                        dst[4] = src[0];
slouken@617
   276
                        dst[5] = src[1];
slouken@617
   277
                    }
slouken@617
   278
                }
slouken@617
   279
                break;
slouken@617
   280
            case 16:
slouken@617
   281
                for ( i=small_len/4; i; --i ) {
slouken@617
   282
                    src -= 4;
slouken@617
   283
                    dst -= 2 * current_output_channels;
slouken@617
   284
                    dst[0] = src[0];
slouken@617
   285
                    dst[1] = src[1];
slouken@617
   286
                    dst[2] = src[2];
slouken@617
   287
                    dst[3] = src[3];
slouken@617
   288
                    dst[4] = src[0];
slouken@617
   289
                    dst[5] = src[1];
slouken@617
   290
                    dst[6] = src[2];
slouken@617
   291
                    dst[7] = src[3];
slouken@617
   292
                    if (current_output_channels == 6) {
slouken@617
   293
                        dst[8] = src[0];
slouken@617
   294
                        dst[9] = src[1];
slouken@617
   295
                        dst[10] = src[2];
slouken@617
   296
                        dst[11] = src[3];
slouken@617
   297
                    }
slouken@617
   298
                }
slouken@617
   299
                break;
slouken@617
   300
        }
slouken@617
   301
    } else {
slouken@617
   302
        mikmod.VC_WriteBytes((SBYTE *)stream, len);
slouken@617
   303
    }
slouken@617
   304
    if ( music_swap8 ) {
slouken@617
   305
        Uint8 *dst;
slouken@617
   306
        int i;
slouken@411
   307
slouken@617
   308
        dst = stream;
slouken@617
   309
        for ( i=len; i; --i ) {
slouken@617
   310
            *dst++ ^= 0x80;
slouken@617
   311
        }
slouken@617
   312
    } else
slouken@617
   313
    if ( music_swap16 ) {
slouken@617
   314
        Uint8 *dst, tmp;
slouken@617
   315
        int i;
slouken@411
   316
slouken@617
   317
        dst = stream;
slouken@617
   318
        for ( i=(len/2); i; --i ) {
slouken@617
   319
            tmp = dst[0];
slouken@617
   320
            dst[0] = dst[1];
slouken@617
   321
            dst[1] = tmp;
slouken@617
   322
            dst += 2;
slouken@617
   323
        }
slouken@617
   324
    }
slouken@617
   325
    return 0;
slouken@411
   326
}
slouken@411
   327
slouken@411
   328
/* Stop playback of a stream previously started with MOD_play() */
slouken@411
   329
void MOD_stop(MODULE *music)
slouken@411
   330
{
slouken@617
   331
    mikmod.Player_Stop();
slouken@411
   332
}
slouken@411
   333
slouken@411
   334
/* Close the given MOD stream */
slouken@411
   335
void MOD_delete(MODULE *music)
slouken@411
   336
{
slouken@617
   337
    mikmod.Player_Free(music);
slouken@411
   338
}
slouken@411
   339
slouken@411
   340
/* Jump (seek) to a given position (time is in seconds) */
slouken@411
   341
void MOD_jump_to_time(MODULE *music, double time)
slouken@411
   342
{
slouken@617
   343
    mikmod.Player_SetPosition((UWORD)time);
slouken@411
   344
}
slouken@411
   345
slouken@411
   346
#endif /* MOD_MUSIC */