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