/*
* Copyright 2013 MovingBlocks
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.terasology.audio.openAL;
import org.lwjgl.openal.AL10;
import org.terasology.audio.AudioManager;
import org.terasology.audio.Sound;
import org.terasology.math.geom.Vector3f;
import static org.lwjgl.openal.AL10.AL_FALSE;
import static org.lwjgl.openal.AL10.AL_GAIN;
import static org.lwjgl.openal.AL10.AL_MAX_DISTANCE;
import static org.lwjgl.openal.AL10.AL_PLAYING;
import static org.lwjgl.openal.AL10.AL_REFERENCE_DISTANCE;
import static org.lwjgl.openal.AL10.AL_ROLLOFF_FACTOR;
import static org.lwjgl.openal.AL10.AL_SOURCE_RELATIVE;
import static org.lwjgl.openal.AL10.AL_SOURCE_STATE;
import static org.lwjgl.openal.AL10.AL_TRUE;
import static org.lwjgl.openal.AL10.alGenSources;
import static org.lwjgl.openal.AL10.alGetSourcei;
import static org.lwjgl.openal.AL10.alSource3f;
import static org.lwjgl.openal.AL10.alSourceRewind;
import static org.lwjgl.openal.AL10.alSourceStop;
import static org.lwjgl.openal.AL10.alSourcef;
public abstract class BaseSoundSource<T extends Sound<?>> implements SoundSource<T> {
private int sourceId;
private float srcGain = 1.0f;
private float targetGain = 1.0f;
private boolean fade;
private Vector3f position = new Vector3f();
private Vector3f velocity = new Vector3f();
private Vector3f direction = new Vector3f();
private boolean absolutePosition;
private SoundPool<T, ? extends SoundSource<T>> owningPool;
public BaseSoundSource(SoundPool<T, ? extends SoundSource<T>> pool) {
this.owningPool = pool;
sourceId = alGenSources();
OpenALException.checkState("Creating sound source");
reset();
}
@Override
public SoundSource<T> play() {
if (!isPlaying()) {
AL10.alSourcePlay(getSourceId());
}
return this;
}
@Override
public SoundSource<T> stop() {
alSourceStop(getSourceId());
OpenALException.checkState("Stop playback");
alSourceRewind(getSourceId());
OpenALException.checkState("Rewind");
return this;
}
@Override
public SoundSource<T> pause() {
if (isPlaying()) {
AL10.alSourcePause(getSourceId());
OpenALException.checkState("Pause playback");
}
return this;
}
@Override
public boolean isPlaying() {
return alGetSourcei(sourceId, AL_SOURCE_STATE) == AL_PLAYING;
}
@Override
public void update(float delta) {
updateFade(delta);
updateState();
}
protected void updateState() {
}
@Override
public SoundSource<T> reset() {
setPitch(1.0f);
setLooping(false);
setGain(1.0f);
setAbsolute(true);
Vector3f zeroVector = new Vector3f();
setPosition(zeroVector);
setVelocity(zeroVector);
setDirection(zeroVector);
// some additional settings
alSourcef(getSourceId(), AL_MAX_DISTANCE, AudioManager.MAX_DISTANCE);
alSourcef(getSourceId(), AL_REFERENCE_DISTANCE, 1f);
AL10.alSourcei(getSourceId(), AL_SOURCE_RELATIVE, AL_FALSE);
AL10.alSourcef(getSourceId(), AL_ROLLOFF_FACTOR, 0.25f);
fade = false;
srcGain = 1.0f;
targetGain = 1.0f;
return this;
}
@Override
public SoundSource<T> setAbsolute(boolean absolute) {
absolutePosition = absolute;
AL10.alSourcei(getSourceId(), AL_SOURCE_RELATIVE, (absolute) ? AL_FALSE : AL_TRUE);
return this;
}
@Override
public boolean isAbsolute() {
return absolutePosition;
}
@Override
public Vector3f getVelocity() {
return velocity;
}
@Override
public SoundSource<T> setVelocity(Vector3f value) {
if (value == null || this.velocity.equals(value)) {
return this;
}
this.velocity.set(value);
AL10.alSource3f(getSourceId(), AL10.AL_VELOCITY, value.x, value.y, value.z);
OpenALException.checkState("Setting sound source velocity");
return this;
}
@Override
public Vector3f getPosition() {
return position;
}
@Override
public SoundSource<T> setPosition(Vector3f value) {
if (value == null || this.position.equals(value)) {
return this;
}
this.position.set(value);
alSource3f(getSourceId(), AL10.AL_POSITION, value.x, value.y, value.z);
OpenALException.checkState("Changing sound position");
return this;
}
@Override
public SoundSource<T> setDirection(Vector3f value) {
if (value == null || this.direction.equals(value)) {
return this;
}
AL10.alSource3f(getSourceId(), AL10.AL_DIRECTION, value.x, value.y, value.z);
OpenALException.checkState("Setting sound source direction");
this.direction.set(value);
return this;
}
@Override
public Vector3f getDirection() {
return direction;
}
@Override
public float getPitch() {
return AL10.alGetSourcef(getSourceId(), AL10.AL_PITCH);
}
@Override
public SoundSource<T> setPitch(float pitch) {
AL10.alSourcef(getSourceId(), AL10.AL_PITCH, pitch);
OpenALException.checkState("Setting sound pitch");
return this;
}
@Override
public float getGain() {
return srcGain;
}
@Override
public void updateGain() {
alSourcef(getSourceId(), AL_GAIN, srcGain * owningPool.getVolume());
OpenALException.checkState("Setting sound gain");
}
@Override
public SoundSource<T> setGain(float gain) {
srcGain = gain;
alSourcef(getSourceId(), AL_GAIN, gain * owningPool.getVolume());
OpenALException.checkState("Setting sound gain");
return this;
}
@Override
public SoundSource<T> fade(float value) {
this.targetGain = value;
fade = true;
return this;
}
protected int getSourceId() {
return sourceId;
}
private void updateFade(float delta) {
if (!fade) {
return;
}
float newGain = Math.max(targetGain, srcGain - delta);
if (newGain == 0.0f) {
stop();
} else {
setGain(newGain);
}
if (targetGain == newGain) {
fade = false;
}
}
public void dispose() {
if (sourceId != 0) {
AL10.alDeleteSources(sourceId);
}
}
}