1 /*
2 ** i_music.cpp
3 ** Plays music
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2010 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #ifdef _WIN32
36 #define WIN32_LEAN_AND_MEAN
37 #include <windows.h>
38 #include <mmsystem.h>
39 #else
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 #include <unistd.h>
46 #include <wordexp.h>
47 #include <stdio.h>
48 #include "mus2midi.h"
49 #define FALSE 0
50 #define TRUE 1
51 extern void ChildSigHandler (int signum);
52 #endif
53
54 #include <ctype.h>
55 #include <assert.h>
56 #include <stdio.h>
57
58 #include "i_musicinterns.h"
59 #include "doomtype.h"
60 #include "m_argv.h"
61 #include "i_music.h"
62 #include "w_wad.h"
63 #include "c_console.h"
64 #include "c_dispatch.h"
65 #include "i_system.h"
66 #include "i_sound.h"
67 #include "s_sound.h"
68 #include "m_swap.h"
69 #include "i_cd.h"
70 #include "tempfiles.h"
71 #include "templates.h"
72 #include "stats.h"
73 #include "timidity/timidity.h"
74
75 #define GZIP_ID1 31
76 #define GZIP_ID2 139
77 #define GZIP_CM 8
78 #define GZIP_ID MAKE_ID(GZIP_ID1,GZIP_ID2,GZIP_CM,0)
79
80 #define GZIP_FTEXT 1
81 #define GZIP_FHCRC 2
82 #define GZIP_FEXTRA 4
83 #define GZIP_FNAME 8
84 #define GZIP_FCOMMENT 16
85
86 enum EMIDIType
87 {
88 MIDI_NOTMIDI,
89 MIDI_MIDI,
90 MIDI_HMI,
91 MIDI_XMI,
92 MIDI_MUS
93 };
94
95 extern int MUSHeaderSearch(const BYTE *head, int len);
96
97 EXTERN_CVAR (Int, snd_samplerate)
98 EXTERN_CVAR (Int, snd_mididevice)
99
100 static bool MusicDown = true;
101
102 static bool ungzip(BYTE *data, int size, TArray<BYTE> &newdata);
103
104 MusInfo *currSong;
105 int nomusic = 0;
106 float relative_volume = 1.f;
107 float saved_relative_volume = 1.0f; // this could be used to implement an ACS FadeMusic function
108
109 //==========================================================================
110 //
111 // CVAR snd_musicvolume
112 //
113 // Maximum volume of MOD/stream music.
114 //==========================================================================
115
116 CUSTOM_CVAR (Float, snd_musicvolume, 0.5f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
117 {
118 if (self < 0.f)
119 self = 0.f;
120 else if (self > 1.f)
121 self = 1.f;
122 else
123 {
124 // Set general music volume.
125 if (GSnd != NULL)
126 {
127 GSnd->SetMusicVolume(clamp<float>(self * relative_volume, 0, 1));
128 }
129 // For music not implemented through the digital sound system,
130 // let them know about the change.
131 if (currSong != NULL)
132 {
133 currSong->MusicVolumeChanged();
134 }
135 else
136 { // If the music was stopped because volume was 0, start it now.
137 S_RestartMusic();
138 }
139 }
140 }
141
142 //==========================================================================
143 //
144 //
145 //
146 //==========================================================================
147
I_InitMusic(void)148 void I_InitMusic (void)
149 {
150 static bool setatterm = false;
151
152 Timidity::LoadConfig();
153
154 snd_musicvolume.Callback ();
155
156 nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound");
157
158 #ifdef _WIN32
159 I_InitMusicWin32 ();
160 #endif // _WIN32
161
162 if (!setatterm)
163 {
164 setatterm = true;
165 atterm (I_ShutdownMusicExit);
166
167 #ifndef _WIN32
168 signal (SIGCHLD, ChildSigHandler);
169 #endif
170 }
171 MusicDown = false;
172 }
173
174
175 //==========================================================================
176 //
177 //
178 //
179 //==========================================================================
180
I_ShutdownMusic(bool onexit)181 void I_ShutdownMusic(bool onexit)
182 {
183 if (MusicDown)
184 return;
185 MusicDown = true;
186 if (currSong)
187 {
188 S_StopMusic (true);
189 assert (currSong == NULL);
190 }
191 Timidity::FreeAll();
192 if (onexit) WildMidi_Shutdown();
193 #ifdef _WIN32
194 I_ShutdownMusicWin32();
195 #endif // _WIN32
196 }
197
I_ShutdownMusicExit()198 void I_ShutdownMusicExit()
199 {
200 I_ShutdownMusic(true);
201 }
202
203
204 //==========================================================================
205 //
206 //
207 //
208 //==========================================================================
209
MusInfo()210 MusInfo::MusInfo()
211 : m_Status(STATE_Stopped), m_Looping(false), m_NotStartedYet(true)
212 {
213 }
214
~MusInfo()215 MusInfo::~MusInfo ()
216 {
217 if (currSong == this) currSong = NULL;
218 }
219
220 //==========================================================================
221 //
222 // starts playing this song
223 //
224 //==========================================================================
225
Start(bool loop,float rel_vol,int subsong)226 void MusInfo::Start(bool loop, float rel_vol, int subsong)
227 {
228 if (nomusic) return;
229
230 if (rel_vol > 0.f) saved_relative_volume = relative_volume = rel_vol;
231 Stop ();
232 Play (loop, subsong);
233 m_NotStartedYet = false;
234
235 if (m_Status == MusInfo::STATE_Playing)
236 currSong = this;
237 else
238 currSong = NULL;
239
240 // Notify the sound system of the changed relative volume
241 snd_musicvolume.Callback();
242 }
243
244 //==========================================================================
245 //
246 //
247 //
248 //==========================================================================
249
SetPosition(unsigned int ms)250 bool MusInfo::SetPosition (unsigned int ms)
251 {
252 return false;
253 }
254
IsMIDI() const255 bool MusInfo::IsMIDI() const
256 {
257 return false;
258 }
259
SetSubsong(int subsong)260 bool MusInfo::SetSubsong (int subsong)
261 {
262 return false;
263 }
264
Update()265 void MusInfo::Update ()
266 {
267 }
268
MusicVolumeChanged()269 void MusInfo::MusicVolumeChanged()
270 {
271 }
272
TimidityVolumeChanged()273 void MusInfo::TimidityVolumeChanged()
274 {
275 }
276
FluidSettingInt(const char *,int)277 void MusInfo::FluidSettingInt(const char *, int)
278 {
279 }
280
FluidSettingNum(const char *,double)281 void MusInfo::FluidSettingNum(const char *, double)
282 {
283 }
284
FluidSettingStr(const char *,const char *)285 void MusInfo::FluidSettingStr(const char *, const char *)
286 {
287 }
288
WildMidiSetOption(int opt,int set)289 void MusInfo::WildMidiSetOption(int opt, int set)
290 {
291 }
292
GetStats()293 FString MusInfo::GetStats()
294 {
295 return "No stats available for this song";
296 }
297
GetOPLDumper(const char * filename)298 MusInfo *MusInfo::GetOPLDumper(const char *filename)
299 {
300 return NULL;
301 }
302
GetWaveDumper(const char * filename,int rate)303 MusInfo *MusInfo::GetWaveDumper(const char *filename, int rate)
304 {
305 return NULL;
306 }
307
308 //==========================================================================
309 //
310 // create a streamer based on MIDI file type
311 //
312 //==========================================================================
313
CreateMIDIStreamer(FileReader & reader,EMidiDevice devtype,EMIDIType miditype,const char * args)314 static MIDIStreamer *CreateMIDIStreamer(FileReader &reader, EMidiDevice devtype, EMIDIType miditype, const char *args)
315 {
316 switch (miditype)
317 {
318 case MIDI_MUS:
319 return new MUSSong2(reader, devtype, args);
320
321 case MIDI_MIDI:
322 return new MIDISong2(reader, devtype, args);
323
324 case MIDI_HMI:
325 return new HMISong(reader, devtype, args);
326
327 case MIDI_XMI:
328 return new XMISong(reader, devtype, args);
329
330 default:
331 return NULL;
332 }
333 }
334
335 //==========================================================================
336 //
337 // identify MIDI file type
338 //
339 //==========================================================================
340
IdentifyMIDIType(DWORD * id,int size)341 static EMIDIType IdentifyMIDIType(DWORD *id, int size)
342 {
343 // Check for MUS format
344 // Tolerate sloppy wads by searching up to 32 bytes for the header
345 if (MUSHeaderSearch((BYTE*)id, size) >= 0)
346 {
347 return MIDI_MUS;
348 }
349 // Check for HMI format
350 else
351 if (id[0] == MAKE_ID('H','M','I','-') &&
352 id[1] == MAKE_ID('M','I','D','I') &&
353 id[2] == MAKE_ID('S','O','N','G'))
354 {
355 return MIDI_HMI;
356 }
357 // Check for HMP format
358 else
359 if (id[0] == MAKE_ID('H','M','I','M') &&
360 id[1] == MAKE_ID('I','D','I','P'))
361 {
362 return MIDI_HMI;
363 }
364 // Check for XMI format
365 else
366 if ((id[0] == MAKE_ID('F','O','R','M') &&
367 id[2] == MAKE_ID('X','D','I','R')) ||
368 ((id[0] == MAKE_ID('C','A','T',' ') || id[0] == MAKE_ID('F','O','R','M')) &&
369 id[2] == MAKE_ID('X','M','I','D')))
370 {
371 return MIDI_XMI;
372 }
373 // Check for MIDI format
374 else if (id[0] == MAKE_ID('M','T','h','d'))
375 {
376 return MIDI_MIDI;
377 }
378 else
379 {
380 return MIDI_NOTMIDI;
381 }
382 }
383
384 //==========================================================================
385 //
386 // identify a music lump's type and set up a player for it
387 //
388 //==========================================================================
389
I_RegisterSong(FileReader * reader,MidiDeviceSetting * device)390 MusInfo *I_RegisterSong (FileReader *reader, MidiDeviceSetting *device)
391 {
392 MusInfo *info = NULL;
393 const char *fmt;
394 DWORD id[32/4];
395
396 if (nomusic)
397 {
398 delete reader;
399 return 0;
400 }
401
402 if(reader->Read(id, 32) != 32 || reader->Seek(-32, SEEK_CUR) != 0)
403 {
404 delete reader;
405 return 0;
406 }
407
408 // Check for gzip compression. Some formats are expected to have players
409 // that can handle it, so it simplifies things if we make all songs
410 // gzippable.
411 if ((id[0] & MAKE_ID(255, 255, 255, 0)) == GZIP_ID)
412 {
413 int len = reader->GetLength();
414 BYTE *gzipped = new BYTE[len];
415 if (reader->Read(gzipped, len) != len)
416 {
417 delete[] gzipped;
418 delete reader;
419 return NULL;
420 }
421 delete reader;
422
423 MemoryArrayReader *memreader = new MemoryArrayReader(NULL, 0);
424 if (!ungzip(gzipped, len, memreader->GetArray()))
425 {
426 delete[] gzipped;
427 delete memreader;
428 return 0;
429 }
430 delete[] gzipped;
431 memreader->UpdateLength();
432
433 if (memreader->Read(id, 32) != 32 || memreader->Seek(-32, SEEK_CUR) != 0)
434 {
435 delete memreader;
436 return 0;
437 }
438 reader = memreader;
439 }
440
441 EMIDIType miditype = IdentifyMIDIType(id, sizeof(id));
442 if (miditype != MIDI_NOTMIDI)
443 {
444 EMidiDevice devtype = device == NULL? MDEV_DEFAULT : (EMidiDevice)device->device;
445 #ifndef _WIN32
446 // non-Windows platforms don't support MDEV_MMAPI so map to MDEV_SNDSYS
447 if (devtype == MDEV_MMAPI)
448 devtype = MDEV_SNDSYS;
449 #endif
450
451 retry_as_sndsys:
452 info = CreateMIDIStreamer(*reader, devtype, miditype, device != NULL? device->args.GetChars() : "");
453 if (info != NULL && !info->IsValid())
454 {
455 delete info;
456 info = NULL;
457 }
458 if (info == NULL && devtype != MDEV_SNDSYS && snd_mididevice < 0)
459 {
460 devtype = MDEV_SNDSYS;
461 goto retry_as_sndsys;
462 }
463 #ifdef _WIN32
464 if (info == NULL && devtype != MDEV_MMAPI && snd_mididevice >= 0)
465 {
466 info = CreateMIDIStreamer(*reader, MDEV_MMAPI, miditype, "");
467 }
468 #endif
469 }
470
471 // Check for various raw OPL formats
472 else if (
473 (id[0] == MAKE_ID('R','A','W','A') && id[1] == MAKE_ID('D','A','T','A')) || // Rdos Raw OPL
474 (id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
475 (id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF
476 {
477 info = new OPLMUSSong (*reader, device != NULL? device->args.GetChars() : "");
478 }
479 // Check for game music
480 else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
481 {
482 info = GME_OpenSong(*reader, fmt);
483 }
484 // Check for module formats
485 else
486 {
487 info = MOD_OpenSong(*reader);
488 }
489
490 if (info == NULL)
491 {
492 // Check for CDDA "format"
493 if (id[0] == (('R')|(('I')<<8)|(('F')<<16)|(('F')<<24)))
494 {
495 DWORD subid;
496
497 reader->Seek(8, SEEK_CUR);
498 if (reader->Read (&subid, 4) != 4)
499 {
500 delete reader;
501 return 0;
502 }
503 reader->Seek(-12, SEEK_CUR);
504
505 if (subid == (('C')|(('D')<<8)|(('D')<<16)|(('A')<<24)))
506 {
507 // This is a CDDA file
508 info = new CDDAFile (*reader);
509 }
510 }
511
512 // no support in sound system => no modules/streams
513 // 1024 bytes is an arbitrary restriction. It's assumed that anything
514 // smaller than this can't possibly be a valid music file if it hasn't
515 // been identified already, so don't even bother trying to load it.
516 // Of course MIDIs shorter than 1024 bytes should pass.
517 if (info == NULL && (reader->GetLength() >= 1024 || id[0] == MAKE_ID('M','T','h','d')))
518 {
519 // Let the sound system figure out what it is.
520 info = new StreamSong (reader);
521 // Assumed ownership
522 reader = NULL;
523 }
524 }
525
526 if (reader != NULL) delete reader;
527
528 if (info && !info->IsValid ())
529 {
530 delete info;
531 info = NULL;
532 }
533
534 return info;
535 }
536
537 //==========================================================================
538 //
539 // play CD music
540 //
541 //==========================================================================
542
I_RegisterCDSong(int track,int id)543 MusInfo *I_RegisterCDSong (int track, int id)
544 {
545 MusInfo *info = new CDSong (track, id);
546
547 if (info && !info->IsValid ())
548 {
549 delete info;
550 info = NULL;
551 }
552
553 return info;
554 }
555
556 //==========================================================================
557 //
558 //
559 //
560 //==========================================================================
561
I_RegisterURLSong(const char * url)562 MusInfo *I_RegisterURLSong (const char *url)
563 {
564 StreamSong *song;
565
566 song = new StreamSong(url);
567 if (song->IsValid())
568 {
569 return song;
570 }
571 delete song;
572 return NULL;
573 }
574
575 //==========================================================================
576 //
577 // ungzip
578 //
579 // VGZ files are compressed with gzip, so we need to uncompress them before
580 // handing them to GME.
581 //
582 //==========================================================================
583
ungzip(BYTE * data,int complen,TArray<BYTE> & newdata)584 static bool ungzip(BYTE *data, int complen, TArray<BYTE> &newdata)
585 {
586 const BYTE *max = data + complen - 8;
587 const BYTE *compstart = data + 10;
588 BYTE flags = data[3];
589 unsigned isize;
590 z_stream stream;
591 int err;
592
593 // Find start of compressed data stream
594 if (flags & GZIP_FEXTRA)
595 {
596 compstart += 2 + LittleShort(*(WORD *)(data + 10));
597 }
598 if (flags & GZIP_FNAME)
599 {
600 while (compstart < max && *compstart != 0)
601 {
602 compstart++;
603 }
604 }
605 if (flags & GZIP_FCOMMENT)
606 {
607 while (compstart < max && *compstart != 0)
608 {
609 compstart++;
610 }
611 }
612 if (flags & GZIP_FHCRC)
613 {
614 compstart += 2;
615 }
616 if (compstart >= max - 1)
617 {
618 return false;
619 }
620
621 // Decompress
622 isize = LittleLong(*(DWORD *)(data + complen - 4));
623 newdata.Resize(isize);
624
625 stream.next_in = (Bytef *)compstart;
626 stream.avail_in = (uInt)(max - compstart);
627 stream.next_out = &newdata[0];
628 stream.avail_out = isize;
629 stream.zalloc = (alloc_func)0;
630 stream.zfree = (free_func)0;
631
632 err = inflateInit2(&stream, -MAX_WBITS);
633 if (err != Z_OK)
634 {
635 return false;
636 }
637 err = inflate(&stream, Z_FINISH);
638 if (err != Z_STREAM_END)
639 {
640 inflateEnd(&stream);
641 return false;
642 }
643 err = inflateEnd(&stream);
644 if (err != Z_OK)
645 {
646 return false;
647 }
648 return true;
649 }
650
651 //==========================================================================
652 //
653 //
654 //
655 //==========================================================================
656
I_UpdateMusic()657 void I_UpdateMusic()
658 {
659 if (currSong != NULL)
660 {
661 currSong->Update();
662 }
663 }
664
665 //==========================================================================
666 //
667 // Sets relative music volume. Takes $musicvolume in SNDINFO into consideration
668 //
669 //==========================================================================
670
I_SetMusicVolume(float factor)671 void I_SetMusicVolume (float factor)
672 {
673 factor = clamp<float>(factor, 0, 2.0f);
674 relative_volume = saved_relative_volume * factor;
675 snd_musicvolume.Callback();
676 }
677
678 //==========================================================================
679 //
680 // test a relative music volume
681 //
682 //==========================================================================
683
CCMD(testmusicvol)684 CCMD(testmusicvol)
685 {
686 if (argv.argc() > 1)
687 {
688 relative_volume = (float)strtod(argv[1], NULL);
689 snd_musicvolume.Callback();
690 }
691 else
692 Printf("Current relative volume is %1.2f\n", relative_volume);
693 }
694
695 //==========================================================================
696 //
697 // STAT music
698 //
699 //==========================================================================
700
ADD_STAT(music)701 ADD_STAT(music)
702 {
703 if (currSong != NULL)
704 {
705 return currSong->GetStats();
706 }
707 return "No song playing";
708 }
709
710 //==========================================================================
711 //
712 // CCMD writeopl
713 //
714 // If the current song can be played with OPL instruments, dump it to
715 // the specified file on disk.
716 //
717 //==========================================================================
718
CCMD(writeopl)719 CCMD (writeopl)
720 {
721 if (argv.argc() == 2)
722 {
723 if (currSong == NULL)
724 {
725 Printf ("No song is currently playing.\n");
726 }
727 else
728 {
729 MusInfo *dumper = currSong->GetOPLDumper(argv[1]);
730 if (dumper == NULL)
731 {
732 Printf ("Current song cannot be saved as OPL data.\n");
733 }
734 else
735 {
736 dumper->Play(false, 0); // FIXME: Remember subsong.
737 delete dumper;
738 }
739 }
740 }
741 else
742 {
743 Printf ("Usage: writeopl <filename>\n");
744 }
745 }
746
747 //==========================================================================
748 //
749 // CCMD writewave
750 //
751 // If the current song can be represented as a waveform, dump it to
752 // the specified file on disk. The sample rate parameter is merely a
753 // suggestion, and the dumper is free to ignore it.
754 //
755 //==========================================================================
756
CCMD(writewave)757 CCMD (writewave)
758 {
759 if (argv.argc() >= 2 && argv.argc() <= 3)
760 {
761 if (currSong == NULL)
762 {
763 Printf ("No song is currently playing.\n");
764 }
765 else
766 {
767 MusInfo *dumper = currSong->GetWaveDumper(argv[1], argv.argc() == 3 ? atoi(argv[2]) : 0);
768 if (dumper == NULL)
769 {
770 Printf ("Current song cannot be saved as wave data.\n");
771 }
772 else
773 {
774 dumper->Play(false, 0); // FIXME: Remember subsong
775 delete dumper;
776 }
777 }
778 }
779 else
780 {
781 Printf ("Usage: writewave <filename> [sample rate]");
782 }
783 }
784
785 //==========================================================================
786 //
787 // CCMD writemidi
788 //
789 // If the currently playing song is a MIDI variant, write it to disk.
790 // If successful, the current song will restart, since MIDI file generation
791 // involves a simulated playthrough of the song.
792 //
793 //==========================================================================
794
CCMD(writemidi)795 CCMD (writemidi)
796 {
797 if (argv.argc() != 2)
798 {
799 Printf("Usage: writemidi <filename>");
800 return;
801 }
802 if (currSong == NULL)
803 {
804 Printf("No song is currently playing.\n");
805 return;
806 }
807 if (!currSong->IsMIDI())
808 {
809 Printf("Current song is not MIDI-based.\n");
810 return;
811 }
812
813 TArray<BYTE> midi;
814 FILE *f;
815 bool success;
816
817 static_cast<MIDIStreamer *>(currSong)->CreateSMF(midi, 1);
818 f = fopen(argv[1], "wb");
819 if (f == NULL)
820 {
821 Printf("Could not open %s.\n", argv[1]);
822 return;
823 }
824 success = (fwrite(&midi[0], 1, midi.Size(), f) == (size_t)midi.Size());
825 fclose (f);
826
827 if (!success)
828 {
829 Printf("Could not write to music file.\n");
830 }
831 }
832