/* * The MIT License (MIT) * * Copyright (c) 2014-2017 Sri Harsha Chilakapati * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.shc.silenceengine.audio; import com.shc.silenceengine.audio.openal.ALSource; import com.shc.silenceengine.core.SilenceEngine; import com.shc.silenceengine.utils.ReusableStack; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import static com.shc.silenceengine.audio.AudioDevice.Constants.*; /** * <p>An AudioScene allows you to play positional sounds, in 3D. The scene updates itself whenever the game updates, * and also updates the position, direction and velocity of the updated sources. It acts like a master of all sounds. * </p> * * @author Sri Harsha Chilakapati */ public final class AudioScene { private ReusableStack<ALSource> sourcesPool; private ReusableStack<PlayingSource> playingSourcesPool; private Map<PlayingSource, AudioSource> playingSources; private AudioSource defaultAudioSource; /** * Prevent instantiation by users. Should only be used via {@code SilenceEngine.audio.scene} */ AudioScene() { sourcesPool = new ReusableStack<>(ALSource::new); playingSourcesPool = new ReusableStack<>(PlayingSource::new); playingSources = new HashMap<>(); defaultAudioSource = new AudioSource(); SilenceEngine.eventManager.addDisposeHandler(this::cleanUp); SilenceEngine.eventManager.addUpdateHandler(this::updateSources); } /** * Stops all the sources and makes sure that no sound is playing from the game. This method does not stop the static * sounds of course because static sounds do not have a source attached. */ public void stopAllSources() { for (PlayingSource source : playingSources.keySet()) { source.alSource.stop(); sourcesPool.push(source.alSource); } playingSources.clear(); } private void cleanUp() { // Cleanup all the sources in the object pool for (ALSource source : sourcesPool.getAsList()) source.dispose(); } private void updateSources(float deltaTime) { Iterator<PlayingSource> iterator = playingSources.keySet().iterator(); while (iterator.hasNext()) { PlayingSource playingSource = iterator.next(); ALSource source = playingSource.alSource; AudioSource audioSource = playingSources.get(playingSource); ALSource.State state = source.getState(); if (state != ALSource.State.PLAYING && state != ALSource.State.LOOPING) { source.attachBuffer(null); iterator.remove(); sourcesPool.push(source); playingSourcesPool.push(playingSource); continue; } if (audioSource.updated) { source.pause(); source.setParameter(AL_POSITION, audioSource.position); source.setParameter(AL_VELOCITY, audioSource.velocity); source.setParameter(AL_DIRECTION, audioSource.direction); audioSource.updated = false; source.play(); } } } /** * Plays a sound as a static audio, that is, it has no position and special effects. * * @param sound The sound to be played. */ public void playStatic(Sound sound) { play(sound, defaultAudioSource, false); } /** * Plays a static sound, optionally allowing you to loop the sound. * * @param sound The sound to be played. * @param loop Whether to loop the sound. */ public void playStatic(Sound sound, boolean loop) { play(sound, defaultAudioSource, loop); } /** * Plays a sound through a specified AudioSource. The AudioSource instance specifies the spatial properties of the * sound to be played. * * @param sound The Sound object to be played. * @param source The AudioSource object which describes the spatial properties. */ public void play(Sound sound, AudioSource source) { play(sound, source, false); } /** * Plays a sound through a specified AudioSource, optionally allowing you to loop the sound. The AudioSource * instance specifies the spatial properties of the sound to be played. * * @param sound The Sound object to be played. * @param source The AudioSource object which describes the spatial properties. * @param loop Whether to play the sound in loop. */ public void play(Sound sound, AudioSource source, boolean loop) { ALSource alSource = sourcesPool.pop(); source.update(); alSource.attachBuffer(sound.buffer); alSource.setParameter(AL_POSITION, source.position); alSource.setParameter(AL_VELOCITY, source.velocity); alSource.setParameter(AL_DIRECTION, source.direction); alSource.setParameter(AL_LOOPING, loop); source.updated = false; alSource.play(); PlayingSource playingSource = playingSourcesPool.pop(); playingSource.sound = sound; playingSource.alSource = alSource; playingSources.put(playingSource, source); } /** * Stops a sound which is playing from all the sources. Other sounds for the sources will not be effected. * * @param sound The sound to stop playing. */ public void stopFromAllSources(Sound sound) { for (PlayingSource source : playingSources.keySet()) { if (source.sound.buffer.getID() == sound.buffer.getID()) source.alSource.stop(); } } /** * Stops all the sounds that are playing from a specific source. * * @param source The source from which the sounds should be stopped. */ public void stopAllFromSource(AudioSource source) { for (PlayingSource playingSource : playingSources.keySet()) { if (playingSources.get(playingSource) == source) playingSource.alSource.stop(); } } /** * Stops the sound which is being played statically without any spatial properties. * * @param sound The sound to be stopped. */ public void stopStatic(Sound sound) { stop(sound, defaultAudioSource); } /** * Stops a sound from playing from a specific source. * * @param sound The sound to be stopped. * @param source The source which is playing that sound. */ public void stop(Sound sound, AudioSource source) { for (PlayingSource playingSource : playingSources.keySet()) { if (playingSource.sound.buffer.getID() == sound.buffer.getID()) if (playingSources.get(playingSource) == source) playingSource.alSource.stop(); } } private static class PlayingSource { private ALSource alSource; private Sound sound; } }