shithub: rott

ref: 3e27f1b2d0d8f020e9d3509934736ee9055b8c31
dir: /src/rt_sound.c/

View raw version
/*
Copyright (C) 1994-1995  Apogee Software, Ltd.
Copyright (C) 2002-2015  icculus.org, GNU/Linux port
Copyright (C) 2017-2018  Steven LeVesque

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
#include "rt_def.h"
#include "rt_sound.h"
#include "_rt_soun.h"
#include "fx_man.h"
#include "music.h"
#include "z_zone.h"
#include "w_wad.h"
#include "rt_main.h"
#include "rt_ted.h"
#include "rt_menu.h"
#include "rt_playr.h"
#include "rt_util.h"
#include "rt_rand.h"
#include "watcom.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "rt_cfg.h"
#include "isr.h"
#include "develop.h"
#include "rt_net.h"

#include "rt_str.h"

#if (SHAREWARE==0)
#include "snd_reg.h"
#else
#include "snd_shar.h"
#endif

// Local Variables

static int soundstart;
static int soundtype;
int SD_Started=false;
static boolean PositionStored=false;
static int NumBadSounds=0;
static int remotestart;
static boolean SoundsRemapped = false;

int musicnums[ 11 ] = {
    -1, -1, -1, -1, -1, -1, SoundScape, -1, -1, -1, -1
};

int fxnums[ 11 ] = {
    -1, -1, -1, -1, -1, -1, SoundScape, -1, -1, -1, -1
};

int MUSIC_GetPosition( void ) {
    songposition pos;

    MUSIC_GetSongPosition( &pos );
    return pos.milliseconds;
}

void MUSIC_SetPosition( int time ) {
    MUSIC_SetSongTime( ( unsigned long )time );
}


//***************************************************************************
//
// SoundNumber
//
//***************************************************************************

int SoundNumber ( int x )
{
    if ((x>=SD_REMOTEM1SND) && (x<=SD_REMOTEM10SND))
        return remotestart + x - SD_REMOTEM1SND;
//      sounds[x].snds[soundtype]+remotestart;
    else
        return sounds[x].snds[soundtype]+soundstart;
}


//***************************************************************************
//
// SD_MakeCacheable - Make a sound that has just finished playing cacheable again
//
//***************************************************************************
void SD_MakeCacheable( unsigned long sndnum )
{
    if (sndnum == (unsigned long) -1)
    {
        return;
    }

    if (sndnum>=MAXSOUNDS)
    {
        SoftError ("Illegal sound value in SD_MakeCacheable value=%ld\n",sndnum);
        return;
    }
    sounds[sndnum].count--;
    if (sounds[sndnum].count>0)
        return;
    else
        W_CacheLumpNum(SoundNumber(sndnum),PU_CACHE, CvtNull, 1);
}

//***************************************************************************
//
// SD_SetupFXCard - Initialize sound Tables and start up sound card
//
//***************************************************************************

int SD_SetupFXCard ( int * numvoices, int * numbits, int * numchannels)
{
    fx_device device;
    int status;
    int card;

    if (SD_Started==true)
        SD_Shutdown();

    if ( ( FXMode < 0 ) || ( FXMode >= 11 ) )
    {
        return( 0 );
    }

    card = fxnums[ FXMode ];
    if (card==-1) // Check if it is off
        return (0);
        //status=FX_SetupCard( card, &device );
        status=FX_Ok;
        if ( status == FX_Ok )
        {
            *numvoices=device.MaxVoices;
            *numbits=device.MaxSampleBits;
            *numchannels=device.MaxChannels;
        }

    return (status);
}

//***************************************************************************
//
// SD_Startup - Initialize sound Tables and start up sound card
//
//***************************************************************************

int SD_Startup ( boolean bombonerror )
{
    int status;
    int card;
    int voices;
    int channels;
    int bits;
    int i;
    extern boolean IS8250;

    if (SD_Started==true)
        SD_Shutdown();

    if ( ( FXMode < 0 ) || ( FXMode >= 11 ) )
    {
        return( 0 );
    }
    card = fxnums[ FXMode ];
    if (card==-1) // Check if it is off
        return (0);

    switch (card)
    {
    case SoundScape:
        soundstart=W_GetNumForName("digistrt")+1;
        soundtype=fx_digital;
        break;
    default:
        Error("FX: Unsupported Card number %d",FXMode);
        break;
    }

    if ( soundtype == fx_digital )
    {
        if ( SoundsRemapped == false )
        {
            for( i = 0; i < SD_LASTSOUND; i++ )
            {
                int snd;

                snd = sounds[ i ].snds[ fx_digital ];
                if ( snd >= 0)
                {
                    sounds[ i ].snds[ fx_digital ] = W_GetNumForName(
                                                         W_GetNameForNum( snd + soundstart ) );
                }
            }
            SoundsRemapped = true;
        }
        soundstart = 0;
    }

    voices   = NumVoices;
    channels = NumChannels;
    bits     = NumBits;

    if ( IS8250 )
    {
        voices   = max( voices, 4 );
        channels = 1;
        bits     = 8;
    }

    status=FX_Init( card, voices, channels, bits, 44100 );
    
    if (status != FX_Ok)
    {
        if (bombonerror)
        {
            DeleteSoundFile ();
            Error( "%s\n", FX_ErrorString( status ) );
        }

        return (status);
    }

    if (stereoreversed == true)
    {
        FX_SetReverseStereo(!FX_GetReverseStereo());
    }

    FX_SetCallBack( SD_MakeCacheable );

    remotestart=W_GetNumForName("remostrt")+1;

    SD_Started=true;

    FX_SetVolume (FXvolume);

    return (0);
}

//***************************************************************************
//
// SD_SoundOkay - checks to see if the sound is okay
//
//***************************************************************************

boolean SD_SoundOkay ( int sndnum )
{
    if (SD_Started==false)
        return false;

    if (sndnum>=MAXSOUNDS)
        Error ("Illegal sound number, sound number = %d\n",sndnum);

    if (SoundOffset(sndnum)==-1)
        return false;

    if ( ( sounds[ sndnum ].flags & SD_PLAYONCE ) &&
            ( SD_SoundActive( sounds[ sndnum ].prevhandle ) ) )
    {
        return false;
    }

    return true;
}

//***************************************************************************
//
// SD_PlayIt - Play a pre-setup sound
//
//***************************************************************************

int SD_PlayIt ( int sndnum, int angle, int distance, int pitch )
{
    int voice;
    byte * snd;

    if (!(sounds[sndnum].flags & SD_WRITE))
    {
        if (sounds[sndnum].count)
        {
            if (distance<=sounds[sndnum].prevdistance)
                FX_StopSound(sounds[sndnum].prevhandle);
            else
                return 0;
        }
    }

    if ( !FX_VoiceAvailable( sounds[sndnum].priority ) )
    {
        return( 0 );
    }

    sounds[sndnum].count++;

    snd=W_CacheLumpNum(SoundNumber(sndnum),PU_STATIC, CvtNull, 1);

    if ( *snd == 'C' )
    {
        voice = FX_PlayVOC3D( (char*)snd, pitch, angle, distance,
                              sounds[sndnum].priority, (unsigned long) sndnum );
    }
    else
    {
        voice = FX_PlayWAV3D( (char*)snd, pitch, angle, distance,
                              sounds[sndnum].priority, (unsigned long) sndnum );
    }

    if ( voice < FX_Ok )
    {
        SD_MakeCacheable( sndnum );

        return 0;
    }

    NumBadSounds=0;

    if (!(sounds[sndnum].flags & SD_WRITE))
    {
        sounds[sndnum].prevhandle=voice;
        sounds[sndnum].prevdistance=distance;
    }
    return voice;
}


//***************************************************************************
//
// SD_Play - Play a sample
//
//***************************************************************************

int SD_Play ( int sndnum )
{
    int voice;
    int pitch;

    if ( SD_SoundOkay ( sndnum ) == false )
        return 0;

    pitch = 0;

    if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
    {
        pitch = PitchOffset();
    }

    voice = SD_PlayIt ( sndnum, 0, 0, pitch );

    return voice;

}

//***************************************************************************
//
// SD_Play3D - Play a positioned sample
//
//***************************************************************************

int SD_Play3D ( int sndnum, int angle, int distance )
{
    int voice;
    int pitch;

    if ( SD_SoundOkay ( sndnum ) == false )
        return 0;

    pitch = 0;
    if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
    {
        pitch = PitchOffset();
    }

    voice = SD_PlayIt ( sndnum, angle, distance, pitch );

    return voice;

}

//***************************************************************************
//
// SD_PlayPositionedSound - Play a positioned sample
//
//***************************************************************************

int SD_PlayPositionedSound ( int sndnum, int px, int py, int x, int y )
{
    int voice;
    int angle;
    int distance;
    int dx;
    int dy;
    int pitch;

    if ( SD_SoundOkay ( sndnum ) == false )
        return 0;

    dx=(x-px);
    dy=(py-y);

    distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

    if (distance>255)
        return 0;

    if (distance!=0)
    {
        angle = ( atan2_appx(dx,dy) & (FINEANGLES-1) ) >> 6;
    }
    else
    {
        angle=0;
    }

    pitch = 0;

    if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
    {
        pitch = PitchOffset();
    }

    voice = SD_PlayIt ( sndnum, angle, distance, pitch );

    return voice;

}

//***************************************************************************
//
// SD_PlaySoundRTP - Play a positioned sample relative to the player
//
//***************************************************************************

int SD_PlaySoundRTP ( int sndnum, int x, int y )
{
    int voice;
    int angle;
    int distance;
    int dx;
    int dy;
    int pitch;


    if ( SD_SoundOkay ( sndnum ) == false )
        return 0;

    dx=(x-player->x);
    dy=(player->y-y);

    distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

    if (distance>255)
        return 0;

    if (distance!=0)
    {
        angle = ( (player->angle - atan2_appx(dx,dy)) & (FINEANGLES-1) ) >> 6;
    }
    else
    {
        angle=0;
    }

    pitch = 0;

    if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
    {
        pitch = PitchOffset();
    }

    voice = SD_PlayIt ( sndnum, angle, distance, pitch );

    return voice;
}

//***************************************************************************
//
// SD_PlayPitchedSound - Play a pitched sample
//
//***************************************************************************

int SD_PlayPitchedSound ( int sndnum, int volume, int pitch )
{
    int voice;
    int distance;

    if ( SD_SoundOkay ( sndnum ) == false )
        return 0;

    distance = 255 - volume;

    voice = SD_PlayIt ( sndnum, 0, distance, pitch );

    return voice;
}

//***************************************************************************
//
// SD_SetSoundPitch - sets the pitch of a sound
//
//***************************************************************************

void SD_SetSoundPitch ( int sndnum, int pitch )
{

    if (SD_Started==false)
        return;

    //FX_SetPitch( sndnum, pitch );
    
   // if (!FX_SoundActive(sndnum))
        return;
}

//***************************************************************************
//
// SD_PanRTP Sound - pan a positioned sample relative to the player
//
//***************************************************************************

void SD_PanRTP ( int handle, int x, int y )
{
    int angle;
    int distance;
    int dx;
    int dy;
    int status;

    if (SD_Started==false)
        return;

    //if (!FX_SoundActive(handle))
        return;

    dx=(x-player->x);
    dy=(player->y-y);

    distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

    if (distance>255)
        return;

    if (distance!=0)
    {
        angle = ( (player->angle - atan2_appx(dx,dy)) & (FINEANGLES-1) ) >> 6;
    }
    else
    {
        angle = 0;
    }
}

//***************************************************************************
//
// SD_SetPan - set the pan of a sample
//
//***************************************************************************

void SD_SetPan ( int handle, int vol, int left, int right )
{
    int status;

    if (SD_Started==false)
        return;

    //if (!FX_SoundActive(handle))
        return;
}

//***************************************************************************
//
// SD_PanPositioned Sound - pan a positioned sample
//
//***************************************************************************

void SD_PanPositionedSound ( int handle, int px, int py, int x, int y )
{
    int angle;
    int distance;
    int dx;
    int dy;
    int status;

    if (SD_Started==false)
        return;

    //if (!FX_SoundActive(handle))
        return;

    dx=(x-px);
    dy=(py-y);

    distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

    if (distance>255)
        return;

    if (distance!=0)
    {
        angle = ( atan2_appx(dx,dy) & (FINEANGLES-1) ) >> 6;
    }
    else
    {
        angle = 0;
    }
}


//***************************************************************************
//
// SD_StopSound - Stop the current sound from playing
//
//***************************************************************************

void SD_StopSound ( int handle )
{
    //int status;
    
    if (SD_Started==false)
        return;
    
    FX_StopSound(handle);

    
}

//***************************************************************************
//
// SD_StopAllSounds - Stop All the sounds currently playing
//
//***************************************************************************

void  SD_StopAllSounds ( void )
{
    int status;

    if (SD_Started==false)
        return;
}

//***************************************************************************
//
// SD_SoundActive - See if a sound is active
//
//***************************************************************************

int SD_SoundActive ( int handle )
{
    if (SD_Started==false)
    {
        return false;
    }
    else
    {
        return false; //(FX_SoundActive(handle));
    }
}

//***************************************************************************
//
// SD_WaitSound - wait until a sound has finished
//
//***************************************************************************
void SD_WaitSound ( int handle )
{
    int time;

    IN_ClearKeysDown();
    return;

    //while (FX_SoundActive(handle)!=0)
    //{
     //   time=GetTicCount()+1;
     //   while (time>GetTicCount()) {}
     //   if ((LastScan) || IN_GetMouseButtons())
      //      break;
    //}
}


//***************************************************************************
//
// SD_Shutdown - Shutdown the sound system
//
//***************************************************************************

void SD_Shutdown (void)
{
    if (SD_Started==false)
        return;

    FX_Shutdown();
    SD_Started=false;
}


//***************************************************************************
//
// SD_PreCacheSound - PreCache sound
//
//***************************************************************************

void SD_PreCacheSound ( int num )
{
    if ( SD_SoundOkay ( num ) == false )
        return;

    PreCacheLump(SoundNumber(num),PU_CACHESOUNDS+sounds[num].priority,cache_other);
}

//***************************************************************************
//
// SD_PreCacheSoundGroup - PreCache sound group
//
//***************************************************************************

void SD_PreCacheSoundGroup ( int lo, int hi )
{
    int i;

    if (SD_Started==false)
        return;

    for (i=lo; i<=hi; i++)
        SD_PreCacheSound(i);
}


#if (SHAREWARE == 1)
#define MAXSONGS 18
static song_t rottsongs[MAXSONGS] = {
    { loop_no,  song_apogee,"FANFARE2","Apogee Fanfare"},
    { loop_yes, song_title,"RISE",    "Rise"},
    { loop_yes, song_menu,"MMMENU",  "MMMenu"},
    { loop_yes, song_christmas,"DEADLY", "Deadly Gentlemen"},
    { loop_yes, song_elevator,"GOINGUP", "Going up?"},
    { loop_yes, song_endlevel,"HOWDIDO", "How'd I do?"},
    { loop_yes, song_secretmenu,"FISHPOLK","Fish Polka"},
    { loop_yes, song_gameover,"YOUSUCK", "You Suck"},
    { loop_yes, song_youwin,"WATZNEXT","Watz Next?"},
    { loop_no,  song_gason,"GAZZ!",   "Gazz!"},
    { loop_yes, song_level,"FASTWAY", "Goin' Down The Fast Way"},
    { loop_yes, song_level,"MISTACHE","Mist Ache"},
    { loop_yes, song_level,"OWW",     "Oww!!!"},
    { loop_yes, song_level,"SMOKE",   "Smoke And Mirrors"},
    { loop_yes, song_level,"SPRAY",   "Spray"},
    { loop_yes, song_level,"RUNLIKE", "Run Like Smeg"},
    { loop_yes, song_level,"SMOOTH",  "Havana Smooth"},
    { loop_yes, song_level,"CHANT",   "Chant"},
};
#else
#define MAXSONGS 34
static song_t rottsongs[MAXSONGS] = {
    { loop_no,  song_apogee,"FANFARE2","Apogee Fanfare"},
    { loop_yes, song_title,"RISE",    "Rise"},
    { loop_yes, song_menu,"MMMENU",  "MMMenu"},
    { loop_yes, song_christmas,"DEADLY", "Deadly Gentlemen"},
    { loop_yes, song_elevator,"GOINGUP", "Going up?"},
    { loop_yes, song_secretmenu,"FISHPOLK","Fish Polka"},
    { loop_yes, song_endlevel,"HOWDIDO", "How'd I do?"},
    { loop_yes, song_gameover,"YOUSUCK", "You Suck"},
    { loop_yes, song_cinematic2,"WATZNEXT","Watz Next?"},
    { loop_no,  song_gason,"GAZZ!",   "Gazz!"},
    { loop_yes, song_level,"FASTWAY", "Goin' Down The Fast Way"},
    { loop_yes, song_level,"MISTACHE","Mist Ache"},
    { loop_yes, song_level,"OWW",     "Oww!!!"},
    { loop_yes, song_level,"SMOKE",   "Smoke And Mirrors"},
    { loop_yes, song_level,"SPRAY",   "Spray"},
    { loop_yes, song_level,"RUNLIKE", "Run Like Smeg"},
    { loop_yes, song_level,"SMOOTH",  "Havana Smooth"},
    { loop_yes, song_level,"CHANT",   "Chant"},
    { loop_yes, song_level,"MEDIEV1A","Funeral of Queen Mary"},
    { loop_yes, song_level,"TASKFORC","Task Force"},
    { loop_yes, song_level,"KISSOFF", "KISS Off"},
    { loop_yes, song_level,"RADAGIO", "Adagio For Strings"},
    { loop_yes, song_level,"SHARDS",  "Shards"},
    { loop_yes, song_level,"STAIRS",  "I Choose the Stairs"},
    { loop_yes, song_level,"SUCKTHIS","Suck This"},
    { loop_yes, song_level,"EXCALIBR","Excalibur"},
    { loop_yes, song_level,"CCCOOL",   "CCCool"},
    { loop_yes, song_level,"WORK_DAY","Work Day"},
    { loop_yes, song_level,"WHERIZIT","Where Iz It?"},
    { loop_no,  song_bossdie,"BOSSBLOW", "Boss Blow"},
    { loop_yes, song_bosssee,"HELLERO", "Hellero"},
    { loop_yes, song_cinematic1,"EVINRUDE","Evin Rude"},
    { loop_yes, song_youwin,"VICTORY", "Victory!"},
    { loop_yes, song_dogend,"HERE_BOY","Here Boy"}
};
#endif

static char * currentsong;
static int MU_Started=false;
static int lastsongnumber=-1;
int storedposition=0;

//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_PlayJukeBoxSong
(
    int which
)

{
    if ( ( MusicMode > 0 ) && ( MU_Started == true ) )
    {
        SetMenuHeader( rottsongs[ which ].songname );
        MU_PlaySong( which );
    }
}


//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_JukeBoxRedraw
(
    void
)

{
    if ( ( MusicMode > 0 ) && ( MU_Started == true ) )
    {
        SetMenuHeader( rottsongs[ lastsongnumber ].songname );
    }
}


//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_JukeBoxMenu
(
    void
)

{
    char *SongNames[ MAXSONGS ];
    int   i;

    for( i = 0; i < MAXSONGS; i++ )
    {
        SongNames[ i ] = rottsongs[ i ].songname;
    }

    HandleMultiPageCustomMenu( SongNames, MAXSONGS, lastsongnumber,
                               "Jukebox", MU_PlayJukeBoxSong, MU_JukeBoxRedraw, false );

    if ( rottsongs[ lastsongnumber ].loopflag == loop_no )
    {
        MU_StartSong(song_level);
    }
}

//***************************************************************************
//
// MusicStarted - see if the music is started
//
//***************************************************************************
boolean MusicStarted( void )
{
    return MU_Started;
}

//***************************************************************************
//
// MU_Startup - Initialize music stuff
//
//***************************************************************************

int MU_Startup ( boolean bombonerror )
{
    int status;
    int card;

    if (MU_Started==true)
    {
        MU_StopSong();
        MU_Shutdown();
    }
    if ( ( MusicMode < 0 ) || ( MusicMode >= 11 ) )
    {
        return( 0 );
    }
    card = musicnums[ MusicMode ];
    if (card==-1) // Check if it is off
        return (0);

    /* Not DOS, no address config needed */
    status=MUSIC_Init( card, 0 );

    
    if (status != MUSIC_Ok) {
        if (bombonerror)
        {
            DeleteSoundFile ();
            Error( "%s\n", MUSIC_ErrorString( status ) );
        }
        else
            return (status);
    }

    currentsong=0;

    MU_Started=true;

    MU_SetVolume (MUvolume);

    return (0);
}

