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