001package jmri.jmrit.audio; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.util.SortedSet; 005import java.util.TreeSet; 006import javax.sound.sampled.AudioSystem; 007import javax.sound.sampled.Mixer; 008import jmri.Audio; 009import jmri.AudioManager; 010import jmri.InstanceManager; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * This is the JavaSound audio system specific AudioFactory. 016 * <p> 017 * The JavaSound sound system supports, where available, 2-channel stereo. 018 * <p> 019 * The implemented Audio objects provide an approximation of a 3D positionable 020 * audio model through the use of calculated panning and gain based on the 3D 021 * position of the individual sound sources. 022 * <p> 023 * This factory initialises JavaSound, provides new JavaSound-specific Audio 024 * objects and deals with clean-up operations. 025 * <p> 026 * For more information about the JavaSound API, visit 027 * <a href="http://java.sun.com/products/java-media/sound/">http://java.sun.com/products/java-media/sound/</a> 028 * 029 * <hr> 030 * This file is part of JMRI. 031 * <p> 032 * JMRI is free software; you can redistribute it and/or modify it under the 033 * terms of version 2 of the GNU General Public License as published by the Free 034 * Software Foundation. See the "COPYING" file for a copy of this license. 035 * <p> 036 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 037 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 038 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 039 * 040 * @author Matthew Harris copyright (c) 2009 041 */ 042public class JavaSoundAudioFactory extends AbstractAudioFactory { 043 044 private static boolean initialised = false; 045 046 private volatile static Mixer mixer; 047 048 private JavaSoundAudioListener activeAudioListener; 049 050 @Override 051 public boolean init() { 052 if (initialised) { 053 return true; 054 } 055 056 // Initialise JavaSound 057 if (JavaSoundAudioFactory.mixer == null) { 058 // Iterate through possible mixers until we find the one we require 059 for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) { 060 if (mixerInfo.getName().equals("Java Sound Audio Engine")) { 061 JavaSoundAudioFactory.setMixer(AudioSystem.getMixer(mixerInfo)); 062 break; 063 } 064 } 065 } 066 // Check to see if a suitable mixer has been found 067 if (JavaSoundAudioFactory.mixer == null) { 068 log.debug("No JavaSound audio system found."); 069 return false; 070 } else { 071 if (log.isInfoEnabled()) { 072 log.info("Initialised JavaSound: vendor - {} version - {}", JavaSoundAudioFactory.mixer.getMixerInfo().getVendor(), JavaSoundAudioFactory.mixer.getMixerInfo().getVersion()); 073 } 074 } 075 076 super.init(); 077 setInit(true); 078 return true; 079 } 080 081 private synchronized static void setInit(boolean newVal) { 082 initialised = newVal; 083 } 084 085 private synchronized static void setMixer(Mixer newMixer) { 086 mixer = newMixer; 087 } 088 089 @Override 090 public String toString() { 091 return "JavaSoundAudioFactory:" 092 + " vendor - " + JavaSoundAudioFactory.mixer.getMixerInfo().getVendor() 093 + " version - " + JavaSoundAudioFactory.mixer.getMixerInfo().getVersion(); 094 } 095 096 @Override 097 @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", 098 justification = "OK to write to static variable mixer as we are cleaning up") 099 public void cleanup() { 100 // Stop the command thread 101 super.cleanup(); 102 103 // Get the active AudioManager 104 AudioManager am = InstanceManager.getDefault(jmri.AudioManager.class); 105 106 // Retrieve list of AudioSource objects and remove the sources 107 SortedSet<Audio> sources = new TreeSet<>(am.getNamedBeanSet(Audio.SOURCE)); 108 for (Audio source: sources) { 109 if (log.isDebugEnabled()) { 110 log.debug("Removing JavaSoundAudioSource: {}", source.getSystemName()); 111 } 112 // Includes cleanup 113 source.dispose(); 114 } 115 116 // Now, retrieve list of AudioBuffer objects and remove the buffers 117 SortedSet<Audio> buffers = new TreeSet<>(am.getNamedBeanSet(Audio.BUFFER)); 118 for (Audio buffer : buffers) { 119 if (log.isDebugEnabled()) { 120 log.debug("Removing JavaSoundAudioBuffer: {}", buffer.getSystemName()); 121 } 122 // Includes cleanup 123 buffer.dispose(); 124 } 125 126 // Lastly, retrieve list of AudioListener objects and remove listener. 127 SortedSet<Audio> listeners = new TreeSet<>(am.getNamedBeanSet(Audio.LISTENER)); 128 for (Audio listener : listeners) { 129 if (log.isDebugEnabled()) { 130 log.debug("Removing JavaSoundAudioListener: {}", listener.getSystemName()); 131 } 132 // Includes cleanup 133 listener.dispose(); 134 } 135 136 // Finally, shutdown JavaSound and close the output device 137 log.debug("Shutting down JavaSound"); 138 mixer = null; 139 initialised = false; 140 } 141 142 @Override 143 public boolean isInitialised() { 144 return initialised; 145 } 146 147 @Override 148 public AudioBuffer createNewBuffer(String systemName, String userName) { 149 return new JavaSoundAudioBuffer(systemName, userName); 150 } 151 152 @Override 153 public AudioListener createNewListener(String systemName, String userName) { 154 activeAudioListener = new JavaSoundAudioListener(systemName, userName); 155 return activeAudioListener; 156 } 157 158 @Override 159 public AudioListener getActiveAudioListener() { 160 return activeAudioListener; 161 } 162 163 @Override 164 public AudioSource createNewSource(String systemName, String userName) { 165 return new JavaSoundAudioSource(systemName, userName); 166 } 167 168 /** 169 * Return reference to the current JavaSound mixer object. 170 * 171 * @return current JavaSound mixer 172 */ 173 public static synchronized Mixer getMixer() { 174 return mixer; 175 } 176 177 private static final Logger log = LoggerFactory.getLogger(JavaSoundAudioFactory.class); 178 179}