1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_timidity.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include "gamedefs.h"
29 #include "snd_local.h"
30 
31 #include "timidity/timidity.h"
32 
33 using namespace LibTimidity;
34 
35 // MACROS ------------------------------------------------------------------
36 
37 // TYPES -------------------------------------------------------------------
38 
39 class VTimidityAudioCodec : public VAudioCodec
40 {
41 public:
42 	MidiSong*			Song;
43 
44 	static ControlMode	MyControlMode;
45 
46 	VTimidityAudioCodec(MidiSong* InSong);
47 	~VTimidityAudioCodec();
48 	int Decode(short* Data, int NumSamples);
49 	bool Finished();
50 	void Restart();
51 
52 	//	Control mode functions.
53 	static int ctl_msg(int, int, const char*, ...);
54 
55 	static VAudioCodec* Create(VStream* InStrm);
56 };
57 
58 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
59 
60 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
61 
62 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
63 
64 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
65 
66 // PUBLIC DATA DEFINITIONS -------------------------------------------------
67 
68 // PRIVATE DATA DEFINITIONS ------------------------------------------------
69 
70 IMPLEMENT_AUDIO_CODEC(VTimidityAudioCodec, "Timidity");
71 
72 ControlMode		VTimidityAudioCodec::MyControlMode =
73 {
74 	VTimidityAudioCodec::ctl_msg,
75 };
76 
77 #if defined(DJGPP) || defined(_WIN32)
78 static VCvarI	s_timidity("s_timidity", "0", CVAR_Archive);
79 static VCvarS	s_timidity_patches("s_timidity_patches", "\\TIMIDITY", CVAR_Archive);
80 #else
81 static VCvarI	s_timidity("s_timidity", "1", CVAR_Archive);
82 static VCvarS	s_timidity_patches("s_timidity_patches", "/usr/share/timidity", CVAR_Archive);
83 #endif
84 static VCvarI	s_timidity_test_dls("s_timidity_test_dls", "0", CVAR_Archive);
85 static VCvarI	s_timidity_test_sf2("s_timidity_test_sf2", "0", CVAR_Archive);
86 static VCvarI	s_timidity_verbosity("s_timidity_verbosity", "0", CVAR_Archive);
87 
88 // CODE --------------------------------------------------------------------
89 
90 //==========================================================================
91 //
92 //	VTimidityAudioCodec::VTimidityAudioCodec
93 //
94 //==========================================================================
95 
VTimidityAudioCodec(MidiSong * InSong)96 VTimidityAudioCodec::VTimidityAudioCodec(MidiSong* InSong)
97 : Song(InSong)
98 {
99 	guard(VTimidityAudioCodec::VTimidityAudioCodec);
100 	Timidity_SetVolume(Song, 100);
101 	Timidity_Start(Song);
102 	unguard;
103 }
104 
105 //==========================================================================
106 //
107 //	VTimidityAudioCodec::~VTimidityAudioCodec
108 //
109 //==========================================================================
110 
~VTimidityAudioCodec()111 VTimidityAudioCodec::~VTimidityAudioCodec()
112 {
113 	guard(VTimidityAudioCodec::~VTimidityAudioCodec);
114 	Timidity_Stop(Song);
115 	if (Song->patches)
116 		Timidity_FreeDLS(Song->patches);
117 	if (Song->sf2_font)
118 		Timidity_FreeSf2(Song->sf2_font);
119 	Timidity_FreeSong(Song);
120 	Timidity_Close();
121 	unguard;
122 }
123 
124 //==========================================================================
125 //
126 //	VTimidityAudioCodec::Decode
127 //
128 //==========================================================================
129 
Decode(short * Data,int NumSamples)130 int VTimidityAudioCodec::Decode(short* Data, int NumSamples)
131 {
132 	guard(VTimidityAudioCodec::Decode);
133 	return Timidity_PlaySome(Song, Data, NumSamples);
134 	unguard;
135 }
136 
137 //==========================================================================
138 //
139 //	VTimidityAudioCodec::Finished
140 //
141 //==========================================================================
142 
Finished()143 bool VTimidityAudioCodec::Finished()
144 {
145 	guard(VTimidityAudioCodec::Finished);
146 	return !Timidity_Active(Song);
147 	unguard;
148 }
149 
150 //==========================================================================
151 //
152 //	VTimidityAudioCodec::Restart
153 //
154 //==========================================================================
155 
Restart()156 void VTimidityAudioCodec::Restart()
157 {
158 	guard(VTimidityAudioCodec::Restart);
159 	Timidity_Start(Song);
160 	unguard;
161 }
162 
163 //==========================================================================
164 //
165 //	Minimal control mode -- no interaction, just stores messages.
166 //
167 //==========================================================================
168 
ctl_msg(int type,int verbosity_level,const char * fmt,...)169 int VTimidityAudioCodec::ctl_msg(int type, int verbosity_level, const char *fmt, ...)
170 {
171 	guard(VTimidityAudioCodec::ctl_msg);
172 	char Buf[1024];
173 	va_list ap;
174 	if ((type == CMSG_TEXT || type == CMSG_INFO || type == CMSG_WARNING) &&
175 		s_timidity_verbosity < verbosity_level)
176 		return 0;
177 	va_start(ap, fmt);
178 	vsprintf(Buf, fmt, ap);
179 	GCon->Log(Buf);
180 	va_end(ap);
181 	return 0;
182 	unguard;
183 }
184 
185 //==========================================================================
186 //
187 //	VTimidityAudioCodec::Create
188 //
189 //==========================================================================
190 
Create(VStream * InStrm)191 VAudioCodec* VTimidityAudioCodec::Create(VStream* InStrm)
192 {
193 	guard(VTimidityAudioCodec::Create);
194 	//	Handle only if enabled.
195 	if (!s_timidity)
196 		return NULL;
197 
198 	//	Check if it's a MIDI file.
199 	char Header[4];
200 	InStrm->Seek(0);
201 	InStrm->Serialise(Header, 4);
202 	if (memcmp(Header, MIDIMAGIC, 4))
203 	{
204 		return NULL;
205 	}
206 
207 	//	Register our control mode.
208 	ctl = &MyControlMode;
209 
210 	//	Initialise Timidity.
211 	add_to_pathlist(s_timidity_patches);
212 	DLS_Data* patches = NULL;
213 	if (Timidity_Init())
214 	{
215 #ifdef _WIN32
216         VStr GMPath = VStr(getenv("WINDIR")) + "/system32/drivers/gm.dls";
217 		FILE* f = fopen(*GMPath, "rb");
218 		if (f)
219 		{
220 			patches = Timidity_LoadDLS(f);
221 			fclose(f);
222 			if (patches)
223 			{
224 				GCon->Logf("Loaded gm.dls");
225 			}
226 		}
227         if (!patches)
228         {
229 		    GCon->Logf("Timidity init failed");
230 		    return NULL;
231         }
232 #else
233 		GCon->Logf("Timidity init failed");
234 		return NULL;
235 #endif
236 	}
237 
238 	if (s_timidity_test_dls)
239 	{
240 		FILE* f = fopen("gm.dls", "rb");
241 		if (f)
242 		{
243 			patches = Timidity_LoadDLS(f);
244 			fclose(f);
245 			if (patches)
246 			{
247 				GCon->Logf("Loaded DLS");
248 			}
249 		}
250 	}
251 
252 	Sf2Data* sf2_data = NULL;
253 	if (s_timidity_test_sf2)
254 	{
255 		sf2_data = Timidity_LoadSF2("gm.sf2");
256 		if (sf2_data)
257 		{
258 			GCon->Logf("Loaded SF2");
259 		}
260 	}
261 
262 	//	Load song.
263 	int Size = InStrm->TotalSize();
264 	void* Data = Z_Malloc(Size);
265 	InStrm->Seek(0);
266 	InStrm->Serialise(Data, Size);
267 	MidiSong* Song = Timidity_LoadSongMem(Data, Size, patches, sf2_data);
268 	Z_Free(Data);
269 	if (!Song)
270 	{
271 		GCon->Logf("Failed to load MIDI song");
272 		Timidity_Close();
273 		return NULL;
274 	}
275 	InStrm->Close();
276 	delete InStrm;
277 	InStrm = NULL;
278 
279 	//	Create codec.
280 	return new VTimidityAudioCodec(Song);
281 	unguard;
282 }
283