//***************************************************************************
//
// MU_Shutdown - Shutdown the music system
//
//***************************************************************************

void MU_Shutdown (void)
{
    if (MU_Started==false)
        return;
    MUSIC_Shutdown();
    MU_Started=false;
}

//***************************************************************************
//
// MU_GetNumForType - returns number of song in rottsongs of specific type
//
//***************************************************************************
int MU_GetNumForType ( int type )
{
    int i;

    for (i=0; i<MAXSONGS; i++)
    {
        if (rottsongs[i].songtype == type)
            return i;
    }
    Error("MU_GetNumForType: could not find song type in list\n");
    return -1;
}


//***************************************************************************
//
// MU_PlaySong - Play a specific song number
//
//***************************************************************************

void MU_PlaySong ( int num )
{
    int lump;
    int size;

    if (MU_Started==false)
        return;

    if (num<0)
        return;

    if (num>=MAXSONGS)
        Error("Song number out of range\n");

    MU_StopSong();

    lastsongnumber=num;

    lump = W_GetNumForName(rottsongs[num].lumpname);
    size = W_LumpLength(lump);

    currentsong=W_CacheLumpNum(lump,PU_STATIC, CvtNull, 1);

    if (rottsongs[num].loopflag == loop_yes)
        MUSIC_PlaySongROTT(currentsong,size,MUSIC_LoopSong);
    else
        MUSIC_PlaySongROTT(currentsong,size,MUSIC_PlayOnce);

    MU_SetVolume (MUvolume);
}

