834 lines
19 KiB
C
834 lines
19 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id:$
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
//
|
|
// This source is available for distribution and/or modification
|
|
// only under the terms of the DOOM Source Code License as
|
|
// published by id Software. All rights reserved.
|
|
//
|
|
// The source is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
// for more details.
|
|
//
|
|
// $Log:$
|
|
//
|
|
// DESCRIPTION:
|
|
// System interface for sound.
|
|
// macOS port: SDL2 audio queue for SFX,
|
|
// AudioToolbox MusicSequence/MusicPlayer for music.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static const char
|
|
rcsid[] = "$Id: i_unix.c,v 1.5 1997/02/03 22:45:10 b1 Exp $";
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#define SDL_MAIN_HANDLED
|
|
#include <SDL.h>
|
|
|
|
#include <AudioToolbox/AudioToolbox.h>
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
// stdbool.h (pulled in by CoreFoundation) clashes
|
|
// with doomtype's enum boolean.
|
|
#undef false
|
|
#undef true
|
|
|
|
#include "z_zone.h"
|
|
|
|
#include "i_system.h"
|
|
#include "i_sound.h"
|
|
#include "m_argv.h"
|
|
#include "m_misc.h"
|
|
#include "w_wad.h"
|
|
|
|
#include "doomdef.h"
|
|
|
|
#include "mus2mid.h"
|
|
|
|
|
|
// The number of internal mixing channels,
|
|
// the samples calculated for each mixing step,
|
|
// the size of the 16bit, 2 hardware channel (stereo)
|
|
// mixing buffer, and the samplerate of the raw data.
|
|
|
|
// Needed for calling the actual sound output.
|
|
#define SAMPLECOUNT 512
|
|
#define NUM_CHANNELS 8
|
|
// It is 2 for 16bit, and 2 for two channels.
|
|
#define BUFMUL 4
|
|
#define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL)
|
|
|
|
#define SAMPLERATE 11025 // Hz
|
|
|
|
// The actual lengths of all sound effects.
|
|
int lengths[NUMSFX];
|
|
|
|
// The actual output device.
|
|
static SDL_AudioDeviceID audio_device;
|
|
|
|
// The global mixing buffer.
|
|
// Basically, samples from all active internal channels
|
|
// are modifed and added, and stored in the buffer
|
|
// that is submitted to the audio device.
|
|
signed short mixbuffer[MIXBUFFERSIZE];
|
|
|
|
|
|
// The channel step amount...
|
|
unsigned int channelstep[NUM_CHANNELS];
|
|
// ... and a 0.16 bit remainder of last step.
|
|
unsigned int channelstepremainder[NUM_CHANNELS];
|
|
|
|
|
|
// The channel data pointers, start and end.
|
|
unsigned char* channels[NUM_CHANNELS];
|
|
unsigned char* channelsend[NUM_CHANNELS];
|
|
|
|
|
|
// Time/gametic that the channel started playing,
|
|
// used to determine oldest, which automatically
|
|
// has lowest priority.
|
|
// In case number of active sounds exceeds
|
|
// available channels.
|
|
int channelstart[NUM_CHANNELS];
|
|
|
|
// The sound in channel handles,
|
|
// determined on registration,
|
|
// might be used to unregister/stop/modify,
|
|
// currently unused.
|
|
int channelhandles[NUM_CHANNELS];
|
|
|
|
// SFX id of the playing sound effect.
|
|
// Used to catch duplicates (like chainsaw).
|
|
int channelids[NUM_CHANNELS];
|
|
|
|
// Pitch to stepping lookup, unused.
|
|
int steptable[256];
|
|
|
|
// Volume lookups.
|
|
int vol_lookup[128*256];
|
|
|
|
// Hardware left and right channel volume lookup.
|
|
int* channelleftvol_lookup[NUM_CHANNELS];
|
|
int* channelrightvol_lookup[NUM_CHANNELS];
|
|
|
|
|
|
|
|
//
|
|
// This function loads the sound data from the WAD lump,
|
|
// for single sound.
|
|
//
|
|
void*
|
|
getsfx
|
|
( char* sfxname,
|
|
int* len )
|
|
{
|
|
unsigned char* sfx;
|
|
unsigned char* paddedsfx;
|
|
int i;
|
|
int size;
|
|
int paddedsize;
|
|
char name[20];
|
|
int sfxlump;
|
|
|
|
|
|
// Get the sound data from the WAD, allocate lump
|
|
// in zone memory.
|
|
sprintf(name, "ds%s", sfxname);
|
|
|
|
// Now, there is a severe problem with the
|
|
// sound handling, in it is not (yet/anymore)
|
|
// gamemode aware. That means, sounds from
|
|
// DOOM II will be requested even with DOOM
|
|
// shareware.
|
|
// The sound list is wired into sounds.c,
|
|
// which sets the external variable.
|
|
// I do not do runtime patches to that
|
|
// variable. Instead, we will use a
|
|
// default sound for replacement.
|
|
if ( W_CheckNumForName(name) == -1 )
|
|
sfxlump = W_GetNumForName("dspistol");
|
|
else
|
|
sfxlump = W_GetNumForName(name);
|
|
|
|
size = W_LumpLength( sfxlump );
|
|
|
|
// Debug.
|
|
// fprintf( stderr, "." );
|
|
//fprintf( stderr, " -loading %s (lump %d, %d bytes)\n",
|
|
// sfxname, sfxlump, size );
|
|
//fflush( stderr );
|
|
|
|
sfx = (unsigned char*)W_CacheLumpNum( sfxlump, PU_STATIC );
|
|
|
|
// Pads the sound effect out to the mixing buffer size.
|
|
// The original realloc would interfere with zone memory.
|
|
paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
|
|
|
|
// Allocate from zone memory.
|
|
paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 );
|
|
// ddt: (unsigned char *) realloc(sfx, paddedsize+8);
|
|
// This should interfere with zone memory handling,
|
|
// which does not kick in in the soundserver.
|
|
|
|
// Now copy and pad.
|
|
memcpy( paddedsfx, sfx, size );
|
|
for (i=size ; i<paddedsize+8 ; i++)
|
|
paddedsfx[i] = 128;
|
|
|
|
// Remove the cached lump.
|
|
Z_Free( sfx );
|
|
|
|
// Preserve padded length.
|
|
*len = paddedsize;
|
|
|
|
// Return allocated padded data.
|
|
return (void *) (paddedsfx + 8);
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// This function adds a sound to the
|
|
// list of currently active sounds,
|
|
// which is maintained as a given number
|
|
// (eight, usually) of internal channels.
|
|
// Returns a handle.
|
|
//
|
|
int
|
|
addsfx
|
|
( int sfxid,
|
|
int volume,
|
|
int step,
|
|
int seperation )
|
|
{
|
|
static unsigned short handlenums = 0;
|
|
|
|
int i;
|
|
int rc = -1;
|
|
|
|
int oldest = gametic;
|
|
int oldestnum = 0;
|
|
int slot;
|
|
|
|
int rightvol;
|
|
int leftvol;
|
|
|
|
// Chainsaw troubles.
|
|
// Play these sound effects only one at a time.
|
|
if ( sfxid == sfx_sawup
|
|
|| sfxid == sfx_sawidl
|
|
|| sfxid == sfx_sawful
|
|
|| sfxid == sfx_sawhit
|
|
|| sfxid == sfx_stnmov
|
|
|| sfxid == sfx_pistol )
|
|
{
|
|
// Loop all channels, check.
|
|
for (i=0 ; i<NUM_CHANNELS ; i++)
|
|
{
|
|
// Active, and using the same SFX?
|
|
if ( (channels[i])
|
|
&& (channelids[i] == sfxid) )
|
|
{
|
|
// Reset.
|
|
channels[i] = 0;
|
|
// We are sure that iff,
|
|
// there will only be one.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Loop all channels to find oldest SFX.
|
|
for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)
|
|
{
|
|
if (channelstart[i] < oldest)
|
|
{
|
|
oldestnum = i;
|
|
oldest = channelstart[i];
|
|
}
|
|
}
|
|
|
|
// Tales from the cryptic.
|
|
// If we found a channel, fine.
|
|
// If not, we simply overwrite the first one, 0.
|
|
// Probably only happens at startup.
|
|
if (i == NUM_CHANNELS)
|
|
slot = oldestnum;
|
|
else
|
|
slot = i;
|
|
|
|
// Okay, in the less recent channel,
|
|
// we will handle the new SFX.
|
|
// Set pointer to raw data.
|
|
channels[slot] = (unsigned char *) S_sfx[sfxid].data;
|
|
// Set pointer to end of raw data.
|
|
channelsend[slot] = channels[slot] + lengths[sfxid];
|
|
|
|
// Reset current handle number, limited to 0..100.
|
|
if (!handlenums)
|
|
handlenums = 100;
|
|
|
|
// Assign current handle number.
|
|
// Preserved so sounds could be stopped (unused).
|
|
channelhandles[slot] = rc = handlenums++;
|
|
|
|
// Set stepping???
|
|
// Kinda getting the impression this is never used.
|
|
channelstep[slot] = step;
|
|
// ???
|
|
channelstepremainder[slot] = 0;
|
|
// Should be gametic, I presume.
|
|
channelstart[slot] = gametic;
|
|
|
|
// Separation, that is, orientation/stereo.
|
|
// range is: 1 - 256
|
|
seperation += 1;
|
|
|
|
// Per left/right channel.
|
|
// x^2 seperation,
|
|
// adjust volume properly.
|
|
leftvol =
|
|
volume - ((volume*seperation*seperation) >> 16); ///(256*256);
|
|
seperation = seperation - 257;
|
|
rightvol =
|
|
volume - ((volume*seperation*seperation) >> 16);
|
|
|
|
// Sanity check, clamp volume.
|
|
if (rightvol < 0 || rightvol > 127)
|
|
I_Error("rightvol out of bounds");
|
|
|
|
if (leftvol < 0 || leftvol > 127)
|
|
I_Error("leftvol out of bounds");
|
|
|
|
// Get the proper lookup table piece
|
|
// for this volume level???
|
|
channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
|
|
channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
|
|
|
|
// Preserve sound SFX id,
|
|
// e.g. for avoiding duplicates of chainsaw.
|
|
channelids[slot] = sfxid;
|
|
|
|
// You tell me.
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// SFX API
|
|
// Note: this was called by S_Init.
|
|
// However, whatever they did in the
|
|
// old DPMS based DOS version, this
|
|
// were simply dummies in the Linux
|
|
// version.
|
|
// See soundserver initdata().
|
|
//
|
|
void I_SetChannels()
|
|
{
|
|
// Init internal lookups (raw data, mixing buffer, channels).
|
|
// This function sets up internal lookups used during
|
|
// the mixing process.
|
|
int i;
|
|
int j;
|
|
|
|
int* steptablemid = steptable + 128;
|
|
|
|
// Okay, reset internal mixing channels to zero.
|
|
/*for (i=0; i<NUM_CHANNELS; i++)
|
|
{
|
|
channels[i] = 0;
|
|
}*/
|
|
|
|
// This table provides step widths for pitch parameters.
|
|
// I fail to see that this is currently used.
|
|
for (i=-128 ; i<128 ; i++)
|
|
steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
|
|
|
|
|
|
// Generates volume lookup tables
|
|
// which also turn the unsigned samples
|
|
// into signed samples.
|
|
for (i=0 ; i<128 ; i++)
|
|
for (j=0 ; j<256 ; j++)
|
|
vol_lookup[i*256+j] = (i*(j-128)*256)/127;
|
|
}
|
|
|
|
|
|
void I_SetSfxVolume(int volume)
|
|
{
|
|
// Identical to DOS.
|
|
// Basically, this should propagate
|
|
// the menu/config file setting
|
|
// to the state variable used in
|
|
// the mixing.
|
|
snd_SfxVolume = volume;
|
|
}
|
|
|
|
// MUSIC API
|
|
void I_SetMusicVolume(int volume)
|
|
{
|
|
// Internal state variable.
|
|
// MusicPlayer has no per-sequence volume control;
|
|
// the setting is kept for config consistency.
|
|
snd_MusicVolume = volume;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the raw data lump index
|
|
// for a given SFX name.
|
|
//
|
|
int I_GetSfxLumpNum(sfxinfo_t* sfx)
|
|
{
|
|
char namebuf[9];
|
|
sprintf(namebuf, "ds%s", sfx->name);
|
|
return W_GetNumForName(namebuf);
|
|
}
|
|
|
|
//
|
|
// Starting a sound means adding it
|
|
// to the current list of active sounds
|
|
// in the internal channels.
|
|
// As the SFX info struct contains
|
|
// e.g. a pointer to the raw data,
|
|
// it is ignored.
|
|
// As our sound handling does not handle
|
|
// priority, it is ignored.
|
|
// Pitching (that is, increased speed of playback)
|
|
// is set, but currently not used by mixing.
|
|
//
|
|
int
|
|
I_StartSound
|
|
( int id,
|
|
int vol,
|
|
int sep,
|
|
int pitch,
|
|
int priority )
|
|
{
|
|
|
|
// UNUSED
|
|
priority = 0;
|
|
|
|
// The menu and config store SFX volume on a 0..15 scale, while the
|
|
// original mixer lookup tables use 0..127.
|
|
vol *= 8;
|
|
if (vol > 127)
|
|
vol = 127;
|
|
|
|
// Returns a handle (not used).
|
|
id = addsfx( id, vol, steptable[pitch], sep );
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
|
|
void I_StopSound (int handle)
|
|
{
|
|
// You need the handle returned by StartSound.
|
|
// Would be looping all channels,
|
|
// tracking down the handle,
|
|
// an setting the channel to zero.
|
|
|
|
// UNUSED.
|
|
handle = 0;
|
|
}
|
|
|
|
|
|
int I_SoundIsPlaying(int handle)
|
|
{
|
|
// Ouch.
|
|
return gametic < handle;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// This function loops all active (internal) sound
|
|
// channels, retrieves a given number of samples
|
|
// from the raw sound data, modifies it according
|
|
// to the current (internal) channel parameters,
|
|
// mixes the per channel samples into the global
|
|
// mixbuffer, clamping it to the allowed range,
|
|
// and sets up everything for transferring the
|
|
// contents of the mixbuffer to the (two)
|
|
// hardware channels (left and right, that is).
|
|
//
|
|
// This function currently supports only 16bit.
|
|
//
|
|
void I_UpdateSound( void )
|
|
{
|
|
// Mix current sound data.
|
|
// Data, from raw sound, for right and left.
|
|
register unsigned int sample;
|
|
register int dl;
|
|
register int dr;
|
|
|
|
// Pointers in global mixbuffer, left, right, end.
|
|
signed short* leftout;
|
|
signed short* rightout;
|
|
signed short* leftend;
|
|
// Step in mixbuffer, left and right, thus two.
|
|
int step;
|
|
|
|
// Mixing channel index.
|
|
int chan;
|
|
|
|
// Left and right channel
|
|
// are in global mixbuffer, alternating.
|
|
leftout = mixbuffer;
|
|
rightout = mixbuffer+1;
|
|
step = 2;
|
|
|
|
// Determine end, for left channel only
|
|
// (right channel is implicit).
|
|
leftend = mixbuffer + SAMPLECOUNT*step;
|
|
|
|
// Mix sounds into the mixing buffer.
|
|
// Loop over step*SAMPLECOUNT,
|
|
// that is 512 values for two channels.
|
|
while (leftout != leftend)
|
|
{
|
|
// Reset left/right value.
|
|
dl = 0;
|
|
dr = 0;
|
|
|
|
// Love thy L2 chache - made this a loop.
|
|
// Now more channels could be set at compile time
|
|
// as well. Thus loop those channels.
|
|
for ( chan = 0; chan < NUM_CHANNELS; chan++ )
|
|
{
|
|
// Check channel, if active.
|
|
if (channels[ chan ])
|
|
{
|
|
// Get the raw data from the channel.
|
|
sample = *channels[ chan ];
|
|
// Add left and right part
|
|
// for this channel (sound)
|
|
// to the current data.
|
|
// Adjust volume accordingly.
|
|
dl += channelleftvol_lookup[ chan ][sample];
|
|
dr += channelrightvol_lookup[ chan ][sample];
|
|
// Increment index ???
|
|
channelstepremainder[ chan ] += channelstep[ chan ];
|
|
// MSB is next sample???
|
|
channels[ chan ] += channelstepremainder[ chan ] >> 16;
|
|
// Limit to LSB???
|
|
channelstepremainder[ chan ] &= 65536-1;
|
|
|
|
// Check whether we are done.
|
|
if (channels[ chan ] >= channelsend[ chan ])
|
|
channels[ chan ] = 0;
|
|
}
|
|
}
|
|
|
|
// Clamp to range. Left hardware channel.
|
|
if (dl > 0x7fff)
|
|
*leftout = 0x7fff;
|
|
else if (dl < -0x8000)
|
|
*leftout = -0x8000;
|
|
else
|
|
*leftout = dl;
|
|
|
|
// Same for right hardware channel.
|
|
if (dr > 0x7fff)
|
|
*rightout = 0x7fff;
|
|
else if (dr < -0x8000)
|
|
*rightout = -0x8000;
|
|
else
|
|
*rightout = dr;
|
|
|
|
// Increment current pointers in mixbuffer.
|
|
leftout += step;
|
|
rightout += step;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Write out the mixbuffer to the audio device
|
|
// during each game loop update.
|
|
//
|
|
void
|
|
I_SubmitSound(void)
|
|
{
|
|
if (!audio_device)
|
|
return;
|
|
|
|
// Throttle: the game produces 512 stereo frames per tic
|
|
// (35 tics/sec) but the device consumes at 11025 Hz.
|
|
// Bound the queue the way the blocking /dev/dsp write did.
|
|
while (SDL_GetQueuedAudioSize(audio_device) > 4*MIXBUFFERSIZE)
|
|
SDL_Delay(1);
|
|
|
|
SDL_QueueAudio(audio_device, mixbuffer, MIXBUFFERSIZE);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
I_UpdateSoundParams
|
|
( int handle,
|
|
int vol,
|
|
int sep,
|
|
int pitch)
|
|
{
|
|
// I fail too see that this is used.
|
|
// Would be using the handle to identify
|
|
// on which channel the sound might be active,
|
|
// and resetting the channel parameters.
|
|
|
|
// UNUSED.
|
|
handle = vol = sep = pitch = 0;
|
|
}
|
|
|
|
|
|
|
|
void I_ShutdownSound(void)
|
|
{
|
|
if (audio_device)
|
|
{
|
|
SDL_CloseAudioDevice(audio_device);
|
|
audio_device = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
I_InitSound()
|
|
{
|
|
int i;
|
|
|
|
SDL_AudioSpec want;
|
|
|
|
SDL_zero(want);
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0)
|
|
{
|
|
fprintf(stderr, "I_InitSound: could not init SDL audio: %s\n",
|
|
SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
want.freq = SAMPLERATE;
|
|
want.format = AUDIO_S16SYS;
|
|
want.channels = 2;
|
|
want.samples = SAMPLECOUNT;
|
|
want.callback = NULL;
|
|
want.userdata = NULL;
|
|
|
|
audio_device = SDL_OpenAudioDevice(NULL, 0, &want, NULL, 0);
|
|
if (!audio_device)
|
|
fprintf(stderr, "I_InitSound: could not open audio device: %s\n",
|
|
SDL_GetError());
|
|
else
|
|
SDL_PauseAudioDevice(audio_device, 0);
|
|
|
|
// Initialize external data (all sounds) at start, keep static.
|
|
fprintf( stderr, "I_InitSound: ");
|
|
|
|
for (i=1 ; i<NUMSFX ; i++)
|
|
{
|
|
// Alias? Example is the chaingun sound linked to pistol.
|
|
if (!S_sfx[i].link)
|
|
{
|
|
// Load data from WAD file.
|
|
S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
|
|
}
|
|
else
|
|
{
|
|
// Previously loaded already?
|
|
S_sfx[i].data = S_sfx[i].link->data;
|
|
lengths[i] = lengths[S_sfx[i].link - S_sfx];
|
|
}
|
|
}
|
|
|
|
fprintf( stderr, " pre-cached all sound data\n");
|
|
|
|
// Now initialize mixbuffer with zero.
|
|
for ( i = 0; i< MIXBUFFERSIZE; i++ )
|
|
mixbuffer[i] = 0;
|
|
|
|
I_InitMusic();
|
|
|
|
// Finished initialization.
|
|
fprintf(stderr, "I_InitSound: sound module ready\n");
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// MUSIC API.
|
|
// MUS lumps are converted to MIDI and played through
|
|
// AudioToolbox MusicSequence/MusicPlayer (system DLS synth).
|
|
//
|
|
|
|
#define MAX_MUSIC_HANDLES 128
|
|
|
|
static MusicSequence music_handles[MAX_MUSIC_HANDLES];
|
|
static int music_handles_used;
|
|
static MusicPlayer music_player;
|
|
static int looping;
|
|
static int current_song;
|
|
|
|
|
|
void I_InitMusic(void)
|
|
{
|
|
NewMusicPlayer(&music_player);
|
|
}
|
|
|
|
void I_ShutdownMusic(void)
|
|
{
|
|
int i;
|
|
|
|
if (music_player)
|
|
{
|
|
MusicPlayerStop(music_player);
|
|
DisposeMusicPlayer(music_player);
|
|
music_player = NULL;
|
|
}
|
|
for (i=0 ; i<music_handles_used ; i++)
|
|
if (music_handles[i])
|
|
DisposeMusicSequence(music_handles[i]);
|
|
music_handles_used = 0;
|
|
}
|
|
|
|
void I_PlaySong(int handle, int loop)
|
|
{
|
|
MusicTrack track;
|
|
MusicTrackLoopInfo loopinfo;
|
|
MusicTimeStamp tracklen;
|
|
UInt32 tracks, i, len32;
|
|
|
|
if (handle <= 0 || handle > music_handles_used
|
|
|| !music_handles[handle-1])
|
|
return;
|
|
|
|
looping = loop;
|
|
current_song = handle;
|
|
MusicPlayerSetSequence(music_player, music_handles[handle-1]);
|
|
|
|
// MusicPlayer has no repeat mode; loop each track
|
|
// via its LoopInfo property (-1 = forever).
|
|
MusicSequenceGetTrackCount(music_handles[handle-1], &tracks);
|
|
for (i=0 ; i<tracks ; i++)
|
|
{
|
|
if (MusicSequenceGetIndTrack(music_handles[handle-1], i, &track))
|
|
continue;
|
|
len32 = sizeof(tracklen);
|
|
MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
|
|
&tracklen, &len32);
|
|
loopinfo.loopDuration = tracklen;
|
|
loopinfo.numberOfLoops = loop ? -1 : 0;
|
|
MusicTrackSetProperty(track, kSequenceTrackProperty_LoopInfo,
|
|
&loopinfo, sizeof(loopinfo));
|
|
}
|
|
|
|
MusicPlayerStart(music_player);
|
|
}
|
|
|
|
void I_PauseSong (int handle)
|
|
{
|
|
// UNUSED.
|
|
handle = 0;
|
|
MusicPlayerStop(music_player);
|
|
}
|
|
|
|
void I_ResumeSong (int handle)
|
|
{
|
|
// UNUSED.
|
|
handle = 0;
|
|
MusicPlayerStart(music_player);
|
|
}
|
|
|
|
void I_StopSong(int handle)
|
|
{
|
|
// UNUSED.
|
|
handle = 0;
|
|
|
|
looping = 0;
|
|
MusicPlayerStop(music_player);
|
|
}
|
|
|
|
void I_UnRegisterSong(int handle)
|
|
{
|
|
if (handle <= 0 || handle > music_handles_used
|
|
|| !music_handles[handle-1])
|
|
return;
|
|
|
|
DisposeMusicSequence(music_handles[handle-1]);
|
|
music_handles[handle-1] = NULL;
|
|
}
|
|
|
|
int I_RegisterSong(void* data)
|
|
{
|
|
byte* mid;
|
|
int midlen;
|
|
CFDataRef middata;
|
|
MusicSequence seq;
|
|
OSStatus err;
|
|
|
|
if (music_handles_used >= MAX_MUSIC_HANDLES)
|
|
return 0;
|
|
|
|
midlen = mus2mid((byte *)data, &mid);
|
|
if (midlen <= 0)
|
|
return 0;
|
|
|
|
middata = CFDataCreate(NULL, mid, midlen);
|
|
free(mid);
|
|
if (!middata)
|
|
return 0;
|
|
|
|
err = NewMusicSequence(&seq);
|
|
if (!err)
|
|
err = MusicSequenceFileLoadData(seq, middata,
|
|
kMusicSequenceFile_MIDIType, 0);
|
|
CFRelease(middata);
|
|
|
|
if (err)
|
|
{
|
|
fprintf(stderr, "I_RegisterSong: MIDI load failed (%d)\n",
|
|
(int)err);
|
|
return 0;
|
|
}
|
|
|
|
music_handles[music_handles_used] = seq;
|
|
return ++music_handles_used;
|
|
}
|
|
|
|
// Is the song playing?
|
|
int I_QrySongPlaying(int handle)
|
|
{
|
|
Boolean playing = false;
|
|
|
|
// UNUSED.
|
|
handle = 0;
|
|
|
|
if (music_player)
|
|
MusicPlayerIsPlaying(music_player, &playing);
|
|
|
|
return playing;
|
|
}
|