1 /*
2  * tracker music (module file) decoding support using libmikmod
3  * Copyright (C) 2013 O.Sezer <sezero@users.sourceforge.net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  *
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #include "quakedef.h"
22 #include "sound.h"
23 
24 #if defined(USE_CODEC_MIKMOD)
25 #include "snd_codec.h"
26 #include "snd_codeci.h"
27 #include "snd_mikmod.h"
28 #include <mikmod.h>
29 
30 #if ((LIBMIKMOD_VERSION+0) < 0x030105)
31 #error libmikmod version is way too old and unusable.
32 #endif
33 #if (LIBMIKMOD_VERSION < 0x030107) /* ancient libmikmod */
34 #define S_MIKMOD_initlib(c) MikMod_Init()
35 #else
36 #define S_MIKMOD_initlib(c) MikMod_Init(c)
37 #endif
38 
39 #ifndef DMODE_NOISEREDUCTION
40 #define DMODE_NOISEREDUCTION 0x1000 /* Low pass filtering */
41 #endif
42 #ifndef DMODE_SIMDMIXER
43 #define DMODE_SIMDMIXER 0x0800 /* enable SIMD mixing */
44 #endif
45 
46 typedef struct _mik_priv {
47 /* struct MREADER in libmikmod <= 3.2.0-beta2
48  * doesn't have iobase members. adding them here
49  * so that if we compile against 3.2.0-beta2, we
50  * can still run OK against 3.2.0b3 and newer. */
51 	struct MREADER reader;
52 	long iobase, prev_iobase;
53 	fshandle_t *fh;
54 	MODULE *module;
55 } mik_priv_t;
56 
MIK_Seek(MREADER * r,long ofs,int whence)57 static int MIK_Seek (MREADER *r, long ofs, int whence)
58 {
59 	return FS_fseek(((mik_priv_t *)r)->fh, ofs, whence);
60 }
61 
MIK_Tell(MREADER * r)62 static long MIK_Tell (MREADER *r)
63 {
64 	return FS_ftell(((mik_priv_t *)r)->fh);
65 }
66 
MIK_Read(MREADER * r,void * ptr,size_t siz)67 static BOOL MIK_Read (MREADER *r, void *ptr, size_t siz)
68 {
69 	return !!FS_fread(ptr, siz, 1, ((mik_priv_t *)r)->fh);
70 }
71 
MIK_Get(MREADER * r)72 static int MIK_Get (MREADER *r)
73 {
74 	return FS_fgetc(((mik_priv_t *)r)->fh);
75 }
76 
MIK_Eof(MREADER * r)77 static BOOL MIK_Eof (MREADER *r)
78 {
79 	return FS_feof(((mik_priv_t *)r)->fh);
80 }
81 
S_MIKMOD_CodecInitialize(void)82 static qboolean S_MIKMOD_CodecInitialize (void)
83 {
84 	if (mikmod_codec.initialized)
85 		return true;
86 
87 	/* set mode flags to only we like: */
88 	md_mode = 0;
89 	if ((shm->samplebits / 8) == 2)
90 		md_mode |= DMODE_16BITS;
91 	if (shm->channels == 2)
92 		md_mode |= DMODE_STEREO;
93 	md_mode |= DMODE_SOFT_MUSIC;	/* this is a software-only mixer */
94 	md_mode |= DMODE_HQMIXER;		/* high-quality mixer is OK */
95 
96 	/* md_mixfreq is UWORD, so something like 96000 isn't OK */
97 	md_mixfreq = (shm->speed < 65536)? shm->speed : 48000;
98 
99 	/* keeping md_device as 0 which is default (auto-detect: we
100 	 * only register drv_nos, and it will be the only one found.)
101 	 * md_pansep (stereo channels separation) default 128 is OK.
102 	 * no reverbation (md_reverb 0 (up to 15)) is OK.
103 	 * md_musicvolume and md_sndfxvolume defaults are 128: OK. */
104 	/* just tone down overall volume md_volume from 128 to 96? */
105 	md_volume = 96;
106 
107 	MikMod_RegisterDriver(&drv_nos);	/* only need the "nosound" driver, none else */
108 	MikMod_RegisterAllLoaders();
109 	if (S_MIKMOD_initlib(NULL))
110 	{
111 		Con_DPrintf("Could not initialize MikMod: %s\n", MikMod_strerror(MikMod_errno));
112 		return false;
113 	}
114 
115 	/* this can't get set with drv_nos, but whatever, be safe: */
116 	md_mode &= ~DMODE_SIMDMIXER;	/* SIMD mixer is buggy when combined with HQMIXER */
117 
118 	mikmod_codec.initialized = true;
119 	return true;
120 }
121 
S_MIKMOD_CodecShutdown(void)122 static void S_MIKMOD_CodecShutdown (void)
123 {
124 	if (mikmod_codec.initialized)
125 	{
126 		mikmod_codec.initialized = false;
127 		MikMod_Exit();
128 	}
129 }
130 
S_MIKMOD_CodecOpenStream(snd_stream_t * stream)131 static qboolean S_MIKMOD_CodecOpenStream (snd_stream_t *stream)
132 {
133 	mik_priv_t *priv;
134 
135 	stream->priv = Z_Malloc(sizeof(mik_priv_t));
136 	priv = (mik_priv_t *) stream->priv;
137 	priv->reader.Seek = MIK_Seek;
138 	priv->reader.Tell = MIK_Tell;
139 	priv->reader.Read = MIK_Read;
140 	priv->reader.Get  = MIK_Get;
141 	priv->reader.Eof  = MIK_Eof;
142 	priv->fh = &stream->fh;
143 
144 	priv->module = Player_LoadGeneric((MREADER *)stream->priv, 64, 0);
145 	if (!priv->module)
146 	{
147 		Con_DPrintf("Could not load module: %s\n", MikMod_strerror(MikMod_errno));
148 		Z_Free(stream->priv);
149 		return false;
150 	}
151 
152 	/* keep default values of fadeout (0: don't fade out volume during when last
153 	 * position of the module is being played), extspd (1: do process Protracker
154 	 * extended speed effect), panflag (1: do process panning effects), wrap (0:
155 	 * don't wrap to restart position when module is finished) are OK with us as
156 	 * set internally by libmikmod::Player_Init(). */
157 	/* just change the loop setting to 0, i.e. don't process in-module loops: */
158 	priv->module->loop	= 0;
159 	Player_Start(priv->module);
160 
161 	stream->info.rate	= md_mixfreq;
162 	stream->info.bits	= (md_mode & DMODE_16BITS)? 16: 8;
163 	stream->info.width	= stream->info.bits / 8;
164 	stream->info.channels	= (md_mode & DMODE_STEREO)? 2 : 1;
165 /*	Con_DPrintf("Playing %s (%d chn)\n", priv->module->songname, priv->module->numchn);*/
166 
167 	return true;
168 }
169 
S_MIKMOD_CodecReadStream(snd_stream_t * stream,int bytes,void * buffer)170 static int S_MIKMOD_CodecReadStream (snd_stream_t *stream, int bytes, void *buffer)
171 {
172 	if (!Player_Active())
173 		return 0;
174 	return (int) VC_WriteBytes((SBYTE *)buffer, bytes);
175 }
176 
S_MIKMOD_CodecCloseStream(snd_stream_t * stream)177 static void S_MIKMOD_CodecCloseStream (snd_stream_t *stream)
178 {
179 	Player_Stop();
180 	Player_Free(((mik_priv_t *)stream->priv)->module);
181 	Z_Free(stream->priv);
182 	S_CodecUtilClose(&stream);
183 }
184 
S_MIKMOD_CodecRewindStream(snd_stream_t * stream)185 static int S_MIKMOD_CodecRewindStream (snd_stream_t *stream)
186 {
187 	Player_SetPosition (0);
188 	return 0;
189 }
190 
191 snd_codec_t mikmod_codec =
192 {
193 	CODECTYPE_MOD,
194 	false,
195 	"s3m",
196 	S_MIKMOD_CodecInitialize,
197 	S_MIKMOD_CodecShutdown,
198 	S_MIKMOD_CodecOpenStream,
199 	S_MIKMOD_CodecReadStream,
200 	S_MIKMOD_CodecRewindStream,
201 	S_MIKMOD_CodecCloseStream,
202 	NULL
203 };
204 
205 #endif	/* USE_CODEC_MIKMOD */
206 
207