001package jmri.jmrit.vsdecoder; 002 003import jmri.AudioException; 004import jmri.AudioManager; 005import jmri.jmrit.audio.AudioBuffer; 006import jmri.jmrit.audio.AudioSource; 007import jmri.util.PhysicalLocation; 008import org.slf4j.Logger; 009import org.slf4j.LoggerFactory; 010 011/** 012 * VSD implementation of an audio sound. 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 */ 029class SoundBite extends VSDSound { 030 031 public static enum BufferMode { 032 033 BOUND_MODE, QUEUE_MODE 034 } 035 036 String filename, system_name, user_name; 037 AudioBuffer sound_buf; 038 AudioSource sound_src; 039 boolean initialized = false; 040 boolean looped = false; 041 int minloops; 042 int maxloops; 043 float rd; 044 long length; 045 BufferMode bufferMode; 046 047 // Constructor for QUEUE_MODE. 048 public SoundBite(String name) { 049 super(name); 050 system_name = name; 051 user_name = name; 052 bufferMode = BufferMode.QUEUE_MODE; 053 initialized = init(null, bufferMode); 054 } 055 056 // Constructor for BOUND_MODE. 057 public SoundBite(VSDFile vf, String filename, String sname, String uname) { 058 super(uname); 059 this.filename = filename; 060 system_name = sname; 061 user_name = uname; 062 bufferMode = BufferMode.BOUND_MODE; 063 initialized = init(vf, bufferMode); 064 } 065 066 public String getFileName() { 067 return filename; 068 } 069 070 public String getSystemName() { 071 return system_name; 072 } 073 074 public String getUserName() { 075 return user_name; 076 } 077 078 public boolean isInitialized() { 079 return initialized; 080 } 081 082 public final boolean init(VSDFile vf, BufferMode mode) { 083 AudioManager am = jmri.InstanceManager.getDefault(jmri.AudioManager.class); 084 if (!initialized) { 085 try { 086 sound_src = (AudioSource) am.provideAudio(SrcSysNamePrefix + system_name); 087 sound_src.setUserName(SrcUserNamePrefix + user_name); 088 setLooped(false); 089 if (mode == BufferMode.BOUND_MODE) { 090 sound_buf = (AudioBuffer) am.provideAudio(BufSysNamePrefix + system_name); 091 sound_buf.setUserName(BufUserNamePrefix + user_name); 092 if (vf == null) { 093 log.debug("No VSD File! Filename: {}", filename); 094 sound_buf.setURL(filename); // Path must be provided by caller. 095 } else { 096 java.io.InputStream ins = vf.getInputStream(filename); 097 if (ins != null) { 098 sound_buf.setInputStream(ins); 099 } else { 100 return false; 101 } 102 } 103 sound_src.setAssignedBuffer(sound_buf); 104 setLength(); 105 } 106 } catch (AudioException | IllegalArgumentException ex) { 107 log.warn("Problem creating SoundBite", ex); 108 } 109 } 110 return true; 111 } 112 113 public void queueBuffer(AudioBuffer b) { 114 if (bufferMode == BufferMode.QUEUE_MODE) { 115 if (b == null) { 116 log.debug("queueAudioBuffer with null buffer input"); 117 return; 118 } 119 if (sound_src == null) { 120 log.debug("queueAudioBuffer with null sound_src"); 121 return; 122 } 123 log.debug("Queueing Buffer: {}", b.getSystemName()); 124 sound_src.queueBuffer(b); 125 } else { 126 log.warn("Attempted to Queue buffer to a Bound SoundBite."); 127 } 128 } 129 130 public void unqueueBuffers() { 131 if (bufferMode == BufferMode.QUEUE_MODE) { 132 sound_src.unqueueBuffers(); 133 } 134 } 135 136 public int numQueuedBuffers() { 137 if (bufferMode == BufferMode.QUEUE_MODE) { 138 return sound_src.numQueuedBuffers(); 139 } else { 140 return 0; 141 } 142 } 143 144 // Direct access to the underlying source. use with caution. 145 public AudioSource getSource() { 146 return sound_src; 147 } 148 149 // WARNING: This will go away when we go to shared buffers... or at least it will 150 // have to do the name lookup on behalf of the caller... 151 public AudioBuffer getBuffer() { 152 return sound_buf; 153 } 154 155 // These can(?) be used to get the underlying AudioSource and AudioBuffer objects 156 // from the DefaultAudioManager. 157 public String getSourceSystemName() { 158 return SrcSysNamePrefix + system_name; 159 } 160 161 public String getSourceUserName() { 162 return SrcUserNamePrefix + user_name; 163 } 164 165 public String getBufferSystemName() { 166 return BufSysNamePrefix + system_name; 167 } 168 169 public String getBufferUserName() { 170 return BufUserNamePrefix + user_name; 171 } 172 173 public void setLooped(boolean loop, int minloops, int maxloops) { 174 this.looped = loop; 175 this.minloops = minloops; 176 this.maxloops = maxloops; 177 sound_src.setLooped(looped); 178 sound_src.setMinLoops(minloops); 179 sound_src.setMaxLoops(maxloops); 180 } 181 182 public void setLooped(boolean loop) { 183 if (loop) { 184 this.setLooped(true, AudioSource.LOOP_CONTINUOUS, AudioSource.LOOP_CONTINUOUS); 185 } else { 186 this.setLooped(false, AudioSource.LOOP_NONE, AudioSource.LOOP_NONE); 187 } 188 } 189 190 public boolean isLooped() { 191 return looped; 192 } 193 194 public int getFadeInTime() { 195 return sound_src.getFadeIn(); 196 } 197 198 public int getFadeOutTime() { 199 return sound_src.getFadeOut(); 200 } 201 202 public void setFadeInTime(int t) { 203 sound_src.setFadeIn(t); 204 } 205 206 public void setFadeOutTime(int t) { 207 sound_src.setFadeOut(t); 208 } 209 210 public void setFadeTimes(int in, int out) { 211 sound_src.setFadeIn(in); 212 sound_src.setFadeOut(out); 213 } 214 215 public float getReferenceDistance() { 216 return sound_src.getReferenceDistance(); 217 } 218 219 public void setReferenceDistance(float r) { 220 this.rd = r; 221 sound_src.setReferenceDistance(rd); 222 } 223 224 @Override 225 public void shutdown() { 226 } 227 228 @Override 229 public void mute(boolean m) { 230 if (m) { 231 volume = sound_src.getGain(); 232 sound_src.setGain(0); 233 } else { 234 sound_src.setGain(volume); 235 } 236 } 237 238 @Override 239 public void setVolume(float v) { 240 volume = v * gain; 241 sound_src.setGain(volume); 242 } 243 244 @Override 245 public void play() { 246 sound_src.play(); 247 } 248 249 @Override 250 public void loop() { 251 sound_src.play(); 252 } 253 254 @Override 255 public void stop() { 256 sound_src.stop(); 257 } 258 259 public void pause() { 260 sound_src.pause(); 261 } 262 263 public void rewind() { 264 sound_src.rewind(); 265 } 266 267 @Override 268 public void fadeOut() { 269 // Skip the fade action if the fade out time is zero. 270 if (sound_src.getFadeOut() == 0) { 271 sound_src.stop(); 272 } else { 273 sound_src.fadeOut(); 274 } 275 } 276 277 @Override 278 public void fadeIn() { 279 // Skip the fade action if the fade in time is zero. 280 if (sound_src.getFadeIn() == 0) { 281 sound_src.play(); 282 } else { 283 sound_src.fadeIn(); 284 } 285 } 286 287 @Override 288 public void setPosition(PhysicalLocation v) { 289 super.setPosition(v); 290 sound_src.setPosition(v); 291 } 292 293 public void setURL(String filename) { 294 this.filename = filename; 295 sound_buf.setURL(filename); // Path must be provided by caller. 296 } 297 298 public long getLength() { 299 return length; 300 } 301 302 public int getLengthAsInt() { 303 // Note: this only works for positive lengths... 304 // Timer only takes an int... cap the length at MAXINT 305 if (length > Integer.MAX_VALUE) { 306 return Integer.MAX_VALUE; 307 } else { // small enough to safely cast. 308 return (int) length; 309 } 310 } 311 312 public void setLength(long p) { 313 length = p; 314 } 315 316 public void setLength() { 317 length = calcLength(this); 318 } 319 320 public static long calcLength(SoundBite s) { 321 return calcLength(s.getBuffer()); 322 } 323 324 public static long calcLength(AudioBuffer buf) { 325 // Assumes later getBuffer() will find the buffer from AudioManager instead 326 // of the current local reference... that's why I'm not directly using sound_buf here. 327 328 // Required buffer functions not yet implemented 329 long num_frames; 330 int frequency; 331 332 if (buf != null) { 333 num_frames = buf.getLength(); 334 frequency = buf.getFrequency(); 335 } else { 336 // No buffer attached! 337 num_frames = 0; 338 frequency = 0; 339 } 340 341 /* 342 long num_frames = 1; 343 long frequency = 125; 344 */ 345 if (frequency <= 0) { 346 // Protect against divide-by-zero errors 347 return 0L; 348 } else { 349 return (1000 * num_frames) / frequency; 350 } 351 } 352 353 private static final Logger log = LoggerFactory.getLogger(SoundBite.class); 354}