1457 lines
35 KiB
C
Executable File
1457 lines
35 KiB
C
Executable File
/*
|
|
* A reimplementation of Jim Dose's FX_MAN routines, using SDL_mixer 1.2.
|
|
* Whee. FX_MAN is also known as the "Apogee Sound System", or "ASS" for
|
|
* short. How strangely appropriate that seems.
|
|
*
|
|
* Written by Ryan C. Gordon. (icculus@clutteredmind.org)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "duke3d.h"
|
|
#include "buildengine/cache1d.h"
|
|
|
|
#if PLATFORM_DOS
|
|
// Use the original Apogee Sound System libs instead. --ryan.
|
|
#error you probably should not compile this.
|
|
#endif
|
|
|
|
#if (defined __WATCOMC__)
|
|
// This is probably out of date. --ryan.
|
|
#include "dukesnd_watcom.h"
|
|
#endif
|
|
|
|
#if (!defined __WATCOMC__)
|
|
#define cdecl
|
|
#endif
|
|
|
|
#include "SDL.h"
|
|
#include "SDL_mixer.h"
|
|
#ifdef ROTT
|
|
#include "rt_def.h" // ROTT music hack
|
|
#include "rt_cfg.h" // ROTT music hack
|
|
#include "rt_util.h" // ROTT music hack
|
|
#endif
|
|
#include "fx_man.h"
|
|
#include "music.h"
|
|
|
|
#define __FX_TRUE (1 == 1)
|
|
#define __FX_FALSE (!__FX_TRUE)
|
|
|
|
#define DUKESND_DEBUG "DUKESND_DEBUG"
|
|
|
|
#ifndef min
|
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
#ifndef max
|
|
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
|
#endif
|
|
|
|
typedef struct __DUKECHANINFO
|
|
{
|
|
int in_use; // 1 or 0.
|
|
int priority; // priority, defined by application.
|
|
Uint32 birthday; // ticks when channel was grabbed.
|
|
unsigned long callbackval; // callback value from application.
|
|
} duke_channel_info;
|
|
|
|
|
|
int MUSIC_ErrorCode = MUSIC_Ok;
|
|
|
|
static char warningMessage[80];
|
|
static char errorMessage[80];
|
|
static int fx_initialized = 0;
|
|
static int numChannels = MIX_CHANNELS;
|
|
static void (*callback)(unsigned long);
|
|
static int reverseStereo = 0;
|
|
static int reverbDelay = 256;
|
|
static int reverbLevel = 0;
|
|
static int fastReverb = 0;
|
|
static FILE *debug_file = NULL;
|
|
static int initialized_debugging = 0;
|
|
static int maxReverbDelay = 256;
|
|
static int mixerIsStereo = 1;
|
|
static duke_channel_info *chaninfo = NULL;
|
|
|
|
// This...is not ideal. --ryan.
|
|
#define CHUNK_CACHE_SIZE 128
|
|
typedef struct __DUKECHUNKCACHE
|
|
{
|
|
Mix_Chunk *chunk;
|
|
void *dataptr;
|
|
} duke_chunk_cache;
|
|
static duke_chunk_cache chunkCache[CHUNK_CACHE_SIZE];
|
|
|
|
|
|
#define HandleOffset 1
|
|
|
|
/* these come from the real ASS */
|
|
#define MV_MaxPanPosition 31
|
|
#define MV_NumPanPositions ( MV_MaxPanPosition + 1 )
|
|
#define MV_MaxVolume 63
|
|
|
|
#define MIX_VOLUME( volume ) \
|
|
( ( max( 0, min( ( volume ), 255 ) ) * ( MV_MaxVolume + 1 ) ) >> 8 )
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char left;
|
|
unsigned char right;
|
|
} Pan;
|
|
|
|
static Pan MV_PanTable[ MV_NumPanPositions ][ MV_MaxVolume + 1 ];
|
|
|
|
static void MV_CalcPanTable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int level;
|
|
int angle;
|
|
int distance;
|
|
int HalfAngle;
|
|
int ramp;
|
|
|
|
HalfAngle = ( MV_NumPanPositions / 2 );
|
|
|
|
for( distance = 0; distance <= MV_MaxVolume; distance++ )
|
|
{
|
|
level = ( 255 * ( MV_MaxVolume - distance ) ) / MV_MaxVolume;
|
|
for( angle = 0; angle <= HalfAngle / 2; angle++ )
|
|
{
|
|
ramp = level - ( ( level * angle ) /
|
|
( MV_NumPanPositions / 4 ) );
|
|
|
|
MV_PanTable[ angle ][ distance ].left = ramp;
|
|
MV_PanTable[ HalfAngle - angle ][ distance ].left = ramp;
|
|
MV_PanTable[ HalfAngle + angle ][ distance ].left = level;
|
|
MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].left = level;
|
|
|
|
MV_PanTable[ angle ][ distance ].right = level;
|
|
MV_PanTable[ HalfAngle - angle ][ distance ].right = level;
|
|
MV_PanTable[ HalfAngle + angle ][ distance ].right = ramp;
|
|
MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].right = ramp;
|
|
}
|
|
}
|
|
}
|
|
/* end ASS copy-pastage */
|
|
|
|
#ifdef __WATCOMC__
|
|
#pragma aux (__cdecl) channelDoneCallback;
|
|
#endif
|
|
|
|
// This function is called whenever an SDL_mixer channel completes playback.
|
|
// We use this for state management and calling the application's callback.
|
|
static void channelDoneCallback(int channel)
|
|
{
|
|
//Mix_FreeChunk(Mix_GetChunk(channel));
|
|
if (callback)
|
|
{
|
|
callback(chaninfo[channel].callbackval);
|
|
chaninfo[channel].in_use = 0;
|
|
} // if
|
|
} // channelDoneCallback
|
|
|
|
|
|
// This gets called all over the place for information and debugging messages.
|
|
// If the user set the DUKESND_DEBUG environment variable, the messages
|
|
// go to the file that is specified in that variable. Otherwise, they
|
|
// are ignored for the expense of the function call. If DUKESND_DEBUG is
|
|
// set to "-" (without the quotes), then the output goes to stdout.
|
|
static void snddebug(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (debug_file)
|
|
{
|
|
fprintf(debug_file, "DUKESND: ");
|
|
va_start(ap, fmt);
|
|
vfprintf(debug_file, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(debug_file, "\n");
|
|
fflush(debug_file);
|
|
} // if
|
|
} // snddebug
|
|
|
|
|
|
// FIXME: Consolidate this code.
|
|
// Same as snddebug(), but a different tag is put on each line.
|
|
static void musdebug(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (debug_file)
|
|
{
|
|
fprintf(debug_file, "DUKEMUS: ");
|
|
va_start(ap, fmt);
|
|
vfprintf(debug_file, fmt, ap);
|
|
va_end(ap);
|
|
fprintf(debug_file, "\n");
|
|
fflush(debug_file);
|
|
} // if
|
|
} // snddebug
|
|
|
|
|
|
static void init_debugging(void)
|
|
{
|
|
const char *envr;
|
|
|
|
if (initialized_debugging)
|
|
return;
|
|
|
|
envr = getenv(DUKESND_DEBUG);
|
|
if (envr != NULL)
|
|
{
|
|
if (strcmp(envr, "-") == 0)
|
|
debug_file = stdout;
|
|
else
|
|
debug_file = fopen(envr, "w");
|
|
|
|
if (debug_file == NULL)
|
|
fprintf(stderr, "DUKESND: -WARNING- Could not open debug file!\n");
|
|
else
|
|
setbuf(debug_file, NULL);
|
|
} // if
|
|
|
|
initialized_debugging = 1;
|
|
} // init_debugging
|
|
|
|
|
|
// find an available SDL_mixer channel, and reserve it.
|
|
// This would be a race condition, but hey, it's for a DOS game. :)
|
|
static int grabMixerChannel(int priority)
|
|
{
|
|
int replaceable = -1;
|
|
int i;
|
|
|
|
if (!fx_initialized)
|
|
return(-1);
|
|
|
|
for (i = 0; i < numChannels; i++)
|
|
{
|
|
if (chaninfo[i].in_use == 0)
|
|
{
|
|
chaninfo[i].in_use = 1;
|
|
chaninfo[i].priority = priority;
|
|
chaninfo[i].birthday = SDL_GetTicks();
|
|
return(i);
|
|
} // if
|
|
|
|
// !!! FIXME: Should this just be lower priority, or equal too?
|
|
if (chaninfo[i].priority <= priority)
|
|
{
|
|
if ((replaceable == -1) ||
|
|
(chaninfo[i].birthday < chaninfo[replaceable].birthday))
|
|
{
|
|
replaceable = i;
|
|
} // if
|
|
} // if
|
|
} // for
|
|
|
|
// if you land here, all mixer channels are playing...
|
|
if (replaceable != -1) // nothing expendable right now.
|
|
{
|
|
chaninfo[replaceable].in_use = 1;
|
|
chaninfo[replaceable].priority = priority;
|
|
chaninfo[replaceable].birthday = SDL_GetTicks();
|
|
} // if
|
|
|
|
return(replaceable);
|
|
} // grabMixerChannel
|
|
|
|
|
|
// !!! FIXME: Is this correct behaviour?
|
|
char *FX_ErrorString( int ErrorNumber )
|
|
{
|
|
switch (ErrorNumber)
|
|
{
|
|
case FX_Warning:
|
|
return(warningMessage);
|
|
|
|
case FX_Error:
|
|
return(errorMessage);
|
|
|
|
case FX_Ok:
|
|
return("OK; no error.");
|
|
|
|
case FX_ASSVersion:
|
|
return("Incorrect sound library version.");
|
|
|
|
case FX_BlasterError:
|
|
return("SoundBlaster Error.");
|
|
|
|
case FX_SoundCardError:
|
|
return("General sound card error.");
|
|
|
|
case FX_InvalidCard:
|
|
return("Invalid sound card.");
|
|
|
|
case FX_MultiVocError:
|
|
return("Multiple voc error.");
|
|
|
|
case FX_DPMI_Error:
|
|
return("DPMI error.");
|
|
|
|
default:
|
|
return("Unknown error.");
|
|
} // switch
|
|
|
|
assert(0); // shouldn't hit this point.
|
|
return(NULL);
|
|
} // FX_ErrorString
|
|
|
|
|
|
static void setWarningMessage(const char *msg)
|
|
{
|
|
strncpy(warningMessage, msg, sizeof (warningMessage));
|
|
// strncpy() doesn't add the null char if there isn't room...
|
|
warningMessage[sizeof (warningMessage) - 1] = '\0';
|
|
snddebug("Warning message set to [%s].", warningMessage);
|
|
} // setErrorMessage
|
|
|
|
|
|
static void setErrorMessage(const char *msg)
|
|
{
|
|
strncpy(errorMessage, msg, sizeof (errorMessage));
|
|
// strncpy() doesn't add the null char if there isn't room...
|
|
errorMessage[sizeof (errorMessage) - 1] = '\0';
|
|
snddebug("Error message set to [%s].", errorMessage);
|
|
} // setErrorMessage
|
|
|
|
|
|
int FX_GetBlasterSettings(fx_blaster_config *blaster)
|
|
{
|
|
setErrorMessage("No SoundBlaster cards available.");
|
|
return(FX_Error);
|
|
} // FX_GetBlasterSettings
|
|
|
|
|
|
int FX_SetupSoundBlaster(fx_blaster_config blaster, int *MaxVoices,
|
|
int *MaxSampleBits, int *MaxChannels)
|
|
{
|
|
setErrorMessage("No SoundBlaster cards available.");
|
|
return(FX_Error);
|
|
} // FX_SetupSoundBlaster
|
|
|
|
|
|
int FX_SetupCard( int SoundCard, fx_device *device )
|
|
{
|
|
init_debugging();
|
|
|
|
snddebug("FX_SetupCard looking at card id #%d...", SoundCard);
|
|
|
|
if (device == NULL) // sanity check.
|
|
{
|
|
setErrorMessage("fx_device is NULL in FX_SetupCard!");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
// Since the actual hardware is abstracted out on modern operating
|
|
// systems, we just pretend that the system's got a SoundScape.
|
|
// I always liked that card, even though Ensoniq screwed me on OS/2
|
|
// drivers back in the day. :)
|
|
if (SoundCard != SoundScape)
|
|
{
|
|
setErrorMessage("Card not found.");
|
|
snddebug("We pretend to be an Ensoniq SoundScape only.");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
device->MaxVoices = 8;
|
|
device->MaxSampleBits = 16; // SDL_mixer downsamples if needed.
|
|
device->MaxChannels = 2; // SDL_mixer converts to mono if needed.
|
|
|
|
return(FX_Ok);
|
|
} // FX_SetupCard
|
|
|
|
|
|
static void output_versions(const char *libname, const SDL_version *compiled,
|
|
const SDL_version *linked)
|
|
{
|
|
snddebug("This program was compiled against %s %d.%d.%d,\n"
|
|
" and is dynamically linked to %d.%d.%d.\n", libname,
|
|
compiled->major, compiled->minor, compiled->patch,
|
|
linked->major, linked->minor, linked->patch);
|
|
}
|
|
|
|
|
|
static void output_version_info(void)
|
|
{
|
|
SDL_version compiled;
|
|
const SDL_version *linked;
|
|
|
|
snddebug("Library check...");
|
|
|
|
SDL_VERSION(&compiled);
|
|
linked = SDL_Linked_Version();
|
|
output_versions("SDL", &compiled, linked);
|
|
|
|
MIX_VERSION(&compiled);
|
|
linked = Mix_Linked_Version();
|
|
output_versions("SDL_mixer", &compiled, linked);
|
|
} // output_version_info
|
|
|
|
|
|
int FX_Init(int SoundCard, int numvoices, int numchannels,
|
|
int samplebits, unsigned mixrate)
|
|
{
|
|
Uint16 audio_format = 0;
|
|
int blocksize;
|
|
|
|
init_debugging();
|
|
|
|
snddebug("INIT! card=>%d, voices=>%d, chan=>%d, bits=>%d, rate=>%du...",
|
|
SoundCard, numvoices, numchannels, samplebits, mixrate);
|
|
|
|
if (fx_initialized)
|
|
{
|
|
setErrorMessage("Sound system is already initialized.\n");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
if (SoundCard != SoundScape) // We pretend there's a SoundScape installed.
|
|
{
|
|
setErrorMessage("Card not found.");
|
|
snddebug("We pretend to be an Ensoniq SoundScape only.");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
// other sanity checks...
|
|
if ((numvoices < 0) || (numvoices > 8))
|
|
{
|
|
setErrorMessage("Invalid number of voices to mix (must be 0-8).");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
if ((numchannels != MonoFx) && (numchannels != StereoFx))
|
|
{
|
|
setErrorMessage("Invalid number of channels (must be 1 or 2).");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
if ((samplebits != 8) && (samplebits != 16))
|
|
{
|
|
setErrorMessage("Invalid sample size (must be 8 or 16).");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
// build pan tables
|
|
MV_CalcPanTable();
|
|
|
|
SDL_ClearError();
|
|
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
|
{
|
|
setErrorMessage("SDL_Init(SDL_INIT_AUDIO) failed!");
|
|
snddebug("SDL_GetError() reports: [%s].", SDL_GetError());
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
audio_format = (samplebits == 8) ? AUDIO_U8 : AUDIO_S16;
|
|
if (Mix_OpenAudio(mixrate, audio_format, numchannels, 256) < 0)
|
|
{
|
|
setErrorMessage(SDL_GetError());
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
output_version_info();
|
|
|
|
numChannels = Mix_AllocateChannels(numvoices);
|
|
if (numChannels != numvoices)
|
|
{
|
|
setErrorMessage(SDL_GetError());
|
|
Mix_CloseAudio();
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
blocksize = sizeof (duke_channel_info) * numvoices;
|
|
chaninfo = malloc(blocksize);
|
|
if (chaninfo == NULL) // uhoh.
|
|
{
|
|
setErrorMessage("Out of memory");
|
|
Mix_CloseAudio();
|
|
return(FX_Error);
|
|
} // if
|
|
memset(chaninfo, '\0', blocksize);
|
|
|
|
Mix_ChannelFinished(channelDoneCallback);
|
|
maxReverbDelay = (int) (((float) mixrate) * 0.5);
|
|
|
|
Mix_QuerySpec(NULL, NULL, &mixerIsStereo);
|
|
mixerIsStereo = (mixerIsStereo == 2);
|
|
|
|
memset(chunkCache, '\0', sizeof (chunkCache));
|
|
|
|
fx_initialized = 1;
|
|
return(FX_Ok);
|
|
} // FX_Init
|
|
|
|
|
|
void FX_CleanCache(void)
|
|
{
|
|
int total = 0;
|
|
int i;
|
|
|
|
snddebug("FX_CleanCache halting all channels.");
|
|
Mix_HaltChannel(-1); // stop everything.
|
|
|
|
snddebug("freeing cached chunks.");
|
|
for (i = 0; i < CHUNK_CACHE_SIZE; i++)
|
|
{
|
|
if (chunkCache[i].chunk != NULL)
|
|
{
|
|
total++;
|
|
Mix_FreeChunk(chunkCache[i].chunk);
|
|
chunkCache[i].chunk = NULL;
|
|
} // if
|
|
chunkCache[i].dataptr = NULL;
|
|
} // for
|
|
|
|
snddebug("cached chunks deallocation complete. (%d) were deleted.", total);
|
|
} // FX_CleanCache
|
|
|
|
|
|
int FX_Shutdown( void )
|
|
{
|
|
snddebug("shutting down sound subsystem.");
|
|
|
|
if (!fx_initialized)
|
|
{
|
|
setErrorMessage("Sound system is not currently initialized.\n");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
FX_CleanCache(); // stops all channels and music.
|
|
Mix_CloseAudio();
|
|
callback = NULL;
|
|
free(chaninfo);
|
|
chaninfo = NULL;
|
|
reverseStereo = 0;
|
|
reverbLevel = 0;
|
|
fastReverb = 0;
|
|
fx_initialized = 0;
|
|
maxReverbDelay = 256;
|
|
|
|
return(FX_Ok);
|
|
} // FX_Shutdown
|
|
|
|
|
|
int FX_SetCallBack(void (*func)(unsigned long))
|
|
{
|
|
callback = func;
|
|
return(FX_Ok);
|
|
} // FX_SetCallBack
|
|
|
|
|
|
void FX_SetVolume(int volume)
|
|
{
|
|
snddebug("setting master volume to %f.2 percent.", (volume / 255.0) * 100);
|
|
Mix_Volume(-1, volume >> 1); // it's 0-128 in SDL_mixer, not 0-255.
|
|
} // FX_SetVolume
|
|
|
|
|
|
int FX_GetVolume(void)
|
|
{
|
|
return(Mix_Volume(-1, -1) << 1);
|
|
} // FX_GetVolume
|
|
|
|
|
|
void FX_SetReverseStereo(int setting)
|
|
{
|
|
snddebug("Reverse stereo set to %s.\n", setting ? "ON" : "OFF");
|
|
Mix_SetReverseStereo(MIX_CHANNEL_POST, setting);
|
|
reverseStereo = setting;
|
|
} // FX_SetReverseStereo
|
|
|
|
|
|
int FX_GetReverseStereo(void)
|
|
{
|
|
return(reverseStereo);
|
|
} // FX_GetReverseStereo
|
|
|
|
|
|
void FX_SetReverb(int reverb)
|
|
{
|
|
reverbLevel = reverb;
|
|
fastReverb = 0;
|
|
|
|
#if 1
|
|
// !!! FIXME
|
|
if (reverbLevel)
|
|
setWarningMessage("reverb filter is not yet implemented!");
|
|
#endif
|
|
} // FX_SetReverb
|
|
|
|
|
|
void FX_SetFastReverb(int reverb)
|
|
{
|
|
reverbLevel = reverb;
|
|
fastReverb = 1;
|
|
|
|
#if 1
|
|
// !!! FIXME
|
|
if (reverbLevel)
|
|
setWarningMessage("fast reverb filter is not yet implemented!");
|
|
#endif
|
|
} // FX_SetFastReverb
|
|
|
|
|
|
int FX_GetMaxReverbDelay(void)
|
|
{
|
|
return(maxReverbDelay);
|
|
} // FX_GetMaxReverbDelay
|
|
|
|
|
|
int FX_GetReverbDelay(void)
|
|
{
|
|
return(reverbDelay);
|
|
} // FX_GetReverbDelay
|
|
|
|
|
|
void FX_SetReverbDelay(int delay)
|
|
{
|
|
// !!! FIXME: Should I be clamping these values?
|
|
if (delay < 256)
|
|
delay = 256;
|
|
|
|
if (delay > maxReverbDelay)
|
|
delay = maxReverbDelay;
|
|
|
|
reverbDelay = delay;
|
|
|
|
#if 1
|
|
// !!! FIXME
|
|
setWarningMessage("reverb delay is not yet implemented!");
|
|
#endif
|
|
} // FX_SetReverbDelay
|
|
|
|
|
|
int FX_VoiceAvailable(int priority)
|
|
{
|
|
int chan = grabMixerChannel(priority);
|
|
int rc = (chan != -1);
|
|
|
|
if (rc)
|
|
chaninfo[chan].in_use = 0;
|
|
|
|
return(rc);
|
|
} // FX_VoiceAvailable
|
|
|
|
|
|
static int doSetPan(int handle, int vol, int left,
|
|
int right, int checkIfPlaying)
|
|
{
|
|
int retval = FX_Warning;
|
|
|
|
if ((handle < 0) || (handle >= numChannels))
|
|
setWarningMessage("Invalid handle in doSetPan().");
|
|
else if ((checkIfPlaying) && (!Mix_Playing(handle)))
|
|
setWarningMessage("voice is no longer playing in doSetPan().");
|
|
else
|
|
{
|
|
if (mixerIsStereo)
|
|
{
|
|
if ((left < 0) || (left > 255) ||
|
|
(right < 0) || (right > 255))
|
|
{
|
|
setErrorMessage("Invalid argument to FX_SetPan().");
|
|
retval = FX_Error;
|
|
} // if
|
|
|
|
else
|
|
{
|
|
Mix_SetPanning(handle, left, right);
|
|
} // else
|
|
} // if
|
|
else
|
|
{
|
|
if ((vol < 0) || (vol > 255))
|
|
{
|
|
setErrorMessage("Invalid argument to FX_SetPan().");
|
|
retval = FX_Error;
|
|
} // if
|
|
else
|
|
{
|
|
// volume must be from 0-128, so the ">> 1" converts.
|
|
Mix_Volume(handle, vol >> 1);
|
|
} // else
|
|
} // else
|
|
|
|
retval = FX_Ok;
|
|
} // else
|
|
|
|
return(retval);
|
|
} // doSetPan
|
|
|
|
|
|
int FX_SetPan(int handle, int vol, int left, int right)
|
|
{
|
|
return(doSetPan(handle - HandleOffset, vol, left, right, 1));
|
|
} // FX_SetPan
|
|
|
|
|
|
int FX_SetPitch(int handle, int pitchoffset)
|
|
{
|
|
snddebug("FX_SetPitch() ... NOT IMPLEMENTED YET!");
|
|
return(FX_Ok);
|
|
} // FX_SetPitch
|
|
|
|
|
|
int FX_SetFrequency(int handle, int frequency)
|
|
{
|
|
snddebug("FX_SetFrequency() ... NOT IMPLEMENTED YET!");
|
|
return(FX_Ok);
|
|
} // FX_SetFrequency
|
|
|
|
|
|
static Mix_Chunk *findChunkInCache(void *ptr)
|
|
{
|
|
// !!! FIXME: Optimize! --ryan.
|
|
int i;
|
|
for (i = 0; i < CHUNK_CACHE_SIZE; i++)
|
|
{
|
|
if (chunkCache[i].dataptr == ptr)
|
|
return(chunkCache[i].chunk);
|
|
else if (chunkCache[i].dataptr == NULL)
|
|
return(NULL);
|
|
} // for
|
|
return(NULL);
|
|
} // findChunkInCache
|
|
|
|
|
|
static void addChunkToCache(Mix_Chunk *chunk, void *ptr)
|
|
{
|
|
// !!! FIXME: Optimize! --ryan.
|
|
int i;
|
|
for (i = 0; i < CHUNK_CACHE_SIZE; i++)
|
|
{
|
|
if (chunkCache[i].dataptr == NULL)
|
|
{
|
|
chunkCache[i].dataptr = ptr;
|
|
chunkCache[i].chunk = chunk;
|
|
return;
|
|
} // if
|
|
} // for
|
|
|
|
snddebug("overflowed chunk cache!");
|
|
assert(0); // !!! FIXME.
|
|
} // addChunkToCache
|
|
|
|
|
|
// If this returns FX_Ok, then chunk and chan will be filled with the
|
|
// the block of audio data in the format desired by the audio device
|
|
// and the SDL_mixer channel it will play on, respectively.
|
|
// If the value is not FX_Ok, then the warning or error message is set,
|
|
// and you should bail.
|
|
// size added by SBF for ROTT
|
|
static int setupVocPlayback(char *ptr, int size, int priority, unsigned long callbackval,
|
|
int *chan, Mix_Chunk **chunk)
|
|
{
|
|
SDL_RWops *rw;
|
|
|
|
*chan = grabMixerChannel(priority);
|
|
if (*chan == -1)
|
|
{
|
|
setErrorMessage("No available channels");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
*chunk = findChunkInCache(ptr);
|
|
if (*chunk == NULL)
|
|
{
|
|
if (size == -1) {
|
|
// !!! FIXME: This could be a problem...SDL/SDL_mixer wants a RWops, which
|
|
// !!! FIXME: is an i/o abstraction. Since we already have the VOC data
|
|
// !!! FIXME: in memory, we fake it with a memory-based RWops. None of
|
|
// !!! FIXME: this is a problem, except the RWops wants to know how big
|
|
// !!! FIXME: its memory block is (so it can do things like seek on an
|
|
// !!! FIXME: offset from the end of the block), and since we don't have
|
|
// !!! FIXME: this information, we have to give it SOMETHING. My VOC
|
|
// !!! FIXME: decoder never does seeks from EOF, nor checks for
|
|
// !!! FIXME: end-of-file, so we should be fine. However, we've got a
|
|
// !!! FIXME: limit of 10 megs for one file. I hope that'll cover it. :)
|
|
|
|
rw = SDL_RWFromMem((void *) ptr, (10 * 1024) * 1024); /* yikes. */
|
|
} else {
|
|
// A valid file size! Excellent.
|
|
rw = SDL_RWFromMem((void *) ptr, size);
|
|
}
|
|
|
|
*chunk = Mix_LoadWAV_RW(rw, 1);
|
|
if (*chunk == NULL)
|
|
{
|
|
setErrorMessage("Couldn't decode voice sample.");
|
|
chaninfo[*chan].in_use = 0;
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
addChunkToCache(*chunk, (void *) ptr);
|
|
} // if
|
|
chaninfo[*chan].callbackval = callbackval;
|
|
return(FX_Ok);
|
|
} // setupVocPlayback
|
|
|
|
static int _FX_SetPosition(int chan, int angle, int distance)
|
|
{
|
|
int left;
|
|
int right;
|
|
int mid;
|
|
int volume;
|
|
int status;
|
|
|
|
if ( distance < 0 ) {
|
|
distance = -distance;
|
|
angle += MV_NumPanPositions / 2;
|
|
}
|
|
|
|
volume = MIX_VOLUME( distance );
|
|
|
|
// Ensure angle is within 0 - 31
|
|
angle &= MV_MaxPanPosition;
|
|
|
|
left = MV_PanTable[ angle ][ volume ].left;
|
|
right = MV_PanTable[ angle ][ volume ].right;
|
|
mid = max( 0, 255 - distance );
|
|
|
|
status = doSetPan( chan, mid, left, right, 0 );
|
|
|
|
return status;
|
|
}
|
|
|
|
int FX_PlayVOC(char *ptr, int pitchoffset,
|
|
int vol, int left, int right,
|
|
int priority, unsigned long callbackval)
|
|
{
|
|
int rc;
|
|
int chan;
|
|
Mix_Chunk *chunk;
|
|
|
|
snddebug("Playing voice: mono (%d), left (%d), right (%d), priority (%d).\n",
|
|
vol, left, right, priority);
|
|
|
|
rc = setupVocPlayback(ptr, -1, priority, callbackval, &chan, &chunk);
|
|
if (rc != FX_Ok)
|
|
return(rc);
|
|
|
|
// !!! FIXME: Need to do something with pitchoffset.
|
|
|
|
rc = doSetPan(chan, vol, left, right, 0);
|
|
if (rc != FX_Ok)
|
|
{
|
|
chaninfo[chan].in_use = 0;
|
|
return(rc);
|
|
} // if
|
|
|
|
Mix_PlayChannel(chan, chunk, 0);
|
|
return(HandleOffset + chan);
|
|
} // FX_PlayVOC
|
|
|
|
|
|
// get the size of a single sample, in bytes.
|
|
static int getSampleSize(void)
|
|
{
|
|
Uint16 format;
|
|
int channels;
|
|
|
|
Mix_QuerySpec(NULL, &format, &channels);
|
|
return( ((format & 0xFF) / 8) * channels );
|
|
} // getSampleSize
|
|
|
|
|
|
int FX_PlayLoopedVOC(char *ptr, long loopstart, long loopend,
|
|
int pitchoffset, int vol, int left, int right, int priority,
|
|
unsigned long callbackval)
|
|
{
|
|
int rc;
|
|
int chan;
|
|
int samplesize = getSampleSize();
|
|
Uint32 totalsamples;
|
|
Mix_Chunk *chunk;
|
|
|
|
snddebug("Playing voice: mono (%d), left (%d), right (%d), priority (%d).\n",
|
|
vol, left, right, priority);
|
|
snddebug("Looping: start (%ld), end (%ld).\n", loopstart, loopend);
|
|
|
|
rc = setupVocPlayback(ptr, -1, priority, callbackval, &chan, &chunk);
|
|
if (rc != FX_Ok)
|
|
return(rc);
|
|
|
|
// !!! FIXME: Need to do something with pitchoffset.
|
|
|
|
totalsamples = chunk->alen / samplesize;
|
|
|
|
if ((loopstart >= 0) && ((unsigned int)loopstart < totalsamples))
|
|
{
|
|
if (loopend < 0) loopend = 0;
|
|
if ((unsigned int)loopend > totalsamples) loopend = totalsamples;
|
|
|
|
if (loopend < loopstart)
|
|
{
|
|
Mix_FreeChunk(chunk);
|
|
chaninfo[chan].in_use = 0;
|
|
setErrorMessage("Loop end is before loop start.");
|
|
return(FX_Error);
|
|
} // if
|
|
|
|
chunk->alen = loopend * samplesize;
|
|
|
|
if (loopstart > 0)
|
|
{
|
|
loopstart *= samplesize;
|
|
memcpy(chunk->abuf, ((Uint8 *) chunk->abuf) + loopstart,
|
|
chunk->alen - loopstart);
|
|
chunk->alen -= loopstart;
|
|
} // if
|
|
} // if
|
|
|
|
Mix_PlayChannel(chan, chunk, -1); /* -1 == looping. */
|
|
return(HandleOffset + chan);
|
|
} // FX_PlayLoopedVOC
|
|
|
|
int FX_PlayVOC3D(char *ptr, int pitchoffset, int angle, int distance,
|
|
int priority, unsigned long callbackval)
|
|
{
|
|
int rc;
|
|
int chan;
|
|
Mix_Chunk *chunk;
|
|
|
|
snddebug("Playing voice at angle (%d), distance (%d), priority (%d).\n",
|
|
angle, distance, priority);
|
|
|
|
rc = setupVocPlayback(ptr, -1, priority, callbackval, &chan, &chunk);
|
|
if (rc != FX_Ok)
|
|
return(rc);
|
|
|
|
// !!! FIXME: Need to do something with pitchoffset.
|
|
|
|
_FX_SetPosition(chan, angle, distance);
|
|
|
|
Mix_PlayChannel(chan, chunk, 0);
|
|
return(HandleOffset + chan);
|
|
} // FX_PlayVOC3D
|
|
|
|
// ROTT Special - SBF
|
|
int FX_PlayVOC3D_ROTT(char *ptr, int size, int pitchoffset, int angle, int distance,
|
|
int priority, unsigned long callbackval)
|
|
{
|
|
int rc;
|
|
int chan;
|
|
Mix_Chunk *chunk;
|
|
|
|
snddebug("Playing voice at angle (%d), distance (%d), priority (%d).\n",
|
|
angle, distance, priority);
|
|
|
|
rc = setupVocPlayback(ptr, size, priority, callbackval, &chan, &chunk);
|
|
if (rc != FX_Ok)
|
|
return(rc);
|
|
|
|
// !!! FIXME: Need to do something with pitchoffset.
|
|
|
|
_FX_SetPosition(chan, angle, distance);
|
|
|
|
Mix_PlayChannel(chan, chunk, 0);
|
|
|
|
return(HandleOffset + chan);
|
|
} // FX_PlayVOC3D_ROTT
|
|
|
|
|
|
// it's all the same to SDL_mixer. :)
|
|
int FX_PlayWAV( char *ptr, int pitchoffset, int vol, int left, int right,
|
|
int priority, unsigned long callbackval )
|
|
{
|
|
return(FX_PlayVOC(ptr, pitchoffset, vol, left, right, priority, callbackval));
|
|
} // FX_PlayWAV
|
|
|
|
|
|
int FX_PlayLoopedWAV( char *ptr, long loopstart, long loopend,
|
|
int pitchoffset, int vol, int left, int right, int priority,
|
|
unsigned long callbackval )
|
|
{
|
|
return(FX_PlayLoopedVOC(ptr, loopstart, loopend, pitchoffset, vol, left,
|
|
right, priority, callbackval));
|
|
} // FX_PlayLoopedWAV
|
|
|
|
|
|
int FX_PlayWAV3D( char *ptr, int pitchoffset, int angle, int distance,
|
|
int priority, unsigned long callbackval )
|
|
{
|
|
return(FX_PlayVOC3D(ptr, pitchoffset, angle, distance, priority, callbackval));
|
|
} // FX_PlayWAV3D
|
|
|
|
// ROTT Special - SBF
|
|
int FX_PlayWAV3D_ROTT( char *ptr, int size, int pitchoffset, int angle, int distance,
|
|
int priority, unsigned long callbackval )
|
|
{
|
|
return(FX_PlayVOC3D_ROTT(ptr, size, pitchoffset, angle, distance, priority, callbackval));
|
|
} // FX_PlayWAV3D_ROTT
|
|
|
|
|
|
int FX_PlayRaw( char *ptr, unsigned long length, unsigned rate,
|
|
int pitchoffset, int vol, int left, int right, int priority,
|
|
unsigned long callbackval )
|
|
{
|
|
setErrorMessage("FX_PlayRaw() ... NOT IMPLEMENTED!");
|
|
return(FX_Error);
|
|
} // FX_PlayRaw
|
|
|
|
|
|
int FX_PlayLoopedRaw( char *ptr, unsigned long length, char *loopstart,
|
|
char *loopend, unsigned rate, int pitchoffset, int vol, int left,
|
|
int right, int priority, unsigned long callbackval )
|
|
{
|
|
setErrorMessage("FX_PlayLoopedRaw() ... NOT IMPLEMENTED!");
|
|
return(FX_Error);
|
|
} // FX_PlayLoopedRaw
|
|
|
|
|
|
int FX_Pan3D(int handle, int angle, int distance)
|
|
{
|
|
int retval = FX_Warning;
|
|
|
|
handle -= HandleOffset;
|
|
|
|
if ((handle < 0) || (handle >= numChannels))
|
|
setWarningMessage("Invalid handle in FX_Pan3D().");
|
|
else if (!Mix_Playing(handle))
|
|
setWarningMessage("voice is no longer playing in FX_Pan3D().");
|
|
else
|
|
{
|
|
_FX_SetPosition(handle, angle, distance);
|
|
|
|
retval = FX_Ok;
|
|
} // else
|
|
|
|
return(retval);
|
|
} // FX_Pan3D
|
|
|
|
|
|
int FX_SoundActive(int handle)
|
|
{
|
|
handle -= HandleOffset;
|
|
|
|
if (chaninfo == NULL)
|
|
return(__FX_FALSE);
|
|
|
|
if ((handle < 0) || (handle >= numChannels))
|
|
{
|
|
setWarningMessage("Invalid handle in FX_SoundActive().");
|
|
return(__FX_FALSE);
|
|
} // if
|
|
|
|
return(chaninfo[handle].in_use != 0);
|
|
} // FX_SoundActive
|
|
|
|
|
|
int FX_SoundsPlaying(void)
|
|
{
|
|
return(Mix_Playing(-1));
|
|
} // FX_SoundsPlaying
|
|
|
|
|
|
int FX_StopSound(int handle)
|
|
{
|
|
int retval = FX_Ok;
|
|
|
|
snddebug("explicitly halting channel (%d).", handle);
|
|
// !!! FIXME: Should the user callback fire for this?
|
|
|
|
handle -= HandleOffset;
|
|
|
|
if ((handle < 0) || (handle >= numChannels))
|
|
{
|
|
setWarningMessage("Invalid handle in FX_Pan3D().");
|
|
retval = FX_Warning;
|
|
} // if
|
|
else
|
|
{
|
|
Mix_HaltChannel(handle);
|
|
} // else
|
|
|
|
return(retval);
|
|
} // FX_StopSound
|
|
|
|
|
|
int FX_StopAllSounds(void)
|
|
{
|
|
snddebug("halting all channels.");
|
|
// !!! FIXME: Should the user callback fire for this?
|
|
Mix_HaltGroup(-1);
|
|
return(FX_Ok);
|
|
} // FX_StopAllSounds
|
|
|
|
|
|
int FX_StartDemandFeedPlayback( void ( *function )( char **ptr, unsigned long *length ),
|
|
int rate, int pitchoffset, int vol, int left, int right,
|
|
int priority, unsigned long callbackval )
|
|
{
|
|
setErrorMessage("FX_StartDemandFeedPlayback() ... NOT IMPLEMENTED!");
|
|
return(FX_Error);
|
|
}
|
|
|
|
|
|
int FX_StartRecording(int MixRate, void (*function)(char *ptr, int length))
|
|
{
|
|
setErrorMessage("FX_StartRecording() ... NOT IMPLEMENTED!");
|
|
return(FX_Error);
|
|
} // FX_StartRecording
|
|
|
|
|
|
void FX_StopRecord( void )
|
|
{
|
|
setErrorMessage("FX_StopRecord() ... NOT IMPLEMENTED!");
|
|
} // FX_StopRecord
|
|
|
|
|
|
|
|
// The music functions...
|
|
|
|
|
|
char *MUSIC_ErrorString(int ErrorNumber)
|
|
{
|
|
switch (ErrorNumber)
|
|
{
|
|
case MUSIC_Warning:
|
|
return(warningMessage);
|
|
|
|
case MUSIC_Error:
|
|
return(errorMessage);
|
|
|
|
case MUSIC_Ok:
|
|
return("OK; no error.");
|
|
|
|
case MUSIC_ASSVersion:
|
|
return("Incorrect sound library version.");
|
|
|
|
case MUSIC_SoundCardError:
|
|
return("General sound card error.");
|
|
|
|
case MUSIC_InvalidCard:
|
|
return("Invalid sound card.");
|
|
|
|
case MUSIC_MidiError:
|
|
return("MIDI error.");
|
|
|
|
case MUSIC_MPU401Error:
|
|
return("MPU401 error.");
|
|
|
|
case MUSIC_TaskManError:
|
|
return("Task Manager error.");
|
|
|
|
case MUSIC_FMNotDetected:
|
|
return("FM not detected error.");
|
|
|
|
case MUSIC_DPMI_Error:
|
|
return("DPMI error.");
|
|
|
|
default:
|
|
return("Unknown error.");
|
|
} // switch
|
|
|
|
assert(0); // shouldn't hit this point.
|
|
return(NULL);
|
|
} // MUSIC_ErrorString
|
|
|
|
|
|
static int music_initialized = 0;
|
|
static int music_context = 0;
|
|
static int music_loopflag = MUSIC_PlayOnce;
|
|
static char *music_songdata = NULL;
|
|
static Mix_Music *music_musicchunk = NULL;
|
|
|
|
int MUSIC_Init(int SoundCard, int Address)
|
|
{
|
|
init_debugging();
|
|
|
|
// eukara: MIDI support.
|
|
Mix_Init(MIX_INIT_MID);
|
|
Mix_SetMusicCMD("timidity");
|
|
puts("Music initialized.\n");
|
|
music_initialized = 1;
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_Init
|
|
|
|
|
|
int MUSIC_Shutdown(void)
|
|
{
|
|
musdebug("shutting down sound subsystem.");
|
|
|
|
if (!music_initialized)
|
|
{
|
|
setErrorMessage("Music system is not currently initialized.");
|
|
return(MUSIC_Error);
|
|
} // if
|
|
|
|
MUSIC_StopSong();
|
|
music_context = 0;
|
|
music_initialized = 0;
|
|
music_loopflag = MUSIC_PlayOnce;
|
|
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_Shutdown
|
|
|
|
|
|
void MUSIC_SetMaxFMMidiChannel(int channel)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetMaxFMMidiChannel(%d).\n", channel);
|
|
} // MUSIC_SetMaxFMMidiChannel
|
|
|
|
|
|
void MUSIC_SetVolume(int volume)
|
|
{
|
|
Mix_VolumeMusic(volume >> 1); // convert 0-255 to 0-128.
|
|
} // MUSIC_SetVolume
|
|
|
|
|
|
void MUSIC_SetMidiChannelVolume(int channel, int volume)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetMidiChannelVolume(%d, %d).\n", channel, volume);
|
|
} // MUSIC_SetMidiChannelVolume
|
|
|
|
|
|
void MUSIC_ResetMidiChannelVolumes(void)
|
|
{
|
|
musdebug("STUB ... MUSIC_ResetMidiChannelVolumes().\n");
|
|
} // MUSIC_ResetMidiChannelVolumes
|
|
|
|
|
|
int MUSIC_GetVolume(void)
|
|
{
|
|
return(Mix_VolumeMusic(-1) << 1); // convert 0-128 to 0-255.
|
|
} // MUSIC_GetVolume
|
|
|
|
|
|
void MUSIC_SetLoopFlag(int loopflag)
|
|
{
|
|
music_loopflag = loopflag;
|
|
} // MUSIC_SetLoopFlag
|
|
|
|
|
|
int MUSIC_SongPlaying(void)
|
|
{
|
|
return((Mix_PlayingMusic()) ? __FX_TRUE : __FX_FALSE);
|
|
} // MUSIC_SongPlaying
|
|
|
|
|
|
void MUSIC_Continue(void)
|
|
{
|
|
if (Mix_PausedMusic())
|
|
Mix_ResumeMusic();
|
|
else if (music_songdata)
|
|
MUSIC_PlaySong(music_songdata, MUSIC_PlayOnce);
|
|
} // MUSIC_Continue
|
|
|
|
|
|
void MUSIC_Pause(void)
|
|
{
|
|
Mix_PauseMusic();
|
|
} // MUSIC_Pause
|
|
|
|
|
|
int MUSIC_StopSong(void)
|
|
{
|
|
if (!fx_initialized)
|
|
{
|
|
setErrorMessage("Need FX system initialized, too. Sorry.");
|
|
return(MUSIC_Error);
|
|
} // if
|
|
|
|
if ( (Mix_PlayingMusic()) || (Mix_PausedMusic()) )
|
|
Mix_HaltMusic();
|
|
|
|
if (music_musicchunk)
|
|
Mix_FreeMusic(music_musicchunk);
|
|
|
|
music_songdata = NULL;
|
|
music_musicchunk = NULL;
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_StopSong
|
|
|
|
|
|
int MUSIC_PlaySong(unsigned char *song, int loopflag)
|
|
{
|
|
music_songdata = song;
|
|
|
|
// eukara: MIDI support.
|
|
Mix_PlayMusic((Mix_Music *) song, loopflag);
|
|
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_PlaySong
|
|
|
|
|
|
extern char ApogeePath[256];
|
|
|
|
// Duke3D-specific. --ryan.
|
|
void PlayMusic(char *_filename)
|
|
{
|
|
//char filename[MAX_PATH];
|
|
//strcpy(filename, _filename);
|
|
//FixFilePath(filename);
|
|
|
|
char filename[MAX_PATH];
|
|
long handle;
|
|
long size;
|
|
void *song;
|
|
long rc;
|
|
|
|
MUSIC_StopSong();
|
|
|
|
// Read from a groupfile, write it to disk so SDL_mixer can read it.
|
|
// Lame. --ryan.
|
|
handle = kopen4load(_filename, 0);
|
|
if (handle == -1)
|
|
return;
|
|
|
|
size = kfilelength(handle);
|
|
if (size == -1)
|
|
{
|
|
kclose(handle);
|
|
return;
|
|
} // if
|
|
|
|
song = malloc(size);
|
|
if (song == NULL)
|
|
{
|
|
kclose(handle);
|
|
return;
|
|
} // if
|
|
|
|
rc = kread(handle, song, size);
|
|
kclose(handle);
|
|
if (rc != size)
|
|
{
|
|
free(song);
|
|
return;
|
|
} // if
|
|
|
|
// save the file somewhere, so SDL_mixer can load it
|
|
//GetPathFromEnvironment(filename, ApogeePath, "tmpsong.mid");
|
|
puts("Write temp midi file to homedir/.duke3d");
|
|
handle = SafeOpenWrite("tmpsong.mid", filetype_binary);
|
|
|
|
SafeWrite(handle, song, size);
|
|
close(handle);
|
|
free(song);
|
|
|
|
//music_songdata = song;
|
|
|
|
music_musicchunk = Mix_LoadMUS("tmpsong.mid");
|
|
if (music_musicchunk != NULL)
|
|
{
|
|
// !!! FIXME: I set the music to loop. Hope that's okay. --ryan.
|
|
Mix_PlayMusic(music_musicchunk, -1);
|
|
} // if
|
|
}
|
|
|
|
|
|
#if ROTT
|
|
// ROTT Special - SBF
|
|
int MUSIC_PlaySongROTT(unsigned char *song, int size, int loopflag)
|
|
{
|
|
char filename[MAX_PATH];
|
|
int handle;
|
|
|
|
MUSIC_StopSong();
|
|
|
|
// save the file somewhere, so SDL_mixer can load it
|
|
GetPathFromEnvironment(filename, ApogeePath, "tmpsong.mid");
|
|
handle = SafeOpenWrite(filename);
|
|
|
|
SafeWrite(handle, song, size);
|
|
close(handle);
|
|
|
|
music_songdata = song;
|
|
|
|
// finally, we can load it with SDL_mixer
|
|
music_musicchunk = Mix_LoadMUS(filename);
|
|
if (music_musicchunk == NULL) {
|
|
return MUSIC_Error;
|
|
}
|
|
|
|
Mix_PlayMusic(music_musicchunk, (loopflag == MUSIC_PlayOnce) ? 0 : -1);
|
|
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_PlaySongROTT
|
|
#endif
|
|
|
|
|
|
void MUSIC_SetContext(int context)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetContext().\n");
|
|
music_context = context;
|
|
} // MUSIC_SetContext
|
|
|
|
|
|
int MUSIC_GetContext(void)
|
|
{
|
|
return(music_context);
|
|
} // MUSIC_GetContext
|
|
|
|
|
|
void MUSIC_SetSongTick(unsigned long PositionInTicks)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetSongTick().\n");
|
|
} // MUSIC_SetSongTick
|
|
|
|
|
|
void MUSIC_SetSongTime(unsigned long milliseconds)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetSongTime().\n");
|
|
}// MUSIC_SetSongTime
|
|
|
|
|
|
void MUSIC_SetSongPosition(int measure, int beat, int tick)
|
|
{
|
|
musdebug("STUB ... MUSIC_SetSongPosition().\n");
|
|
} // MUSIC_SetSongPosition
|
|
|
|
|
|
void MUSIC_GetSongPosition(songposition *pos)
|
|
{
|
|
musdebug("STUB ... MUSIC_GetSongPosition().\n");
|
|
} // MUSIC_GetSongPosition
|
|
|
|
|
|
void MUSIC_GetSongLength(songposition *pos)
|
|
{
|
|
musdebug("STUB ... MUSIC_GetSongLength().\n");
|
|
} // MUSIC_GetSongLength
|
|
|
|
|
|
int MUSIC_FadeVolume(int tovolume, int milliseconds)
|
|
{
|
|
Mix_FadeOutMusic(milliseconds);
|
|
return(MUSIC_Ok);
|
|
} // MUSIC_FadeVolume
|
|
|
|
|
|
int MUSIC_FadeActive(void)
|
|
{
|
|
return((Mix_FadingMusic() == MIX_FADING_OUT) ? __FX_TRUE : __FX_FALSE);
|
|
} // MUSIC_FadeActive
|
|
|
|
|
|
void MUSIC_StopFade(void)
|
|
{
|
|
musdebug("STUB ... MUSIC_StopFade().\n");
|
|
} // MUSIC_StopFade
|
|
|
|
|
|
void MUSIC_RerouteMidiChannel(int channel, int cdecl function( int event, int c1, int c2 ))
|
|
{
|
|
musdebug("STUB ... MUSIC_RerouteMidiChannel().\n");
|
|
} // MUSIC_RerouteMidiChannel
|
|
|
|
|
|
void MUSIC_RegisterTimbreBank(unsigned char *timbres)
|
|
{
|
|
musdebug("STUB ... MUSIC_RegisterTimbreBank().\n");
|
|
} // MUSIC_RegisterTimbreBank
|
|
|
|
|
|
// end of fx_man.c ...
|