//***************************************************************************
//
// MU_StopSong - Play a specific song number
//
//***************************************************************************

void MU_StopSong ( void )
{
    if (MU_Started==false)
        return;

    MUSIC_StopSong ();
    if (currentsong)
    {
        W_CacheLumpName(rottsongs[lastsongnumber].lumpname,PU_CACHE, CvtNull, 1);
        currentsong=0;
    }
}

//***************************************************************************
//
// MU_GetSongNumber - get current song number
//
//***************************************************************************

int MU_GetSongNumber ( void )
{
    return lastsongnumber;
}


//***************************************************************************
//
// MU_FadeToSong - Fade to a specific song in a certain time
//
//***************************************************************************

void MU_FadeToSong ( int num, int time )
{
    int t;

    if (MU_Started==false)
        return;

    MU_FadeOut(time>>1);

    while (MU_FadeActive())
    {
        t=GetTicCount();
        while (GetTicCount()==t) {}
    }

    MU_FadeIn (num,time>>1);
}

//***************************************************************************
//
// MU_FadeIn - Fade in
//
//***************************************************************************

void MU_FadeIn ( int num, int time )
{
    if (MU_Started==false)
        return;

    MUSIC_SetVolume(0);
    MU_PlaySong ( num );
    MUSIC_FadeVolume (MUvolume, time);
}

