1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_sound.c 1471 2019-10-04 08:59:55Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // Revision 1.2  2003/07/13 13:18:59  hurdler
18 // Revision 1.1  2001/04/17 22:23:38  calumr
19 // Revision 1.1  2000/08/21 21:17:32  metzgermeister
20 // Initial import to CVS
21 //
22 // DESCRIPTION:
23 //	System interface for sound.
24 //
25 //-----------------------------------------------------------------------------
26 
27 #include <math.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <Carbon/Carbon.h>
31 
32 #include "doomincl.h"
33 #include "doomstat.h"
34 
35 #include "i_system.h"
36 #include "i_sound.h"
37 #include "m_argv.h"
38 #include "m_misc.h"
39 #include "m_random.h"
40 #include "w_wad.h"
41   // ?? no longer gets own lumps
42 #include "s_sound.h"
43 #include "command.h"
44   // consvar_t
45 #include "m_swap.h"
46 #include "d_main.h"
47 #include "z_zone.h"
48 
49 
50 static void COM_PlaySong (void);
51 
52 // The number of internal mixing channels,
53 //  the samples calculated for each mixing step,
54 //  the size of the 16bit, 2 hardware channel (stereo)
55 //  mixing buffer, and the samplerate of the raw data.
56 
57 // Needed for calling the actual sound output.
58 static int SAMPLECOUNT=		512;
59 #define NUM_CHANNELS		16
60 
61 #define SAMPLERATE		11025	// Hz
62 
63 // The channel data pointers, start and end.
64 unsigned char*	channels[NUM_CHANNELS];
65 
66 // Time/gametic that the channel started playing,
67 //  used to determine oldest, which automatically
68 //  has lowest priority.
69 // In case number of active sounds exceeds
70 //  available channels.
71 int		channelstart[NUM_CHANNELS];
72 
73 // SFX id of the playing sound effect.
74 // Used to catch duplicates (like chainsaw).
75 int		channelids[NUM_CHANNELS];
76 
77 // Flags for the -nosound and -nomusic options
78 extern boolean nosoundfx;
79 extern boolean nomusic;
80 
81 //start of mac stuff
82 static SndChannelPtr	soundChannels[NUM_CHANNELS];
83 static int		channelbusy[NUM_CHANNELS];
84 
soundCallback(SndChannelPtr soundChannel,SndCommand * pCmd)85 static pascal void soundCallback (SndChannelPtr soundChannel, SndCommand *pCmd)
86 {
87     if (pCmd->param1 == 0x1234)
88     {
89         int *channelInUse = (int *)pCmd->param2;
90         *channelInUse = 0;
91     }
92 }
93 
94 
95 //
96 // This function adds a sound to the list of currently active sounds,
97 //  which is maintained as a given number
98 //  (eight, usually) of internal channels.
99 // Returns a handle.
100 //
101 //  vol : volume, 0..255
102 //  sep : separation, +/- 127, SURROUND_SEP special operation
addsfx(int sfxid,int vol,int step,int sep)103 static int addsfx ( int		sfxid,
104 		    int		vol,
105 		    int		step,
106 		    int		sep )
107 {
108     int  i;
109     int	 slot;
110     int	 rightvol, leftvol;
111 
112     // Chainsaw troubles.
113     // Play these sound effects only one at a time.
114     if (S_sfx[sfxid].flags & SFX_single)
115     {
116         // Loop all channels, check.
117         for (i=0 ; i<NUM_CHANNELS ; i++)
118         {
119 	    // Active, and using the same SFX?
120 	    if ( (channels[i])
121 		  && (channelids[i] == sfxid) )
122 	    {
123 	        if( S_sfx[sfxid].flags & SFX_id_fin )
124 		    return i;  // already have one, return slot
125 	        // Reset.
126 	        channels[i] = 0;
127 	        break;
128 	    }
129 	}
130     }
131 
132     // Loop all channels to find oldest SFX.
133     slot = 0;  // default
134     int oldest = INT_MAX;
135     for (i=0; i<NUM_CHANNELS; i++)
136     {
137         if ( channels[i] == 0 )  // unused
138         {
139 	    slot = i;
140 	    break;
141 	}
142         if (channelstart[i] < oldest)  // older
143         {
144 	    slot = i;
145 	    oldest = channelstart[i];
146 	}
147     }
148 
149     // Okay, in the less recent channel,
150     //  we will handle the new SFX.
151     // Set pointer to raw data.
152     channels[slot] = (unsigned char *) S_sfx[sfxid].data;
153 
154     // Should be gametic, I presume.
155     channelstart[slot] = gametic;
156 
157     // volume : range 0..255
158     // mix_sfxvolume : range 0..31
159     vol = (vol * mix_sfxvolume) >> 6;
160     // Notice : sdldoom replaced all the calls to avoid this conversion
161 
162 #ifdef SURROUND_SOUND
163     if( sep > 128 )   sep = 0;  // No SURROUND
164 #endif
165     // Separation, that is, orientation/stereo.
166     // sep : +/- 127, <0 is left, >0 is right
167     sep += 129;  // 129 +/- 127 ; ( 1 - 256 )
168     leftvol = vol - ((vol * sep * sep) >> 16);
169     sep = 258 - sep;  // 129 +/- 127
170     rightvol = vol - ((vol * sep * sep) >> 16);
171 
172     // Sanity check, clamp volume.
173     if (rightvol < 0 || rightvol > 127)
174     {
175         I_SoftError("rightvol out of bounds\n");
176         rightvol = ( rightvol < 0 ) ? 0 : 127;
177     }
178 
179     if (leftvol < 0 || leftvol > 127)
180     {
181         I_SoftError("leftvol out of bounds\n");
182         leftvol = ( leftvol < 0 ) ? 0 : 127;
183     }
184 
185     // Preserve sound SFX id,
186     //  e.g. for avoiding duplicates of chainsaw.
187     channelids[slot] = sfxid;
188 
189     {
190         ExtSoundHeader theSndBuffer;
191         SndCommand theCmd;
192 
193         theCmd.param1 = 0;
194         theCmd.param2 = (rightvol << 16) + leftvol;
195         theCmd.cmd = volumeCmd;
196         SndDoImmediate (soundChannels[slot], &theCmd);
197 
198         theSndBuffer.samplePtr = (Ptr) S_sfx[sfxid].data;
199         theSndBuffer.numFrames = S_sfx[sfxid].length;
200         theSndBuffer.numChannels = 1; // 2 for stereo
201         theSndBuffer.sampleRate = rate11025hz;
202         theSndBuffer.encode = extSH;
203         theSndBuffer.sampleSize = 8; // 8-bit data
204 
205         // Send the buffer to the channel
206         theCmd.param1 = 0;
207         theCmd.param2 = (long) &theSndBuffer;
208         theCmd.cmd = bufferCmd;
209 
210         //SndDoCommand (soundChannels[slot], &theCmd, false);
211         SndDoImmediate (soundChannels[slot], &theCmd);
212         channelbusy[slot] = 1;
213 
214         theCmd.param1 = 0x1234;
215         theCmd.param2 = (long) &channelbusy[slot];
216         theCmd.cmd = callBackCmd;
217         SndDoCommand (soundChannels[slot], &theCmd, false);
218     }
219 
220     return slot;   // handle is slot
221 }
222 
I_SetChannels(void)223 void I_SetChannels(void)
224 {}
225 
I_SetSfxVolume(int volume)226 void I_SetSfxVolume(int volume)
227 {
228     // Can use mix_sfxvolume (0..31), or set local volume vars.
229     // mix_sfxvolume = volume;
230 }
231 
232 
I_GetSfx(sfxinfo_t * sfx)233 void I_GetSfx (sfxinfo_t*  sfx)
234 {
235     unsigned char*      sfxdata;
236     unsigned char*      paddedsfx;
237     int                 i;
238     int                 size;
239     int                 paddedsize;
240 
241     S_GetSfxLump( sfx );  // lump to sfx
242     sfxdata = (unsigned char*) sfx->data;
243     if( ! sfxdata ) return;
244     size = sfx->length;
245 
246     // Pads the sound effect out to the mixing buffer size.
247     // The original realloc would interfere with zone memory.
248     paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
249 
250     // Allocate from zone memory.
251     paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 );
252     // ddt: (unsigned char *) realloc(sfx, paddedsize+8);
253     // This should interfere with zone memory handling,
254     //  which does not kick in in the soundserver.
255 
256     // Now copy and pad.
257     memcpy(  paddedsfx, sfxdata, size );
258     for (i=size ; i<paddedsize+8 ; i++)
259         paddedsfx[i] = 128;
260 
261     // Remove the cached lump.
262     Z_Free( sfxdata );
263 
264     // Preserve padded length.
265     sfx->length = paddedsize;
266     // Return allocated padded data.
267     sfx->data = (void *) (paddedsfx + 8);  // skip header
268 }
269 
I_FreeSfx(sfxinfo_t * sfx)270 void I_FreeSfx (sfxinfo_t* sfx)
271 {
272     // use default Free
273     if( sfx->data )
274         sfx->data -= 8;   // undo skip header, for Z_Free
275 }
276 
277 //  vol : volume, 0..255
278 // Return a channel handle.
I_StartSound(sfxid_t sfxid,int vol,int sep,int pitch,int priority)279 int I_StartSound(sfxid_t sfxid, int vol, int sep, int pitch, int priority)
280 {
281     // UNUSED
282     priority = 0;
283 
284     if(nosoundfx)
285 	return 0;
286 
287     id = addsfx( id, vol, pitch, sep );
288 
289     return id;
290 }
291 
292 //   handle : the handle returned by StartSound.
I_StopSound(int handle)293 void I_StopSound (int handle)
294 {
295     SndCommand theCmd;
296 
297     if (handle < 0 || handle >= NUM_CHANNELS) return;
298 
299     // Immediately stop this sound
300     theCmd.param1 = 0;
301     theCmd.param2 = 0;
302     theCmd.cmd = quietCmd;
303     SndDoImmediate (soundChannels[handle], &theCmd);
304     theCmd.cmd = flushCmd;
305     SndDoImmediate (soundChannels[handle], &theCmd);
306 
307     channelbusy[handle] = 0;
308 }
309 
310 //   handle : the handle returned by StartSound.
I_SoundIsPlaying(int handle)311 int I_SoundIsPlaying(int handle)
312 {
313     return channelbusy[handle];
314 }
315 
I_UpdateSound(void)316 void I_UpdateSound (void)
317 {
318     MusicEvents();  //for QuickTime music playing
319 }
320 
I_SubmitSound(void)321 void I_SubmitSound(void)
322 {}
323 
324 // You need the handle returned by StartSound.
I_UpdateSoundParams(int handle,int vol,int sep,int pitch)325 void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
326 {
327     SndCommand theCmd;
328     int leftvol, rightvol;
329 
330     if(nosoundfx)
331         return;
332 
333     // vol : range 0..255
334     // mix_sfxvolume : range 0..31
335     vol = (vol * mix_sfxvolume) >> 6;
336 
337 #ifdef SURROUND_SOUND
338     if( sep > 128 )   sep = 0;  // No SURROUND
339 #endif
340     // Separation, that is, orientation/stereo.
341     // sep : +/- 127, <0 is left, >0 is right
342     sep += 129;  // 129 +/- 127 ; ( 1 - 256 )
343     leftvol = vol - ((vol * sep * sep) >> 16);
344     sep = 258 - sep;  // 129 +/- 127
345     rightvol = vol - ((vol * sep * sep) >> 16);
346 
347     // Send the volume to the channel
348     theCmd.param1 = 0;
349     theCmd.param2 = (rightvol << 16) + leftvol;
350     theCmd.cmd = volumeCmd;
351     SndDoImmediate (soundChannels[handle], &theCmd);
352 }
353 
354 
I_ShutdownSound(void)355 void I_ShutdownSound(void)
356 {
357     int i;
358 
359     if(nosoundfx)
360 	return;
361 
362     CONS_Printf("I_ShutdownSound:\n");
363 
364     for (i = 0; i < NUM_CHANNELS; i++)
365     {
366         SndDisposeChannel (soundChannels[i], true);
367     }
368 
369     CONS_Printf("\tshut down\n");
370 }
371 
372 
I_StartupSound()373 void I_StartupSound()
374 {
375     int i;
376     int err;
377 
378     if(nosoundfx)
379 	return;
380 
381     // Configure sound device
382     CONS_Printf("I_InitSound: \n");
383 
384     for (i = 0; i < NUM_CHANNELS; i ++)
385     {
386 	soundChannels[i] = NULL;
387 	channelbusy[i] = 0;
388 	err = SndNewChannel (&soundChannels[i], sampledSynth, initMono, 		NewSndCallBackUPP(soundCallback));
389     }
390 
391 #if 0
392     for (i=1 ; i<NUMSFX ; i++)
393     {
394 	// Alias? Example is the chaingun sound linked to pistol.
395 	if (S_sfx[i].name) {
396             if (!S_sfx[i].link)
397 	    {
398 	        // Load data from WAD file.
399 		S_sfx[i].data = getsfx( &S_sfx[i] );
400 	    }
401 	    else
402 	    {
403 	        // Previously loaded already?
404 	        S_sfx[i].data = S_sfx[i].link->data;
405 	        S_sfx[i].length = S_sfx[i].link->length;
406 	    }
407 	}
408     }
409 #endif
410 
411     CONS_Printf("\tpre-cached all sound data\n");
412 }
413 
414 //
415 // MUSIC API.
416 //
417 
418 #include <QuickTime/Movies.h>
419 
420 static int mus_song = 0;
421 static int musicVolume = 15;
422 
423 static Movie		midiMovie;
424 static Boolean		midiLoop;
425 
426 consvar_t user_songs[PLAYLIST_LENGTH];
427 
428 boolean PlayThis(char *name);
429 
COM_SkipNext(void)430 static void COM_SkipNext (void)
431 {
432     mus_song++;
433 
434     DisposeMovie (midiMovie);
435     midiMovie = NULL;
436 
437     if (mus_song==PLAYLIST_LENGTH)
438        mus_song = 0;
439 
440     if (PlayThis(user_songs[mus_song].string))
441     {
442         CONS_Printf("Playing next song\n");
443     }
444     else
445     {
446         CV_Set(&user_songs[mus_song], " ");
447     }
448 }
449 
COM_SkipPrev(void)450 static void COM_SkipPrev (void)
451 {
452     mus_song--;
453 
454     DisposeMovie (midiMovie);
455     midiMovie = NULL;
456 
457     if (mus_song==-1)
458         mus_song = PLAYLIST_LENGTH;
459 
460     if (PlayThis(user_songs[mus_song].string))
461     {
462         CONS_Printf("Playing next song\n");
463     }
464     else
465     {
466         CV_Set(&user_songs[mus_song], " ");
467     }
468 }
469 
COM_PlayListRandom(void)470 static void COM_PlayListRandom (void)
471 {
472     CV_SetValue(&play_mode, playlist_random);
473     mus_song = M_Random() % PLAYLIST_LENGTH;
474 
475     CONS_Printf("Playing random user_songs from play list\n");
476 }
477 
COM_PlayList(void)478 static void COM_PlayList (void)
479 {
480     CV_SetValue(&play_mode, playlist_normal);
481     mus_song = 0;
482 
483     CONS_Printf("Playing play list\n");
484 }
485 
COM_PlayListStop(void)486 static void COM_PlayListStop (void)
487 {
488     CV_SetValue(&play_mode, music_normal);
489     mus_song = 0;
490     DisposeMovie (midiMovie);
491     midiMovie = NULL;
492 
493     CONS_Printf("Stopped play list\n");
494 }
495 
MusicEvents(void)496 void MusicEvents (void)
497 {
498     if (nomusic)
499         return;
500 
501     if (midiMovie)
502     {
503         // Let QuickTime get some time
504         MoviesTask (midiMovie, 0);
505 
506         // If this song is looping, restart it
507         if (IsMovieDone (midiMovie))
508         {
509 	    if (midiLoop)
510 	    {
511 	        GoToBeginningOfMovie (midiMovie);
512 	        StartMovie (midiMovie);
513 	    }
514 	    else
515 	    {
516 	        DisposeMovie (midiMovie);
517 	        midiMovie = NULL;
518 	    }
519 	}
520     }
521     else if (play_mode.value == playlist_normal)
522     {
523         mus_song++;
524         if (mus_song==PLAYLIST_LENGTH)
525 	    mus_song = 0;
526         if (PlayThis(user_songs[mus_song].string))
527         {
528 	    CONS_Printf("Playing next song\n");
529 	}
530         else
531         {
532             CV_Set(&user_songs[mus_song], "");
533         }
534     }
535     else if (play_mode.value == playlist_random)
536     {
537         mus_song = M_Random() % PLAYLIST_LENGTH;
538         if (PlayThis(user_songs[mus_song].string))
539         {
540     	    CONS_Printf("Playing next song\n");
541 	}
542         else
543         {
544             CV_Set(&user_songs[mus_song], "");
545         }
546     }
547 }
548 
I_ShutdownMusic(void)549 void I_ShutdownMusic(void)
550 {
551     if(nomusic)
552 	return;
553 
554     CONS_Printf("I_ShutdownMusic:\n");
555 
556     if (midiMovie)
557     {
558         StopMovie (midiMovie);
559         DisposeMovie (midiMovie);
560         ExitMovies ();
561         midiMovie = NULL;
562     }
563 
564     CONS_Printf("\tshut down\n");
565 }
566 
I_InitMusic(void)567 void I_InitMusic(void)
568 {
569     if(nomusic)
570 	return;
571 
572     CONS_Printf("I_InitMusic:\n");
573 
574     if (EnterMovies () != noErr)
575     {
576         CONS_Printf("\tI_InitMusic: Couldn't initialise Quicktime\n");
577         nomusic = true;
578     }
579 
580     mus_song = 0;
581     midiMovie = NULL;
582 
583     COM_AddCommand ("playsong",COM_PlaySong);
584     COM_AddCommand ("playrandom",COM_PlayListRandom);
585     COM_AddCommand ("playlist",COM_PlayList);
586     COM_AddCommand ("stopplaylist",COM_PlayListStop);
587 
588     COM_AddCommand ("nextsong",COM_SkipNext);
589     COM_AddCommand ("prevsong",COM_SkipPrev);
590 
591     CONS_Printf("\tdone\n");
592 }
593 
I_PlaySong(int handle,int looping)594 void I_PlaySong(int handle, int looping)
595 {
596     if(nomusic)
597         return;
598 
599     if (play_mode.value != music_normal)
600         return;
601 
602     midiLoop = looping;
603     if (midiMovie)
604     {
605         StartMovie (midiMovie);
606     }
607 }
608 
I_PauseSong(int handle)609 void I_PauseSong (int handle)
610 {
611     if(nomusic)
612         return;
613 
614     if (play_mode.value != music_normal)
615         return;
616 
617     if (midiMovie)
618     {
619         StopMovie (midiMovie);
620     }
621 }
622 
I_ResumeSong(int handle)623 void I_ResumeSong (int handle)
624 {
625     if(nomusic)
626 	return;
627 
628     if (play_mode.value != music_normal)
629         return;
630 
631     if (midiMovie)
632     {
633         StartMovie (midiMovie);
634     }
635 }
636 
I_StopSong(int handle)637 void I_StopSong(int handle)
638 {
639     if(nomusic)
640 	return;
641 
642     if (play_mode.value != music_normal)
643         return;
644 
645     if (midiMovie)
646     {
647         StopMovie (midiMovie);
648     }
649 }
650 
I_UnRegisterSong(int handle)651 void I_UnRegisterSong(int handle)
652 {
653     if(nomusic)
654 	return;
655 
656     if (play_mode.value != music_normal)
657         return;
658 
659     if (midiMovie)
660     {
661         StopMovie (midiMovie);
662         DisposeMovie (midiMovie);
663         midiMovie = NULL;
664     }
665 }
666 
PlayThis(char * name)667 boolean PlayThis(char *name)
668 {
669     FSSpec midiSpec;
670     OSErr err;
671     short midiRef;
672     char  mid_file[256];
673     FSRef ref;
674 
675     if(nomusic)
676         return false;
677 
678     if (midiMovie)
679         DisposeMovie (midiMovie);
680     midiMovie = NULL;
681 
682     if (!name || *name == 0)
683         return false;
684 
685     {
686         char *path;
687 
688         if (getenv("DOOMMUSICDIR"))
689         {
690 	    path = getenv("DOOMMUSICDIR");
691 	    sprintf(mid_file, "%s/%s", path, name);
692 	}
693         else
694         {
695 #ifdef __MACH__
696 	    //[segabor]: If Music folder is in the Resources folder get mid from there
697 	    extern char mac_music_home[256];
698 
699 	    if (mac_music_home[0] == '.')
700 	        sprintf(mid_file, "./Music/%s", name);
701 	    else
702 	        sprintf(mid_file, "%s/%s", mac_music_home, name);
703 #else
704 	    path = malloc(256);
705 	    if ( getcwd(path, 256) == NULL )
706 	        path = ".";
707 	    sprintf(mid_file, "%s/Music/%s", path, name);
708 	    free(path);
709 #endif
710 	}
711     }
712 
713     I_OutputMsg("i_sound: Attempting to play %s\n", mid_file);
714 
715     err = FSPathMakeRef(mid_file, &ref, NULL);
716     if (err)
717     {
718         I_OutputMsg("PlayThis: FSPathMakeRef = %i\n", err);
719         return false;
720     }
721 
722     err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, &midiSpec, NULL);
723     if (err)
724     {
725         I_OutputMsg("PlayThis: FSGetCatalogInfo = %i\n", err);
726         return false;
727     }
728 
729     err = OpenMovieFile (&midiSpec, &midiRef, fsRdPerm);
730     if (err)
731     {
732         I_OutputMsg("PlayThis: OpenMovieFile = %i\n", err);
733         return false;
734     }
735 
736     err = NewMovieFromFile (&midiMovie, midiRef, NULL, NULL, newMovieActive, NULL);
737     if (err)
738     {
739         I_OutputMsg("PlayThis: NewMovieFromFile = %i\n", err);
740         return false;
741     }
742 
743     GoToBeginningOfMovie (midiMovie);
744     PrerollMovie (midiMovie, 0, 0x10000);
745     SetMovieVolume (midiMovie, musicVolume << 3);
746     StartMovie (midiMovie);
747 
748     CloseMovieFile (midiRef);
749 
750     return true;
751 }
752 
753 // [WDJ] len is unused, keep compatible API
I_RegisterSong(int handle,int len)754 int I_RegisterSong(int handle, int len)
755 {
756     Str63 name = "";
757 
758     if (play_mode.value != music_normal)
759         return handle;
760 
761     // Make sure song number is valid
762     if (handle < mus_e1m1 || handle > NUMMUSIC)
763         return -1;
764 
765     mus_song = handle;
766 
767     strcat ((char *)name, S_music[handle].name);
768     strcat((char *)name, ".mid!");
769     strupr(name);
770     PlayThis(name);
771 
772     return handle;
773 }
774 
COM_PlaySong(void)775 static void COM_PlaySong (void)
776 {
777     char  *name;
778 
779     if (COM_Argc()<2)
780     {
781         CONS_Printf("Usage: playsong \"name\"\n\tplaysong <number>\n");
782         CONS_Printf("\tRemember to use quotes around the name!\n");
783         return;
784     }
785 
786     name = Z_StrDup (COM_Argv(1));
787 
788     if (strlen(name)==1)
789     {
790         CONS_Printf("Playing song 0%i from playlist\n", (name[0]-'0'));
791         PlayThis(user_songs[(name[0]-'0')].string);
792     }
793     else if (strlen(name)==2)
794     {
795         CONS_Printf("Playing song %i from playlist\n", (name[1]-'0') + (name[0]-'0')*10);
796         PlayThis(user_songs[(name[1]-'0') + (name[0]-'0')*10].string);
797     }
798     else
799         PlayThis(name);
800 
801     Z_Free(name);
802 }
803 
I_SetMusicVolume(int volume)804 void I_SetMusicVolume(int volume)
805 {
806     if(nomusic)
807 	return;
808 
809     musicVolume = volume;
810     if (midiMovie)
811         SetMovieVolume (midiMovie, musicVolume << 3);
812 }
813 
814 //Hurdler: TODO
I_StartFMODSong()815 void I_StartFMODSong()
816 {
817     CONS_Printf("I_StartFMODSong: Not yet supported under MacOS.\n");
818 }
819 
I_StopFMODSong()820 void I_StopFMODSong()
821 {
822     CONS_Printf("I_StopFMODSong: Not yet supported under MacOS.\n");
823 }
I_SetFMODVolume(int volume)824 void I_SetFMODVolume(int volume)
825 {
826     CONS_Printf("I_SetFMODVolume: Not yet supported under MacOS.\n");
827 }
828