Files
starruler-linux/source/sound/source/CStreamSound.cpp
T
2018-07-17 14:15:37 +02:00

313 lines
6.7 KiB
C++

#include "CStreamSound.h"
#include "ISoundDevice.h"
#include "CSoundSource.h"
#include "SAutoLocker.h"
#include "sound_vector.h"
#include "al.h"
#include "alc.h"
namespace audio {
CStreamSound::CStreamSound(const ISoundSource* sound, bool is3D, bool priority)
: soundID(invalidSource), filterID(invalidSource), soundSource(0), streamPos(0), loop(false), timestamp(0), resetStampID(invalidBuffer)
{
const CSoundSource* snd = dynamic_cast<const CSoundSource*>(sound);
if(snd == 0)
return;
ISoundDevice* device = snd->getDevice();
SAutoLocker<ISoundDevice> lock(device);
alGetError();
bufferID buffer = snd->getStreamBuffer(streamPos);
if(!alIsBuffer(buffer))
return;
sourceID id = device->getFreeSourceID(priority);
if(!alIsSource(id)) {
device->freeBuffer(buffer);
return;
}
alSourceQueueBuffers(id, 1, &buffer);
if(alGetError() != AL_NO_ERROR) {
device->freeBuffer(buffer);
device->freeSource(id);
return;
}
buffers.insert(buffer);
{
ALint freq, channels, bits;
alGetBufferi(buffer, AL_FREQUENCY, &freq);
alGetBufferi(buffer, AL_CHANNELS, &channels);
alGetBufferi(buffer, AL_BITS, &bits);
byterate = freq * channels * (bits/8);
}
//ALenum err = alGetError();
snd->setDefaultSettings(id);
if(is3D) {
alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
alSourcef(id, AL_ROLLOFF_FACTOR, 1.f);
alSource3f(id, AL_VELOCITY, 0,0,0);
}
else {
alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
alSource3f(id, AL_POSITION, 0,0,0);
alSource3f(id, AL_VELOCITY, 0,0,0);
alSourcef(id, AL_ROLLOFF_FACTOR, 0.f);
}
soundSource = snd; snd->grab();
soundID = id;
}
void CStreamSound::freeQueuedBuffers() {
ALint count = 0;
alGetSourcei(soundID,AL_BUFFERS_PROCESSED,&count);
ALuint* buffList = (ALuint*)alloca(count * sizeof(ALint));
alSourceUnqueueBuffers(soundID,count,buffList);
for(ALint i = 0; i < count; ++i) {
bufferID buffer = buffList[i];
if(buffer == resetStampID) {
timestamp = 0;
resetStampID = invalidBuffer;
}
ALint size;
alGetBufferi(buffer, AL_SIZE, &size);
timestamp += unsigned(size);
soundSource->getDevice()->freeBuffer(buffer);
buffers.erase(buffer);
}
checkError();
}
CStreamSound::~CStreamSound() {
if(soundSource) {
freeQueuedBuffers();
alSourcei(soundID, AL_BUFFER, 0);
checkError();
auto* device = soundSource->getDevice();
device->freeSource(soundID);
for(auto i = buffers.begin(); i != buffers.end(); ++i)
device->freeBuffer(*i);
if(filterID != invalidSource)
device->freeFilter(soundID, filterID);
soundSource->drop();
soundSource = 0;
}
}
void CStreamSound::streamData() {
acquire();
if(soundSource) {
freeQueuedBuffers();
//Queue up to 4 sound buffers
ALint queued;
alGetSourcei(soundID, AL_BUFFERS_QUEUED, &queued);
if(queued < 4) {
bufferID buffer = soundSource->getStreamBuffer(streamPos);
if(alIsBuffer(buffer)) {
alSourceQueueBuffers(soundID, 1, &buffer);
buffers.insert(buffer);
}
else if(loop) {
//If we fail to allocate a buffer, assume we reached the end
// If we're looping, we start at the beginning again
streamPos = 0;
buffer = soundSource->getStreamBuffer(streamPos);
if(alIsBuffer(buffer)) {
alSourceQueueBuffers(soundID, 1, &buffer);
resetStampID = buffer;
buffers.insert(buffer);
}
}
}
}
release();
}
bool CStreamSound::isStopped() const {
if(soundSource == 0)
return true;
acquire();
ALint state;
alGetSourcei(soundID, AL_SOURCE_STATE, &state);
release();
return state == AL_STOPPED;
}
bool CStreamSound::isPlaying() const {
if(!soundSource)
return false;
acquire();
ALint state;
alGetSourcei(soundID, AL_SOURCE_STATE, &state);
release();
return state == AL_PLAYING;
}
void CStreamSound::stop() {
acquire();
if(soundSource)
alSourceStop(soundID);
release();
}
void CStreamSound::pause() {
acquire();
if(soundSource)
alSourcePause(soundID);
release();
}
void CStreamSound::resume() {
acquire();
if(soundSource)
alSourcePlay(soundID);
release();
}
void CStreamSound::seek(int timeMS) {
acquire();
if(soundSource)
alSourcef(soundID, AL_SEC_OFFSET, (float)timeMS / 1000.f);
release();
}
void CStreamSound::setLooped(bool Loop) {
loop = Loop;
}
bool CStreamSound::isLooped() const {
return loop;
}
void CStreamSound::setPosition(float x, float y, float z) {
if(!valid(x,y,z))
return;
sync(x,y,z);
acquire();
if(soundSource)
alSource3f(soundID, AL_POSITION, x, y, z);
release();
}
void CStreamSound::setVelocity(float x, float y, float z) {
if(!valid(x,y,z))
return;
sync(x,y,z);
acquire();
if(soundSource)
alSource3f(soundID, AL_VELOCITY, x, y, z);
release();
}
void CStreamSound::setRolloff(float factor) {
acquire();
if(factor >= 0.f && soundSource)
alSourcef(soundID, AL_ROLLOFF_FACTOR, factor);
release();
}
void CStreamSound::setLowPass(float strength) {
acquire();
if(soundSource) {
if(filterID != invalidSource) {
soundSource->getDevice()->freeFilter(soundID, filterID);
filterID = invalidSource;
}
if(strength > 0)
filterID = soundSource->getDevice()->applyLowPassFilter(soundID, strength);
}
release();
}
void CStreamSound::setVolume(float volume) {
acquire();
if(soundSource)
alSourcef(soundID, AL_GAIN, volume * soundSource->getDefaultVolume());
release();
}
void CStreamSound::setPitch(float pitch) {
acquire();
if(soundSource)
alSourcef(soundID, AL_PITCH, pitch);
release();
}
void CStreamSound::setPlayPosition(int timeMs) {
acquire();
if(soundSource)
alSourcef(soundID, AL_SEC_OFFSET, (float)timeMs / 1000.f);
release();
}
int CStreamSound::getPlayPosition() {
acquire();
if(soundSource) {
float offset;
alGetSourcef(soundID, AL_SEC_OFFSET, &offset);
int time = int(((double(timestamp) * 1000.0) / double(byterate)) + double(offset * 1000.f));
//When looping, the timestamp can exceed the file length; correct for this
if(loop) {
int length = soundSource->getLength_ms();
if(time > length)
time = time % length;
}
release();
return time;
}
release();
return 0;
}
int CStreamSound::getPlayLength() {
int len = -1;
acquire();
if(soundSource)
len = soundSource->getLength_ms();
release();
return len;
}
void CStreamSound::setMinDistance(float minDist) {
acquire();
if(soundSource)
alSourcef(soundID, AL_REFERENCE_DISTANCE, minDist);
release();
}
void CStreamSound::setMaxDistance(float maxDist) {
acquire();
if(soundSource)
alSourcef(soundID, AL_MAX_DISTANCE, maxDist);
release();
}
const ISoundSource* CStreamSound::getSoundSource() {
return soundSource;
}
};