001package jmri.jmrit.vsdecoder; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005import jmri.util.PhysicalLocation; 006import org.jdom2.Element; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Configurable Sound initial version. 012 * 013 * <hr> 014 * This file is part of JMRI. 015 * <p> 016 * JMRI is free software; you can redistribute it and/or modify it under 017 * the terms of version 2 of the GNU General Public License as published 018 * by the Free Software Foundation. See the "COPYING" file for a copy 019 * of this license. 020 * <p> 021 * JMRI is distributed in the hope that it will be useful, but WITHOUT 022 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 023 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 024 * for more details. 025 * 026 * @author Mark Underwood Copyright (C) 2011 027 */ 028class ConfigurableSound extends VSDSound { 029 030 protected String start_file; 031 protected String mid_file; 032 protected String end_file; 033 protected String short_file; 034 035 SoundBite start_sound; 036 SoundBite mid_sound; 037 SoundBite end_sound; 038 SoundBite short_sound; 039 040 boolean initialized = false; 041 042 protected boolean use_start_sound = false; 043 protected boolean use_mid_sound = false; 044 protected boolean use_end_sound = false; 045 protected boolean use_short_sound = false; 046 047 private float rd; 048 049 public ConfigurableSound(String name) { 050 super(name); 051 } 052 053 public boolean init() { 054 return this.init(null); 055 } 056 057 public boolean init(VSDFile vf) { 058 if (!initialized) { 059 if (use_start_sound) { 060 start_sound = new SoundBite(vf, start_file, name + "_Start", name + "_Start"); 061 if (start_sound.isInitialized()) { 062 start_sound.setLooped(false); 063 start_sound.setReferenceDistance(rd); 064 start_sound.setGain(gain); 065 } else { 066 use_start_sound = false; 067 } 068 } 069 if (use_mid_sound) { 070 mid_sound = new SoundBite(vf, mid_file, name + "_Mid", name + "_Mid"); 071 if (mid_sound.isInitialized()) { 072 mid_sound.setLooped(false); 073 mid_sound.setReferenceDistance(rd); 074 mid_sound.setGain(gain); 075 } else { 076 use_mid_sound = false; 077 } 078 } 079 if (use_end_sound) { 080 end_sound = new SoundBite(vf, end_file, name + "_End", name + "_End"); 081 if (end_sound.isInitialized()) { 082 end_sound.setLooped(false); 083 end_sound.setReferenceDistance(rd); 084 end_sound.setGain(gain); 085 } else { 086 use_end_sound = false; 087 } 088 } 089 if (use_short_sound) { 090 short_sound = new SoundBite(vf, short_file, name + "_Short", name + "_Short"); 091 if (short_sound.isInitialized()) { 092 short_sound.setLooped(false); 093 short_sound.setReferenceDistance(rd); 094 short_sound.setGain(gain); 095 } else { 096 use_short_sound = false; 097 } 098 } 099 } 100 return true; 101 } 102 103 @Override 104 public void play() { 105 if (use_short_sound) { 106 short_sound.play(); 107 } else { 108 if (use_start_sound) { 109 t = newTimer(start_sound.getLengthAsInt(), false, new ActionListener() { 110 @Override 111 public void actionPerformed(ActionEvent e) { 112 handleTimerPop(e); 113 } 114 }); 115 start_sound.play(); 116 if (use_mid_sound) { 117 t.start(); 118 } 119 } else if (use_mid_sound) { 120 mid_sound.setLooped(true); 121 mid_sound.play(); 122 } 123 } 124 } 125 126 @Override 127 public void loop() { 128 if (use_start_sound) { 129 start_sound.setLooped(false); 130 start_sound.play(); 131 // The newTimer method in the super class makes sure that the delay value is positive 132 t = newTimer(start_sound.getLengthAsInt() - 100, false, new ActionListener() { 133 @Override 134 public void actionPerformed(ActionEvent e) { 135 handleTimerPop(e); 136 } 137 }); 138 t.setRepeats(false); // timer pop only once to trigger the sustain sound. 139 t.start(); 140 } else if (use_mid_sound) { 141 mid_sound.setLooped(true); 142 mid_sound.play(); 143 } 144 } 145 146 // Catch the timer pop after the start sound is played and trigger the (looped) sustain sound. 147 protected void handleTimerPop(ActionEvent e) { 148 log.debug("Received timer pop after start sound played."); 149 //TODO: Need to validate that this is the timer pop 150 if (use_mid_sound) { 151 mid_sound.setLooped(true); 152 mid_sound.play(); 153 } 154 t.stop(); 155 } 156 157 @Override 158 public void stop() { 159 log.debug("Stopping"); 160 // make sure the start sound is killed 161 if (use_start_sound) { 162 start_sound.stop(); 163 } 164 165 // If the mid sound is used, turn off the looping. 166 // this will allow it to naturally die. 167 if (use_mid_sound) { 168 mid_sound.setLooped(false); 169 mid_sound.fadeOut(); 170 } 171 172 // If the timer is running, stop it. 173 if (t != null) { 174 t.stop(); 175 } 176 177 // If we're using the end sound, stop the mid sound 178 // and play the end sound. 179 if (use_end_sound) { 180 if (use_mid_sound) { 181 mid_sound.stop(); 182 } 183 end_sound.setLooped(false); 184 end_sound.play(); 185 } 186 } 187 188 @Override 189 public void fadeIn() { 190 this.play(); 191 } 192 193 @Override 194 public void fadeOut() { 195 this.stop(); 196 } 197 198 @Override 199 public void shutdown() { 200 if (use_start_sound) { 201 start_sound.stop(); 202 } 203 if (use_mid_sound) { 204 mid_sound.stop(); 205 } 206 if (use_end_sound) { 207 end_sound.stop(); 208 } 209 if (use_short_sound) { 210 short_sound.stop(); 211 } 212 } 213 214 @Override 215 public void mute(boolean m) { 216 if (use_start_sound) { 217 start_sound.mute(m); 218 } 219 if (use_mid_sound) { 220 mid_sound.mute(m); 221 } 222 if (use_end_sound) { 223 end_sound.mute(m); 224 } 225 if (use_short_sound) { 226 short_sound.mute(m); 227 } 228 } 229 230 @Override 231 public void setVolume(float v) { 232 if (use_start_sound) { 233 start_sound.setVolume(v); 234 } 235 if (use_mid_sound) { 236 mid_sound.setVolume(v); 237 } 238 if (use_end_sound) { 239 end_sound.setVolume(v); 240 } 241 if (use_short_sound) { 242 short_sound.setVolume(v); 243 } 244 } 245 246 @Override 247 public void setPosition(PhysicalLocation p) { 248 super.setPosition(p); 249 if (use_start_sound) { 250 start_sound.setPosition(p); 251 } 252 if (use_mid_sound) { 253 mid_sound.setPosition(p); 254 } 255 if (use_end_sound) { 256 end_sound.setPosition(p); 257 } 258 if (use_short_sound) { 259 short_sound.setPosition(p); 260 } 261 } 262 263 @Override 264 public Element getXml() { 265 Element me = new Element("sound"); 266 267 if (log.isDebugEnabled()) { 268 log.debug("Configurable Sound:"); 269 log.debug(" name: {}", this.getName()); 270 log.debug(" start_file: {}", start_file); 271 log.debug(" mid_file: {}", mid_file); 272 log.debug(" end_file: {}", end_file); 273 log.debug(" short_file: {}", short_file); 274 log.debug(" use_start_file: {}", start_file); 275 } 276 277 me.setAttribute("name", this.getName()); 278 me.setAttribute("type", "configurable"); 279 if (use_start_sound) { 280 me.addContent(new Element("start-file").addContent(start_file)); 281 } 282 if (use_mid_sound) { 283 me.addContent(new Element("mid-file").addContent(mid_file)); 284 } 285 if (use_end_sound) { 286 me.addContent(new Element("end-file").addContent(end_file)); 287 } 288 if (use_short_sound) { 289 me.addContent(new Element("short-file").addContent(short_file)); 290 } 291 292 return me; 293 } 294 295 @Override 296 public void setXml(Element e) { 297 this.setXml(e, null); 298 } 299 300 public void setXml(Element e, VSDFile vf) { 301 log.debug("ConfigurableSound: {}", e.getAttributeValue("name")); 302 if (((start_file = e.getChildText("start-file")) != null) && (!start_file.isEmpty())) { 303 use_start_sound = true; 304 } else { 305 use_start_sound = false; 306 } 307 if (((mid_file = e.getChildText("mid-file")) != null) && (!mid_file.isEmpty())) { 308 use_mid_sound = true; 309 } else { 310 use_mid_sound = false; 311 } 312 if (((end_file = e.getChildText("end-file")) != null) && (!end_file.isEmpty())) { 313 use_end_sound = true; 314 } else { 315 use_end_sound = false; 316 } 317 if (((short_file = e.getChildText("short-file")) != null) && (!short_file.isEmpty())) { 318 use_short_sound = true; 319 } else { 320 use_short_sound = false; 321 } 322 323 String g = e.getChildText("gain"); 324 if ((g != null) && (!g.isEmpty())) { 325 gain = Float.parseFloat(g); 326 } else { 327 gain = default_gain; 328 } 329 330 String rds = e.getChildText("reference-distance"); 331 if ((rds != null) && (!rds.isEmpty())) { 332 rd = Float.parseFloat(rds); 333 } else { 334 rd = default_reference_distance; 335 } 336 337 /* 338 log.debug("Use: start: {}, mid: {}, end: {}, short: {}", use_start_sound, 339 use_mid_sound, use_end_sound, use_short_sound); 340 */ 341 // Reboot the sound 342 initialized = false; 343 this.init(vf); 344 } 345 346 private final static Logger log = LoggerFactory.getLogger(ConfigurableSound.class); 347 348}