Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Handle stereo panning in OAL manually for streams
  • Loading branch information
Sergeanur committed Jan 4, 2021
1 parent 047f9c4 commit 150f5302b735331780815194fb7d397a477fcb19
Showing with 184 additions and 60 deletions.
  1. +164 −49 src/audio/oal/stream.cpp
  2. +5 −5 src/audio/oal/stream.h
  3. +15 −6 src/audio/sampman_oal.cpp
@@ -19,6 +19,64 @@
#include "crossplatform.h"
#endif

/*
As we ran onto an issue of having different volume levels for mono streams
and stereo streams we are now handling all the stereo panning ourselves.
Each stream now has two sources - one panned to the left and one to the right,
and uses two separate buffers to store data for each individual channel.
For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to
LLLLRRRR (handled by CSortStereoBuffer).
*/

class CSortStereoBuffer
{
uint16* PcmBuf;
size_t BufSize;
public:
CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
~CSortStereoBuffer()
{
if (PcmBuf)
free(PcmBuf);
}

uint16* GetBuffer(size_t size)
{
if (size == 0) return nil;
if (!PcmBuf)
{
BufSize = size;
PcmBuf = (uint16*)malloc(BufSize);
}
else if (BufSize < size)
{
BufSize = size;
PcmBuf = (uint16*)realloc(PcmBuf, size);
}
return PcmBuf;
}

void SortStereo(void* buf, size_t size)
{
uint16* InBuf = (uint16*)buf;
uint16* OutBuf = GetBuffer(size);

if (!OutBuf) return;

size_t rightStart = size / 4;
for (size_t i = 0; i < size / 4; i++)
{
OutBuf[i] = InBuf[i*2];
OutBuf[i+rightStart] = InBuf[i*2+1];
}

memcpy(InBuf, OutBuf, size);
}

};

CSortStereoBuffer SortStereoBuffer;

#ifndef AUDIO_OPUS
class CSndFile : public IDecoder
{
@@ -81,7 +139,11 @@ class CSndFile : public IDecoder
uint32 Decode(void *buffer)
{
if ( !IsOpened() ) return 0;
return sf_read_short(m_pfSound, (short *)buffer, GetBufferSamples()) * GetSampleSize();

size_t size = sf_read_short(m_pfSound, (short*)buffer, GetBufferSamples()) * GetSampleSize();
if (GetChannels()==2)
SortStereoBuffer.SortStereo(buffer, size);
return size;
}
};

@@ -101,6 +163,8 @@ class CMP3File : public IDecoder
m_pMH = mpg123_new(nil, nil);
if ( m_pMH )
{
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);

long rate = 0;
int channels = 0;
int encoding = 0;
@@ -176,6 +240,8 @@ class CMP3File : public IDecoder
assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX));
#endif
if (err != MPG123_OK && err != MPG123_DONE) return 0;
if (GetChannels() == 2)
SortStereoBuffer.SortStereo(buffer, size);
return (uint32)size;
}
};
@@ -267,6 +333,9 @@ class COpusFile : public IDecoder
if (size < 0)
return 0;

if (GetChannels() == 2)
SortStereoBuffer.SortStereo(buffer, size * m_nChannels * GetSampleSize());

return size * m_nChannels * GetSampleSize();
}
};
@@ -286,8 +355,8 @@ void CStream::Terminate()
#endif
}

CStream::CStream(char *filename, ALuint &source, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
m_alSource(source),
CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
m_pAlSources(sources),
m_alBuffers(buffers),
m_pBuffer(nil),
m_bPaused(false),
@@ -368,7 +437,7 @@ void CStream::Delete()

bool CStream::HasSource()
{
return m_alSource != AL_NONE;
return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE);
}

bool CStream::IsOpened()
@@ -382,9 +451,10 @@ bool CStream::IsPlaying()

if ( !m_bPaused )
{
ALint sourceState;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
if ( m_bActive || sourceState == AL_PLAYING )
ALint sourceState[2];
alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]);
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
if ( m_bActive || sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
return true;
}

@@ -395,9 +465,12 @@ void CStream::Pause()
{
if ( !HasSource() ) return;
ALint sourceState = AL_PAUSED;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PAUSED )
alSourcePause(m_alSource);
alSourcePause(m_pAlSources[0]);
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PAUSED)
alSourcePause(m_pAlSources[1]);
}

void CStream::SetPause(bool bPause)
@@ -419,19 +492,21 @@ void CStream::SetPause(bool bPause)
void CStream::SetPitch(float pitch)
{
if ( !HasSource() ) return;
alSourcef(m_alSource, AL_PITCH, pitch);
alSourcef(m_pAlSources[0], AL_PITCH, pitch);
alSourcef(m_pAlSources[1], AL_PITCH, pitch);
}

void CStream::SetGain(float gain)
{
if ( !HasSource() ) return;
alSourcef(m_alSource, AL_GAIN, gain);
alSourcef(m_pAlSources[0], AL_GAIN, gain);
alSourcef(m_pAlSources[1], AL_GAIN, gain);
}

void CStream::SetPosition(float x, float y, float z)
void CStream::SetPosition(int i, float x, float y, float z)
{
if ( !HasSource() ) return;
alSource3f(m_alSource, AL_POSITION, x, y, z);
alSource3f(m_pAlSources[i], AL_POSITION, x, y, z);
}

void CStream::SetVolume(uint32 nVol)
@@ -442,8 +517,13 @@ void CStream::SetVolume(uint32 nVol)

void CStream::SetPan(uint8 nPan)
{
m_nPan = clamp((int8)nPan - 63, 0, 63);
SetPosition(0, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));

m_nPan = clamp((int8)nPan + 64, 64, 127);
SetPosition(1, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));

