001package jmri.jmrit.vsdecoder; 002 003import java.awt.event.ActionEvent; 004import java.awt.event.ActionListener; 005import javax.swing.SwingUtilities; 006import org.jdom2.Element; 007 008/** 009 * Superclass for Steam, Diesel and Electric Sound. 010 * 011 * <hr> 012 * This file is part of JMRI. 013 * <p> 014 * JMRI is free software; you can redistribute it and/or modify it under 015 * the terms of version 2 of the GNU General Public License as published 016 * by the Free Software Foundation. See the "COPYING" file for a copy 017 * of this license. 018 * <p> 019 * JMRI is distributed in the hope that it will be useful, but WITHOUT 020 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 021 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 022 * for more details. 023 * 024 * @author Mark Underwood Copyright (C) 2011 025 * @author Klaus Killinger Copyright (C) 2018, 2021 026 */ 027public class EngineSound extends VSDSound { 028 029 private boolean engine_started = false; 030 boolean auto_start_engine = false; 031 boolean is_auto_start; // Can be used in config.xml 032 private boolean is_first = false; 033 034 int fade_length = 100; 035 int fade_in_time = 100; 036 int fade_out_time = 100; 037 038 float engine_rd; 039 float engine_gain; 040 int sleep_interval; 041 float exponent; 042 private float actual_speed; 043 044 EnginePane engine_pane; 045 046 public EngineSound(String name) { 047 super(name); 048 setEngineStarted(false); 049 auto_start_engine = VSDecoderManager.instance().getVSDecoderPreferences().isAutoStartingEngine(); 050 } 051 052 // Note: Play and Loop do the same thing, since all of the notch sounds are set to loop. 053 @Override 054 public void play() { 055 log.debug("EngineSound Play"); 056 } 057 058 // Note: Play and Loop do the same thing, since all of the notch sounds are set to loop. 059 @Override 060 public void loop() { 061 log.debug("EngineSound Loop"); 062 } 063 064 @Override 065 public void stop() { 066 log.info("Emergency Stop called!"); 067 } 068 069 @Override 070 public void fadeIn() { 071 this.play(); 072 } 073 074 @Override 075 public void fadeOut() { 076 this.stop(); 077 } 078 079 public int getFadeInTime() { 080 return this.fade_in_time; 081 } 082 083 public int getFadeOutTime() { 084 return this.fade_out_time; 085 } 086 087 protected void setFadeInTime(int t) { 088 this.fade_in_time = t; 089 } 090 091 protected void setFadeInTime(String s) { 092 if (s == null) { 093 log.debug("setFadeInTime null string"); 094 return; 095 } 096 try { 097 this.setFadeInTime(Integer.parseInt(s)); 098 } catch (NumberFormatException e) { 099 log.debug("setFadeInTime Failed to parse int from: {}", s); 100 } 101 } 102 103 protected void setFadeOutTime(int t) { 104 this.fade_out_time = t; 105 } 106 107 protected void setFadeOutTime(String s) { 108 if (s == null) { 109 log.debug("setFadeInTime null string"); 110 return; 111 } 112 113 try { 114 this.setFadeOutTime(Integer.parseInt(s)); 115 } catch (NumberFormatException e) { 116 log.debug("setFadeOutTime Failed to parse int from: {}", s); 117 } 118 } 119 120 static final public int calcEngineNotch(final float throttle) { 121 // This will convert to a value 0-8. 122 int notch = ((int) Math.rint(throttle * 8)) + 1; 123 if (notch < 1) { 124 notch = 1; 125 } 126 log.debug("Throttle: {}, Notch: {}", throttle, notch); 127 return notch; 128 } 129 130 static final public int calcEngineNotch(final double throttle) { 131 // This will convert from a % to a value 0-8. 132 int notch = ((int) Math.rint(throttle * 8)) + 1; 133 if (notch < 1) { 134 notch = 1; 135 } 136 return notch; 137 } 138 139 // This is the default behavior. Subclasses can do fancier things 140 // if they want. 141 public void handleSpeedChange(Float s, EnginePane e) { 142 engine_pane = e; 143 engine_pane.setSpeed(s); 144 } 145 146 void setFirstSpeed(boolean f) { 147 is_first = f; 148 } 149 150 boolean getFirstSpeed() { 151 return is_first; 152 } 153 154 void setActualSpeed(float a) { 155 actual_speed = a; 156 } 157 158 public float getActualSpeed() { 159 return actual_speed; 160 } 161 162 double speedCurve(float t) { 163 return Math.pow(t, exponent); 164 } 165 166 public void startEngine() { 167 log.debug("Starting Engine"); 168 } 169 170 public void stopEngine() { 171 } 172 173 public boolean isEngineStarted() { 174 return engine_started; 175 } 176 177 public void setEngineStarted(boolean es) { 178 engine_started = es; 179 } 180 181 public void functionKey(String e, boolean v, String n) { 182 } 183 184 public void changeLocoDirection(int d) { 185 } 186 187 @Override 188 public void shutdown() { 189 // do nothing. 190 } 191 192 @Override 193 public void mute(boolean m) { 194 // do nothing. 195 } 196 197 @Override 198 public void setVolume(float v) { 199 // do nothing. 200 } 201 202 // Note: We have to invoke engine_pane later because everything's not really setup yet 203 // Need some more time to get the speed from the assigned throttle 204 void autoStartCheck() { 205 if (auto_start_engine || is_auto_start) { 206 SwingUtilities.invokeLater(() -> { 207 t = newTimer(40, false, new ActionListener() { 208 @Override 209 public void actionPerformed(ActionEvent e) { 210 if (engine_pane != null && getFirstSpeed()) { 211 engine_pane.startButtonClick(); 212 } else { 213 log.warn("engine pane or speed not found"); 214 } 215 } 216 }); 217 t.start(); 218 }); 219 } 220 } 221 222 protected boolean setXMLAutoStart(Element e) { 223 String a = e.getChildText("auto-start"); 224 if ((a != null) && (a.equals("yes"))) { 225 return true; 226 } else { 227 return false; 228 } 229 } 230 231 protected float setXMLExponent(Element e) { 232 String ex = e.getChildText("exponent"); 233 if (ex != null) { 234 try { 235 return Float.parseFloat(ex.trim()); 236 } catch (NumberFormatException en) { 237 log.warn("invalid exponent; default {} used", default_exponent); 238 } 239 } 240 return default_exponent; 241 } 242 243 protected float setXMLGain(Element e) { 244 String g = e.getChildText("gain"); 245 log.debug(" gain: {}", g); 246 if ((g != null) && !(g.isEmpty())) { 247 return Float.parseFloat(g); 248 } else { 249 return default_gain; 250 } 251 } 252 253 protected float setXMLReferenceDistance(Element e) { 254 String a = e.getChildText("reference-distance"); 255 if ((a != null) && (!a.isEmpty())) { 256 return Float.parseFloat(a); 257 } else { 258 return default_reference_distance; 259 } 260 } 261 262 protected float setXMLEngineReferenceDistance(Element e) { 263 String a = e.getChildText("engine-reference-distance"); 264 if ((a != null) && (!a.isEmpty())) { 265 return Float.parseFloat(a); 266 } else { 267 return default_reference_distance; 268 } 269 } 270 271 protected int setXMLSleepInterval(Element e) { 272 String a = e.getChildText("sleep-interval"); 273 if ((a != null) && (!a.isEmpty())) { 274 // Make some restrictions, since the variable is used for calculations later 275 int sleep_interval = Integer.parseInt(a); 276 if ((sleep_interval < 38) || (sleep_interval > 55)) { 277 log.info("Invalid sleep-interval {} was set to default {}", sleep_interval, default_sleep_interval); 278 return default_sleep_interval; 279 } else { 280 return sleep_interval; 281 } 282 } else { 283 return default_sleep_interval; 284 } 285 } 286 287 @Override 288 public Element getXml() { 289 Element me = new Element("sound"); 290 me.setAttribute("name", this.getName()); 291 me.setAttribute("type", "engine"); 292 // Do something, eventually... 293 return me; 294 } 295 296 public void setXml(Element e, VSDFile vf) { 297 // Do only the stuff common... 298 if (this.getName() == null) { 299 this.setName(e.getAttributeValue("name")); 300 } 301 this.setFadeInTime(e.getChildText("fade-in-time")); 302 this.setFadeOutTime(e.getChildText("fade-out-time")); 303 log.debug("Name: {}, Fade-In-Time: {}, Fade-Out-Time: {}", this.getName(), 304 this.getFadeInTime(), this.getFadeOutTime()); 305 } 306 307 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(EngineSound.class); 308 309}