001package jmri.jmrit.audio; 002 003import javax.vecmath.Vector3f; 004import jmri.Audio; 005import jmri.InstanceManager; 006import jmri.implementation.AbstractAudio; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Base implementation of the AudioListener class. 012 * <p> 013 * Specific implementations will extend this base class. 014 * <br> 015 * <hr> 016 * This file is part of JMRI. 017 * <p> 018 * JMRI is free software; you can redistribute it and/or modify it under the 019 * terms of version 2 of the GNU General Public License as published by the Free 020 * Software Foundation. See the "COPYING" file for a copy of this license. 021 * <p> 022 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 024 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 025 * 026 * @author Matthew Harris copyright (c) 2009 027 */ 028public abstract class AbstractAudioListener extends AbstractAudio implements AudioListener { 029 030 private Vector3f position = new Vector3f(0.0f, 0.0f, 0.0f); 031 private Vector3f currentPosition = new Vector3f(0.0f, 0.0f, 0.0f); 032 private Vector3f velocity = new Vector3f(0.0f, 0.0f, 0.0f); 033 private Vector3f orientationAt = new Vector3f(0.0f, 1.0f, 0.0f); 034 private Vector3f orientationUp = new Vector3f(0.0f, 0.0f, 1.0f); 035 private Vector3f currentOriAt = new Vector3f(0.0f, 1.0f, 0.0f); 036 private Vector3f currentOriUp = new Vector3f(0.0f, 0.0f, 1.0f); 037 private float gain = 1.0f; 038 private float metersPerUnit = 1.0f; 039 private long timeOfLastPositionCheck = 0; 040 041 private static final AudioFactory activeAudioFactory = InstanceManager.getDefault(jmri.AudioManager.class).getActiveAudioFactory(); 042 043 /** 044 * Abstract constructor for new AudioListener with system name 045 * 046 * @param systemName AudioListener object system name (e.g. IAL) 047 */ 048 public AbstractAudioListener(String systemName) { 049 super(systemName); 050 this.setState(STATE_POSITIONED); 051 } 052 053 /** 054 * Abstract constructor for new AudioListener with system name and user name 055 * 056 * @param systemName AudioListener object system name (e.g. IAL) 057 * @param userName AudioListener object user name 058 */ 059 public AbstractAudioListener(String systemName, String userName) { 060 super(systemName, userName); 061 this.setState(STATE_POSITIONED); 062 } 063 064 @Override 065 public char getSubType() { 066 return LISTENER; 067 } 068 069 @Override 070 public void setPosition(Vector3f pos) { 071 this.position = pos; 072 changePosition(pos); 073 if (log.isDebugEnabled()) { 074 log.debug("Set position of Listener {} to {}", this.getSystemName(), pos); 075 } 076 } 077 078 @Override 079 public void setPosition(float x, float y, float z) { 080 this.setPosition(new Vector3f(x, y, z)); 081 } 082 083 @Override 084 public void setPosition(float x, float y) { 085 this.setPosition(new Vector3f(x, y, 0.0f)); 086 } 087 088 @Override 089 public Vector3f getPosition() { 090 return this.position; 091 } 092 093 @Override 094 public Vector3f getCurrentPosition() { 095 return this.currentPosition; 096 } 097 098 @Override 099 public void setVelocity(Vector3f vel) { 100 this.velocity = vel; 101 this.setState(vel.length() != 0 ? STATE_MOVING : STATE_POSITIONED); 102 if (log.isDebugEnabled()) { 103 log.debug("Set velocity of Listener {} to {}", this.getSystemName(), vel); 104 } 105 } 106 107 @Override 108 public Vector3f getVelocity() { 109 return this.velocity; 110 } 111 112 /** 113 * Method to calculate current position based on velocity 114 */ 115 protected void calculateCurrentPosition() { 116 117 // Calculate how long it's been since we lasted checked position 118 long currentTime = System.currentTimeMillis(); 119 long timePassed = (currentTime - this.timeOfLastPositionCheck) / 1000; 120 this.timeOfLastPositionCheck = currentTime; 121 122 if (this.velocity.length() != 0) { 123 this.currentPosition.scaleAdd(timePassed * this.metersPerUnit, 124 this.velocity, 125 this.currentPosition); 126 this.currentOriAt.scaleAdd(timePassed * this.metersPerUnit, 127 this.velocity, 128 this.currentOriAt); 129 this.currentOriUp.scaleAdd(timePassed * this.metersPerUnit, 130 this.velocity, 131 this.currentOriUp); 132 } 133 } 134 135 @Override 136 public void resetCurrentPosition() { 137 activeAudioFactory.audioCommandQueue(new AudioCommand(this, Audio.CMD_RESET_POSITION)); 138 activeAudioFactory.getCommandThread().interrupt(); 139 } 140 141 /** 142 * Method to reset the current position 143 */ 144 protected void doResetCurrentPosition() { 145 this.currentPosition = this.position; 146 } 147 148 /** 149 * Method to change the current position of this source 150 * 151 * @param pos new position 152 */ 153 abstract protected void changePosition(Vector3f pos); 154 155 @Override 156 public void setOrientation(Vector3f at, Vector3f up) { 157 this.orientationAt = at; 158 this.orientationUp = up; 159 if (log.isDebugEnabled()) { 160 log.debug("Set orientation of Listener {} to (at) {} (up) {}", this.getSystemName(), at, up); 161 } 162 } 163 164 @Override 165 public Vector3f getOrientation(int which) { 166 Vector3f orientation = null; 167 switch (which) { 168 case AT: { 169 orientation = this.orientationAt; 170 break; 171 } 172 case UP: { 173 orientation = this.orientationUp; 174 break; 175 } 176 default: 177 throw new IllegalArgumentException(); 178 } 179 return orientation; 180 } 181 182 @Override 183 public Vector3f getCurrentOrientation(int which) { 184 Vector3f orientation = null; 185 switch (which) { 186 case AT: { 187 orientation = this.currentOriAt; 188 break; 189 } 190 case UP: { 191 orientation = this.currentOriUp; 192 break; 193 } 194 default: 195 throw new IllegalArgumentException(); 196 } 197 return orientation; 198 } 199 200 @Override 201 public void setGain(float gain) { 202 this.gain = gain; 203 if (log.isDebugEnabled()) { 204 log.debug("Set gain of Listener {} to {}", this.getSystemName(), gain); 205 } 206 } 207 208 @Override 209 public float getGain() { 210 return this.gain; 211 } 212 213 @Override 214 public void setMetersPerUnit(float metersPerUnit) { 215 this.metersPerUnit = metersPerUnit; 216 if (log.isDebugEnabled()) { 217 log.debug("Set Meters per unit of Listener {} to {}", this.getSystemName(), metersPerUnit); 218 } 219 } 220 221 @Override 222 public float getMetersPerUnit() { 223 return this.metersPerUnit; 224 } 225 226 @Override 227 public void stateChanged(int oldState) { 228 // Move along... nothing to see here... 229 } 230 231 private static final Logger log = LoggerFactory.getLogger(AbstractAudioListener.class); 232 233 /** 234 * An internal class used to create a new thread to monitor and maintain 235 * current listener position with respect to velocity. 236 */ 237 protected static class AudioListenerMoveThread extends AbstractAudioThread { 238 239 /** 240 * Reference to the AudioListener object being monitored 241 */ 242 private AbstractAudioListener audioListener; 243 244 /** 245 * Constructor that takes handle to AudioListener to monitor 246 * 247 * @param audioListener AudioListener to monitor 248 */ 249 AudioListenerMoveThread(AbstractAudioListener audioListener) { 250 super(); 251 this.setName("movelis-" + super.getName()); 252 this.audioListener = audioListener; 253 if (log.isDebugEnabled()) { 254 log.debug("Created AudioListenerMoveThread for AudioListener {}", audioListener.getSystemName()); 255 } 256 } 257 258 /** 259 * Main processing loop 260 */ 261 @Override 262 public void run() { 263 264 while (!dying()) { 265 266 // Recalculate the position 267 audioListener.calculateCurrentPosition(); 268 269 // Check state and die if not playing 270 if (audioListener.getState() != STATE_MOVING) { 271 die(); 272 } 273 274 // sleep for a while so as not to overload CPU 275 snooze(20); 276 } 277 278// // Reset the current position 279// audioListener.resetCurrentPosition(); 280 // Finish up 281 if (log.isDebugEnabled()) { 282 log.debug("Clean up thread {}", this.getName()); 283 } 284 cleanup(); 285 } 286 287 /** 288 * Shuts this thread down and clears references to created objects 289 */ 290 @Override 291 protected void cleanup() { 292 // Thread is to shutdown 293 die(); 294 295 // Clear references to objects 296 this.audioListener = null; 297 298 // Finalise cleanup in super-class 299 super.cleanup(); 300 } 301 } 302}