Descent3/libmve/lnxdsound.cpp
Ryan C. Gordon 65b1a7dc3b
sdl2: Fix broken audio in MVE playback.
This doesn't fix the audio gaps, just the static introduced in the SDL2 port.

SDL2 does not initialize the audio callback's buffer, unlike SDL 1.2, under
the assumption the callback is going to fully write it anyhow. But since
the movie player wants to mix against the current contents of the buffer,
we need to explicitly initialize it to silence first.
2024-05-05 10:14:50 -04:00

789 lines
21 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <memory.h>
#include "lnxdsound.h"
#include "args.h"
#include "SDL.h"
#include "SDL_audio.h"
#include <sched.h>
#define FRAGMENT_LENGTH (LnxBuffers[0]->bps >> 4)
#define FREQUENCY_SHIFT (14)
/*
* TODO:
* * Might be wise to use mutex's for the enter/exit critical functions
*/
static int LnxNumBuffers = 0;
static LnxSoundBuffer **LnxBuffers = NULL;
static LnxSoundDevice LinuxSoundDevice;
static bool StartupSoundSystem(LnxSoundDevice *dev);
static void ShutdownSoundSystem(void);
static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb, unsigned char *buf, unsigned int len);
static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb, unsigned char *buf, unsigned int len);
static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len);
static void LinuxSoundMixBuffersIntoMain(int len);
static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len);
static inline void enter_critical(void) { SDL_LockAudio(); }
static inline void exit_critical(void) { SDL_UnlockAudio(); }
///////////////////////////////
// LnxSound_CreateSoundBuffer
///////////////////////////////
// Creates a sound buffer to be used with mixing and output.
//
// Returns:
// -1 : Invalid Parameter
// -2 : Out of memory
// 0 : Ok!
int LnxSound_CreateSoundBuffer(LnxSoundDevice *dev, LnxBufferDesc *lbdesc, LnxSoundBuffer **lsndb) {
WAVEFORMATEX *wfex;
if (!lbdesc || !lsndb || !dev)
return -1;
wfex = lbdesc->lpwfxFormat;
if (!wfex)
return -1;
// Check to see if we have a primary buffer yet, if not, create it
// now
if (lbdesc->dwFlags & LNXSND_CAPS_PRIMARYBUFFER) {
if (LnxNumBuffers != 0)
return -1;
} else {
if (LnxNumBuffers == 0) {
// we need to create a primary buffer
LnxSoundBuffer *primary;
LnxBufferDesc primdesc;
WAVEFORMATEX wf;
memset(&primdesc, 0, sizeof(LnxBufferDesc));
memset(&wf, 0, sizeof(wf));
primdesc.dwBufferBytes = 0;
primdesc.dwFlags = LNXSND_CAPS_PRIMARYBUFFER;
primdesc.lpwfxFormat = &wf;
int ret = LnxSound_CreateSoundBuffer(dev, &primdesc, &primary);
if (ret != 0)
return ret;
}
}
*lsndb = (LnxSoundBuffer *)malloc(sizeof(LnxSoundBuffer));
if (!(*lsndb))
return -2;
memset(*lsndb, 0, sizeof(LnxSoundBuffer));
if (lbdesc->dwFlags & LNXSND_CAPS_PRIMARYBUFFER) {
(*lsndb)->buffer_len = dev->bps;
(*lsndb)->freq = dev->freq;
(*lsndb)->bps = dev->bps;
(*lsndb)->buffer = NULL;
} else {
(*lsndb)->buffer_len = lbdesc->dwBufferBytes;
(*lsndb)->freq = lbdesc->lpwfxFormat->nSamplesPerSec;
(*lsndb)->buffer = (unsigned char *)malloc((*lsndb)->buffer_len);
if (!(*lsndb)->buffer) {
free(*lsndb);
*lsndb = NULL;
return -2;
}
memset((*lsndb)->buffer, 0, (*lsndb)->buffer_len);
}
(*lsndb)->play_cursor = 0;
(*lsndb)->write_cursor = 0;
(*lsndb)->playing = 0;
(*lsndb)->left_vol = (1 << 15);
(*lsndb)->right_vol = (1 << 15);
if (!(lbdesc->dwFlags & LNXSND_CAPS_PRIMARYBUFFER)) {
(*lsndb)->freq_adjustment = ((*lsndb)->freq << FREQUENCY_SHIFT) / LnxBuffers[0]->freq;
(*lsndb)->bps = (*lsndb)->freq * lbdesc->lpwfxFormat->nBlockAlign;
}
memcpy(&((*lsndb)->lbdesc), lbdesc, sizeof(LnxBufferDesc));
if (!(lbdesc->dwFlags & LNXSND_CAPS_PRIMARYBUFFER)) {
memcpy(&((*lsndb)->wfx), lbdesc->lpwfxFormat, sizeof(WAVEFORMATEX));
} else {
// set up the wave format based on the device settings (primary)
(*lsndb)->wfx.wFormatTag = WAVE_FORMAT_PCM;
(*lsndb)->wfx.nChannels = dev->channels;
(*lsndb)->wfx.nSamplesPerSec = dev->freq;
(*lsndb)->wfx.nBlockAlign = dev->channels * (dev->bit_depth / 8);
(*lsndb)->wfx.nAvgBytesPerSec = dev->freq * (*lsndb)->wfx.nBlockAlign;
(*lsndb)->wfx.wBitsPerSample = dev->bit_depth;
(*lsndb)->wfx.cbSize = 0;
}
if (LnxBuffers) {
enter_critical();
LnxBuffers = (LnxSoundBuffer **)realloc(LnxBuffers, sizeof(LnxSoundBuffer *) * (LnxNumBuffers + 1));
LnxBuffers[LnxNumBuffers] = *lsndb;
LnxNumBuffers++;
exit_critical();
} else {
LnxBuffers = (LnxSoundBuffer **)malloc(sizeof(LnxSoundBuffer *));
LnxBuffers[0] = *lsndb;
LnxNumBuffers++;
// Initialize the Sound system and thread
StartupSoundSystem(dev);
}
return 0;
}
////////////////////////////
// LnxSoundBuffer_Release
////////////////////////////
// Releases the memory associated with a sound buffer. This pointer is
// no longer valid after return.
//
// Returns:
// -1 : Invalid Parameter
// 0 : Ok!
int LnxSoundBuffer_Release(LnxSoundBuffer *buff) {
int i;
if (!buff)
return -1;
for (i = 0; i < LnxNumBuffers; i++) {
if (LnxBuffers[i] == buff)
break;
}
if (i < LnxNumBuffers) {
if (LnxNumBuffers == 1) {
// stop the thread! primary going down
ShutdownSoundSystem();
LnxNumBuffers = 0;
LnxBuffers = NULL;
} else {
// wait until it is ok (our thread is in a good position)
enter_critical();
if (i == 0) {
// can't delete the primary! whats going on here?
return -1;
}
LnxBuffers[i] = LnxBuffers[LnxNumBuffers - 1];
LnxBuffers = (LnxSoundBuffer **)realloc(LnxBuffers, sizeof(LnxSoundBuffer *) * (LnxNumBuffers - 1));
LnxNumBuffers--;
exit_critical();
}
if (buff->buffer)
free(buff->buffer);
free(buff);
} else
return -1;
if (LnxNumBuffers == 1) {
// we freed the last non-primary buffer
// so remove the primary buffer that is remaining
return LnxSoundBuffer_Release(LnxBuffers[0]);
}
return 0;
}
//////////////////////////////
// LnxSoundBuffer_SetVolume
//////////////////////////////
// Sets the volume of a buffer.
//
// Returns:
// 0 : no error
// -1 : Cannot set volume
// -2 : Invalid parameters
int LnxSoundBuffer_SetVolume(LnxSoundBuffer *buff, signed long vol) {
if (!buff)
return -1;
if (!(buff->lbdesc.dwFlags & LNXSND_CAPS_CTRLVOLUME))
return -1;
if ((vol > LNXSND_VOLUME_MAX) || (vol < LNXSND_VOLUME_MIN))
return -2;
if (buff->lbdesc.dwFlags & LNXSND_CAPS_PRIMARYBUFFER) {
// not supported
enter_critical();
buff->volume = vol;
exit_critical();
return 0;
}
enter_critical();
buff->volume = vol;
double vt;
vt = (double)(buff->volume - (buff->pan > 0 ? buff->pan : 0));
buff->left_vol = (unsigned long)(pow(2.0, vt / 600.0) * 32768.0);
vt = (double)(buff->volume + (buff->pan < 0 ? buff->pan : 0));
buff->right_vol = (unsigned long)(pow(2.0, vt / 600.0) * 32768.0);
exit_critical();
return 0;
}
///////////////////////////
// LnxSoundBuffer_SetPan
///////////////////////////
// Sets the pan of a buffer.
//
// Returns:
// 0 : no error
// -1 : Cannot set pan
// -2 : Invalid parameters
int LnxSoundBuffer_SetPan(LnxSoundBuffer *buff, signed long pan) {
if (!buff)
return -1;
if ((pan > LNXSND_PAN_RIGHT) || (pan < LNXSND_PAN_LEFT))
return -2;
if (!(buff->lbdesc.dwFlags & LNXSND_CAPS_CTRLPAN) || (buff->lbdesc.dwFlags & LNXSND_CAPS_PRIMARYBUFFER)) {
return -1;
}
enter_critical();
buff->pan = pan;
double pt;
pt = (double)(buff->volume - (buff->pan > 0 ? buff->pan : 0));
buff->left_vol = (unsigned long)(pow(2.0, pt / 600.0) * 32768.0);
pt = (double)(buff->volume + (buff->pan < 0 ? buff->pan : 0));
buff->right_vol = (unsigned long)(pow(2.0, pt / 600.0) * 32768.0);
exit_critical();
return 0;
}
/////////////////////////
// LnxSoundBuffer_Stop
/////////////////////////
// Stops a buffer from playing
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_Stop(LnxSoundBuffer *buff) {
if (!buff)
return -1;
enter_critical();
buff->playing = 0;
exit_critical();
return 0;
}
/////////////////////////
// LnxSoundBuffer_Play
/////////////////////////
// Starts a buffer playing (or changes the flags for a buffer currently
// playing).
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_Play(LnxSoundBuffer *buff, unsigned int flags) {
if (!buff)
return -1;
enter_critical();
buff->flags = flags;
buff->playing = 1;
exit_critical();
return 0;
}
////////////////////////////
// LnxSoundBuffer_GetCaps
////////////////////////////
// Starts a buffer playing (or changes the flags for a buffer currently
// playing).
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_GetCaps(LnxSoundBuffer *buff, LinuxSoundCaps *caps) {
if (!caps || !buff)
return -1;
caps->dwFlags = buff->lbdesc.dwFlags | LNXSND_CAPS_LOCSOFTWARE;
caps->dwBufferBytes = buff->lbdesc.dwBufferBytes;
return 0;
}
//////////////////////////////
// LnxSoundBuffer_GetStatus
//////////////////////////////
// Returns the status of a buffer
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_GetStatus(LnxSoundBuffer *buff, unsigned int *status) {
if (!status || !buff)
return -1;
*status = 0;
if (buff->playing)
*status |= LNXSND_PLAYING;
if (buff->flags & LNXSND_LOOPING)
*status |= LNXSND_LOOPING;
return 0;
}
///////////////////////////////////////
// LnxSoundBuffer_GetCurrentPosition
///////////////////////////////////////
// Returns the current play and write positions of the buffer
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_GetCurrentPosition(LnxSoundBuffer *buff, unsigned int *ppos, unsigned int *wpos) {
if (!buff)
return -1;
if (ppos)
*ppos = buff->play_cursor;
if (wpos)
*wpos = buff->write_cursor;
return 0;
}
///////////////////////////////////////
// LnxSoundBuffer_SetCurrentPosition
///////////////////////////////////////
// Sets the current play position of the buffer
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_SetCurrentPosition(LnxSoundBuffer *buff, unsigned int pos) {
if (!buff)
return -1;
enter_critical();
buff->play_cursor = pos;
exit_critical();
return 0;
}
/////////////////////////
// LnxSoundBuffer_Lock
/////////////////////////
// Locks the given buffer, returning pointer(s) to the buffer(s) along with
// available the size of the buffer(s) for writing.
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_Lock(LnxSoundBuffer *buff, unsigned int pos, unsigned int numbytes, void **ptr1,
unsigned int *numbytes1, void **ptr2, unsigned int *numbytes2, unsigned int flags) {
if (!buff)
return -1;
if (flags & LNXSND_LOCK_FROMWRITECURSOR)
pos += buff->write_cursor;
if (flags & LNXSND_LOCK_ENTIREBUFFER)
numbytes = buff->buffer_len;
if (numbytes > buff->buffer_len)
numbytes = buff->buffer_len;
assert(numbytes1 != numbytes2);
assert(ptr1 != ptr2);
if (pos + numbytes <= buff->buffer_len) {
*(unsigned char **)ptr1 = buff->buffer + pos;
*numbytes1 = numbytes;
if (ptr2)
*(unsigned char **)ptr2 = NULL;
if (numbytes2)
*numbytes2 = 0;
} else {
*(unsigned char **)ptr1 = buff->buffer + pos;
*numbytes1 = buff->buffer_len - pos;
if (ptr2)
*(unsigned char **)ptr2 = buff->buffer;
if (numbytes2)
*numbytes2 = numbytes - (buff->buffer_len - pos);
}
return 0;
}
///////////////////////////
// LnxSoundBuffer_Unlock
///////////////////////////
// Unlocks a buffer.
//
// Returns:
// 0 : no error
// -1 : invalid parameters
int LnxSoundBuffer_Unlock(LnxSoundBuffer *buff, void *ptr1, unsigned int num1, void *ptr2, unsigned int num2) {
if (!buff)
return -1;
return 0;
}
///////////////////////////////////////////
// Internal Sound System routines
//////////////////////////////////////////////////////////////
// Starts up the sound processing thread
static bool StartupSoundSystem(LnxSoundDevice *dev) {
SDL_AudioSpec spec;
if (LnxNumBuffers < 1)
return false;
const int kDefaultSampleCount = 1024;
int sampleCount = kDefaultSampleCount;
int sampleArgIndex = FindArg("-sdlSndSizeMovie");
if (sampleArgIndex == 0) {
sampleArgIndex = FindArg("-sdlSndSize");
}
if (sampleArgIndex != 0) {
const char *sampleCountStr = GetArg(sampleArgIndex + 1);
if (sampleCountStr) {
sampleCount = atoi(sampleCountStr);
if (sampleCount <= 0) {
sampleCount = kDefaultSampleCount;
}
}
}
memcpy(&LinuxSoundDevice, dev, sizeof(LnxSoundDevice));
spec.freq = dev->freq;
spec.format = dev->bit_depth == 8 ? AUDIO_U8 : AUDIO_S16SYS;
spec.channels = dev->channels;
spec.samples = sampleCount;
spec.callback = LinuxSoundThreadHandler;
if (SDL_OpenAudio(&spec, NULL) < 0) {
return false;
}
SDL_PauseAudio(0);
return true;
}
// Shutsdown the sound processing thread
static void ShutdownSoundSystem(void) { SDL_CloseAudio(); }
static inline void GetValues(const LnxSoundBuffer *dsb, unsigned char *buf, unsigned int *fl, unsigned int *fr) {
signed short *bufs = (signed short *)buf;
// 8 bit stereo
if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
*fl = (*buf - 128) << 8;
*fr = (*(buf + 1) - 128) << 8;
return;
}
// 16 bit stereo
if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
*fl = *bufs;
*fr = *(bufs + 1);
return;
}
// 8 bit mono
if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
*fl = (*buf - 128) << 8;
*fr = *fl;
return;
}
// 16 bit mono
if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
*fl = *bufs;
*fr = *bufs;
return;
}
return;
}
static inline void SetValues(unsigned char *buf, unsigned int fl, unsigned int fr) {
signed short *bufs = (signed short *)buf;
// 8 bit stereo
if ((LnxBuffers[0]->wfx.wBitsPerSample == 8) && (LnxBuffers[0]->wfx.nChannels == 2)) {
*buf = (fl + 32768) >> 8;
*(buf + 1) = (fr + 32768) >> 8;
return;
}
// 16 bit stereo
if ((LnxBuffers[0]->wfx.wBitsPerSample == 16) && (LnxBuffers[0]->wfx.nChannels == 2)) {
*bufs = fl;
*(bufs + 1) = fr;
return;
}
// 8 bit mono
if ((LnxBuffers[0]->wfx.wBitsPerSample == 8) && (LnxBuffers[0]->wfx.nChannels == 1)) {
*buf = (((fl + fr) >> 1) + 32768) >> 8;
return;
}
// 16 bit mono
if ((LnxBuffers[0]->wfx.wBitsPerSample == 16) && (LnxBuffers[0]->wfx.nChannels == 1)) {
*bufs = (fl + fr) >> 1;
return;
}
return;
}
static void LinuxSoundMixWithVolume(LnxSoundBuffer *dsb, unsigned char *buf, unsigned int len) {
unsigned int i, inc = (LnxBuffers[0]->wfx.wBitsPerSample >> 3);
unsigned char *bpc = buf;
signed short *bps = (signed short *)buf;
if ((!(dsb->lbdesc.dwFlags & LNXSND_CAPS_CTRLPAN) || (dsb->pan == 0)) &&
(!(dsb->lbdesc.dwFlags & LNXSND_CAPS_CTRLVOLUME) || (dsb->volume == 0)))
return;
for (i = 0; i < len; i += inc) {
unsigned int val;
switch (inc) {
case 1: {
val = *bpc - 128;
val = ((val * (i & inc ? dsb->right_vol : dsb->left_vol)) >> 15);
*bpc = val + 128;
bpc++;
} break;
case 2: {
val = *bps;
val = ((val * ((i & inc) ? dsb->right_vol : dsb->left_vol)) >> 15);
*bps = val;
bps++;
} break;
}
}
}
static unsigned int LinuxSoundMixNormalize(LnxSoundBuffer *dsb, unsigned char *buf, unsigned int len) {
unsigned int i, size, ipos, ilen, fieldL = 0, fieldR = 0;
unsigned char *ibp, *obp;
unsigned int iAdvance = dsb->wfx.nBlockAlign;
unsigned int oAdvance = LnxBuffers[0]->wfx.nBlockAlign;
ibp = dsb->buffer + dsb->play_cursor;
obp = buf;
if ((dsb->freq == LnxBuffers[0]->wfx.nSamplesPerSec) &&
(dsb->wfx.wBitsPerSample == LnxBuffers[0]->wfx.wBitsPerSample) &&
(dsb->wfx.nChannels == LnxBuffers[0]->wfx.nChannels)) {
if ((ibp + len) < (unsigned char *)(dsb->buffer + dsb->buffer_len))
memcpy(obp, ibp, len);
else {
memcpy(obp, ibp, dsb->buffer_len - dsb->play_cursor);
memcpy(obp + (dsb->buffer_len - dsb->play_cursor), dsb->buffer, len - (dsb->buffer_len - dsb->play_cursor));
}
return len;
}
if (dsb->freq == LnxBuffers[0]->wfx.nSamplesPerSec) {
ilen = 0;
for (i = 0; i < len; i += oAdvance) {
GetValues(dsb, ibp, &fieldL, &fieldR);
ibp += iAdvance;
ilen += iAdvance;
SetValues(obp, fieldL, fieldR);
obp += oAdvance;
if (ibp >= (unsigned char *)(dsb->buffer + dsb->buffer_len))
ibp = dsb->buffer;
}
return (ilen);
}
size = len / oAdvance;
ilen = ((size * dsb->freq_adjustment) >> FREQUENCY_SHIFT) * iAdvance;
for (i = 0; i < size; i++) {
ipos = (((i * dsb->freq_adjustment) >> FREQUENCY_SHIFT) * iAdvance) + dsb->play_cursor;
if (ipos >= dsb->buffer_len)
ipos %= dsb->buffer_len;
GetValues(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
SetValues(obp, fieldL, fieldR);
obp += oAdvance;
}
return ilen;
}
int DoMulDiv(int nNumber, int nNumerator, int nDenominator) {
if (!nDenominator)
return -1;
long long ret;
ret = (((long long)nNumber * nNumerator) + (nDenominator / 2)) / nDenominator;
if ((ret > 0x7FFFFFFF) || (ret < 0xFFFFFFFF))
return -1;
return ret;
}
static void *TempSoundBuffer = NULL;
static int TempSoundBufferLen = 0;
static unsigned int LinuxSoundMixInMainBuffer(LnxSoundBuffer *dsb, int len) {
unsigned int i, ilen, advance = (LnxBuffers[0]->wfx.wBitsPerSample >> 3);
unsigned char *buf, *ibuf, *obuf;
signed int temp, field;
signed short *ibufs, *obufs;
if (!(dsb->flags & LNXSND_LOOPING)) {
temp = DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec, dsb->buffer_len, dsb->bps) -
DoMulDiv(LnxBuffers[0]->wfx.nAvgBytesPerSec, dsb->play_cursor, dsb->bps);
len = (len > temp) ? temp : len;
}
len &= ~3; // align to 4 byte boundary
if (!len) {
dsb->playing = 0;
dsb->write_cursor = 0;
dsb->play_cursor = 0;
return 0;
}
if (len > TempSoundBufferLen) {
void *nb = realloc(TempSoundBuffer, len);
if (nb) {
TempSoundBuffer = nb;
TempSoundBufferLen = len;
buf = ibuf = (unsigned char *)nb;
} else {
return 0;
}
} else {
buf = ibuf = (unsigned char *)TempSoundBuffer;
}
ilen = LinuxSoundMixNormalize(dsb, ibuf, len);
if ((dsb->lbdesc.dwFlags & LNXSND_CAPS_CTRLPAN) || (dsb->lbdesc.dwFlags & LNXSND_CAPS_CTRLVOLUME)) {
LinuxSoundMixWithVolume(dsb, ibuf, len);
}
obuf = LnxBuffers[0]->buffer + LnxBuffers[0]->play_cursor;
for (i = 0; i < len; i += advance) {
obufs = (signed short *)obuf;
ibufs = (signed short *)ibuf;
if (LnxBuffers[0]->wfx.wBitsPerSample == 16) {
field = *ibufs;
field += *obufs;
field = (field > 32767) ? (32767) : field;
field = (field < -32768) ? (-32768) : field;
*obufs = field;
} else {
field = (*ibuf - 128);
field += (*obuf - 128);
field = (field > 127) ? (127) : field;
field = (field < -128) ? (-128) : field;
*obuf = field + 128;
}
ibuf += advance;
obuf += advance;
if (obuf >= (unsigned char *)(LnxBuffers[0]->buffer + LnxBuffers[0]->buffer_len))
obuf = LnxBuffers[0]->buffer;
}
// adjust positions of the cursors in the buffer
dsb->play_cursor += ilen;
dsb->write_cursor = dsb->play_cursor + ilen;
if (dsb->play_cursor >= dsb->buffer_len) {
if (!(dsb->flags & LNXSND_LOOPING)) {
// we're not looping, this buffer is done, reset it
dsb->playing = 0;
dsb->write_cursor = 0;
dsb->play_cursor = 0;
} else {
// loop back around
dsb->play_cursor = dsb->play_cursor % dsb->buffer_len;
}
}
if (dsb->write_cursor >= dsb->buffer_len) {
dsb->write_cursor = dsb->write_cursor % dsb->buffer_len;
}
return len;
}
static void LinuxSoundMixBuffersIntoMain(int len) {
LnxSoundBuffer *dsb;
// only go to 1 since 0 is the main buffer
for (int i = LnxNumBuffers - 1; i > 0; i--) {
if (!(dsb = LnxBuffers[i]))
continue;
if (dsb->buffer_len && dsb->playing) {
LinuxSoundMixInMainBuffer(dsb, len);
}
}
}
static void LinuxSoundThreadHandler(void *unused, Uint8 *stream, int len) {
SDL_memset(stream, '\0', len);
LnxBuffers[0]->buffer = stream;
LnxBuffers[0]->buffer_len = len;
LnxBuffers[0]->play_cursor = 0;
LnxBuffers[0]->write_cursor = 0;
LinuxSoundMixBuffersIntoMain(len);
LnxBuffers[0]->buffer = NULL;
}