m_nPan = nPan;
SetPosition((nPan - 63)/64.0f, 0.0f, Sqrt(1.0f-SQR((nPan-63)/64.0f)));
}

void CStream::SetPosMS(uint32 nPos)
@@ -460,10 +540,10 @@ uint32 CStream::GetPosMS()

ALint offset;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
alGetSourcei(m_alSource, AL_BYTE_OFFSET, &offset);
alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);

return m_pSoundFile->Tell()
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS-1)) / m_pSoundFile->GetChannels()
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
}

@@ -473,33 +553,41 @@ uint32 CStream::GetLengthMS()
return m_pSoundFile->GetLength();
}

bool CStream::FillBuffer(ALuint alBuffer)
bool CStream::FillBuffer(ALuint *alBuffer)
{
if ( !HasSource() )
return false;
if ( !IsOpened() )
return false;
if ( !(alBuffer != AL_NONE && alIsBuffer(alBuffer)) )
if ( !(alBuffer[0] != AL_NONE && alIsBuffer(alBuffer[0])) )
return false;
if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
return false;

uint32 size = m_pSoundFile->Decode(m_pBuffer);
if( size == 0 )
return false;

uint32 channelSize = size / m_pSoundFile->GetChannels();

alBufferData(alBuffer, m_pSoundFile->GetChannels() == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
m_pBuffer, size, m_pSoundFile->GetSampleRate());

alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
// TODO: use just one buffer if we play mono
if (m_pSoundFile->GetChannels() == 1)
alBufferData(alBuffer[1], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
else
alBufferData(alBuffer[1], AL_FORMAT_MONO16, (uint8*)m_pBuffer + channelSize, channelSize, m_pSoundFile->GetSampleRate());
return true;
}

int32 CStream::FillBuffers()
{
int32 i = 0;
for ( i = 0; i < NUM_STREAMBUFFERS; i++ )
for ( i = 0; i < NUM_STREAMBUFFERS/2; i++ )
{
if ( !FillBuffer(m_alBuffers[i]) )
if ( !FillBuffer(&m_alBuffers[i*2]) )
break;
alSourceQueueBuffers(m_alSource, 1, &m_alBuffers[i]);
alSourceQueueBuffers(m_pAlSources[0], 1, &m_alBuffers[i*2]);
alSourceQueueBuffers(m_pAlSources[1], 1, &m_alBuffers[i*2+1]);
}

return i;
@@ -508,21 +596,23 @@ int32 CStream::FillBuffers()
void CStream::ClearBuffers()
{
if ( !HasSource() ) return;

ALint buffersQueued;
alGetSourcei(m_alSource, AL_BUFFERS_QUEUED, &buffersQueued);

ALint buffersQueued[2];
alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued[0]);
alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &buffersQueued[1]);

ALuint value;
while (buffersQueued--)
alSourceUnqueueBuffers(m_alSource, 1, &value);
while (buffersQueued[0]--)
alSourceUnqueueBuffers(m_pAlSources[0], 1, &value);
while (buffersQueued[1]--)
alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
}

bool CStream::Setup()
{
if ( IsOpened() )
{
m_pSoundFile->Seek(0);
alSourcei(m_alSource, AL_SOURCE_RELATIVE, AL_TRUE);
//SetPosition(0.0f, 0.0f, 0.0f);
SetPitch(1.0f);
//SetPan(m_nPan);
@@ -538,17 +628,29 @@ void CStream::SetPlay(bool state)
if ( state )
{
ALint sourceState = AL_PLAYING;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PLAYING )
alSourcePlay(m_alSource);
alSourcePlay(m_pAlSources[0]);

sourceState = AL_PLAYING;
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PLAYING)
alSourcePlay(m_pAlSources[1]);

m_bActive = true;
}
else
{
ALint sourceState = AL_STOPPED;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_STOPPED )
alSourceStop(m_alSource);
alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_STOPPED)
alSourceStop(m_pAlSources[0]);

sourceState = AL_STOPPED;
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_STOPPED)
alSourceStop(m_pAlSources[1]);

m_bActive = false;
}
}
@@ -579,35 +681,48 @@ void CStream::Update()

if ( !m_bPaused )
{
ALint sourceState;
ALint buffersProcessed = 0;
ALint sourceState[2];
ALint buffersProcessed[2] = { 0, 0 };

alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
alGetSourcei(m_alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
// Relying a lot on left buffer states in here

//alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f);
alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]);
alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
//alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f);
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]);

ALint looping = AL_FALSE;
alGetSourcei(m_alSource, AL_LOOPING, &looping);
alGetSourcei(m_pAlSources[0], AL_LOOPING, &looping);

if ( looping == AL_TRUE )
{
TRACE("stream set looping");
alSourcei(m_alSource, AL_LOOPING, AL_TRUE);
alSourcei(m_pAlSources[0], AL_LOOPING, AL_TRUE);
alSourcei(m_pAlSources[1], AL_LOOPING, AL_TRUE);
}

assert(buffersProcessed[0] == buffersProcessed[1]);

while( buffersProcessed-- )
while( buffersProcessed[0]-- )
{
ALuint buffer;
ALuint buffer[2];

alSourceUnqueueBuffers(m_alSource, 1, &buffer);
alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);

if ( m_bActive && FillBuffer(buffer) )
alSourceQueueBuffers(m_alSource, 1, &buffer);
if (m_bActive && FillBuffer(buffer))
{
alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]);
alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]);
}
}

if ( sourceState != AL_PLAYING )
if ( sourceState[0] != AL_PLAYING )
{
alGetSourcei(m_alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
SetPlay(buffersProcessed!=0);
alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
SetPlay(buffersProcessed[0]!=0);
}
}
}

0 comments on commit 150f530

Please sign in to comment.