001package jmri.jmrit.vsdecoder; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005import java.util.ArrayList; 006import java.util.HashMap; 007import java.util.Iterator; 008import jmri.util.PhysicalLocation; 009import org.jdom2.Element; 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013/** 014 * Diesel Sound initial version. 015 * 016 * <hr> 017 * This file is part of JMRI. 018 * <p> 019 * JMRI is free software; you can redistribute it and/or modify it under 020 * the terms of version 2 of the GNU General Public License as published 021 * by the Free Software Foundation. See the "COPYING" file for a copy 022 * of this license. 023 * <p> 024 * JMRI is distributed in the hope that it will be useful, but WITHOUT 025 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 026 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 027 * for more details. 028 * 029 * @author Mark Underwood Copyright (C) 2011 030 */ 031class DieselSound extends EngineSound { 032 033 // Engine Sounds 034 HashMap<Integer, SoundBite> notch_sounds; 035 ArrayList<NotchTransition> transition_sounds; 036 SoundBite start_sound; 037 SoundBite shutdown_sound; 038 NotchTransition notch_transition; // used for changing notches 039 040 int current_notch = 1; 041 042 public DieselSound(String name) { 043 super(name); 044 } 045 046 // Note: Play and Loop do the same thing, since all of the notch sounds are set to loop. 047 @Override 048 public void play() { 049 log.debug("EngineSound Play: current_notch = {}", current_notch); 050 if (notch_sounds.containsKey(current_notch) && (isEngineStarted() || auto_start_engine)) { 051 notch_sounds.get(current_notch).play(); 052 } 053 } 054 055 // Note: Play and Loop do the same thing, since all of the notch sounds are set to loop. 056 @Override 057 public void loop() { 058 if (notch_sounds.containsKey(current_notch) && (isEngineStarted() || auto_start_engine)) { 059 notch_sounds.get(current_notch).play(); 060 } 061 } 062 063 @Override 064 public void stop() { 065 if (notch_sounds.containsKey(current_notch)) { 066 notch_sounds.get(current_notch).stop(); 067 } 068 } 069 070 @Override 071 public void changeNotch(int new_notch) { 072 log.debug("EngineSound.changeNotch() current = {} new notch = {}", current_notch, new_notch); 073 if (new_notch != current_notch) { 074 if (notch_sounds.containsKey(current_notch) && (isEngineStarted() || auto_start_engine)) { 075 notch_sounds.get(current_notch).fadeOut(); 076 } 077 078 notch_transition = findNotchTransient(current_notch, new_notch); 079 if (notch_transition != null) { 080 log.debug("notch transition: name = {} length = {}, fade_length = {}", notch_transition.getFileName(), notch_transition.getLengthAsInt(), fade_length); 081 // Handle notch transition... 082 t = newTimer(notch_transition.getLengthAsInt() - notch_sounds.get(new_notch).getFadeInTime(), false, 083 new ActionListener() { 084 @Override 085 public void actionPerformed(ActionEvent e) { 086 handleNotchTimerPop(e); 087 } 088 }); 089 t.start(); 090 notch_transition.fadeIn(); 091 } else { 092 log.debug("notch transition not found!"); 093 if (notch_sounds.containsKey(new_notch) && (isEngineStarted() || auto_start_engine)) { 094 notch_sounds.get(new_notch).fadeIn(); 095 } 096 } 097 current_notch = new_notch; 098 } 099 } 100 101 protected void handleNotchTimerPop(ActionEvent e) { 102 // notch value has already been changed 103 log.debug("Notch timer pop. nt.next_notch = {}, file = {}", notch_transition.getNextNotch(), notch_sounds.get(notch_transition.getNextNotch()).getFileName()); 104 if (notch_sounds.containsKey(notch_transition.getNextNotch()) && (isEngineStarted() || auto_start_engine)) { 105 notch_sounds.get(notch_transition.getNextNotch()).fadeIn(); 106 } 107 notch_transition.fadeOut(); 108 } 109 110 private NotchTransition findNotchTransient(int prev, int next) { 111 log.debug("Looking for Transient: prev = {} next = {}", prev, next); 112 for (NotchTransition nt : transition_sounds) { 113 log.debug("searching: nt.prev = {} nt.next = {}", nt.getPrevNotch(), nt.getNextNotch()); 114 if ((nt.getPrevNotch() == prev) && (nt.getNextNotch() == next)) { 115 log.debug("Found transient: prev = {} next = {}", nt.getPrevNotch(), nt.getNextNotch()); 116 return nt; 117 } 118 } 119 // If we loop out, there's no transition that matches. 120 return null; 121 } 122 123 @Override 124 public void startEngine() { 125 start_sound.play(); 126 current_notch = calcEngineNotch(0.0f); 127 //t = newTimer(4500, false, new ActionListener() { 128 t = newTimer(start_sound.getLengthAsInt() - start_sound.getFadeOutTime(), false, new ActionListener() { 129 @Override 130 public void actionPerformed(ActionEvent e) { 131 startToIdleAction(e); 132 } 133 }); 134 //t.setInitialDelay(4500); 135 t.setInitialDelay(start_sound.getLengthAsInt() - start_sound.getFadeOutTime()); 136 t.setRepeats(false); 137 log.debug("Starting Engine"); 138 t.start(); 139 } 140 141 @Override 142 public void stopEngine() { 143 notch_sounds.get(current_notch).fadeOut(); 144 shutdown_sound.play(); 145 setEngineStarted(false); 146 } 147 148 private void startToIdleAction(ActionEvent e) { 149 log.debug("Starting idle sound notch = {} sound = {}", current_notch, notch_sounds.get(current_notch)); 150 notch_sounds.get(current_notch).loop(); 151 setEngineStarted(true); 152 } 153 154 @Override 155 public void shutdown() { 156 for (SoundBite ns : notch_sounds.values()) { 157 ns.stop(); 158 } 159 for (NotchTransition nt : transition_sounds) { 160 nt.stop(); 161 } 162 if (start_sound != null) { 163 start_sound.stop(); 164 } 165 if (shutdown_sound != null) { 166 shutdown_sound.stop(); 167 } 168 } 169 170 @Override 171 public void mute(boolean m) { 172 for (SoundBite ns : notch_sounds.values()) { 173 ns.mute(m); 174 } 175 for (NotchTransition nt : transition_sounds) { 176 nt.mute(m); 177 } 178 if (start_sound != null) { 179 start_sound.mute(m); 180 } 181 if (shutdown_sound != null) { 182 shutdown_sound.mute(m); 183 } 184 } 185 186 @Override 187 public void setVolume(float v) { 188 for (SoundBite ns : notch_sounds.values()) { 189 ns.setVolume(v); 190 } 191 for (NotchTransition nt : transition_sounds) { 192 nt.setVolume(v); 193 } 194 if (start_sound != null) { 195 start_sound.setVolume(v); 196 } 197 if (shutdown_sound != null) { 198 shutdown_sound.setVolume(v); 199 } 200 } 201 202 @Override 203 public void setPosition(PhysicalLocation p) { 204 for (SoundBite ns : notch_sounds.values()) { 205 ns.setPosition(p); 206 } 207 for (NotchTransition nt : transition_sounds) { 208 nt.setPosition(p); 209 } 210 if (start_sound != null) { 211 start_sound.setPosition(p); 212 } 213 if (shutdown_sound != null) { 214 shutdown_sound.setPosition(p); 215 } 216 } 217 218 @Override 219 public Element getXml() { 220 Element me = new Element("sound"); 221 me.setAttribute("name", this.getName()); 222 me.setAttribute("type", "engine"); 223 // Do something, eventually... 224 return me; 225 } 226 227 @Override 228 public void setXml(Element e, VSDFile vf) { 229 Element el; 230 //int num_notches; 231 String fn; 232 SoundBite sb; 233 234 // Handle the common stuff. 235 super.setXml(e, vf); 236 237 log.debug("Diesel EngineSound: {}", e.getAttribute("name").getValue()); 238 notch_sounds = new HashMap<Integer, SoundBite>(); 239 transition_sounds = new ArrayList<NotchTransition>(); 240 241 // Get the notch sounds 242 Iterator<Element> itr = (e.getChildren("notch-sound")).iterator(); 243 int i = 0; 244 while (itr.hasNext()) { 245 el = itr.next(); 246 fn = el.getChildText("file"); 247 int nn = Integer.parseInt(el.getChildText("notch")); 248 sb = new SoundBite(vf, fn, "Engine_n" + i, "Engine_" + i); 249 sb.setLooped(true); 250 sb.setFadeTimes(this.getFadeInTime(), this.getFadeOutTime()); 251 sb.setGain(setXMLGain(el)); 252 // Store in the list. 253 notch_sounds.put(nn, sb); 254 i++; 255 } 256 257 // Get the notch transitions 258 itr = (e.getChildren("notch-transition")).iterator(); 259 i = 0; 260 NotchTransition nt; 261 while (itr.hasNext()) { 262 el = itr.next(); 263 fn = el.getChildText("file"); 264 nt = new NotchTransition(vf, fn, "Engine_nt" + i, "Engine_nt" + i); 265 nt.setPrevNotch(Integer.parseInt(el.getChildText("prev-notch"))); 266 nt.setNextNotch(Integer.parseInt(el.getChildText("next-notch"))); 267 nt.setLooped(false); 268 nt.setFadeTimes(this.getFadeInTime(), this.getFadeOutTime()); 269 // Handle gain 270 nt.setGain(setXMLGain(el)); 271 transition_sounds.add(nt); 272 i++; 273 } 274 275 // Get the start and stop sounds 276 el = e.getChild("start-sound"); 277 if (el != null) { 278 fn = el.getChild("file").getValue(); 279 start_sound = new SoundBite(vf, fn, "Engine_start", 280 "Engine_Start"); 281 // Handle gain 282 start_sound.setGain(setXMLGain(el)); 283 start_sound.setFadeTimes(this.getFadeInTime(), this.getFadeOutTime()); 284 start_sound.setLooped(false); 285 } 286 el = e.getChild("shutdown-sound"); 287 if (el != null) { 288 fn = el.getChild("file").getValue(); 289 shutdown_sound = new SoundBite(vf, fn, "Engine_shutdown", "Engine_Shutdown"); 290 shutdown_sound.setLooped(false); 291 // Handle gain 292 shutdown_sound.setGain(setXMLGain(el)); 293 shutdown_sound.setFadeTimes(this.getFadeInTime(), this.getFadeOutTime()); 294 } 295 } 296 297 private static final Logger log = LoggerFactory.getLogger(DieselSound.class); 298 299}