/* DSM.C
*
* Digital Sound Mixer
*
* $Id: dsm.c,v 1.11 1997/01/16 18:41:59 pekangas Exp $
*
* Copyright 1996,1997 Housemarque Inc.
*
* This file is part of the MIDAS Sound System, and may only be
* used, modified and distributed under the terms of the MIDAS
* Sound System license, LICENSE.TXT. By continuing to use,
* modify or distribute this file you indicate that you have
* read the license and understand and accept it fully.
*/
/* Possibly environment dependent code is marked with *!!* */
#include "lang.h"
#include "mtypes.h"
#include "errors.h"
#include "mmem.h"
#include "sdevice.h"
#include "dsm.h"
#include "mutils.h"
#include "mglobals.h"
#ifndef NOEMS
#include "ems.h"
#endif
#ifdef __DPMI__
#include "dpmi.h"
#endif
RCSID(const char *dsm_rcsid = "$Id: dsm.c,v 1.11 1997/01/16 18:41:59 pekangas Exp $";)
#ifndef NULL
#define NULL ((void*) 0L)
#endif
#define MIXBUFLEN 40 /* mixing buffer length 1/40th of a
second */
unsigned *dsmMixBuffer; /* DSM mixing buffer. dsmPlay() writes
the mixed data here. Post-
processing is usually necessary. */
unsigned dsmMixBufferSize; /* DSM mixing buffer size */
/* The following global variables are used internally by different DSM
functions and should not be accessed by other modules: */
unsigned dsmMixRate; /* mixing rate in Hz */
unsigned dsmMode; /* output mode (see enum
dsmMixMode) */
#ifdef __16__
unsigned dsmVolTableSeg; /* volume table segment */
#endif
unsigned *dsmVolumeTable; /* pointer to volume table */
dsmChannel *dsmChannels; /* pointer to channel datas */
dsmSample *dsmSamples; /* sample structures */
unsigned dsmOutputBits; /* output bit width */
/* DSM internal variables: */
static unsigned *dsmVolTableMem; /* pointer to volume table returned
by memAlloc(). Used for
deallocating */
static unsigned dsmChOpen; /* number of open channels */
static volatile unsigned dsmChPlay; /* 1 if data on channels may be
played */
static unsigned dsmMasterVolume; /* master volume */
static int dsmMuted; /* 1 if muted, 0 if not */
static int dsmPaused; /* 1 if paused, 0 if not */
static unsigned dsmAmplification; /* amplification level */
static void *mixingRoutines[3][5] = /* Pointers to mixing routines */
{
{
/* Make sure we'll crash right away if mixing mode is 0: */
NULL, NULL, NULL, NULL, NULL
},
{
/* Mixing routines for all sample types to mono output: */
&dsmMix8bitMonoMono,
&dsmMix8bitMonoMono,
&dsmMix16bitMonoMono,
&dsmMix8bitStereoMono,
&dsmMix16bitStereoMono
},
{
/* Mixing routines for all sample types to stereo output: */
&dsmMix8bitMonoStereo,
&dsmMix8bitMonoStereo,
&dsmMix16bitMonoStereo,
&dsmMix8bitStereoStereo,
&dsmMix16bitStereoStereo
}
};
/* Calculate log2(sampleSize) based on sample type */
static int dsmSampleShift(int sampleType)
{
switch ( sampleType )
{
case smp8bitStereo:
case smp16bitMono:
return 1;
case smp16bitStereo:
return 2;
}
return 0;
}
/****************************************************************************\
*
* Function: int dsmInit(unsigned mixRate, unsigned mode,
* unsigned outputBits);
*
* Description: Initializes Digital Sound Mixer
*
* Input: unsigned mixRate mixing rate in Hz
* unsigned mode mixing mode (see enum dsmMixMode)
* unsigned outputBits output bit width (if less than
* 16, output values are divided
* accordingly - mixing buffer is
* always a sequence of unsigned ints)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmInit(unsigned mixRate, unsigned mode, unsigned outputBits)
{
int error, i;
#if defined(__16__) & defined(__PROTMODE__)
ulong baseAddr
#endif
dsmMixRate = mixRate;
dsmMode = mode;
dsmOutputBits = outputBits;
dsmChOpen = 0; /* no open channels */
dsmChPlay = 0; /* do not play data in channels */
dsmChannels = NULL; /* channel structures not allocated */
dsmMuted = 0; /* not muted */
dsmPaused = 0; /* not paused */
dsmMasterVolume = 64; /* master volume maximum */
/* Calculate mixing buffer size: (FIXME) */
if ( mode == dsmMixStereo )
dsmMixBufferSize = 2 * sizeof(unsigned) * mixRate / MIXBUFLEN;
else
dsmMixBufferSize = sizeof(unsigned) * mixRate / MIXBUFLEN;
#if 0
--------------------
This code is only for the new fast stereo mixing that is not implemented
yet
--------------------
#ifdef __16__
if ( mode == dsmMixStereo )
dsmMixBufferSize = 4 * mixRate / MIXBUFLEN;
else
dsmMixBufferSize = 2 * mixRate / MIXBUFLEN;
#else
dsmMixBufferSize = sizeof(unsigned) * mixRate / MIXBUFLEN;
#endif
#endif /* #if 0 */
/* Round up mixing buffer size to nearest paragraph: */
dsmMixBufferSize = (dsmMixBufferSize + 15) & 0xFFFFFFF0;
/* Allocate memory for volume table: (mixing buffer follows
volume table) */
#ifdef __16__
if ( (error = memAlloc(VOLLEVELS * 256 * sizeof(unsigned) +
dsmMixBufferSize+ 16, (void**) &dsmVolTableMem)) != OK )
PASSERROR(ID_dsmInit);
#else
if ( (error = memAlloc(VOLLEVELS * 256 * sizeof(unsigned) +
dsmMixBufferSize + 1024 + 16,
(void**) &dsmVolTableMem)) != OK )
PASSERROR(ID_dsmInit);
#endif
/* (in 16-bit modes the volume table is paragraph aligned and in 32-bit
modes it has to be aligned to a 1024-byte boundary, hence the
difference) */
#ifdef __16__
#ifdef __REALMODE__
/* *!!* */
/* Real mode - align volume table to a beginning of a segment: */
dsmVolTableSeg = *((unsigned*) ((uchar*)&dsmVolTableMem + 2)) +
(((*(unsigned*) &dsmVolTableMem) + 15) >> 4);
/* (now if that isn't ugly code I don't know what is) */
#else
/* 16-bit protected mode under DPMI - allocate descriptor for volume
table: */
if ( (error = dpmiAllocDescriptor(&dsmVolTableSeg)) != OK )
PASSERROR(ID_dsmInit);
/* Get allocated memory area segment base address to baseAddr: */
if ( (error = dpmiGetSegmentBase((unsigned)
(((ulong) dsmVolTableMem) >> 16), &baseAddr)) != OK )
PASSERROR(ID_dsmInit);
/* Calculate volume table memory start address: */
baseAddr += ((ulong) dsmVolTableMem) & 0xFFFF;
/* Align volume table to paragraph boundary: */
baseAddr = (baseAddr + 0x0F) & 0xFFFFFFF0L;
/* Set volume table segment base address: */
if ( (error = dpmiSetSegmentBase(dsmVolTableSeg, baseAddr)) != OK )
PASSERROR(ID_dsmInit);
/* Set correct segment limit for volume table area (volume table +
mixing buffer): */
if ( (error = dpmiSetSegmentLimit(dsmVolTableSeg, 256*2*VOLLEVELS +
dsmMixBufferSize)) != OK )
PASSERROR(ID_dsmInit);
#endif
/* Point mixing buffer to the memory immediately after the volume
table: */
dsmMixBuffer = (unsigned*) ((((ulong) dsmVolTableSeg) << 16) +
256 * 2 * VOLLEVELS);
/* Build a normal pointer to the volume table: */
dsmVolumeTable = (unsigned*) (((ulong) dsmVolTableSeg) << 16);
#else
/* 32-bit mode - align volume table to a 1024-byte boundary: */
dsmVolumeTable = (unsigned*) ((((unsigned) dsmVolTableMem) + 1023) &
0xFFFFFC00L);
/* Point mixing buffer to the memory immediately after the volume
table: */
dsmMixBuffer = (unsigned*) (((unsigned)dsmVolumeTable) +
256 * VOLLEVELS * sizeof(unsigned));
#endif
/* Allocate memory for sample structures: */
if ( (error = memAlloc(MAXSAMPLES * sizeof(dsmSample), (void**)
&dsmSamples)) != OK )
PASSERROR(ID_dsmInit);
/* Mark all samples unused: */
for ( i = 0; i < MAXSAMPLES; i++ )
dsmSamples[i].inUse = 0;
return OK;
}
/****************************************************************************\
*
* Function: int dsmClose(void)
*
* Description: Uninitializes Digital Sound Mixer
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmClose(void)
{
int error;
#if defined(__16__) & defined(__PROTMODE__)
/* Deallocate volume table segment descriptor: */
if ( (error = dpmiFreeDescriptor(dsmVolTableSeg)) != OK )
PASSERROR(ID_dsmClose);
#endif
/* Deallocate volume table and mixing buffer: */
if ( (error = memFree(dsmVolTableMem)) != OK )
PASSERROR(ID_dsmClose);
/* Deallocate sample structures: */
if ( (error = memFree(dsmSamples)) != OK )
PASSERROR(ID_dsmClose);
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetMixRate(unsigned *mixRate)
*
* Description: Reads the actual mixing rate
*
* Input: unsigned *mixRate pointer to mixing rate variable
*
* Returns: MIDAS error code.
* Mixing rate, in Hz, is stored in *mixRate
*
\****************************************************************************/
int CALLING dsmGetMixRate(unsigned *mixRate)
{
*mixRate = dsmMixRate;
return OK;
}
/****************************************************************************\
*
* Function: int dsmOpenChannels(unsigned channels)
*
* Description: Opens channels for output
*
* Input: unsigned channels number of channels to open
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmOpenChannels(unsigned channels)
{
int error;
dsmChPlay = 0; /* data on channels may not be
played */
dsmChOpen = channels;
dsmMuted = 0; /* not muted */
dsmPaused = 0; /* not paused */
/* Allocate memory for channel structures: */
if ( (error = memAlloc(channels * sizeof(dsmChannel), (void**)
&dsmChannels)) != OK )
PASSERROR(ID_dsmOpenChannels);
/* Set default amplification level and calculate volume table: */
if ( (error = dsmSetAmplification(64)) != OK )
PASSERROR(ID_dsmOpenChannels);
/* Clear all channels: */
if ( (error = dsmClearChannels()) != OK )
PASSERROR(ID_dsmOpenChannels);
dsmChPlay = 1; /* data on channels may now be
played */
return OK;
}
/****************************************************************************\
*
* Function: int dsmCalcVolTable(unsigned amplification)
*
* Description: Calculates a new volume table
*
* Input: unsigned amplification Amplification level. 64 - normal
* (100%), 32 = 50%, 128 = 200% etc.
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmCalcVolTable(unsigned amplification)
{
int volume, value;
long temp;
unsigned *tablePtr; /* current volume table position */
/* FIXME - does not support new fast stereo mixing */
tablePtr = dsmVolumeTable;
for ( volume = 0; volume < VOLLEVELS; volume++ )
{
for ( value = -128; value < 128; value++ )
{
/*!!*/
temp = ((long) (value * volume)) * ((long) amplification) / 64L;
temp = (temp * 256L / ((long)(VOLLEVELS-1)) / ((long) dsmChOpen))
>> (16 - dsmOutputBits);
*(tablePtr++) = (unsigned) (temp);
}
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmCloseChannels(void)
*
* Description: Closes open output channels
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmCloseChannels(void)
{
int error;
/* Check that channels have been opened: */
if ( dsmChOpen == 0 )
{
/* No open channels - return error: */
ERROR(errNoChannels, ID_dsmCloseChannels);
return errNoChannels;
}
dsmChPlay = 0; /* do not play data on channels */
/* Deallocate channel structures: */
if ( (error = memFree(dsmChannels)) != OK )
PASSERROR(ID_dsmCloseChannels)
dsmChOpen = 0; /* no open channels */
return OK;
}
/****************************************************************************\
*
* Function: int dsmClearChannels(void)
*
* Description: Clears open channels (removes all sounds)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmClearChannels(void)
{
unsigned i;
dsmChannel *chan;
/* Check that channels have been opened: */
if ( dsmChOpen == 0 )
{
/* No open channels - return error: */
ERROR(errNoChannels, ID_dsmCloseChannels);
return errNoChannels;
}
/* Remove sounds from channels: */
for ( i = 0; i < dsmChOpen; i++ )
{
chan = &dsmChannels[i];
chan->status = dsmChanStopped; /* playing is stopped */
chan->sampleHandle = 0; /* no sample selected */
chan->sampleChanged = 0; /* sample not changed */
chan->sampleType = smpNone; /* no sample */
chan->samplePos = sdSmpNone; /* no sample */
chan->rate = 0; /* no playing rate set */
chan->direction = 1; /* forward direction */
chan->panning = panMiddle; /* channel at middle */
chan->muted = 0; /* channel not muted */
chan->LoopCallback = NULL; /* no loop callback */
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmMute(int mute)
*
* Description: Mutes all channels
*
* Input: int mute 1 = mute, 0 = un-mute
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmMute(int mute)
{
dsmMuted = mute;
return OK;
}
/****************************************************************************\
*
* Function: int dsmPause(int pause)
*
* Description: Pauses or resumes playing
*
* Input: int pause 1 = pause, 0 = resume
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmPause(int pause)
{
dsmPaused = pause;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetMasterVolume(unsigned masterVolume)
*
* Description: Sets the master volume
*
* Input: unsigned masterVolume master volume (0 - 64)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetMasterVolume(unsigned masterVolume)
{
dsmMasterVolume = masterVolume;
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetMasterVolume(unsigned *masterVolume)
*
* Description: Reads the master volume
*
* Input: unsigned *masterVolume pointer to master volume
*
* Returns: MIDAS error code. Master volume is written to *masterVolume.
*
\****************************************************************************/
int CALLING dsmGetMasterVolume(unsigned *masterVolume)
{
*masterVolume = dsmMasterVolume;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetAmplification(unsigned amplification)
*
* Description: Sets amplification level and calculates new volume table.
*
* Input: unsigned amplification amplification level, 64 = normal
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetAmplification(unsigned amplification)
{
int error;
dsmAmplification = amplification;
if ( (error = dsmCalcVolTable(amplification)) != OK )
PASSERROR(ID_dsmSetAmplification)
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetAmplification(unsigned *amplification)
*
* Description: Reads the amplification level
*
* Input: unsigned *amplification pointer to amplification level
*
* Returns: MIDAS error code. Amplification level is written to
* *amplification.
*
\****************************************************************************/
int CALLING dsmGetAmplification(unsigned *amplification)
{
*amplification = dsmAmplification;
return OK;
}
/****************************************************************************\
*
* Function: int dsmPlaySound(unsigned channel, ulong rate)
*
* Description: Starts playing a sound
*
* Input: unsigned channel channel number
* ulong rate playing rate in Hz
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmPlaySound(unsigned channel, ulong rate)
{
int error;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmPlaySound);
return errInvalidChanNumber;
}
/* Playing sound: */
dsmChannels[channel].status = dsmChanPlaying;
/* Set playing rate: */
if ( (error = dsmSetRate(channel, rate)) != OK )
PASSERROR(ID_dsmPlaySound)
/* Set playing position to the beginning of the sample: */
if ( (error = dsmSetPosition(channel, 0)) != OK )
PASSERROR(ID_dsmPlaySound)
return OK;
}
/****************************************************************************\
*
* Function: int dsmReleaseSound(unsigned channel)
*
* Description: Releases the current sound from the channel. If sdLoop1Rel or
* sdLoop2 looping modes are used, playing will be continued from
* the release part of the current sample (data after the end
* of the first loop) after the end of the first loop is reached
* next time, otherwise the sound will be stopped.
*
* Input: unsigned channel channel number
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmReleaseSound(unsigned channel)
{
dsmChannel *chan;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmReleaseSound);
return errInvalidChanNumber;
}
chan = &dsmChannels[channel];
/* If no sound is being played in the channel do nothing: */
if ( chan->status == dsmChanPlaying )
{
if ( (chan->loopMode == sdLoop1Rel) || (chan->loopMode == sdLoop2) )
{
/* Release sound - continue from release portion of the sample: */
chan->status = dsmChanReleased;
}
else
{
/* One loop only or no looping - let playback continue normally */
}
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmStopSound(unsigned channel)
*
* Description: Stops playing a sound
*
* Input: unsigned channel channel number
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmStopSound(unsigned channel)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmStopSound);
return errInvalidChanNumber;
}
/* Stop sound: */
dsmChannels[channel].status = dsmChanStopped;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetRate(unsigned channel, ulong rate)
*
* Description: Sets the playing rate
*
* Input: unsigned channel channel number
* ulong rate playing rate in Hz
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetRate(unsigned channel, ulong rate)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetRate);
return errInvalidChanNumber;
}
/* Set the playing rate: */
dsmChannels[channel].rate = rate;
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetRate(unsigned channel, ulong *rate)
*
* Description: Reads the playing rate on a channel
*
* Input: unsigned channel channel number
* ulong *rate pointer to playing rate
*
* Returns: MIDAS error code. Playing rate is written to *rate, 0 if
* no sound is being played.
*
\****************************************************************************/
int CALLING dsmGetRate(unsigned channel, ulong *rate)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetRate);
return errInvalidChanNumber;
}
if ( (dsmChannels[channel].status == dsmChanStopped) ||
(dsmChannels[channel].status == dsmChanEnd) )
{
/* Nothing is being played - write 0 to *rate: */
*rate = 0;
}
else
{
/* Write the playing rate: */
*rate = dsmChannels[channel].rate;
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetVolume(unsigned channel, unsigned volume)
*
* Description: Sets the playing volume
*
* Input: unsigned channel channel number
* unsigned volume playing volume (0-64)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetVolume(unsigned channel, unsigned volume)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetVolume);
return errInvalidChanNumber;
}
/* Set the volume: */
dsmChannels[channel].volume = volume;
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetVolume(unsigned channel, unsigned *volume)
*
* Description: Reads the playing volume
*
* Input: unsigned channel channel number
* unsigned *volume pointer to volume
*
* Returns: MIDAS error code. Playing volume is written to *volume.
*
\****************************************************************************/
int CALLING dsmGetVolume(unsigned channel, unsigned *volume)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetVolume);
return errInvalidChanNumber;
}
/* Get the volume: */
*volume = dsmChannels[channel].volume;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetSample(unsigned channel, unsigned smpHandle)
*
* Description: Sets the sample number on a channel
*
* Input: unsigned channel channel number
* unsigned smpHandle sample handle returned by
* dsmAddSample()
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetSample(unsigned channel, unsigned smpHandle)
{
dsmChannel *chan;
dsmSample *sample;
int error;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetSample);
return errInvalidChanNumber;
}
/* Check that the sample handle is valid and the sample is in use: */
if ( (smpHandle > MAXSAMPLES) || (dsmSamples[smpHandle-1].inUse == 0) )
{
ERROR(errInvalidSampleHandle, ID_dsmSetSample);
return errInvalidSampleHandle;
}
chan = &dsmChannels[channel];
sample = &dsmSamples[smpHandle-1];
/* Set new sample number to channel: */
chan->sampleHandle = smpHandle;
/* Sample has been changed: */
chan->sampleChanged = 1;
/* If the new sample has one Amiga-compatible loop and playing has ended
(not released or stopped), set the new sample and start playing from
loop start: */
if ( (sample->loopMode == sdLoopAmiga) && (chan->status == dsmChanEnd) )
{
/* Set sample and start playing: */
chan->status = dsmChanPlaying;
if ( (error = dsmSetPosition(channel, sample->loop1Start)) != OK )
PASSERROR(ID_dsmSetSample)
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetSample(unsigned channel, unsigned *smpHandle)
*
* Description: Reads current sample handle
*
* Input: unsigned channel channel number
* unsigned *smpHandle pointer to sample handle
*
* Returns: MIDAS error code. Sample handle is written to *smpHandle;
*
\****************************************************************************/
int CALLING dsmGetSample(unsigned channel, unsigned *smpHandle)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetSample);
return errInvalidChanNumber;
}
/* Write sample handle to *smpHandle: */
*smpHandle = dsmChannels[channel].sampleHandle;
return OK;
}
/****************************************************************************\
*
* Function: int dsmChangeSample(unsigned channel)
*
* Description: Changes the sample used in a channel to the one specified
* by the channel's sample handle. Used only internally by
* other DSM functions, does no error checking.
*
* Input: unsigned channel channel number
*
* Returns: MIDAS error code (does not fail)
*
\****************************************************************************/
int CALLING dsmChangeSample(unsigned channel)
{
dsmChannel *chan = &dsmChannels[channel];
dsmSample *sample = &dsmSamples[chan->sampleHandle-1];
/* Start using the sample specified by chan->sampleHandle: */
chan->sample = sample->sample;
chan->sampleType = sample->sampleType;
chan->samplePos = sample->samplePos;
chan->sampleLength = sample->sampleLength;
chan->loopMode = sample->loopMode;
chan->loop1Start = sample->loop1Start;
chan->loop1End = sample->loop1End;
chan->loop1Type = sample->loop1Type;
chan->loop2Start = sample->loop2Start;
chan->loop2End = sample->loop2End;
chan->loop2Type = sample->loop2Type;
chan->sampleChanged = 0;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetPosition(unsigned channel, unsigned position)
*
* Description: Sets the playing position from the beginning of the sample
*
* Input: unsigned channel channel number
* unsigned position new playing position
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetPosition(unsigned channel, unsigned position)
{
dsmChannel *chan;
int error;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetPosition);
return errInvalidChanNumber;
}
chan = &dsmChannels[channel];
/* Convert position from bytes to samples: */
position = position >> dsmSampleShift(chan->sampleType);
/* Check if sample has been changed, and if so, set the values to the
channel structure: */
if ( chan->sampleChanged )
{
if ( (error = dsmChangeSample(channel)) != OK )
PASSERROR(ID_dsmSetPosition)
/* If channel status is released and the new channel does not have
two loops, end the sample: */
if ( (chan->loopMode != sdLoop1Rel) && (chan->loopMode != sdLoop2) &&
(chan->status == dsmChanReleased) )
{
chan->status = dsmChanEnd;
return OK;
}
}
/* Check that sample and playing rate have been set on the channel: */
if ( (chan->sampleHandle != 0) && (chan->rate != 0) )
{
switch ( chan->status )
{
case dsmChanEnd:
case dsmChanPlaying:
/* Either playing sample before releasing or playing has
ended - check the first loop type: */
chan->loopNum = 1;
switch ( chan->loop1Type )
{
case loopNone:
/* No looping - if position is below sample end, set
it and start playing there: */
if ( position < chan->sampleLength )
{
chan->playPos = position;
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
chan->direction = dsmPlayForward;
}
else
chan->status = dsmChanEnd;
break;
case loopUnidir:
/* Unidirectional looping - if position is below
loop end, set it, otherwise set loop start as the
new position. Start playing in any case: */
if ( position < chan->loop1End )
chan->playPos = position;
else
chan->playPos = chan->loop1Start;
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
chan->direction = dsmPlayForward;
break;
case loopBidir:
/* Bidirectional looping - if position is below loop
end, set it and start playing forward, otherwise
set loop end as the new position and start playing
backwards: */
if ( position < chan->loop1End )
{
chan->playPos = position;
chan->direction = dsmPlayForward;
}
else
{
chan->playPos = chan->loop1End;
chan->direction = dsmPlayBackwards;
}
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
}
break;
case dsmChanReleased:
/* Playing after sample has been released - check second loop
type: */
chan->loopNum = 2;
switch ( chan->loop2Type )
{
case loopNone:
/* No looping - if position is below sample end, set
it and start playing there: */
if ( position < chan->sampleLength )
{
chan->playPos = position;
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
chan->direction = dsmPlayForward;
}
else
chan->status = dsmChanEnd;
break;
case loopUnidir:
/* Unidirectional looping - if position is below
loop end, set it, otherwise set loop start as the
new position. Start playing in any case: */
if ( position < chan->loop2End )
chan->playPos = position;
else
chan->playPos = chan->loop2Start;
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
chan->direction = dsmPlayForward;
break;
case loopBidir:
/* Bidirectional looping - if position is below loop
end, set it and start playing forward, otherwise
set loop end as the new position and start playing
backwards: */
if ( position < chan->loop2End )
{
chan->playPos = position;
chan->direction = dsmPlayForward;
}
else
{
chan->playPos = chan->loop2End;
chan->direction = dsmPlayBackwards;
}
chan->playPosLow = 0;
chan->status = dsmChanPlaying;
}
break;
case dsmChanStopped:
default:
/* If sound has been stopped do nothing: */
break;
}
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetPosition(unsigned channel, unsigned *position)
*
* Description: Reads the current playing position
*
* Input: unsigned channel channel number
* unsigned *position pointer to playing position
*
* Returns: MIDAS error code. Playing position is written to *position.
*
\****************************************************************************/
int CALLING dsmGetPosition(unsigned channel, unsigned *position)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetPosition);
return errInvalidChanNumber;
}
/* Write position to *position and convert to bytes: */
*position = dsmChannels[channel].playPos << dsmSampleShift(
dsmChannels[channel].sampleType);;
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetDirection(unsigned channel, int *direction)
*
* Description: Reads current playing direction
*
* Input: unsigned channel channel number
* int *direction pointer to playing direction. 1 is
* forward, -1 backwards
*
* Returns: MIDAS error code. Playing direction is written to *direction.
*
\****************************************************************************/
int CALLING dsmGetDirection(unsigned channel, int *direction)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetDirection);
return errInvalidChanNumber;
}
/* Write position to *position: */
*direction = dsmChannels[channel].direction;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetPanning(unsigned channel, int panning)
*
* Description: Sets the panning position of a channel
*
* Input: unsigned channel channel number
* int panning panning position (see enum sdPanning)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetPanning(unsigned channel, int panning)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetPanning);
return errInvalidChanNumber;
}
/* Set panning position to channel: */
dsmChannels[channel].panning = panning;
return OK;
}
/****************************************************************************\
*
* Function: int dsmGetPanning(unsigned channel, int *panning)
*
* Description: Reads the panning position of a channel
*
* Input: unsigned channel channel number
* int *panning pointer to panning position
*
* Returns: MIDAS error code. Panning position is written to *panning.
*
\****************************************************************************/
int CALLING dsmGetPanning(unsigned channel, int *panning)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmGetPanning);
return errInvalidChanNumber;
}
/* Write panning position to *panning: */
*panning = dsmChannels[channel].panning;
return OK;
}
/****************************************************************************\
*
* Function: int dsmMuteChannel(unsigned channel, int mute)
*
* Description: Mutes/un-mutes a channel
*
* Input: unsigned channel channel number
* int mute muting status - 1 = mute, 0 = un-mute
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmMuteChannel(unsigned channel, int mute)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmMuteChannel);
return errInvalidChanNumber;
}
/* Set muting status: */
dsmChannels[channel].muted = mute;
return OK;
}
/****************************************************************************\
*
* Function: int dsmAddSample(sdSample *sample, int copySample,
* unsigned *smpHandle);
*
* Description: Adds a new sample to the DSM sample list and prepares it for
* DSM use
*
* Input: sdSample *sample pointer to sample information
* structure
* int copySample copy sample data to a new place in
* memory? 1 = yes, 0 = no
* unsigned *smpHandle pointer to sample handle
*
* Returns: MIDAS error code. Sample handle for the new sample is written
* to *smpHandle
*
* Notes: If copySample = 1, sample data must not be in EMS memory.
* If copySample = 0 and sample is 16-bit, the sample data WILL
* be modified by this function.
*
\****************************************************************************/
int CALLING dsmAddSample(sdSample *sample, int copySample, unsigned
*smpHandle)
{
int i, handle, error;
static void *copyDest;
dsmSample *dsmSmp;
unsigned destLength; /* destination sample length */
int smpShift;
/* Find first unused sample handle: */
handle = 0;
for ( i = 0; i < MAXSAMPLES; i++ )
{
if ( dsmSamples[i].inUse == 0 )
{
handle = i+1;
break;
}
}
/* Check if an empty handle was found. If not, return an error: */
if ( handle == 0 )
{
ERROR(errNoSampleHandles, ID_dsmAddSample);
return errNoSampleHandles;
}
/* Point dsmSmp to new sample: */
dsmSmp = &dsmSamples[handle-1];
/* Mark sample used: */
dsmSmp->inUse = 1;
smpShift = dsmSampleShift(sample->sampleType);
/* Copy sample information: */
dsmSmp->sampleType = sample->sampleType;
dsmSmp->sampleLength = sample->sampleLength >> smpShift;
dsmSmp->loopMode = sample->loopMode;
dsmSmp->loop1Start = sample->loop1Start >> smpShift;
dsmSmp->loop1End = sample->loop1End >> smpShift;
dsmSmp->loop1Type = sample->loop1Type;
dsmSmp->loop2Start = sample->loop2Start >> smpShift;
dsmSmp->loop2End = sample->loop2End >> smpShift;
dsmSmp->loop2Type = sample->loop2Type;
if ( (sample->sampleType == smpNone) || (sample->sampleLength == 0) ||
(sample->sample == NULL) || (sample->samplePos == sdSmpNone) )
{
/* There is no sample - set up DSM sample structure accordingly: */
dsmSmp->sampleType = smpNone;
dsmSmp->sampleLength = 0;
dsmSmp->sample = NULL;
dsmSmp->copied = 0;
dsmSmp->samplePos = sdSmpNone;
}
else
{
if ( copySample )
{
/* Sample data should be copied elsewhere in memory */
destLength = sample->sampleLength;
/* Allocate memory for sample: */
if ( (error = memAlloc(destLength, (void**) &dsmSmp->sample))
!= OK )
PASSERROR(ID_dsmAddSample)
copyDest = dsmSmp->sample;
/* Sample is in conventional memory: */
dsmSmp->samplePos = sdSmpConv;
/* Copy sample data: */
mMemCopy(copyDest, sample->sample, destLength);
/* Sample is copied and should be deallocated when removed: */
dsmSmp->copied = 1;
}
else
{
/* There is sample data, but it should not be copied - copy sample
pointer and position: */
dsmSmp->sample = sample->sample;
dsmSmp->samplePos = sample->samplePos;
dsmSmp->copied = 0;
}
}
/* Write sample handle to *smpHandle: */
*smpHandle = handle;
return OK;
}
/****************************************************************************\
*
* Function: int dsmRemoveSample(unsigned smpHandle)
*
* Description: Removes a sample from the sample list and deallocates it if
* necessary.
*
* Input: unsigned smpHandle sample handle returned by
* dsmAddSample()
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmRemoveSample(unsigned smpHandle)
{
dsmSample *sample;
int error;
/* Check that the sample handle is valid and the sample is in use: */
if ( (smpHandle > MAXSAMPLES) || (dsmSamples[smpHandle-1].inUse == 0) )
{
ERROR(errInvalidSampleHandle, ID_dsmRemoveSample);
return errInvalidSampleHandle;
}
sample = &dsmSamples[smpHandle-1];
/* Mark sample unused: */
sample->inUse = 0;
/* Check if sample data should be deallocated: */
if ( sample->copied )
{
if ( (error = memFree(sample->sample)) != OK )
PASSERROR(ID_dsmRemoveSample)
}
return OK;
}
/****************************************************************************\
*
* Function: int dsmMixData(unsigned numElems)
*
* Description: Mixes data to dsmMixBuffer.
*
* Input: unsigned numElems number of buffer elements to be mixed.
* In mono modes an "element" is an
* unsigned integer, and in stereo
* two.
*
* Returns: MIDAS error code. Mixed data is written to *dsmMixBuffer.
*
\****************************************************************************/
int CALLING dsmMixData(unsigned numElems)
{
int error;
unsigned ch;
unsigned volume;
dsmChannel *chan;
void *mixRoutine;
/* If playing is paused, no channels are open or data on channels may not
be used, just clear the buffer and exit: */
if ( dsmPaused || (dsmChOpen == 0) || (!dsmChPlay) )
{
if ( (error = dsmClearBuffer(numElems)) != OK )
PASSERROR(ID_dsmMixData)
return OK;
}
for ( ch = 0; ch < dsmChOpen; ch++ )
{
chan = &dsmChannels[ch];
/* Point mixRoutine to correct low-level mixing routine: */
mixRoutine = mixingRoutines[dsmMode][chan->sampleType];
/* If current channel is muted or DSM is muted, set channel volume to
zero, otherwise calculate it: */
if ( chan->muted || dsmMuted )
volume = 0;
else
volume = (chan->volume * dsmMasterVolume) / 64;
/* Mix data for this channel: */
if ( (error = dsmMix(ch, mixRoutine, volume, numElems)) != OK )
PASSERROR(ID_dsmMixData)
}
return OK;
}
#ifdef SUPPORTSTREAMS
/****************************************************************************\
*
* Function: int dsmStartStream(unsigned channel, uchar *buffer,
* unsigned bufferLength, int sampleType);
*
* Description: Starts playing a digital audio stream on a channel
*
* Input: unsigned channel channel number
* uchar *buffer pointer to stream buffer
* unsigned bufferLength buffer length in bytes
* int sampleType stream sample type
* ulong rate stream playing rate (in Hz)
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmStartStream(unsigned channel, uchar *buffer, unsigned
bufferLength, int sampleType, ulong rate)
{
dsmChannel *chan;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmStartStream);
return errInvalidChanNumber;
}
chan = &dsmChannels[channel];
/* Set up channel for playing the stream: */
chan->sample = buffer;
chan->sampleType = sampleType;
chan->samplePos = sdSmpConv;
chan->sampleLength = bufferLength >> dsmSampleShift(sampleType);
chan->loopMode = sdLoop1;
chan->loop1Start = 0;
chan->loop1End = bufferLength >> dsmSampleShift(sampleType);
chan->loop1Type = loopUnidir;
chan->loop2Start = chan->loop2End = 0;
chan->loop2Type = sdLoopNone;
chan->playPos = chan->playPosLow = 0;
chan->rate = rate;
chan->direction = dsmPlayForward;
chan->sampleHandle = DSM_SMP_STREAM; /* magic */
chan->sampleChanged = 0;
chan->panning = panMiddle;
chan->volume = 64;
chan->muted = 0;
chan->loopNum = 1;
chan->status = dsmChanPlaying;
chan->streamWritePos = 0;
return OK;
}
/****************************************************************************\
*
* Function: int dsmStopStream(unsigned channel);
*
* Description: Stops playing digital audio stream on a channel
*
* Input: unsigned channel channel number
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmStopStream(unsigned channel)
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmStopSound);
return errInvalidChanNumber;
}
/* Stop sound: */
dsmChannels[channel].status = dsmChanStopped;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetLoopCallback(unsigned channel,
* void (CALLING *callback)(unsigned channel));
*
* Description: Sets sample looping callback to a channel
*
* Input: unsigned channel channel number
* [..] *callback pointer to callback function, NULL to
* disable callback
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetLoopCallback(unsigned channel,
void (CALLING *callback)(unsigned channel))
{
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmStopSound);
return errInvalidChanNumber;
}
/* Set callback: */
dsmChannels[channel].LoopCallback = callback;
return OK;
}
/****************************************************************************\
*
* Function: int dsmSetStreamWritePosition(unsigned channel,
* unsigned position)
*
* Description: Sets the stream write position on a channel
*
* Input: unsigned channel channel number
* unsigned position new stream write position
*
* Returns: MIDAS error code
*
\****************************************************************************/
int CALLING dsmSetStreamWritePosition(unsigned channel, unsigned position)
{
dsmChannel *chan;
/* Check that the channel number is legal and channels are open: */
if ( channel >= dsmChOpen )
{
ERROR(errInvalidChanNumber, ID_dsmSetStreamWritePosition);
return errInvalidChanNumber;
}
chan = &dsmChannels[channel];
chan->streamWritePos = position >> dsmSampleShift(chan->sampleType);
return OK;
}
#endif /* #ifdef SUPPORTSTREAMS */
/*
* $Log: dsm.c,v $
* Revision 1.11 1997/01/16 18:41:59 pekangas
* Changed copyright messages to Housemarque
*
* Revision 1.10 1997/01/16 18:19:10 pekangas
* Added support for setting the stream write position.
* Stream data is no longer played past the write position
*
* Revision 1.9 1996/10/09 15:54:22 pekangas
* Fixed dsmReleaseSound() to work as specified
*
* Revision 1.8 1996/07/13 19:44:22 pekangas
* Eliminated Visual C warnings
*
* Revision 1.7 1996/07/13 18:40:48 pekangas
* Fixed to compile with Visual C
*
* Revision 1.6 1996/06/26 19:14:55 pekangas
* Added sample loop callbacks
*
* Revision 1.5 1996/05/30 21:10:27 pekangas
* Fixed a small bug in looping other samples than 8-bit mono
*
* Revision 1.4 1996/05/28 20:31:11 pekangas
* Added support for 8-bit stereo and 16-bit mono and stereo samples
*
* Revision 1.3 1996/05/26 20:55:39 pekangas
* Implemented digital audio stream support
*
* Revision 1.2 1996/05/24 16:19:39 jpaana
* Misc fixes for Linux
*
* Revision 1.1 1996/05/22 20:49:33 pekangas
* Initial revision
*
*/