/* * $Id: SoundSystemImpl.java 536 2008-02-19 06:03:27Z weiju $ * * Created on 2006/01/29 * Copyright 2005-2008 by Wei-ju Wu * This file is part of The Z-machine Preservation Project (ZMPP). * * ZMPP is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZMPP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZMPP. If not, see <http://www.gnu.org/licenses/>. */ package org.zmpp.media; import java.awt.Toolkit; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.zmpp.base.Interruptable; /** * This class implements the SoundSystem interface. The implementation is using * a Java 5 thread executor which makes it very easy to assign a control task to * each sound which can handle the stopping of a sound easily. * * @author Wei-ju Wu * @version 1.0 */ public class SoundSystemImpl implements SoundSystem { /** * The resource database. */ private MediaCollection<SoundEffect> sounds; /** * The executor service. */ private ExecutorService executor; /** * The interruptable. */ private Interruptable interruptable; /** * The current sound task. */ protected PlaySoundTask currentTask; /** * Constructor. * * @param sounds the sound resources */ public SoundSystemImpl(final MediaCollection<SoundEffect> sounds) { super(); this.sounds = sounds; // That's pretty cool: // We can control the number of concurrent sounds to be played // simultaneously by the size of the thread pool. this.executor = Executors.newSingleThreadExecutor(); } /** * This method handles the situation if a sound effect is going to be played * and a previous one is not finished. */ protected void handlePreviousNotFinished() { // The default behaviour is to stop the previous sound currentTask.stop(); } /** * {@inheritDoc} */ public void reset() { // no resetting supported } /** * {@inheritDoc} */ public void play(final int number, final int effect, final int volume, final int repeats, final int routine) { SoundEffect sound = null; // @sound_effect 0 3 followed by @sound_effect 0 4 is called // by "The Lurking Horror" and hints that all sound effects should // be stopped and unloaded. ZMPP's sound system implementation does // nothing at the moment (hey, we have plenty of memory and are // in a Java environment) if (number == 0) { return; } if (sounds != null) { sound = sounds.getResource(number); } if (sound == null) { Toolkit.getDefaultToolkit().beep(); } else { if (effect == SoundSystem.EFFECT_START) { startSound(number, sound, volume, repeats, routine); } else if (effect == SoundSystem.EFFECT_STOP) { stopSound(number); } else if (effect == SoundSystem.EFFECT_PREPARE) { sounds.loadResource(number); } else if (effect == SoundSystem.EFFECT_FINISH) { stopSound(number); sounds.unloadResource(number); } } } /** * Starts the specified sound. * * @param number the sound number * @param sound the sound object * @param volume the volume * @param repeats the number of repeats * @param routine the interrupt routine */ private void startSound(final int number, final SoundEffect sound, final int volume, final int repeats, final int routine) { if (currentTask != null && !currentTask.wasPlayed()) { handlePreviousNotFinished(); } currentTask = (routine <= 0) ? new PlaySoundTask(number, sound, volume, repeats) : new PlaySoundTask(number, sound, volume, repeats, interruptable, routine); executor.submit(currentTask); } /** * Stops the sound with the given number. * * @param number the number */ private void stopSound(final int number) { // only stop the sound if the numbers match if (currentTask != null && currentTask.getResourceNumber() == number) { currentTask.stop(); } } }