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
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2013 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     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 */