//***************************************************************************
//
// MU_FadeOut - Fade out
//
//***************************************************************************

void MU_FadeOut ( int time )
{
    if (MU_Started==false)
        return;
    if (!MUSIC_SongPlaying())
    {
        return;
    }
    MUSIC_FadeVolume(0,time);
}


//***************************************************************************
//
// MU_StartSong - Start a context sensitive song
//
//***************************************************************************

void MU_StartSong ( int songtype )
{
    int songnum;
    
    //printf("%d \n", songtype);

    if (MU_Started==false)
        return;

    MU_StopSong();

    //songtype++;
    
    songnum = MU_GetNumForType ( songtype );
    
    //printf("%d : %d \n", songtype, songnum);
    
    switch (songtype)
    {
    case song_level:
        if (IsChristmas())
        {
            songnum = MU_GetNumForType ( song_christmas );
        }
        else
        {
            songnum += GetSongForLevel ();
        }
        break;
    }
    MU_PlaySong (songnum);
}

//***************************************************************************
//
// MU_StoreSongPostition - Save off Song Position
//
//***************************************************************************

void MU_StoreSongPosition ( void )
{
    if (MU_Started==false)
        return;
    PositionStored=true;
    storedposition=MUSIC_GetPosition();
}

//***************************************************************************
//
// MU_RestoreSongPostition - Save off Song Position
//
//***************************************************************************

