/* * 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 com.google.common.collect.Maps; import org.terasology.audio.AudioManager; import org.terasology.audio.Sound; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; public abstract class BaseSoundPool<SOUND extends Sound<?>, SOURCE extends SoundSource<SOUND>> implements SoundPool<SOUND, SOURCE> { private static final int DEFAULT_POOL_SIZE = 32; protected Map<SOURCE, Integer> soundSources; private float volume; public BaseSoundPool(int capacity) { soundSources = Maps.newHashMapWithExpectedSize(capacity); this.fillPool(capacity); } public BaseSoundPool() { this(DEFAULT_POOL_SIZE); } public SOURCE getLockedSource() { for (SOURCE source : soundSources.keySet()) { if (!isActive(source)) { if (lock(source)) { return source; } } } return null; } @Override public SOURCE getSource(SOUND sound, int priority) { if (sound == null) { return null; } // TODO: should be optimized (performance crucial) for (SOURCE source : soundSources.keySet()) { if (!isActive(source)) { soundSources.put(source, priority); return (SOURCE) source.setAudio(sound); } } // No free sound found, will look by priority for (Map.Entry<SOURCE, Integer> entry : soundSources.entrySet()) { SOURCE source = entry.getKey(); Integer soundPriority = entry.getValue(); if (soundPriority < priority) { // sound playing wil lower priority than our query soundSources.put(source, priority); return (SOURCE) source.setAudio(sound); } } return null; } @Override public SOURCE getSource(SOUND sound) { return getSource(sound, AudioManager.PRIORITY_NORMAL); } @Override public Set<SOURCE> getSources() { return soundSources.keySet(); } @Override public Set<SOURCE> getInactiveSources() { return soundSources.keySet().stream().filter(source -> !isActive(source)).collect(Collectors.toCollection(HashSet::new)); } @Override public Set<SOURCE> getActiveSources() { return soundSources.keySet().stream().filter(this::isActive).collect(Collectors.toCollection(HashSet::new)); } @Override public void stopAll() { soundSources.keySet().forEach(SOURCE::stop); } @Override public void update(float delta) { soundSources.keySet().stream().filter(SoundSource::isPlaying).forEach(source -> source.update(delta)); } @Override public void setVolume(float volume) { this.volume = volume; soundSources.keySet().forEach(SOURCE::updateGain); } @Override public float getVolume() { return this.volume; } @Override public int size() { return soundSources.size(); } @Override public boolean isInPool(SOURCE source) { return soundSources.containsKey(source); } public boolean isLocked(SOURCE source) { Integer lock = soundSources.get(source); return lock != null && lock == AudioManager.PRIORITY_LOCKED; } public boolean lock(SOURCE source) { if (isLocked(source) && !isInPool(source)) { return false; } soundSources.put(source, AudioManager.PRIORITY_LOCKED); return true; } public void unlock(SOURCE source) { soundSources.put(source, null); } public boolean isActive(SOURCE source) { return isLocked(source) || source.isPlaying(); } protected abstract SOURCE createSoundSource(); private void fillPool(int capacity) { for (int i = 0; i < capacity; i++) { this.soundSources.put(createSoundSource(), null); } } @Override public void purge(Sound<?> sound) { soundSources.keySet().stream().filter(source -> sound.equals(source.getAudio())).forEach(SOURCE::purge); } }