1 /* 2 * SDL_sound -- An abstract sound format decoding API. 3 * Copyright (C) 2001 Ryan C. Gordon. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20 /* 21 * Module player for SDL_sound. This driver handles anything MikMod does, and 22 * is based on SDL_mixer. 23 * 24 * Please see the file COPYING in the source's root directory. 25 * 26 * This file written by Torbjörn Andersson (d91tan@Update.UU.SE) 27 */ 28 29 #if HAVE_CONFIG_H 30 # include <config.h> 31 #endif 32 33 #ifdef SOUND_SUPPORTS_MIKMOD 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include "SDL_sound.h" 40 41 #define __SDL_SOUND_INTERNAL__ 42 #include "SDL_sound_internal.h" 43 44 #include "mikmod.h" 45 46 47 static int MIKMOD_init(void); 48 static void MIKMOD_quit(void); 49 static int MIKMOD_open(Sound_Sample *sample, const char *ext); 50 static void MIKMOD_close(Sound_Sample *sample); 51 static Uint32 MIKMOD_read(Sound_Sample *sample); 52 static int MIKMOD_rewind(Sound_Sample *sample); 53 static int MIKMOD_seek(Sound_Sample *sample, Uint32 ms); 54 55 static const char *extensions_mikmod[] = 56 { 57 "669", /* Composer 669 */ 58 "AMF", /* DMP Advanced Module Format */ 59 "DSM", /* DSIK internal format */ 60 "FAR", /* Farandole module */ 61 "GDM", /* General DigiMusic module */ 62 "IMF", /* Imago Orpheus module */ 63 "IT", /* Impulse tracker */ 64 "M15", /* 15 instrument MOD / Ultimate Sound Tracker (old M15 format) */ 65 "MED", /* Amiga MED module */ 66 "MOD", /* Generic MOD (Protracker, StarTracker, FastTracker, etc) */ 67 "MTM", /* MTM module */ 68 "OKT", /* Oktalyzer module */ 69 "S3M", /* Screamtracker module */ 70 "STM", /* Screamtracker 2 module */ 71 "STX", /* STMIK 0.2 module */ 72 "ULT", /* Ultratracker module */ 73 "UNI", /* UNIMOD - libmikmod's and APlayer's internal module format */ 74 "XM", /* Fasttracker module */ 75 NULL 76 }; 77 78 const Sound_DecoderFunctions __Sound_DecoderFunctions_MIKMOD = 79 { 80 { 81 extensions_mikmod, 82 "Play modules through MikMod", 83 "Torbjörn Andersson <d91tan@Update.UU.SE>", 84 "http://mikmod.raphnet.net/" 85 }, 86 87 MIKMOD_init, /* init() method */ 88 MIKMOD_quit, /* quit() method */ 89 MIKMOD_open, /* open() method */ 90 MIKMOD_close, /* close() method */ 91 MIKMOD_read, /* read() method */ 92 MIKMOD_rewind, /* rewind() method */ 93 MIKMOD_seek /* seek() method */ 94 }; 95 96 97 /* Make MikMod read from a RWops... */ 98 99 typedef struct MRWOPSREADER { 100 MREADER core; 101 Sound_Sample *sample; 102 int end; 103 } MRWOPSREADER; 104 105 static BOOL _mm_RWopsReader_eof(MREADER *reader) 106 { 107 MRWOPSREADER *rwops_reader = (MRWOPSREADER *) reader; 108 Sound_Sample *sample = rwops_reader->sample; 109 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 110 int pos = SDL_RWtell(internal->rw); 111 112 if (rwops_reader->end == pos) 113 return(1); 114 115 return(0); 116 } /* _mm_RWopsReader_eof */ 117 118 119 static BOOL _mm_RWopsReader_read(MREADER *reader, void *ptr, size_t size) 120 { 121 MRWOPSREADER *rwops_reader = (MRWOPSREADER *) reader; 122 Sound_Sample *sample = rwops_reader->sample; 123 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 124 return(SDL_RWread(internal->rw, ptr, size, 1)); 125 } /* _mm_RWopsReader_Read */ 126 127 128 static int _mm_RWopsReader_get(MREADER *reader) 129 { 130 char buf; 131 MRWOPSREADER *rwops_reader = (MRWOPSREADER *) reader; 132 Sound_Sample *sample = rwops_reader->sample; 133 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 134 135 if (SDL_RWread(internal->rw, &buf, 1, 1) != 1) 136 return(EOF); 137 138 return((int) buf); 139 } /* _mm_RWopsReader_get */ 140 141 142 static BOOL _mm_RWopsReader_seek(MREADER *reader, long offset, int whence) 143 { 144 MRWOPSREADER *rwops_reader = (MRWOPSREADER *) reader; 145 Sound_Sample *sample = rwops_reader->sample; 146 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 147 148 return(SDL_RWseek(internal->rw, offset, whence)); 149 } /* _mm_RWopsReader_seek */ 150 151 152 static long _mm_RWopsReader_tell(MREADER *reader) 153 { 154 MRWOPSREADER *rwops_reader = (MRWOPSREADER *) reader; 155 Sound_Sample *sample = rwops_reader->sample; 156 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 157 158 return(SDL_RWtell(internal->rw)); 159 } /* _mm_RWopsReader_tell */ 160 161 162 static MREADER *_mm_new_rwops_reader(Sound_Sample *sample) 163 { 164 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 165 166 MRWOPSREADER *reader = (MRWOPSREADER *) malloc(sizeof (MRWOPSREADER)); 167 if (reader != NULL) 168 { 169 int failed_seek = 1; 170 int here; 171 reader->core.Eof = _mm_RWopsReader_eof; 172 reader->core.Read = _mm_RWopsReader_read; 173 reader->core.Get = _mm_RWopsReader_get; 174 reader->core.Seek = _mm_RWopsReader_seek; 175 reader->core.Tell = _mm_RWopsReader_tell; 176 reader->sample = sample; 177 178 /* RWops does not explicitly support an eof check, so we shall find 179 the end manually - this requires seek support for the RWop */ 180 here = SDL_RWtell(internal->rw); 181 if (here != -1) 182 { 183 reader->end = SDL_RWseek(internal->rw, 0, SEEK_END); 184 if (reader->end != -1) 185 { 186 /* Move back */ 187 if (SDL_RWseek(internal->rw, here, SEEK_SET) != -1) 188 failed_seek = 0; 189 } /* if */ 190 } /* if */ 191 192 if (failed_seek) 193 { 194 free(reader); 195 reader = NULL; 196 } /* if */ 197 } /* if */ 198 199 return((MREADER *) reader); 200 } /* _mm_new_rwops_reader */ 201 202 203 static void _mm_delete_rwops_reader(MREADER *reader) 204 { 205 /* SDL_sound will delete the RWops and sample at a higher level... */ 206 if (reader != NULL) 207 free(reader); 208 } /* _mm_delete_rwops_reader */ 209 210 211 212 static int MIKMOD_init(void) 213 { 214 MikMod_RegisterDriver(&drv_nos); 215 216 /* Quick and dirty hack to prevent an infinite loop problem 217 * found when using SDL_mixer and SDL_sound together and 218 * both have MikMod compiled in. So, check to see if 219 * MikMod has already been registered first before calling 220 * RegisterAllLoaders() 221 */ 222 if(MikMod_InfoLoader() == NULL) 223 { 224 MikMod_RegisterAllLoaders(); 225 } 226 /* 227 * Both DMODE_SOFT_MUSIC and DMODE_16BITS should be set by default, 228 * so this is just for clarity. I haven't experimented with any of 229 * the other flags. There are a few which are said to give better 230 * sound quality. 231 */ 232 md_mode |= (DMODE_SOFT_MUSIC | DMODE_16BITS); 233 md_mixfreq = 0; 234 md_reverb = 1; 235 236 BAIL_IF_MACRO(MikMod_Init(""), MikMod_strerror(MikMod_errno), 0); 237 238 return(1); /* success. */ 239 } /* MIKMOD_init */ 240 241 242 static void MIKMOD_quit(void) 243 { 244 MikMod_Exit(); 245 md_mixfreq = 0; 246 } /* MIKMOD_quit */ 247 248 249 static int MIKMOD_open(Sound_Sample *sample, const char *ext) 250 { 251 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 252 MREADER *reader; 253 MODULE *module; 254 255 reader = _mm_new_rwops_reader(sample); 256 BAIL_IF_MACRO(reader == NULL, ERR_OUT_OF_MEMORY, 0); 257 module = Player_LoadGeneric(reader, 64, 0); 258 _mm_delete_rwops_reader(reader); 259 BAIL_IF_MACRO(module == NULL, "MIKMOD: Not a module file.", 0); 260 261 module->extspd = 1; 262 module->panflag = 1; 263 module->wrap = 0; 264 module->loop = 0; 265 266 if (md_mixfreq == 0) 267 md_mixfreq = (!sample->desired.rate) ? 44100 : sample->desired.rate; 268 269 sample->actual.channels = 2; 270 sample->actual.rate = md_mixfreq; 271 sample->actual.format = AUDIO_S16SYS; 272 internal->decoder_private = (void *) module; 273 274 Player_Start(module); 275 Player_SetPosition(0); 276 277 sample->flags = SOUND_SAMPLEFLAG_NONE; 278 279 SNDDBG(("MIKMOD: Name: %s\n", module->songname)); 280 SNDDBG(("MIKMOD: Type: %s\n", module->modtype)); 281 SNDDBG(("MIKMOD: Accepting data stream\n")); 282 283 return(1); /* we'll handle this data. */ 284 } /* MIKMOD_open */ 285 286 287 static void MIKMOD_close(Sound_Sample *sample) 288 { 289 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 290 MODULE *module = (MODULE *) internal->decoder_private; 291 292 Player_Free(module); 293 } /* MIKMOD_close */ 294 295 296 static Uint32 MIKMOD_read(Sound_Sample *sample) 297 { 298 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 299 MODULE *module = (MODULE *) internal->decoder_private; 300 301 /* Switch to the current module, stopping any previous one. */ 302 Player_Start(module); 303 if (!Player_Active()) 304 { 305 sample->flags |= SOUND_SAMPLEFLAG_EOF; 306 return(0); 307 } /* if */ 308 return((Uint32) VC_WriteBytes(internal->buffer, internal->buffer_size)); 309 } /* MIKMOD_read */ 310 311 312 static int MIKMOD_rewind(Sound_Sample *sample) 313 { 314 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 315 MODULE *module = (MODULE *) internal->decoder_private; 316 317 Player_Start(module); 318 Player_SetPosition(0); 319 return(1); 320 } /* MIKMOD_rewind */ 321 322 323 static int MIKMOD_seek(Sound_Sample *sample, Uint32 ms) 324 { 325 #if 0 326 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; 327 MODULE *module = (MODULE *) internal->decoder_private; 328 329 /* 330 * Heaven may know what the argument to Player_SetPosition() is. 331 * I, however, haven't the faintest idea. 332 */ 333 Player_Start(module); 334 Player_SetPosition(ms); 335 return(1); 336 #else 337 BAIL_MACRO("MIKMOD: Seeking not implemented", 0); 338 #endif 339 } /* MIKMOD_seek */ 340 341 #endif /* SOUND_SUPPORTS_MIKMOD */ 342 343 344 /* end of mikmod.c ... */ 345