void MU_RestoreSongPosition ( void )
{
    if (MU_Started==false)
        return;
    if (PositionStored==false)
        return;
    PositionStored=false;

    MUSIC_SetPosition(storedposition);
}

//***************************************************************************
//
// MU_GetStoredPostition - Get Stored song Position
//
//***************************************************************************

int MU_GetStoredPosition ( void )
{
    if (PositionStored)
        return storedposition;
    else
        return -1;
}

//***************************************************************************
//
// MU_SetStoredPostition - Get Stored song Position
//
//***************************************************************************

void MU_SetStoredPosition ( int position )
{
    if (MU_Started==false)
        return;
    if (position==-1)
        return;
    PositionStored=true;
    storedposition=position;
}



//***************************************************************************
//
// MU_GetSongPostition - Get Song Position
//
//***************************************************************************

int MU_GetSongPosition ( void )
{
    if (MU_Started==false)
        return 0;
    return MUSIC_GetPosition();
}

//***************************************************************************
//
// MU_SetSongPostition - Set Song Position
//
//***************************************************************************

void MU_SetSongPosition ( int position )
{
    if (MU_Started==false)
        return;
    MUSIC_SetPosition(position);
}

//***************************************************************************
//
// MU_SaveMusic
//
//***************************************************************************

void MU_SaveMusic (byte ** buf, int * size)
{
    int unitsize;
    byte *ptr;
    int vsize;
    int i;

    //
    // Size
    //

    unitsize=0;

    unitsize+=sizeof(i);
    unitsize+=sizeof(i);
    unitsize+=sizeof(i);


    *size = unitsize;
    *buf = (byte *) SafeMalloc (*size);

    ptr = *buf;

    i=MU_GetSongNumber();
    if (rottsongs[i].songtype == song_menu)
    {
        i = MU_GetNumForType ( song_level );
        if (IsChristmas())
        {
            i = MU_GetNumForType ( song_christmas );
        }
        else
        {
            i += GetSongForLevel ();
        }
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;

        i=MU_GetStoredPosition();
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;

        i=-1;
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;
    }
    else
    {
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;

        i=MU_GetSongPosition();
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;

        i=MU_GetStoredPosition();
        vsize=sizeof(i);
        memcpy(ptr,&i,vsize);
        ptr+=vsize;
    }
}


//***************************************************************************
//
// MU_LoadMusic
//
//***************************************************************************

void MU_LoadMusic (byte * buf, int size)
{
    int unitsize;
    byte *ptr;
    int i;
    int songnumber;
    boolean differentsong=false;
    int vsize;

    //
    // Size
    //

    unitsize=0;

    unitsize+=sizeof(i);
    unitsize+=sizeof(i);
    unitsize+=sizeof(i);

    if (size!=unitsize)
        Error("LoadMusic: Different number of parameters\n");

    ptr  = buf;

    vsize=sizeof(songnumber);
    memcpy(&songnumber,ptr,vsize);
    ptr+=vsize;
    if (MU_GetSongNumber () != songnumber)
    {
        MU_PlaySong(songnumber);
        differentsong=true;
    }

    vsize=sizeof(i);
    memcpy(&i,ptr,vsize);
    ptr+=vsize;
    if (differentsong==true)
    {
        MU_SetSongPosition(i);
    }

    vsize=sizeof(i);
    memcpy(&i,ptr,vsize);
    ptr+=vsize;
    MU_SetStoredPosition(i);

    // Check if game was saved with NOSOUND

    if (MU_GetSongNumber () != songnumber)
    {
        MU_StartSong ( song_level );
    }
}