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