1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 //	System interface for music.
17 //
18 
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 
25 #include "SDL.h"
26 #include "SDL_mixer.h"
27 
28 #include "i_glob.h"
29 #include "i_midipipe.h"
30 
31 #include "config.h"
32 #include "doomtype.h"
33 #include "memio.h"
34 #include "mus2mid.h"
35 
36 #include "deh_str.h"
37 #include "gusconf.h"
38 #include "i_sound.h"
39 #include "i_system.h"
40 #include "i_swap.h"
41 #include "m_argv.h"
42 #include "m_config.h"
43 #include "m_misc.h"
44 #include "sha1.h"
45 #include "w_wad.h"
46 #include "z_zone.h"
47 
48 #define MID_HEADER_MAGIC "MThd"
49 #define MUS_HEADER_MAGIC "MUS\x1a"
50 
51 #define FLAC_HEADER "fLaC"
52 #define OGG_HEADER "OggS"
53 
54 // Looping Vorbis metadata tag names. These have been defined by ZDoom
55 // for specifying the start and end positions for looping music tracks
56 // in .ogg and .flac files.
57 // More information is here: http://zdoom.org/wiki/Audio_loop
58 #define LOOP_START_TAG "LOOP_START"
59 #define LOOP_END_TAG   "LOOP_END"
60 
61 // FLAC metadata headers that we care about.
62 #define FLAC_STREAMINFO      0
63 #define FLAC_VORBIS_COMMENT  4
64 
65 // Ogg metadata headers that we care about.
66 #define OGG_ID_HEADER        1
67 #define OGG_COMMENT_HEADER   3
68 
69 // Structure for music substitution.
70 // We store a mapping based on SHA1 checksum -> filename of substitute music
71 // file to play, so that substitution occurs based on content rather than
72 // lump name. This has some inherent advantages:
73 //  * Music for Plutonia (reused from Doom 1) works automatically.
74 //  * If a PWAD replaces music, the replacement music is used rather than
75 //    the substitute music for the IWAD.
76 //  * If a PWAD reuses music from an IWAD (even from a different game), we get
77 //    the high quality version of the music automatically (neat!)
78 
79 typedef struct
80 {
81     const char *hash_prefix;
82     const char *filename;
83 } subst_music_t;
84 
85 // Structure containing parsed metadata read from a digital music track:
86 typedef struct
87 {
88     boolean valid;
89     unsigned int samplerate_hz;
90     int start_time, end_time;
91 } file_metadata_t;
92 
93 static subst_music_t *subst_music = NULL;
94 static unsigned int subst_music_len = 0;
95 
96 static boolean music_initialized = false;
97 
98 // If this is true, this module initialized SDL sound and has the
99 // responsibility to shut it down
100 
101 static boolean sdl_was_initialized = false;
102 
103 char *music_pack_path = "";
104 
105 // If true, we are playing a substitute digital track rather than in-WAD
106 // MIDI/MUS track, and file_metadata contains loop metadata.
107 static file_metadata_t file_metadata;
108 
109 // Position (in samples) that we have reached in the current track.
110 // This is updated by the TrackPositionCallback function.
111 static unsigned int current_track_pos;
112 
113 // Currently playing music track.
114 static Mix_Music *current_track_music = NULL;
115 
116 // If true, the currently playing track is being played on loop.
117 static boolean current_track_loop;
118 
119 // Table of known hashes and filenames to look up for them. This allows
120 // users to drop in a set of files without having to also provide a
121 // configuration file.
122 static const subst_music_t known_filenames[] = {
123     // Doom 1 music files.
124     {"b2e05b4e8dff8d76f8f4", "d_inter.{ext}"},
125     {"0c0acce45130bab935d2", "d_intro.{ext}"},
126     {"fca4086939a68ae4ed84", "d_victor.{ext}"},
127     {"5971e5e20554f47ca065", "d_intro.{ext}"},
128     {"99767e32769229897f77", "d_e1m1.{ext}"},
129     {"b5e7dfb4efe9e688bf2a", "d_e1m2.{ext}"},
130     {"fda8fa73e4d30a6b961c", "d_e1m3.{ext}"},
131     {"3805f9bf3f1702f7e7f5", "d_e1m4.{ext}"},
132     {"f546ed823b234fe39165", "d_e1m5.{ext}"},
133     {"4450811b5a6748cfd83e", "d_e1m6.{ext}"},
134     {"73edb50d96b0ac03be34", "d_e1m7.{ext}"},
135     {"47d711a6fd32f5047879", "d_e1m8.{ext}"},
136     {"62c631c2fdaa5ecd9a8d", "d_e1m9.{ext}"},
137     {"7702a6449585428e7185", "d_e2m1.{ext}"},
138     {"1cb1810989cbfae2b29b", "d_e2m2.{ext}"},
139     {"7d740f3c881a22945e47", "d_e2m4.{ext}"},
140     {"ae9c3dc2f9aeea002327", "d_e2m6.{ext}"},
141     {"b26aad3caa420e9a2c76", "d_e2m7.{ext}"},
142     {"90f06251a2a90bfaefd4", "d_e2m8.{ext}"},
143     {"b2fb439f23c08c8e2577", "d_e3m1.{ext}"},
144     {"b6c07bb249526b864208", "d_e3m2.{ext}"},
145     {"ce3587ee503ffe707b2d", "d_e3m3.{ext}"},
146     {"d746ea2aa16b3237422c", "d_e3m8.{ext}"},
147     {"3da3b1335560a92912e6", "d_bunny.{ext}"},
148 
149     // Duplicates that don't have identical hashes:
150     {"4a5badc4f10a7d4ed021", "d_inter.{ext}"},  // E2M3
151     {"36b14bf165b3fdd3958e", "d_e1m7.{ext}"},   // E3M5
152     {"e77c3d42f2ea87f04607", "d_e1m6.{ext}"},   // E3M6
153     {"3d85ec9c10b5ea465568", "d_e2m7.{ext}"},   // E3M7
154     {"4d42e2ce1c1ff192500e", "d_e1m9.{ext}"},   // E3M9
155 
156     // These tracks are reused in Alien Vendetta, but are MIDs:
157     {"a05e45f67e1b64733fe3", "d_e2m1.{ext}"},   // MAP02
158     {"8024ae1616ddd97ce330", "d_e1m4.{ext}"},   // MAP03
159     {"3af8d79ddba49edaf9eb", "d_victor.{ext}"}, // MAP05
160     {"a55352c96c025b6bd08a", "d_inter.{ext}"},  // MAP07
161     {"76d1fc25ab7b1b4a58d6", "d_e1m8.{ext}"},   // MAP11
162     {"497777f0863eca7cea87", "d_e1m2.{ext}"},   // MAP12
163     {"0228fd87f8762f112fb6", "d_e2m2.{ext}"},   // MAP13
164     {"db94e8e1d7c02092eab5", "d_e1m6.{ext}"},   // MAP14
165     {"5a8d7a307eebc952795c", "d_e2m7.{ext}"},   // MAP16
166     {"1a36b692bf26d94a72cc", "d_e1m7.{ext}"},   // MAP23
167     {"37c6cefa351b06995152", "d_e1m5.{ext}"},   // MAP27
168     {"36b97b87fe98348d44b6", "d_e2m6.{ext}"},   // MAP28
169 
170     // Doom II music files.
171     {"79080e9681a2d7bec3fb", "d_runnin.{ext}"},  // MAP01,15
172     {"868b3aae73c7b12e92c0", "d_stalks.{ext}"},  // MAP02,11,17
173     {"19237754d2eb85f41d84", "d_countd.{ext}"},  // MAP03,21
174     {"00abff3b61b25a6855d2", "d_betwee.{ext}"},  // MAP04
175     {"954636c7ee09edf5d98f", "d_doom.{ext}"},    // MAP05,13
176     {"8d32b2b7aa3b806474c1", "d_the_da.{ext}"},  // MAP06,12,24
177     {"41efc3c84bb321af2b6b", "d_shawn.{ext}"},   // MAP07,19,29
178     // Assuming single D_DDTBLU: http://doomwiki.org/wiki/Doom_II_music#Trivia
179     {"51c0872fec9f43259318", "d_ddtblu.{ext}"},  // MAP08
180     {"acb7ad85494d18235df8", "d_ddtblu.{ext}"},  // MAP14,22
181     {"4b7ceccbf47e78e2fa0b", "d_in_cit.{ext}"},  // MAP09
182     {"1d1f4a9edba174584e11", "d_dead.{ext}"},    // MAP10,16
183     {"1736c81aac77f9bffd3d", "d_romero.{ext}"},  // MAP18,27
184     {"a55d400570ad255a576b", "d_messag.{ext}"},  // MAP20,26
185     {"29d30c3fbd712016f2e5", "d_ampie.{ext}"},   // MAP23
186     {"bcfe9786afdcfb704afa", "d_adrian.{ext}"},  // MAP25
187     {"e05c10389e71836834ae", "d_tense.{ext}"},   // MAP28
188     {"b779022b1d0f0010b8f0", "d_openin.{ext}"},  // MAP30
189     {"a9a5f7b0ab3be0f4fc24", "d_evil.{ext}"},    // MAP31
190     {"4503d155aafec0296689", "d_ultima.{ext}"},  // MAP32
191     {"56f2363f01df38908c77", "d_dm2ttl.{ext}"},
192     {"71e58baf9e9dea4dd24a", "d_dm2int.{ext}"},
193     {"e632318629869811f7dc", "d_read_m.{ext}"},
194 
195     // Duplicate filenames: the above filenames are the "canonical" files
196     // for the given SHA1 hashes, but we can also look for these filenames
197     // corresponding to the duplicated music tracks too.
198     {"868b3aae73c7b12e92c0", "d_stlks2.{ext}"},
199     {"868b3aae73c7b12e92c0", "d_stlks3.{ext}"},
200     {"8d32b2b7aa3b806474c1", "d_theda2.{ext}"},
201     {"8d32b2b7aa3b806474c1", "d_theda3.{ext}"},
202     {"954636c7ee09edf5d98f", "d_doom2.{ext}"},
203     {"acb7ad85494d18235df8", "d_ddtbl2.{ext}"},
204     {"acb7ad85494d18235df8", "d_ddtbl3.{ext}"},
205     {"79080e9681a2d7bec3fb", "d_runni2.{ext}"},
206     {"1d1f4a9edba174584e11", "d_dead2.{ext}"},
207     {"41efc3c84bb321af2b6b", "d_shawn2.{ext}"},
208     {"41efc3c84bb321af2b6b", "d_shawn3.{ext}"},
209     {"19237754d2eb85f41d84", "d_count2.{ext}"},
210     {"a55d400570ad255a576b", "d_messg2.{ext}"},
211     {"1736c81aac77f9bffd3d", "d_romer2.{ext}"},
212 
213     // These tracks are reused in Alien Vendetta, but are MIDs:
214     {"9433604c098b7b1119a4", "d_in_cit.{ext}"},  // MAP26
215 
216     // Heretic tracks.
217     {"12818ca0d3c957e7d57e", "mus_titl.{ext}"},
218     {"5cb988538ce1b1857349", "mus_intr.{ext}"},
219     {"6f126abe35a78b61b930", "mus_cptd.{ext}"},
220     {"62557250f0427c067dc9", "mus_e1m1.{ext}"},
221     {"1e8d5fd814490b9ae166", "mus_e1m2.{ext}"},
222     {"f0f31e8834e85035d434", "mus_e1m3.{ext}"},
223     {"054d6997405cc5a32b46", "mus_e1m4.{ext}"},
224     {"31950ab062cc1e5ca49d", "mus_e1m5.{ext}"},
225     {"7389024fbab0dff47211", "mus_e1m6.{ext}"},
226     {"f2aa312dddd0a294a095", "mus_e1m7.{ext}"},
227     {"cd6856731d1ae1f3aa4e", "mus_e1m8.{ext}"},
228     {"d7fe793f266733d92e61", "mus_e1m9.{ext}"},
229     {"933545b48fad8c66f042", "mus_e2m1.{ext}"},
230     {"bf88ecd4ae1621222592", "mus_e2m2.{ext}"},
231     {"4f619f87a828c2ca4801", "mus_e2m3.{ext}"},
232     {"13033a83c49424b2f2ab", "mus_e2m4.{ext}"},
233     {"b3851f9351ae411d9de3", "mus_e2m6.{ext}"},
234     {"82539791159fbbc02a23", "mus_e2m7.{ext}"},
235     {"fd9e53a49cfa62c463a0", "mus_e2m8.{ext}"},
236     {"29503959324d2ca67958", "mus_e2m9.{ext}"},
237     {"3aa632257c5be375b97b", "mus_e3m2.{ext}"},
238     {"69ba0dce7913d53b67a8", "mus_e3m3.{ext}"},
239 
240     // These Heretic tracks are reused in Alien Vendetta, but are MIDs:
241     {"51344131e8d260753ce7", "mus_e2m3.{ext}"},  // MAP15
242     {"78b570b2397570440aff", "mus_e1m1.{ext}"},  // MAP19
243     {"ee21ba9fad4de3dfaef0", "mus_e1m4.{ext}"},  // MAP29
244     {"d2bb643a60696ccbca03", "mus_e1m9.{ext}"},  // MAP32
245 
246     // Hexen tracks:
247     {"fbf55fc1ee26bd01266b", "winnowr.{ext}"},
248     {"71776e2da2b7ba607d81", "jachr.{ext}"},
249     {"c5c8630608b8132b33cd", "simonr.{ext}"},
250     {"43683b3f55a031de88d4", "wutzitr.{ext}"},
251     {"a6062883f29436ef73db", "falconr.{ext}"},
252     {"512cb6cc9b558d5f0fef", "levelr.{ext}"},
253     {"d31226ae75fce6a24208", "chartr.{ext}"},
254     {"bf1f1e561bbdba4e699f", "swampr.{ext}"},
255     {"b303193f756ca0e2de0f", "deepr.{ext}"},
256     {"f0635f0386d883b00186", "fubasr.{ext}"},
257     {"18f2a01f83df6e3abedc", "grover.{ext}"},
258     {"b2527eb0522f08b2cf5f", "fortr.{ext}"},
259     {"343addba8ba53a20a160", "foojar.{ext}"},
260     {"c13109045b06b5a63386", "sixater.{ext}"},
261     {"693525aaf69eac5429ab", "wobabyr.{ext}"},
262     {"8f884223811c2bb8311d", "cryptr.{ext}"},
263     {"de540e6826e62b32c01c", "fantar.{ext}"},
264     {"efdff548df918934f71f", "blechr.{ext}"},
265     {"de91f150f6a127e72e35", "voidr.{ext}"},
266     {"e0497fe27289fe18515b", "chap_1r.{ext}"},
267     {"f2ef1abdc3f672a3519a", "chap_2r.{ext}"},
268     {"78cd9882f61cc441bef4", "chap_3r.{ext}"},
269     {"97b2b575d9d096c1f89f", "chap_4r.{ext}"},
270     {"ad0197a0f6c52ac30915", "chippyr.{ext}"},
271     {"30506c62e9f0989ffe09", "percr.{ext}"},
272     {"3542803beaa43bf1de1a", "secretr.{ext}"},
273     {"81067721f40c611d09fb", "bonesr.{ext}"},
274     {"4822af2e1a2eb7faf660", "octor.{ext}"},
275     {"26bb3cec902ed8008fc2", "rithmr.{ext}"},
276     {"94ab641c7aa93caac77a", "stalkr.{ext}"},
277     {"d0a3f337c54b0703b4d3", "borkr.{ext}"},
278     {"79e7781ec7eb9b9434b5", "crucibr.{ext}"},
279     {"c2786e5581a7f8801969", "hexen.{ext}"},
280     {"97fae9a084c0efda5151", "hub.{ext}"},
281     {"c5da52d5c2ec4803ef8f", "hall.{ext}"},
282     {"1e71bc0e2feafb06214e", "orb.{ext}"},
283     {"bc9dcfa6632e847e03af", "chess.{ext}"},
284 
285     // Hexen CD tracks: alternate filenames for a ripped copy of
286     // the CD soundtrack.
287     {"71776e2da2b7ba607d81", "hexen02.{ext}"},   // level  2 (jachr)
288     {"efdff548df918934f71f", "hexen03.{ext}"},   // level 26 (blechr)
289     {"c2786e5581a7f8801969", "hexen04.{ext}"},   // (hexen)
290     {"1e71bc0e2feafb06214e", "hexen05.{ext}"},   // (orb)
291     {"f0635f0386d883b00186", "hexen06.{ext}"},   // level 10 (fubasr)
292     {"bc9dcfa6632e847e03af", "hexen07.{ext}"},   // (chess)
293     {"8f884223811c2bb8311d", "hexen08.{ext}"},   // level 24 (cryptr)
294     {"a6062883f29436ef73db", "hexen09.{ext}"},   // level  5 (falconr)
295     {"4822af2e1a2eb7faf660", "hexen10.{ext}"},   // level 36 (octor)
296     {"26bb3cec902ed8008fc2", "hexen11.{ext}"},   // level 37 (rithmr)
297     {"c13109045b06b5a63386", "hexen12.{ext}"},   // level 22 (sixater)
298     {"fbf55fc1ee26bd01266b", "hexen13.{ext}"},   // level  1 (winnowr)
299     {"bf1f1e561bbdba4e699f", "hexen14.{ext}"},   // level  8 (swampr)
300     {"43683b3f55a031de88d4", "hexen15.{ext}"},   // level  4 (wutzitr)
301     {"81067721f40c611d09fb", "hexen16.{ext}"},   // level 35 (bonesr)
302     {"e0497fe27289fe18515b", "hexen17.{ext}"},   // level 28 (chap_1r)
303     {"97b2b575d9d096c1f89f", "hexen18.{ext}"},   // level 31 (chap_4r)
304     {"de540e6826e62b32c01c", "hexen19.{ext}"},   // level 25 (fantar)
305     {"343addba8ba53a20a160", "hexen20.{ext}"},   // level 21 (foojar)
306     {"512cb6cc9b558d5f0fef", "hexen21.{ext}"},   // level  6 (levelr)
307     {"c5c8630608b8132b33cd", "hexen22.{ext}"},   // level  3 (simonr)
308 
309     // Strife:
310     {"8ac2b2b47707f0fdf8f6", "d_logo.{ext}"},   // Title
311     {"62e1c58054a1f1bc39b2", "d_action.{ext}"}, // 1,15,28
312     {"12fa000f3fa1edac5c4f", "d_tavern.{ext}"}, // 2
313     {"695e56ab3251792d20e5", "d_danger.{ext}"}, // 3,11
314     {"96fe30e8712217b60dd7", "d_fast.{ext}"},   // 4
315     {"61345598a3de04aad508", "d_darker.{ext}"}, // 6,14
316     {"52353e9a435b7b1cb268", "d_strike.{ext}"}, // 7,19
317     {"061164504907bffc9c22", "d_slide.{ext}"},  // 8,18,22
318     {"3dbb4b703ce69aafcdd5", "d_tribal.{ext}"}, // 9
319     {"393773688eba050c3548", "d_march.{ext}"},  // 10
320     {"3cba3c627de065a667dd", "d_mood.{ext}"},   // 12
321     {"b1f65a333e5c70255784", "d_castle.{ext}"}, // 13
322     {"e1455a83a04c9ac4a09f", "d_fight.{ext}"},  // 16,31
323     {"17f822b7374b1f069b89", "d_spense.{ext}"}, // 17
324     {"e66c5a1a7d05f021f4ae", "d_dark.{ext}"},   // 20
325     {"1c92bd0625026af30dad", "d_tech.{ext}"},   // 21,27
326     {"7ae280713d078de7933a", "d_drone.{ext}"},  // 23,30
327     {"4a664afd0d7eae79c97a", "d_panthr.{ext}"}, // 24
328     {"4a7d62beeac5601ccf21", "d_sad.{ext}"},    // 25
329     {"e60e109779400f2855d7", "d_instry.{ext}"}, // 26,29
330     {"b7d36878faeb291d6df5", "d_happy.{ext}"},  // Better ending
331     {"ff4a342c8c5ec51b06c3", "d_end.{ext}"},    // Worse ending
332     // This conflicts with Doom's d_intro:
333     //{"ec8fa484c4e85adbf700", "d_intro.{ext}"},  // 5
334 };
335 
336 // Given a time string (for LOOP_START/LOOP_END), parse it and return
337 // the time (in # samples since start of track) it represents.
ParseVorbisTime(unsigned int samplerate_hz,char * value)338 static unsigned int ParseVorbisTime(unsigned int samplerate_hz, char *value)
339 {
340     char *num_start, *p;
341     unsigned int result = 0;
342     char c;
343 
344     if (strchr(value, ':') == NULL)
345     {
346 	return atoi(value);
347     }
348 
349     result = 0;
350     num_start = value;
351 
352     for (p = value; *p != '\0'; ++p)
353     {
354         if (*p == '.' || *p == ':')
355         {
356             c = *p; *p = '\0';
357             result = result * 60 + atoi(num_start);
358             num_start = p + 1;
359             *p = c;
360         }
361 
362         if (*p == '.')
363         {
364             return result * samplerate_hz
365 	         + (unsigned int) (atof(p) * samplerate_hz);
366         }
367     }
368 
369     return (result * 60 + atoi(num_start)) * samplerate_hz;
370 }
371 
372 // Given a vorbis comment string (eg. "LOOP_START=12345"), set fields
373 // in the metadata structure as appropriate.
ParseVorbisComment(file_metadata_t * metadata,char * comment)374 static void ParseVorbisComment(file_metadata_t *metadata, char *comment)
375 {
376     char *eq, *key, *value;
377 
378     eq = strchr(comment, '=');
379 
380     if (eq == NULL)
381     {
382         return;
383     }
384 
385     key = comment;
386     *eq = '\0';
387     value = eq + 1;
388 
389     if (!strcmp(key, LOOP_START_TAG))
390     {
391         metadata->start_time = ParseVorbisTime(metadata->samplerate_hz, value);
392     }
393     else if (!strcmp(key, LOOP_END_TAG))
394     {
395         metadata->end_time = ParseVorbisTime(metadata->samplerate_hz, value);
396     }
397 }
398 
399 // Parse a vorbis comments structure, reading from the given file.
ParseVorbisComments(file_metadata_t * metadata,FILE * fs)400 static void ParseVorbisComments(file_metadata_t *metadata, FILE *fs)
401 {
402     uint32_t buf;
403     unsigned int num_comments, i, comment_len;
404     char *comment;
405 
406     // We must have read the sample rate already from an earlier header.
407     if (metadata->samplerate_hz == 0)
408     {
409 	return;
410     }
411 
412     // Skip the starting part we don't care about.
413     if (fread(&buf, 4, 1, fs) < 1)
414     {
415         return;
416     }
417     if (fseek(fs, LONG(buf), SEEK_CUR) != 0)
418     {
419 	return;
420     }
421 
422     // Read count field for number of comments.
423     if (fread(&buf, 4, 1, fs) < 1)
424     {
425         return;
426     }
427     num_comments = LONG(buf);
428 
429     // Read each individual comment.
430     for (i = 0; i < num_comments; ++i)
431     {
432         // Read length of comment.
433         if (fread(&buf, 4, 1, fs) < 1)
434 	{
435             return;
436 	}
437 
438         comment_len = LONG(buf);
439 
440         // Read actual comment data into string buffer.
441         comment = calloc(1, comment_len + 1);
442         if (comment == NULL
443          || fread(comment, 1, comment_len, fs) < comment_len)
444         {
445             free(comment);
446             break;
447         }
448 
449         // Parse comment string.
450         ParseVorbisComment(metadata, comment);
451         free(comment);
452     }
453 }
454 
ParseFlacStreaminfo(file_metadata_t * metadata,FILE * fs)455 static void ParseFlacStreaminfo(file_metadata_t *metadata, FILE *fs)
456 {
457     byte buf[34];
458 
459     // Read block data.
460     if (fread(buf, sizeof(buf), 1, fs) < 1)
461     {
462         return;
463     }
464 
465     // We only care about sample rate and song length.
466     metadata->samplerate_hz = (buf[10] << 12) | (buf[11] << 4)
467                             | (buf[12] >> 4);
468     // Song length is actually a 36 bit field, but 32 bits should be
469     // enough for everybody.
470     //metadata->song_length = (buf[14] << 24) | (buf[15] << 16)
471     //                      | (buf[16] << 8) | buf[17];
472 }
473 
ParseFlacFile(file_metadata_t * metadata,FILE * fs)474 static void ParseFlacFile(file_metadata_t *metadata, FILE *fs)
475 {
476     byte header[4];
477     unsigned int block_type;
478     size_t block_len;
479     boolean last_block;
480 
481     for (;;)
482     {
483         long pos = -1;
484 
485         // Read METADATA_BLOCK_HEADER:
486         if (fread(header, 4, 1, fs) < 1)
487         {
488             return;
489         }
490 
491         block_type = header[0] & ~0x80;
492         last_block = (header[0] & 0x80) != 0;
493         block_len = (header[1] << 16) | (header[2] << 8) | header[3];
494 
495         pos = ftell(fs);
496         if (pos < 0)
497         {
498             return;
499         }
500 
501         if (block_type == FLAC_STREAMINFO)
502         {
503             ParseFlacStreaminfo(metadata, fs);
504         }
505         else if (block_type == FLAC_VORBIS_COMMENT)
506         {
507             ParseVorbisComments(metadata, fs);
508         }
509 
510         if (last_block)
511         {
512             break;
513         }
514 
515         // Seek to start of next block.
516         if (fseek(fs, pos + block_len, SEEK_SET) != 0)
517         {
518             return;
519         }
520     }
521 }
522 
ParseOggIdHeader(file_metadata_t * metadata,FILE * fs)523 static void ParseOggIdHeader(file_metadata_t *metadata, FILE *fs)
524 {
525     byte buf[21];
526 
527     if (fread(buf, sizeof(buf), 1, fs) < 1)
528     {
529         return;
530     }
531 
532     metadata->samplerate_hz = (buf[8] << 24) | (buf[7] << 16)
533                             | (buf[6] << 8) | buf[5];
534 }
535 
ParseOggFile(file_metadata_t * metadata,FILE * fs)536 static void ParseOggFile(file_metadata_t *metadata, FILE *fs)
537 {
538     byte buf[7];
539     unsigned int offset;
540 
541     // Scan through the start of the file looking for headers. They
542     // begin '[byte]vorbis' where the byte value indicates header type.
543     memset(buf, 0, sizeof(buf));
544 
545     for (offset = 0; offset < 100 * 1024; ++offset)
546     {
547 	// buf[] is used as a sliding window. Each iteration, we
548 	// move the buffer one byte to the left and read an extra
549 	// byte onto the end.
550         memmove(buf, buf + 1, sizeof(buf) - 1);
551 
552         if (fread(&buf[6], 1, 1, fs) < 1)
553         {
554             return;
555         }
556 
557         if (!memcmp(buf + 1, "vorbis", 6))
558         {
559             switch (buf[0])
560             {
561                 case OGG_ID_HEADER:
562                     ParseOggIdHeader(metadata, fs);
563                     break;
564                 case OGG_COMMENT_HEADER:
565 		    ParseVorbisComments(metadata, fs);
566                     break;
567                 default:
568                     break;
569             }
570         }
571     }
572 }
573 
ReadLoopPoints(const char * filename,file_metadata_t * metadata)574 static void ReadLoopPoints(const char *filename, file_metadata_t *metadata)
575 {
576     FILE *fs;
577     char header[4];
578 
579     metadata->valid = false;
580     metadata->samplerate_hz = 0;
581     metadata->start_time = 0;
582     metadata->end_time = -1;
583 
584     fs = fopen(filename, "rb");
585 
586     if (fs == NULL)
587     {
588         return;
589     }
590 
591     // Check for a recognized file format; use the first four bytes
592     // of the file.
593 
594     if (fread(header, 4, 1, fs) < 1)
595     {
596         fclose(fs);
597         return;
598     }
599 
600     if (memcmp(header, FLAC_HEADER, 4) == 0)
601     {
602         ParseFlacFile(metadata, fs);
603     }
604     else if (memcmp(header, OGG_HEADER, 4) == 0)
605     {
606         ParseOggFile(metadata, fs);
607     }
608 
609     fclose(fs);
610 
611     // Only valid if at the very least we read the sample rate.
612     metadata->valid = metadata->samplerate_hz > 0;
613 
614     // If start and end time are both zero, ignore the loop tags.
615     // This is consistent with other source ports.
616     if (metadata->start_time == 0 && metadata->end_time == 0)
617     {
618         metadata->valid = false;
619     }
620 }
621 
622 // Given a MUS lump, look up a substitute MUS file to play instead
623 // (or NULL to just use normal MIDI playback).
624 
GetSubstituteMusicFile(void * data,size_t data_len)625 static const char *GetSubstituteMusicFile(void *data, size_t data_len)
626 {
627     sha1_context_t context;
628     sha1_digest_t hash;
629     const char *filename;
630     char hash_str[sizeof(sha1_digest_t) * 2 + 1];
631     unsigned int i;
632 
633     // Don't bother doing a hash if we're never going to find anything.
634     if (subst_music_len == 0)
635     {
636         return NULL;
637     }
638 
639     SHA1_Init(&context);
640     SHA1_Update(&context, data, data_len);
641     SHA1_Final(hash, &context);
642 
643     // Build a string representation of the hash.
644     for (i = 0; i < sizeof(sha1_digest_t); ++i)
645     {
646         M_snprintf(hash_str + i * 2, sizeof(hash_str) - i * 2,
647                    "%02x", hash[i]);
648     }
649 
650     // Look for a hash that matches.
651     // The substitute mapping list can (intentionally) contain multiple
652     // filename mappings for the same hash. This allows us to try
653     // different files and fall back if our first choice isn't found.
654 
655     filename = NULL;
656 
657     for (i = 0; i < subst_music_len; ++i)
658     {
659         if (M_StringStartsWith(hash_str, subst_music[i].hash_prefix))
660         {
661             filename = subst_music[i].filename;
662 
663             // If the file exists, then use this file in preference to
664             // any fallbacks. But we always return a filename if it's
665             // in the list, even if it's just so we can print an error
666             // message to the user saying it doesn't exist.
667             if (M_FileExists(filename))
668             {
669                 break;
670             }
671         }
672     }
673 
674     return filename;
675 }
676 
GetFullPath(const char * musicdir,const char * path)677 static char *GetFullPath(const char *musicdir, const char *path)
678 {
679     char *result;
680     char *systemized_path;
681 
682     // Starting with directory separator means we have an absolute path,
683     // so just return it.
684     if (path[0] == DIR_SEPARATOR)
685     {
686         return M_StringDuplicate(path);
687     }
688 
689 #ifdef _WIN32
690     // d:\path\...
691     if (isalpha(path[0]) && path[1] == ':' && path[2] == DIR_SEPARATOR)
692     {
693         return M_StringDuplicate(path);
694     }
695 #endif
696 
697     // Paths in the substitute filenames can contain Unix-style /
698     // path separators, but we should convert this to the separator
699     // for the native platform.
700     systemized_path = M_StringReplace(path, "/", DIR_SEPARATOR_S);
701 
702     // Copy config filename and cut off the filename to just get the
703     // parent dir.
704     result = M_StringJoin(musicdir, systemized_path, NULL);
705     free(systemized_path);
706 
707     return result;
708 }
709 
710 // If filename ends with .{ext}, check if a .ogg, .flac or .mp3 exists with
711 // that name, returning it if found. If none exist, NULL is returned. If the
712 // filename doesn't end with .{ext} then it just acts as a wrapper around
713 // GetFullPath().
ExpandFileExtension(const char * musicdir,const char * filename)714 static char *ExpandFileExtension(const char *musicdir, const char *filename)
715 {
716     static const char *extns[] = {".flac", ".ogg", ".mp3"};
717     char *replaced, *result;
718     int i;
719 
720     if (!M_StringEndsWith(filename, ".{ext}"))
721     {
722         return GetFullPath(musicdir, filename);
723     }
724 
725     for (i = 0; i < arrlen(extns); ++i)
726     {
727         replaced = M_StringReplace(filename, ".{ext}", extns[i]);
728         result = GetFullPath(musicdir, replaced);
729         free(replaced);
730         if (M_FileExists(result))
731         {
732             return result;
733         }
734         free(result);
735     }
736 
737     return NULL;
738 }
739 
740 // Add a substitute music file to the lookup list.
AddSubstituteMusic(const char * musicdir,const char * hash_prefix,const char * filename)741 static void AddSubstituteMusic(const char *musicdir, const char *hash_prefix,
742                                const char *filename)
743 {
744     subst_music_t *s;
745     char *path;
746 
747     path = ExpandFileExtension(musicdir, filename);
748     if (path == NULL)
749     {
750         return;
751     }
752 
753     ++subst_music_len;
754     subst_music =
755         I_Realloc(subst_music, sizeof(subst_music_t) * subst_music_len);
756     s = &subst_music[subst_music_len - 1];
757     s->hash_prefix = hash_prefix;
758     s->filename = path;
759 }
760 
ReadHashPrefix(char * line)761 static const char *ReadHashPrefix(char *line)
762 {
763     char *result;
764     char *p;
765     int i, len;
766 
767     for (p = line; *p != '\0' && !isspace(*p) && *p != '='; ++p)
768     {
769         if (!isxdigit(*p))
770         {
771             return NULL;
772         }
773     }
774 
775     len = p - line;
776     if (len == 0 || len > sizeof(sha1_digest_t) * 2)
777     {
778         return NULL;
779     }
780 
781     result = malloc(len + 1);
782     if (result == NULL)
783     {
784         return NULL;
785     }
786 
787     for (i = 0; i < len; ++i)
788     {
789         result[i] = tolower(line[i]);
790     }
791     result[len] = '\0';
792 
793     return result;
794 }
795 
796 // Parse a line from substitute music configuration file; returns error
797 // message or NULL for no error.
798 
ParseSubstituteLine(char * musicdir,char * line)799 static const char *ParseSubstituteLine(char *musicdir, char *line)
800 {
801     const char *hash_prefix;
802     char *filename;
803     char *p;
804 
805     // Strip out comments if present.
806     p = strchr(line, '#');
807     if (p != NULL)
808     {
809         while (p > line && isspace(*(p - 1)))
810         {
811             --p;
812         }
813         *p = '\0';
814     }
815 
816     // Skip leading spaces.
817     for (p = line; *p != '\0' && isspace(*p); ++p);
818 
819     // Empty line? This includes comment lines now that comments have
820     // been stripped.
821     if (*p == '\0')
822     {
823         return NULL;
824     }
825 
826     hash_prefix = ReadHashPrefix(p);
827     if (hash_prefix == NULL)
828     {
829         return "Invalid hash prefix";
830     }
831 
832     p += strlen(hash_prefix);
833 
834     // Skip spaces.
835     for (; *p != '\0' && isspace(*p); ++p);
836 
837     if (*p != '=')
838     {
839         return "Expected '='";
840     }
841 
842     ++p;
843 
844     // Skip spaces.
845     for (; *p != '\0' && isspace(*p); ++p);
846 
847     filename = p;
848 
849     // We're now at the filename. Cut off trailing space characters.
850     while (strlen(p) > 0 && isspace(p[strlen(p) - 1]))
851     {
852         p[strlen(p) - 1] = '\0';
853     }
854 
855     if (strlen(p) == 0)
856     {
857         return "No filename specified for music substitution";
858     }
859 
860     // Expand full path and add to our database of substitutes.
861     AddSubstituteMusic(musicdir, hash_prefix, filename);
862 
863     return NULL;
864 }
865 
866 // Read a substitute music configuration file.
867 
ReadSubstituteConfig(char * musicdir,const char * filename)868 static boolean ReadSubstituteConfig(char *musicdir, const char *filename)
869 {
870     char *buffer;
871     char *line;
872     int linenum = 1;
873 
874     // This unnecessarily opens the file twice...
875     if (!M_FileExists(filename))
876     {
877         return false;
878     }
879 
880     M_ReadFile(filename, (byte **) &buffer);
881 
882     line = buffer;
883 
884     while (line != NULL)
885     {
886         const char *error;
887         char *next;
888 
889         // find end of line
890         char *eol = strchr(line, '\n');
891         if (eol != NULL)
892         {
893             // change the newline into NUL
894             *eol = '\0';
895             next = eol + 1;
896         }
897         else
898         {
899             // end of buffer
900             next = NULL;
901         }
902 
903         error = ParseSubstituteLine(musicdir, line);
904 
905         if (error != NULL)
906         {
907             fprintf(stderr, "%s:%i: Error: %s\n", filename, linenum, error);
908         }
909 
910         ++linenum;
911         line = next;
912     }
913 
914     Z_Free(buffer);
915 
916     return true;
917 }
918 
919 // Find substitute configs and try to load them.
920 
LoadSubstituteConfigs(void)921 static void LoadSubstituteConfigs(void)
922 {
923     glob_t *glob;
924     char *musicdir;
925     const char *path;
926     unsigned int old_music_len;
927     unsigned int i;
928 
929     // We can configure the path to music packs using the music_pack_path
930     // configuration variable. Otherwise we use the current directory, or
931     // $configdir/music to look for .cfg files.
932     if (strcmp(music_pack_path, "") != 0)
933     {
934         musicdir = M_StringJoin(music_pack_path, DIR_SEPARATOR_S, NULL);
935     }
936     else if (!strcmp(configdir, exedir))
937     {
938         musicdir = M_StringDuplicate("");
939     }
940     else
941     {
942         musicdir = M_StringJoin(configdir, "music", DIR_SEPARATOR_S, NULL);
943     }
944 
945     // Load all music packs, by searching for .cfg files.
946     glob = I_StartGlob(musicdir, "*.cfg", GLOB_FLAG_SORTED|GLOB_FLAG_NOCASE);
947     for (;;)
948     {
949         path = I_NextGlob(glob);
950         if (path == NULL)
951         {
952             break;
953         }
954         ReadSubstituteConfig(musicdir, path);
955     }
956     I_EndGlob(glob);
957 
958     if (subst_music_len > 0)
959     {
960         printf("Loaded %u music substitutions from config files.\n",
961                subst_music_len);
962     }
963 
964     old_music_len = subst_music_len;
965 
966     // Add entries from known filenames list. We add this after those from the
967     // configuration files, so that the entries here can be overridden.
968     for (i = 0; i < arrlen(known_filenames); ++i)
969     {
970         AddSubstituteMusic(musicdir, known_filenames[i].hash_prefix,
971                            known_filenames[i].filename);
972     }
973 
974     if (subst_music_len > old_music_len)
975     {
976         printf("Configured %u music substitutions based on filename.\n",
977                subst_music_len - old_music_len);
978     }
979 
980     free(musicdir);
981 }
982 
983 // Returns true if the given lump number is a music lump that should
984 // be included in substitute configs.
985 // Identifying music lumps by name is not feasible; some games (eg.
986 // Heretic, Hexen) don't have a common naming pattern for music lumps.
987 
IsMusicLump(int lumpnum)988 static boolean IsMusicLump(int lumpnum)
989 {
990     byte *data;
991     boolean result;
992 
993     if (W_LumpLength(lumpnum) < 4)
994     {
995         return false;
996     }
997 
998     data = W_CacheLumpNum(lumpnum, PU_STATIC);
999 
1000     result = memcmp(data, MUS_HEADER_MAGIC, 4) == 0
1001           || memcmp(data, MID_HEADER_MAGIC, 4) == 0;
1002 
1003     W_ReleaseLumpNum(lumpnum);
1004 
1005     return result;
1006 }
1007 
1008 // Dump an example config file containing checksums for all MIDI music
1009 // found in the WAD directory.
1010 
DumpSubstituteConfig(const char * filename)1011 static void DumpSubstituteConfig(const char *filename)
1012 {
1013     sha1_context_t context;
1014     sha1_digest_t digest;
1015     char name[9];
1016     byte *data;
1017     FILE *fs;
1018     unsigned int lumpnum;
1019     size_t h;
1020 
1021     fs = fopen(filename, "w");
1022 
1023     if (fs == NULL)
1024     {
1025         I_Error("Failed to open %s for writing", filename);
1026         return;
1027     }
1028 
1029     fprintf(fs, "# Example %s substitute MIDI file.\n\n", PACKAGE_NAME);
1030     fprintf(fs, "# SHA1 hash                              = filename\n");
1031 
1032     for (lumpnum = 0; lumpnum < numlumps; ++lumpnum)
1033     {
1034         strncpy(name, lumpinfo[lumpnum]->name, 8);
1035         name[8] = '\0';
1036 
1037         if (!IsMusicLump(lumpnum))
1038         {
1039             continue;
1040         }
1041 
1042         // Calculate hash.
1043         data = W_CacheLumpNum(lumpnum, PU_STATIC);
1044         SHA1_Init(&context);
1045         SHA1_Update(&context, data, W_LumpLength(lumpnum));
1046         SHA1_Final(digest, &context);
1047         W_ReleaseLumpNum(lumpnum);
1048 
1049         // Print line.
1050         for (h = 0; h < sizeof(sha1_digest_t); ++h)
1051         {
1052             fprintf(fs, "%02x", digest[h]);
1053         }
1054 
1055         fprintf(fs, " = %s.ogg\n", name);
1056     }
1057 
1058     fprintf(fs, "\n");
1059     fclose(fs);
1060 
1061     printf("Substitute MIDI config file written to %s.\n", filename);
1062     I_Quit();
1063 }
1064 
1065 // Shutdown music
1066 
I_MP_ShutdownMusic(void)1067 static void I_MP_ShutdownMusic(void)
1068 {
1069     if (music_initialized)
1070     {
1071         Mix_HaltMusic();
1072         music_initialized = false;
1073 
1074         if (sdl_was_initialized)
1075         {
1076             Mix_CloseAudio();
1077             SDL_QuitSubSystem(SDL_INIT_AUDIO);
1078             sdl_was_initialized = false;
1079         }
1080     }
1081 }
1082 
SDLIsInitialized(void)1083 static boolean SDLIsInitialized(void)
1084 {
1085     int freq, channels;
1086     Uint16 format;
1087 
1088     return Mix_QuerySpec(&freq, &format, &channels) != 0;
1089 }
1090 
1091 // Callback function that is invoked to track current track position.
TrackPositionCallback(int chan,void * stream,int len,void * udata)1092 void TrackPositionCallback(int chan, void *stream, int len, void *udata)
1093 {
1094     // Position is doubled up twice: for 16-bit samples and for stereo.
1095     current_track_pos += len / 4;
1096 }
1097 
1098 // Initialize music subsystem
I_MP_InitMusic(void)1099 static boolean I_MP_InitMusic(void)
1100 {
1101     int i;
1102 
1103     //!
1104     // @category obscure
1105     // @arg <filename>
1106     //
1107     // Read all MIDI files from loaded WAD files, dump an example substitution
1108     // music config file to the specified filename and quit.
1109     //
1110     i = M_CheckParmWithArgs("-dumpsubstconfig", 1);
1111 
1112     if (i > 0)
1113     {
1114         DumpSubstituteConfig(myargv[i + 1]);
1115     }
1116 
1117     // If we're in GENMIDI mode, try to load sound packs.
1118     LoadSubstituteConfigs();
1119 
1120     // We can't initialize if we don't have any substitute files to work with.
1121     // If so, don't bother with SDL initialization etc.
1122     if (subst_music_len == 0)
1123     {
1124         return false;
1125     }
1126 
1127     // If SDL_mixer is not initialized, we have to initialize it
1128     // and have the responsibility to shut it down later on.
1129     if (SDLIsInitialized())
1130     {
1131         music_initialized = true;
1132     }
1133     else if (SDL_Init(SDL_INIT_AUDIO) < 0)
1134     {
1135         fprintf(stderr, "Unable to set up sound.\n");
1136     }
1137     else if (Mix_OpenAudioDevice(snd_samplerate, AUDIO_S16SYS, 2, 1024, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) < 0)
1138     {
1139         fprintf(stderr, "Error initializing SDL_mixer: %s\n",
1140                 Mix_GetError());
1141         SDL_QuitSubSystem(SDL_INIT_AUDIO);
1142     }
1143     else
1144     {
1145         SDL_PauseAudio(0);
1146 
1147         sdl_was_initialized = true;
1148         music_initialized = true;
1149     }
1150 
1151     // Initialize SDL_Mixer for digital music playback
1152     Mix_Init(MIX_INIT_FLAC | MIX_INIT_OGG | MIX_INIT_MP3);
1153 
1154     // Register an effect function to track the music position.
1155     Mix_RegisterEffect(MIX_CHANNEL_POST, TrackPositionCallback, NULL, NULL);
1156 
1157     return music_initialized;
1158 }
1159 
1160 // Set music volume (0 - 127)
1161 
I_MP_SetMusicVolume(int volume)1162 static void I_MP_SetMusicVolume(int volume)
1163 {
1164     Mix_VolumeMusic((volume * MIX_MAX_VOLUME) / 127);
1165 }
1166 
1167 // Start playing a mid
1168 
I_MP_PlaySong(void * handle,boolean looping)1169 static void I_MP_PlaySong(void *handle, boolean looping)
1170 {
1171     int loops;
1172 
1173     if (!music_initialized)
1174     {
1175         return;
1176     }
1177 
1178     if (handle == NULL)
1179     {
1180         return;
1181     }
1182 
1183     current_track_music = (Mix_Music *) handle;
1184     current_track_loop = looping;
1185 
1186     if (looping)
1187     {
1188         loops = -1;
1189     }
1190     else
1191     {
1192         loops = 1;
1193     }
1194 
1195     // Don't loop when playing substitute music, as we do it
1196     // ourselves instead.
1197     if (file_metadata.valid)
1198     {
1199         loops = 1;
1200         SDL_LockAudio();
1201         current_track_pos = 0;  // start of track
1202         SDL_UnlockAudio();
1203     }
1204 
1205     if (Mix_PlayMusic(current_track_music, loops) == -1)
1206     {
1207         fprintf(stderr, "I_MP_PlaySong: Error starting track: %s\n",
1208                 Mix_GetError());
1209     }
1210 }
1211 
I_MP_PauseSong(void)1212 static void I_MP_PauseSong(void)
1213 {
1214     if (!music_initialized)
1215     {
1216         return;
1217     }
1218 
1219     Mix_PauseMusic();
1220 }
1221 
I_MP_ResumeSong(void)1222 static void I_MP_ResumeSong(void)
1223 {
1224     if (!music_initialized)
1225     {
1226         return;
1227     }
1228 
1229     Mix_ResumeMusic();
1230 }
1231 
I_MP_StopSong(void)1232 static void I_MP_StopSong(void)
1233 {
1234     if (!music_initialized)
1235     {
1236         return;
1237     }
1238 
1239     Mix_HaltMusic();
1240     current_track_music = NULL;
1241 }
1242 
I_MP_UnRegisterSong(void * handle)1243 static void I_MP_UnRegisterSong(void *handle)
1244 {
1245     Mix_Music *music = (Mix_Music *) handle;
1246 
1247     if (!music_initialized)
1248     {
1249         return;
1250     }
1251 
1252     if (handle == NULL)
1253     {
1254         return;
1255     }
1256 
1257     Mix_FreeMusic(music);
1258 }
1259 
I_MP_RegisterSong(void * data,int len)1260 static void *I_MP_RegisterSong(void *data, int len)
1261 {
1262     const char *filename;
1263     Mix_Music *music;
1264 
1265     if (!music_initialized)
1266     {
1267         return NULL;
1268     }
1269 
1270     // See if we're substituting this MUS for a high-quality replacement.
1271     filename = GetSubstituteMusicFile(data, len);
1272     if (filename == NULL)
1273     {
1274         return NULL;
1275     }
1276 
1277     music = Mix_LoadMUS(filename);
1278     if (music == NULL)
1279     {
1280         // Fall through and play MIDI normally, but print an error
1281         // message.
1282         fprintf(stderr, "Failed to load substitute music file: %s: %s\n",
1283                 filename, Mix_GetError());
1284         return NULL;
1285     }
1286 
1287     // Read loop point metadata from the file so that we know where
1288     // to loop the music.
1289     ReadLoopPoints(filename, &file_metadata);
1290     return music;
1291 }
1292 
1293 // Is the song playing?
I_MP_MusicIsPlaying(void)1294 static boolean I_MP_MusicIsPlaying(void)
1295 {
1296     if (!music_initialized)
1297     {
1298         return false;
1299     }
1300 
1301     return Mix_PlayingMusic();
1302 }
1303 
1304 // Get position in substitute music track, in seconds since start of track.
GetMusicPosition(void)1305 static double GetMusicPosition(void)
1306 {
1307     unsigned int music_pos;
1308     int freq;
1309 
1310     Mix_QuerySpec(&freq, NULL, NULL);
1311 
1312     SDL_LockAudio();
1313     music_pos = current_track_pos;
1314     SDL_UnlockAudio();
1315 
1316     return (double) music_pos / freq;
1317 }
1318 
RestartCurrentTrack(void)1319 static void RestartCurrentTrack(void)
1320 {
1321     double start = (double) file_metadata.start_time
1322                  / file_metadata.samplerate_hz;
1323 
1324     // If the track finished we need to restart it.
1325     if (current_track_music != NULL)
1326     {
1327         Mix_PlayMusic(current_track_music, 1);
1328     }
1329 
1330     Mix_SetMusicPosition(start);
1331     SDL_LockAudio();
1332     current_track_pos = file_metadata.start_time;
1333     SDL_UnlockAudio();
1334 }
1335 
1336 // Poll music position; if we have passed the loop point end position
1337 // then we need to go back.
I_MP_PollMusic(void)1338 static void I_MP_PollMusic(void)
1339 {
1340     // When playing substitute tracks, loop tags only apply if we're playing
1341     // a looping track. Tracks like the title screen music have the loop
1342     // tags ignored.
1343     if (current_track_loop && file_metadata.valid)
1344     {
1345         double end = (double) file_metadata.end_time
1346                    / file_metadata.samplerate_hz;
1347 
1348         // If we have reached the loop end point then we have to take action.
1349         if (file_metadata.end_time >= 0 && GetMusicPosition() >= end)
1350         {
1351             RestartCurrentTrack();
1352         }
1353 
1354         // Have we reached the actual end of track (not loop end)?
1355         if (!Mix_PlayingMusic())
1356         {
1357             RestartCurrentTrack();
1358         }
1359     }
1360 }
1361 
1362 music_module_t music_pack_module =
1363 {
1364     NULL,
1365     0,
1366     I_MP_InitMusic,
1367     I_MP_ShutdownMusic,
1368     I_MP_SetMusicVolume,
1369     I_MP_PauseSong,
1370     I_MP_ResumeSong,
1371     I_MP_RegisterSong,
1372     I_MP_UnRegisterSong,
1373     I_MP_PlaySong,
1374     I_MP_StopSong,
1375     I_MP_MusicIsPlaying,
1376     I_MP_PollMusic,
1377 };
1378 
1379