001package jmri.jmrit.audio; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.LinkedList; 005import java.util.List; 006import jmri.Audio; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Abstract implementation of the AudioFactory class. 012 * <p> 013 * All code shared amongst the concrete AudioFactory classes is defined here. 014 * <hr> 015 * This file is part of JMRI. 016 * <p> 017 * JMRI is free software; you can redistribute it and/or modify it under the 018 * terms of version 2 of the GNU General Public License as published by the Free 019 * Software Foundation. See the "COPYING" file for a copy of this license. 020 * <p> 021 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 022 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 023 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 024 * 025 * @author Matthew Harris copyright (c) 2009 026 */ 027public abstract class AbstractAudioFactory implements AudioFactory { 028 029 /** 030 * List of queued audio commands to process 031 */ 032 private static List<AudioCommand> commandQueue = null; 033 034 /** 035 * Boolean used to determine if this AudioFactory has been initialised 036 */ 037 private static boolean initialised = false; 038 039 /** 040 * Boolean used to determine if this AudioFactory should attenuate sources 041 * based on their distance from the Listener 042 */ 043 private static boolean distanceAttenuated = true; 044 045 /** 046 * Reference to the separate thread used to process all AudioCommands 047 */ 048 private static AbstractAudioThread audioCommandThread = null; 049 050 @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") 051 @Override 052 public boolean init() { 053 if (initialised) { 054 log.debug("Already initialised"); 055 return true; 056 } 057 058 // Create the command queue 059 commandQueue = new LinkedList<>(); 060 061 // Create and start the command thread 062 audioCommandThread = new AudioCommandThread(this); 063 audioCommandThread.start(); 064 065 initialised = true; 066 return true; 067 } 068 069 @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", 070 justification = "OK to write to static variables to record static library status") 071 @Override 072 public void cleanup() { 073 074 boolean dieException = false; 075 076 // End the command thread 077 try { 078 audioCommandThread.die(); // send the die signal to the thread 079 audioCommandThread.interrupt(); // interrupt the thread to process die signal 080 } catch (Exception e) { 081 dieException = true; 082 } 083 084 if (!dieException) { 085 // wait for up to 5 seconds for thread to end 086 for (int i = 0; i < 50; i++) { 087 if (!audioCommandThread.isThreadAlive()) { 088 break; 089 } 090 AbstractAudioThread.snooze(100); 091 } 092 } 093 initialised = false; 094 } 095 096 @Override 097 public synchronized boolean audioCommandQueue(AudioCommand queueAudioCommand) { 098 if (queueAudioCommand == null) { 099 log.debug("Processing command queue"); 100 // Process command queue 101 AudioCommand audioCommand; 102 while (commandQueue != null && commandQueue.size() > 0) { 103 audioCommand = commandQueue.remove(0); 104 if (audioCommand != null) { 105 if (log.isDebugEnabled()) { 106 log.debug("Process command: {} ({} remaining)", audioCommand.toString(), commandQueue.size()); 107 } 108 Audio audio = audioCommand.getAudio(); 109 110 // Process AudioSource commands 111 if (audio instanceof AudioSource) { 112 AbstractAudioSource audioSource = (AbstractAudioSource) audio; 113 switch (audioCommand.getCommand()) { 114 case Audio.CMD_BIND_BUFFER: 115 audioSource.setBound(audioSource.bindAudioBuffer(audioSource.getAssignedBuffer())); 116 break; 117 case Audio.CMD_QUEUE_BUFFERS: 118 audioSource.setQueued(audioSource.queueAudioBuffers(audioSource.getQueuedBuffers())); 119 break; 120 case Audio.CMD_UNQUEUE_BUFFERS: 121 audioSource.setQueued(audioSource.unqueueAudioBuffers()); 122 break; 123 case Audio.CMD_PLAY: 124 audioSource.doPlay(); 125 break; 126 case Audio.CMD_STOP: 127 audioSource.doStop(); 128 break; 129 case Audio.CMD_PLAY_TOGGLE: 130 audioSource.doTogglePlay(); 131 break; 132 case Audio.CMD_PAUSE: 133 audioSource.doPause(); 134 break; 135 case Audio.CMD_RESUME: 136 audioSource.doResume(); 137 break; 138 case Audio.CMD_PAUSE_TOGGLE: 139 audioSource.doTogglePause(); 140 break; 141 case Audio.CMD_REWIND: 142 audioSource.doRewind(); 143 break; 144 case Audio.CMD_FADE_IN: 145 audioSource.doFadeIn(); 146 break; 147 case Audio.CMD_FADE_OUT: 148 audioSource.doFadeOut(); 149 break; 150 case Audio.CMD_RESET_POSITION: 151 audioSource.doResetCurrentPosition(); 152 break; 153 default: 154 log.warn("Command {} not suitable for AudioSource ({})", audioCommand.toString(), audioSource.getSystemName()); 155 } 156 } // Process AudioBuffer commands 157 else if (audio instanceof AudioBuffer) { 158 AbstractAudioBuffer audioBuffer = (AbstractAudioBuffer) audio; 159 switch (audioCommand.getCommand()) { 160 case Audio.CMD_LOAD_SOUND: 161 audioBuffer.loadBuffer(); 162 break; 163 default: 164 log.warn("Command {} not suitable for AudioBuffer ({})", audioCommand.toString(), audioBuffer.getSystemName()); 165 } 166 } // Process AudioListener commands 167 else if (audio instanceof AudioListener) { 168 AbstractAudioListener audioListener = (AbstractAudioListener) audio; 169 switch (audioCommand.getCommand()) { 170 case Audio.CMD_RESET_POSITION: 171 audioListener.doResetCurrentPosition(); 172 break; 173 default: 174 log.warn("Command {} not suitable for AudioListener ({})", audioCommand.toString(), audioListener.getSystemName()); 175 } 176 } 177 } 178 } 179 return (commandQueue != null && commandQueue.size() > 0); 180 } else { 181 if (commandQueue == null) { 182 log.warn("Audio commandQueue not initialised"); 183 return false; 184 } 185 commandQueue.add(queueAudioCommand); 186 if (log.isDebugEnabled()) { 187 log.debug("New audio command: {}", queueAudioCommand.toString()); 188 } 189 return true; 190 } 191 } 192 193 @Override 194 public Thread getCommandThread() { 195 return audioCommandThread; 196 } 197 198 @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD") 199 @Override 200 public void setDistanceAttenuated(boolean attenuated) { 201 distanceAttenuated = attenuated; 202 } 203 204 @Override 205 public boolean isDistanceAttenuated() { 206 return distanceAttenuated; 207 } 208 209 private static final Logger log = LoggerFactory.getLogger(AbstractAudioFactory.class); 210 211}