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" #include "crossplatform.h"
#endif #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 #ifndef AUDIO_OPUS
class CSndFile : public IDecoder class CSndFile : public IDecoder
{ {
@@ -81,7 +139,11 @@ class CSndFile : public IDecoder
uint32 Decode(void *buffer) uint32 Decode(void *buffer)
{ {
if ( !IsOpened() ) return 0; 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); m_pMH = mpg123_new(nil, nil);
if ( m_pMH ) if ( m_pMH )
{ {
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);

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


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

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


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


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


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


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


@@ -395,9 +465,12 @@ void CStream::Pause()
{ {
if ( !HasSource() ) return; if ( !HasSource() ) return;
ALint sourceState = AL_PAUSED; ALint sourceState = AL_PAUSED;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState); alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PAUSED ) 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) void CStream::SetPause(bool bPause)
@@ -419,19 +492,21 @@ void CStream::SetPause(bool bPause)
void CStream::SetPitch(float pitch) void CStream::SetPitch(float pitch)
{ {
if ( !HasSource() ) return; 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) void CStream::SetGain(float gain)
{ {
if ( !HasSource() ) return; 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; 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) void CStream::SetVolume(uint32 nVol)
@@ -442,8 +517,13 @@ void CStream::SetVolume(uint32 nVol)


void CStream::SetPan(uint8 nPan) 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; m_nPan = nPan;
SetPosition((nPan - 63)/64.0f, 0.0f, Sqrt(1.0f-SQR((nPan-63)/64.0f)));
} }


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


ALint offset; ALint offset;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &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() 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(); + m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
} }


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


bool CStream::FillBuffer(ALuint alBuffer) bool CStream::FillBuffer(ALuint *alBuffer)
{ {
if ( !HasSource() ) if ( !HasSource() )
return false; return false;
if ( !IsOpened() ) if ( !IsOpened() )
return false; 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; return false;


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

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


alBufferData(alBuffer, m_pSoundFile->GetChannels() == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
m_pBuffer, size, 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; return true;
} }


int32 CStream::FillBuffers() int32 CStream::FillBuffers()
{ {
int32 i = 0; 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; 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; return i;
@@ -508,21 +596,23 @@ int32 CStream::FillBuffers()
void CStream::ClearBuffers() void CStream::ClearBuffers()
{ {
if ( !HasSource() ) return; if ( !HasSource() ) return;


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


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


bool CStream::Setup() bool CStream::Setup()
{ {
if ( IsOpened() ) if ( IsOpened() )
{ {
m_pSoundFile->Seek(0); m_pSoundFile->Seek(0);
alSourcei(m_alSource, AL_SOURCE_RELATIVE, AL_TRUE);
//SetPosition(0.0f, 0.0f, 0.0f); //SetPosition(0.0f, 0.0f, 0.0f);
SetPitch(1.0f); SetPitch(1.0f);
//SetPan(m_nPan); //SetPan(m_nPan);
@@ -538,17 +628,29 @@ void CStream::SetPlay(bool state)
if ( state ) if ( state )
{ {
ALint sourceState = AL_PLAYING; ALint sourceState = AL_PLAYING;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState); alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PLAYING ) 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; m_bActive = true;
} }
else else
{ {
ALint sourceState = AL_STOPPED; ALint sourceState = AL_STOPPED;
alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState); alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_STOPPED ) if (sourceState != AL_STOPPED)
alSourceStop(m_alSource); 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; m_bActive = false;
} }
} }
@@ -579,35 +681,48 @@ void CStream::Update()


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


alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState); // Relying a lot on left buffer states in here
alGetSourcei(m_alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
//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; ALint looping = AL_FALSE;
alGetSourcei(m_alSource, AL_LOOPING, &looping); alGetSourcei(m_pAlSources[0], AL_LOOPING, &looping);


if ( looping == AL_TRUE ) if ( looping == AL_TRUE )
{ {
TRACE("stream set looping"); 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) ) if (m_bActive && FillBuffer(buffer))
alSourceQueueBuffers(m_alSource, 1, &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); alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
SetPlay(buffersProcessed!=0); SetPlay(buffersProcessed[0]!=0);
} }
} }
} }

0 comments on commit 150f530

Please sign in to comment.