ddnet/src/engine/client/sound.cpp

815 lines
17 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include <base/system.h>
2011-02-27 14:03:57 +00:00
#include <engine/graphics.h>
#include <engine/storage.h>
2010-05-29 07:25:38 +00:00
#include <engine/shared/config.h>
#include "SDL.h"
#include "sound.h"
extern "C" { // wavpack
#include <engine/external/wavpack/wavpack.h>
#include <opusfile.h>
2010-05-29 07:25:38 +00:00
}
#include <math.h>
enum
{
NUM_SAMPLES = 512,
2014-10-29 23:14:16 +00:00
NUM_VOICES = 256,
2010-05-29 07:25:38 +00:00
NUM_CHANNELS = 16,
};
struct CSample
{
short *m_pData;
int m_NumFrames;
int m_Rate;
int m_Channels;
int m_LoopStart;
int m_LoopEnd;
int m_PausedAt;
2010-05-29 07:25:38 +00:00
};
struct CChannel
{
int m_Vol;
int m_Pan;
} ;
struct CVoice
{
CSample *m_pSample;
CChannel *m_pChannel;
2014-10-12 14:12:13 +00:00
int m_Age; // increases when reused
2010-05-29 07:25:38 +00:00
int m_Tick;
int m_Vol; // 0 - 255
int m_Flags;
int m_X, m_Y;
} ;
static CSample m_aSamples[NUM_SAMPLES] = { {0} };
static CVoice m_aVoices[NUM_VOICES] = { {0} };
static CChannel m_aChannels[NUM_CHANNELS] = { {255, 0} };
static LOCK m_SoundLock = 0;
static int m_CenterX = 0;
static int m_CenterY = 0;
static int m_MixingRate = 48000;
static volatile int m_SoundVolume = 100;
static int m_NextVoice = 0;
static int *m_pMixBuffer = 0; // buffer only used by the thread callback function
static unsigned m_MaxFrames = 0;
2010-05-29 07:25:38 +00:00
2014-10-11 12:50:16 +00:00
static const void *ms_pWVBuffer = 0x0;
static int ms_WVBufferPosition = 0;
static int ms_WVBufferSize = 0;
const int DefaultDistance = 1500;
2010-05-29 07:25:38 +00:00
// TODO: there should be a faster way todo this
static short Int2Short(int i)
{
if(i > 0x7fff)
return 0x7fff;
else if(i < -0x7fff)
return -0x7fff;
return i;
}
static int IntAbs(int i)
{
if(i<0)
return -i;
return i;
}
static void Mix(short *pFinalOut, unsigned Frames)
{
int MasterVol;
mem_zero(m_pMixBuffer, m_MaxFrames*2*sizeof(int));
Frames = min(Frames, m_MaxFrames);
2010-05-29 07:25:38 +00:00
// aquire lock while we are mixing
lock_wait(m_SoundLock);
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
MasterVol = m_SoundVolume;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
for(unsigned i = 0; i < NUM_VOICES; i++)
{
if(m_aVoices[i].m_pSample)
{
// mix voice
CVoice *v = &m_aVoices[i];
int *pOut = m_pMixBuffer;
2010-05-29 07:25:38 +00:00
int Step = v->m_pSample->m_Channels; // setup input sources
short *pInL = &v->m_pSample->m_pData[v->m_Tick*Step];
short *pInR = &v->m_pSample->m_pData[v->m_Tick*Step+1];
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
unsigned End = v->m_pSample->m_NumFrames-v->m_Tick;
int Rvol = (int)(v->m_pChannel->m_Vol*(v->m_Vol/255.0f));
int Lvol = (int)(v->m_pChannel->m_Vol*(v->m_Vol/255.0f));
2010-05-29 07:25:38 +00:00
// make sure that we don't go outside the sound data
if(Frames < End)
End = Frames;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// check if we have a mono sound
if(v->m_pSample->m_Channels == 1)
pInR = pInL;
// volume calculation
if(v->m_Flags&ISound::FLAG_POS && v->m_pChannel->m_Pan)
{
// TODO: we should respect the channel panning value
int dx = v->m_X - m_CenterX;
int dy = v->m_Y - m_CenterY;
int Dist = (int)sqrtf((float)dx*dx+dy*dy); // float here. nasty
2010-05-29 07:25:38 +00:00
int p = IntAbs(dx);
2014-11-28 18:21:59 +00:00
int Range = DefaultDistance;
if(Dist >= 0 && Dist < Range)
2010-05-29 07:25:38 +00:00
{
// panning
if(dx > 0)
Lvol = ((Range-p)*Lvol)/Range;
else
Rvol = ((Range-p)*Rvol)/Range;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// falloff
Lvol = (Lvol*(Range-Dist))/Range;
Rvol = (Rvol*(Range-Dist))/Range;
}
else
{
Lvol = 0;
Rvol = 0;
}
}
// process all frames
for(unsigned s = 0; s < End; s++)
{
*pOut++ += (*pInL)*Lvol;
*pOut++ += (*pInR)*Rvol;
pInL += Step;
pInR += Step;
v->m_Tick++;
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// free voice if not used any more
if(v->m_Tick == v->m_pSample->m_NumFrames)
{
if(v->m_Flags&ISound::FLAG_LOOP)
v->m_Tick = 0;
else
2014-10-12 14:12:13 +00:00
{
v->m_pSample = 0;
2014-10-12 14:12:13 +00:00
v->m_Age++;
}
}
2010-05-29 07:25:38 +00:00
}
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// release the lock
lock_release(m_SoundLock);
{
// clamp accumulated values
// TODO: this seams slow
for(unsigned i = 0; i < Frames; i++)
{
int j = i<<1;
int vl = ((m_pMixBuffer[j]*MasterVol)/101)>>8;
int vr = ((m_pMixBuffer[j+1]*MasterVol)/101)>>8;
2010-05-29 07:25:38 +00:00
pFinalOut[j] = Int2Short(vl);
pFinalOut[j+1] = Int2Short(vr);
}
}
#if defined(CONF_ARCH_ENDIAN_BIG)
swap_endian(pFinalOut, sizeof(short), Frames * 2);
#endif
}
static void SdlCallback(void *pUnused, Uint8 *pStream, int Len)
{
(void)pUnused;
Mix((short *)pStream, Len/2/2);
}
int CSound::Init()
{
m_SoundEnabled = 0;
2010-05-29 07:25:38 +00:00
m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
2011-08-11 08:59:14 +00:00
2014-06-16 11:29:18 +00:00
SDL_AudioSpec Format, FormatOut;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
m_SoundLock = lock_create();
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
if(!g_Config.m_SndEnable)
return 0;
2011-08-11 08:59:14 +00:00
if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
{
dbg_msg("gfx", "unable to init SDL audio: %s", SDL_GetError());
return -1;
}
2010-05-29 07:25:38 +00:00
m_MixingRate = g_Config.m_SndRate;
// Set 16-bit stereo audio at 22Khz
Format.freq = g_Config.m_SndRate; // ignore_convention
Format.format = AUDIO_S16; // ignore_convention
Format.channels = 2; // ignore_convention
Format.samples = g_Config.m_SndBufferSize; // ignore_convention
Format.callback = SdlCallback; // ignore_convention
Format.userdata = NULL; // ignore_convention
// Open the audio device and start playing sound!
2014-06-16 11:29:18 +00:00
if(SDL_OpenAudio(&Format, &FormatOut) < 0)
2010-05-29 07:25:38 +00:00
{
dbg_msg("client/sound", "unable to open audio: %s", SDL_GetError());
return -1;
}
else
dbg_msg("client/sound", "sound init successful");
2014-06-16 11:29:18 +00:00
m_MaxFrames = FormatOut.samples*2;
m_pMixBuffer = (int *)mem_alloc(m_MaxFrames*2*sizeof(int), 1);
2010-05-29 07:25:38 +00:00
SDL_PauseAudio(0);
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
m_SoundEnabled = 1;
Update(); // update the volume
return 0;
}
int CSound::Update()
{
// update volume
int WantedVolume = g_Config.m_SndVolume;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
if(!m_pGraphics->WindowActive() && g_Config.m_SndNonactiveMute)
WantedVolume = 0;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
if(WantedVolume != m_SoundVolume)
{
lock_wait(m_SoundLock);
m_SoundVolume = WantedVolume;
lock_release(m_SoundLock);
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
return 0;
}
int CSound::Shutdown()
{
SDL_CloseAudio();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
2010-05-29 07:25:38 +00:00
lock_destroy(m_SoundLock);
if(m_pMixBuffer)
{
mem_free(m_pMixBuffer);
m_pMixBuffer = 0;
}
2010-05-29 07:25:38 +00:00
return 0;
}
int CSound::AllocID()
2010-05-29 07:25:38 +00:00
{
// TODO: linear search, get rid of it
for(unsigned SampleID = 0; SampleID < NUM_SAMPLES; SampleID++)
2010-05-29 07:25:38 +00:00
{
if(m_aSamples[SampleID].m_pData == 0x0)
return SampleID;
2010-05-29 07:25:38 +00:00
}
return -1;
}
void CSound::RateConvert(int SampleID)
2010-05-29 07:25:38 +00:00
{
CSample *pSample = &m_aSamples[SampleID];
2010-05-29 07:25:38 +00:00
int NumFrames = 0;
short *pNewData = 0;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// make sure that we need to convert this sound
if(!pSample->m_pData || pSample->m_Rate == m_MixingRate)
return;
// allocate new data
NumFrames = (int)((pSample->m_NumFrames/(float)pSample->m_Rate)*m_MixingRate);
pNewData = (short *)mem_alloc(NumFrames*pSample->m_Channels*sizeof(short), 1);
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
for(int i = 0; i < NumFrames; i++)
{
// resample TODO: this should be done better, like linear atleast
float a = i/(float)NumFrames;
int f = (int)(a*pSample->m_NumFrames);
if(f >= pSample->m_NumFrames)
f = pSample->m_NumFrames-1;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// set new data
if(pSample->m_Channels == 1)
pNewData[i] = pSample->m_pData[f];
else if(pSample->m_Channels == 2)
{
pNewData[i*2] = pSample->m_pData[f*2];
pNewData[i*2+1] = pSample->m_pData[f*2+1];
}
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// free old data and apply new
mem_free(pSample->m_pData);
pSample->m_pData = pNewData;
pSample->m_NumFrames = NumFrames;
2014-10-18 13:21:13 +00:00
pSample->m_Rate = m_MixingRate;
2010-05-29 07:25:38 +00:00
}
int CSound::ReadData(void *pBuffer, int Size)
{
2014-10-11 12:50:16 +00:00
int ChunkSize = min(Size, ms_WVBufferSize - ms_WVBufferPosition);
mem_copy(pBuffer, (const char *)ms_pWVBuffer + ms_WVBufferPosition, ChunkSize);
ms_WVBufferPosition += ChunkSize;
return ChunkSize;
2010-05-29 07:25:38 +00:00
}
int CSound::DecodeOpus(int SampleID, const void *pData, unsigned DataSize)
{
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
return -1;
CSample *pSample = &m_aSamples[SampleID];
OggOpusFile *OpusFile = op_open_memory((const unsigned char *) pData, DataSize, NULL);
if (OpusFile)
{
int NumChannels = op_channel_count(OpusFile, -1);
2014-10-28 13:01:21 +00:00
int NumSamples = op_pcm_total(OpusFile, -1); // per channel!
pSample->m_Channels = NumChannels;
if(pSample->m_Channels > 2)
{
dbg_msg("sound/opus", "file is not mono or stereo.");
return -1;
}
2014-10-28 13:01:21 +00:00
pSample->m_pData = (short *)mem_alloc(NumSamples * sizeof(short) * NumChannels, 1);
int Read;
int Pos = 0;
2014-10-28 13:01:21 +00:00
while (Pos < NumSamples)
{
2014-10-28 13:01:21 +00:00
Read = op_read(OpusFile, pSample->m_pData + Pos*NumChannels, NumSamples*NumChannels, NULL);
Pos += Read;
2014-10-28 13:01:21 +00:00
}
2014-10-28 13:01:21 +00:00
pSample->m_NumFrames = NumSamples; // ?
2014-10-28 11:22:49 +00:00
pSample->m_Rate = 48000;
pSample->m_LoopStart = -1;
pSample->m_LoopEnd = -1;
pSample->m_PausedAt = 0;
}
else
{
2014-10-28 21:42:31 +00:00
dbg_msg("sound/opus", "failed to decode sample");
return -1;
}
return SampleID;
}
2014-10-11 12:50:16 +00:00
int CSound::DecodeWV(int SampleID, const void *pData, unsigned DataSize)
2010-05-29 07:25:38 +00:00
{
2014-10-11 12:50:16 +00:00
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
2010-05-29 07:25:38 +00:00
return -1;
2014-10-11 12:50:16 +00:00
CSample *pSample = &m_aSamples[SampleID];
char aError[100];
WavpackContext *pContext;
2010-05-29 07:25:38 +00:00
2014-10-11 12:50:16 +00:00
ms_pWVBuffer = pData;
ms_WVBufferSize = DataSize;
ms_WVBufferPosition = 0;
2010-05-29 07:25:38 +00:00
pContext = WavpackOpenFileInput(ReadData, aError);
if (pContext)
{
2014-10-11 12:50:16 +00:00
int NumSamples = WavpackGetNumSamples(pContext);
2010-05-29 07:25:38 +00:00
int BitsPerSample = WavpackGetBitsPerSample(pContext);
unsigned int SampleRate = WavpackGetSampleRate(pContext);
2014-10-11 12:50:16 +00:00
int NumChannels = WavpackGetNumChannels(pContext);
2010-05-29 07:25:38 +00:00
int *pSrc;
short *pDst;
int i;
2014-10-11 12:50:16 +00:00
pSample->m_Channels = NumChannels;
2010-05-29 07:25:38 +00:00
pSample->m_Rate = SampleRate;
if(pSample->m_Channels > 2)
{
2014-10-11 12:50:16 +00:00
dbg_msg("sound/wv", "file is not mono or stereo.");
2010-05-29 07:25:38 +00:00
return -1;
}
if(BitsPerSample != 16)
{
2014-10-11 12:50:16 +00:00
dbg_msg("sound/wv", "bps is %d, not 16", BitsPerSample);
2010-05-29 07:25:38 +00:00
return -1;
}
2014-10-11 12:50:16 +00:00
int *pBuffer = (int *)mem_alloc(4*NumSamples*NumChannels, 1);
WavpackUnpackSamples(pContext, pBuffer, NumSamples); // TODO: check return value
pSrc = pBuffer;
2011-08-11 08:59:14 +00:00
2014-10-11 12:50:16 +00:00
pSample->m_pData = (short *)mem_alloc(2*NumSamples*NumChannels, 1);
2010-05-29 07:25:38 +00:00
pDst = pSample->m_pData;
2014-10-11 12:50:16 +00:00
for (i = 0; i < NumSamples*NumChannels; i++)
2010-05-29 07:25:38 +00:00
*pDst++ = (short)*pSrc++;
2014-10-11 12:50:16 +00:00
mem_free(pBuffer);
2010-05-29 07:25:38 +00:00
2014-10-11 12:50:16 +00:00
pSample->m_NumFrames = NumSamples;
2010-05-29 07:25:38 +00:00
pSample->m_LoopStart = -1;
pSample->m_LoopEnd = -1;
pSample->m_PausedAt = 0;
2010-05-29 07:25:38 +00:00
}
else
{
2014-10-11 12:50:16 +00:00
dbg_msg("sound/wv", "failed to decode sample (%s)", aError);
2014-10-28 21:42:31 +00:00
return -1;
2010-05-29 07:25:38 +00:00
}
2014-10-11 12:50:16 +00:00
return SampleID;
}
int CSound::LoadOpus(const char *pFilename)
{
// don't waste memory on sound when we are stress testing
if(g_Config.m_DbgStress)
return -1;
// no need to load sound when we are running with no sound
if(!m_SoundEnabled)
return -1;
if(!m_pStorage)
return -1;
ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL);
if(!ms_File)
{
dbg_msg("sound/opus", "failed to open file. filename='%s'", pFilename);
return -1;
}
int SampleID = AllocID();
if(SampleID < 0)
return -1;
// read the whole file into memory
int DataSize = io_length(ms_File);
if(DataSize <= 0)
{
io_close(ms_File);
dbg_msg("sound/opus", "failed to open file. filename='%s'", pFilename);
return -1;
}
char *pData = new char[DataSize];
io_read(ms_File, pData, DataSize);
SampleID = DecodeOpus(SampleID, pData, DataSize);
delete[] pData;
io_close(ms_File);
ms_File = NULL;
if(g_Config.m_Debug)
dbg_msg("sound/opus", "loaded %s", pFilename);
RateConvert(SampleID);
return SampleID;
}
2014-10-11 12:50:16 +00:00
int CSound::LoadWV(const char *pFilename)
{
// don't waste memory on sound when we are stress testing
if(g_Config.m_DbgStress)
return -1;
// no need to load sound when we are running with no sound
if(!m_SoundEnabled)
return -1;
if(!m_pStorage)
return -1;
ms_File = m_pStorage->OpenFile(pFilename, IOFLAG_READ, IStorage::TYPE_ALL);
if(!ms_File)
{
dbg_msg("sound/wv", "failed to open file. filename='%s'", pFilename);
return -1;
}
int SampleID = AllocID();
if(SampleID < 0)
return -1;
2014-10-24 21:03:16 +00:00
2014-10-11 12:50:16 +00:00
// read the whole file into memory
int DataSize = io_length(ms_File);
2014-10-24 21:03:16 +00:00
if(DataSize <= 0)
{
io_close(ms_File);
dbg_msg("sound/wv", "failed to open file. filename='%s'", pFilename);
return -1;
}
2014-10-11 12:50:16 +00:00
char *pData = new char[DataSize];
io_read(ms_File, pData, DataSize);
SampleID = DecodeWV(SampleID, pData, DataSize);
delete[] pData;
2010-05-29 07:25:38 +00:00
io_close(ms_File);
ms_File = NULL;
if(g_Config.m_Debug)
dbg_msg("sound/wv", "loaded %s", pFilename);
RateConvert(SampleID);
return SampleID;
2010-05-29 07:25:38 +00:00
}
int CSound::LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEditor = false)
{
// don't waste memory on sound when we are stress testing
if(g_Config.m_DbgStress)
return -1;
// no need to load sound when we are running with no sound
if(!m_SoundEnabled && !FromEditor)
return -1;
if(!pData)
return -1;
int SampleID = AllocID();
if(SampleID < 0)
return -1;
SampleID = DecodeOpus(SampleID, pData, DataSize);
RateConvert(SampleID);
return SampleID;
}
int CSound::LoadWVFromMem(const void *pData, unsigned DataSize, bool FromEditor = false)
2014-10-11 12:50:16 +00:00
{
// don't waste memory on sound when we are stress testing
if(g_Config.m_DbgStress)
return -1;
// no need to load sound when we are running with no sound
if(!m_SoundEnabled && !FromEditor)
2014-10-11 12:50:16 +00:00
return -1;
if(!pData)
return -1;
int SampleID = AllocID();
if(SampleID < 0)
return -1;
SampleID = DecodeWV(SampleID, pData, DataSize);
RateConvert(SampleID);
return SampleID;
}
2014-10-11 11:38:08 +00:00
void CSound::UnloadSample(int SampleID)
{
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
return;
Stop(SampleID);
mem_free(m_aSamples[SampleID].m_pData);
m_aSamples[SampleID].m_pData = 0x0;
}
float CSound::GetSampleDuration(int SampleID)
{
if(SampleID == -1 || SampleID >= NUM_SAMPLES)
return 0.0f;
return (m_aSamples[SampleID].m_NumFrames/m_aSamples[SampleID].m_Rate);
}
2010-05-29 07:25:38 +00:00
void CSound::SetListenerPos(float x, float y)
{
m_CenterX = (int)x;
m_CenterY = (int)y;
}
2014-10-12 14:12:13 +00:00
void CSound::SetVoiceVolume(CVoiceHandle Voice, float Volume)
{
2014-10-12 14:12:13 +00:00
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
Volume = clamp(Volume, 0.0f, 1.0f);
m_aVoices[VoiceID].m_Vol = (int)(Volume*255.0f);
}
2014-10-12 14:12:13 +00:00
void CSound::SetVoiceLocation(CVoiceHandle Voice, float x, float y)
{
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
m_aVoices[VoiceID].m_X = x;
m_aVoices[VoiceID].m_Y = y;
}
2014-10-18 13:21:13 +00:00
void CSound::SetVoiceTimeOffset(CVoiceHandle Voice, float offset)
{
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
lock_wait(m_SoundLock);
{
if(m_aVoices[VoiceID].m_pSample)
{
int Tick = 0;
bool IsLooping = m_aVoices[VoiceID].m_Flags&ISound::FLAG_LOOP;
uint64_t TickOffset = m_aVoices[VoiceID].m_pSample->m_Rate * offset;
if(m_aVoices[VoiceID].m_pSample->m_NumFrames > 0 && IsLooping)
Tick = TickOffset % m_aVoices[VoiceID].m_pSample->m_NumFrames;
2014-10-18 13:21:13 +00:00
else
Tick = clamp(TickOffset, (uint64_t)0, (uint64_t)m_aVoices[VoiceID].m_pSample->m_NumFrames);
2014-10-18 13:21:13 +00:00
// at least 200msec off, else depend on buffer size
float Threshold = max(0.2f * m_aVoices[VoiceID].m_pSample->m_Rate, (float)m_MaxFrames);
if(abs(m_aVoices[VoiceID].m_Tick-Tick) > Threshold)
2014-11-14 23:13:20 +00:00
{
// take care of looping (modulo!)
if( !(IsLooping && (min(m_aVoices[VoiceID].m_Tick, Tick) + m_aVoices[VoiceID].m_pSample->m_NumFrames - max(m_aVoices[VoiceID].m_Tick, Tick)) <= Threshold))
{
m_aVoices[VoiceID].m_Tick = Tick;
}
2014-11-14 23:13:20 +00:00
}
2014-10-18 13:21:13 +00:00
}
}
lock_release(m_SoundLock);
}
void CSound::SetChannel(int ChannelID, float Vol, float Pan)
2010-05-29 07:25:38 +00:00
{
m_aChannels[ChannelID].m_Vol = (int)(Vol*255.0f);
m_aChannels[ChannelID].m_Pan = (int)(Pan*255.0f); // TODO: this is only on and off right now
2010-05-29 07:25:38 +00:00
}
2014-10-12 14:12:13 +00:00
ISound::CVoiceHandle CSound::Play(int ChannelID, int SampleID, int Flags, float x, float y)
2010-05-29 07:25:38 +00:00
{
int VoiceID = -1;
2014-10-12 14:12:13 +00:00
int Age = -1;
2010-05-29 07:25:38 +00:00
int i;
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
lock_wait(m_SoundLock);
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// search for voice
for(i = 0; i < NUM_VOICES; i++)
{
int id = (m_NextVoice + i) % NUM_VOICES;
if(!m_aVoices[id].m_pSample)
{
VoiceID = id;
2010-05-29 07:25:38 +00:00
m_NextVoice = id+1;
break;
}
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
// voice found, use it
if(VoiceID != -1)
2010-05-29 07:25:38 +00:00
{
m_aVoices[VoiceID].m_pSample = &m_aSamples[SampleID];
m_aVoices[VoiceID].m_pChannel = &m_aChannels[ChannelID];
if(Flags & FLAG_LOOP)
m_aVoices[VoiceID].m_Tick = m_aSamples[SampleID].m_PausedAt;
else
m_aVoices[VoiceID].m_Tick = 0;
m_aVoices[VoiceID].m_Vol = 255;
m_aVoices[VoiceID].m_Flags = Flags;
m_aVoices[VoiceID].m_X = (int)x;
m_aVoices[VoiceID].m_Y = (int)y;
2014-10-12 14:12:13 +00:00
Age = m_aVoices[VoiceID].m_Age;
2010-05-29 07:25:38 +00:00
}
2011-08-11 08:59:14 +00:00
2010-05-29 07:25:38 +00:00
lock_release(m_SoundLock);
2014-10-12 14:12:13 +00:00
return CreateVoiceHandle(VoiceID, Age);
2010-05-29 07:25:38 +00:00
}
2014-10-12 14:12:13 +00:00
ISound::CVoiceHandle CSound::PlayAt(int ChannelID, int SampleID, int Flags, float x, float y)
2010-05-29 07:25:38 +00:00
{
return Play(ChannelID, SampleID, Flags|ISound::FLAG_POS, x, y);
2010-05-29 07:25:38 +00:00
}
2014-10-12 14:12:13 +00:00
ISound::CVoiceHandle CSound::Play(int ChannelID, int SampleID, int Flags)
2010-05-29 07:25:38 +00:00
{
return Play(ChannelID, SampleID, Flags, 0, 0);
2010-05-29 07:25:38 +00:00
}
void CSound::Stop(int SampleID)
2010-05-29 07:25:38 +00:00
{
// TODO: a nice fade out
lock_wait(m_SoundLock);
CSample *pSample = &m_aSamples[SampleID];
for(int i = 0; i < NUM_VOICES; i++)
{
if(m_aVoices[i].m_pSample == pSample)
{
if(m_aVoices[i].m_Flags & FLAG_LOOP)
m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick;
else
m_aVoices[i].m_pSample->m_PausedAt = 0;
m_aVoices[i].m_pSample = 0;
}
}
2010-05-29 07:25:38 +00:00
lock_release(m_SoundLock);
}
void CSound::StopAll()
{
// TODO: a nice fade out
lock_wait(m_SoundLock);
for(int i = 0; i < NUM_VOICES; i++)
{
if(m_aVoices[i].m_pSample)
{
if(m_aVoices[i].m_Flags & FLAG_LOOP)
m_aVoices[i].m_pSample->m_PausedAt = m_aVoices[i].m_Tick;
else
m_aVoices[i].m_pSample->m_PausedAt = 0;
}
2010-05-29 07:25:38 +00:00
m_aVoices[i].m_pSample = 0;
}
lock_release(m_SoundLock);
}
2014-10-18 13:21:13 +00:00
void CSound::StopVoice(CVoiceHandle Voice)
{
if(!Voice.IsValid())
return;
int VoiceID = Voice.Id();
if(m_aVoices[VoiceID].m_Age != Voice.Age())
return;
lock_wait(m_SoundLock);
{
m_aVoices[VoiceID].m_pSample = 0;
m_aVoices[VoiceID].m_Age++;
}
lock_release(m_SoundLock);
}
2010-05-29 07:25:38 +00:00
IOHANDLE CSound::ms_File = 0;
IEngineSound *CreateEngineSound() { return new CSound; }