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