/******************************************************************************* * Copyright (c) 2003, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - Initial API and implementation *******************************************************************************/ package org.eclipse.wst.server.ui.internal.audio; import java.util.*; import java.io.*; import java.net.URL; import javax.sound.sampled.*; import org.eclipse.core.runtime.*; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.wst.server.ui.internal.ServerUIPlugin; import org.eclipse.wst.server.ui.internal.Trace; /** * Main audio plugin class. */ public class AudioCore { protected static AudioCore instance; public static final String PREF_SOUND_ENABLED = "soundEnabled"; public static final String PREF_VOLUME = "volume"; public static final String SOUNDS_FILE = "sounds.xml"; public static final String DISABLED_FILE = "disabled-sounds.xml"; // Categories - map of String id to String names private Map<String, String> categories; // Sounds - map of String id to Sound private Map<String, Sound> sounds; // specific sounds or categories that have been disabled, by id private List<String> disabledSounds; private List<String> disabledCategories; // SoundMap - map of String id to an IPath private Map<String, IPath> userSoundMap; /** * AudioCore constructor comment. */ private AudioCore() { super(); loadExtensionPoints(); loadSoundMap(); loadDisabledLists(); } /** * Return the categories * * @return java.util.Map */ protected Map<String, String> getCategories() { return categories; } /** * Returns the audio clip. * * @param url java.net.URL * @return javax.sound.sampled.Clip */ protected static Clip getClip(URL url) { try { AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url); AudioFormat format = audioInputStream.getFormat(); /** * we can't yet open the device for ALAW/ULAW playback, * convert ALAW/ULAW to PCM */ if ((format.getEncoding() == AudioFormat.Encoding.ULAW) || (format.getEncoding() == AudioFormat.Encoding.ALAW)) { AudioFormat tmp = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), format.getSampleSizeInBits() * 2, format.getChannels(), format.getFrameSize() * 2, format.getFrameRate(), true); audioInputStream = AudioSystem.getAudioInputStream(tmp, audioInputStream); format = tmp; } DataLine.Info info = new DataLine.Info( Clip.class, audioInputStream.getFormat(), ((int) audioInputStream.getFrameLength() * format.getFrameSize())); Clip clip = (Clip) AudioSystem.getLine(info); clip.open(audioInputStream); return clip; } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not get clip: " + url, e); } } return null; } /** * Returns true if audio is currently available on this system. * * @return boolean */ protected static boolean isAudioSupported() { try { boolean sound = false; Mixer.Info[] info2 = AudioSystem.getMixerInfo(); if (info2 != null) { int size = info2.length; for (int i = 0; i < size; i++) { //Trace.trace(" " + info2[i]); Mixer mixer = AudioSystem.getMixer(info2[i]); if (mixer != null) { //Trace.trace(" Mixer:" + mixer); //Trace.trace(" " + mixer.getLineInfo()); try { Line.Info info = mixer.getLineInfo(); Line line = mixer.getLine(info); //Trace.trace(" Line:" + line); if (line != null && line.toString().indexOf("Output") >= 0) sound = true; } catch (Exception e) { // ignore } } } } return sound; } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not verify audio status", e); } } return true; } /** * Returns true if sound is enabled. * * @return boolean */ public boolean getDefaultSoundsEnabled() { return getPreferenceStore().getDefaultBoolean(PREF_SOUND_ENABLED); } /** * Returns the default volume. * * @return int */ public int getDefaultVolume() { return getPreferenceStore().getDefaultInt(PREF_VOLUME); } /** * Returns the singleton instance. * * @return org.eclipse.audio.internal.AudioCore */ public static AudioCore getInstance() { if (instance == null) instance = new AudioCore(); return instance; } /** * * @return org.eclipse.jface.preference.IPreferenceStore */ protected IPreferenceStore getPreferenceStore() { return ServerUIPlugin.getInstance().getPreferenceStore(); } /** * Returns the sound with the given id. * * @param id java.lang.String * @return org.eclipse.audio.Sound */ protected Sound getSound(String id) { try { return sounds.get(id); } catch (Exception e) { return null; } } /** * Return the sounds. * * @return java.util.Map */ protected Map<String, Sound> getSounds() { return sounds; } /** * Returns the full user sound map. * * @return java.util.Map */ protected Map<String, IPath> getUserSoundMap() { if (userSoundMap == null) loadSoundMap(); return userSoundMap; } /** * Return the current URL for this sound. * * @param id java.lang.String * @return java.net.URL */ protected IPath getUserSoundPath(String id) { try { if (userSoundMap == null) loadSoundMap(); IPath path = userSoundMap.get(id); if (path != null) return path; } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not get sound URL: " + id, e); } } return null; } /** * Returns the preferred volume. * * @return int */ public int getVolume() { return getPreferenceStore().getInt(PREF_VOLUME); } /** * Initialize the default preferences. * * @param store org.eclipse.jface.preference.IPreferenceStore */ public static void initializeDefaultPreferences(IPreferenceStore store) { store.setDefault(PREF_VOLUME, 18); } /** * Returns true if the given category is enabled. * * @param id java.lang.String * @return boolean */ public boolean isCategoryEnabled(String id) { if (id == null) return false; if (disabledCategories == null) loadDisabledLists(); return (!disabledCategories.contains(id)); } /** * Returns true if sound is enabled. * * @return boolean */ public boolean isSoundEnabled() { return getPreferenceStore().getBoolean(PREF_SOUND_ENABLED); } /** * Returns true if the given sound is enabled. * * @param id java.lang.String * @return boolean */ public boolean isSoundEnabled(String id) { if (id == null) return false; if (disabledSounds == null) loadDisabledLists(); return (!disabledSounds.contains(id)); } /** * Saves the disabled sound list. */ private void loadDisabledLists() { String filename = ServerUIPlugin.getInstance().getStateLocation().append(DISABLED_FILE).toOSString(); FileInputStream in = null; disabledCategories = new ArrayList<String>(); disabledSounds = new ArrayList<String>(); try { in = new FileInputStream(filename); IMemento memento = XMLMemento.loadMemento(in); IMemento cat = memento.getChild("categories"); IMemento[] children = cat.getChildren("category"); int size = children.length; for (int i = 0; i < size; i++) { try { IMemento child = children[i]; String id = child.getString("id"); disabledCategories.add(id); } catch (Exception ex) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Error reading URL map ", ex); } } } IMemento sound = memento.getChild("sounds"); children = sound.getChildren("sound"); size = children.length; for (int i = 0; i < size; i++) { try { IMemento child = children[i]; String id = child.getString("id"); disabledSounds.add(id); } catch (Exception ex) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Error reading URL map ", ex); } } } } catch (Exception e) { //AudioPlugin.log(new Status(IStatus.WARNING, AudioPlugin.PLUGIN_ID, 0, "Could not load disabled sound information", e)); } finally { if (in != null) { try { in.close(); } catch (Exception e) { // ignore } } } } /** * Load extension point. */ private void loadExtensionPoints() { // load extension points IExtensionRegistry registry = Platform.getExtensionRegistry(); IConfigurationElement[] cf = registry.getConfigurationElementsFor(ServerUIPlugin.PLUGIN_ID, "audio"); int size = cf.length; categories = new HashMap<String, String>(); sounds = new HashMap<String, Sound>(); for (int i = 0; i < size; i++) { try { String elementName = cf[i].getName(); String id = cf[i].getAttribute("id"); String name = cf[i].getAttribute("name"); if ("category".equals(elementName)) { categories.put(id, name); } else if ("sound".equals(elementName)) { String category = cf[i].getAttribute("category"); String location = cf[i].getAttribute("location"); URL realURL = null; if (location != null && location.length() > 0) { String pluginId = cf[i].getDeclaringExtension().getContributor().getName(); URL url = FileLocator.find(Platform.getBundle(pluginId), new Path(location), null); realURL = FileLocator.resolve(url); } Sound sound = new Sound(id, category, name, realURL); sounds.put(id, sound); } } catch (Throwable t) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not load audio: " + cf[i].getAttribute("id"), t); } } } } /** * Saves the disabled sound list. */ private void loadSoundMap() { String filename = ServerUIPlugin.getInstance().getStateLocation().append(SOUNDS_FILE).toOSString(); InputStream in = null; userSoundMap = new HashMap<String, IPath>(); try { in = new FileInputStream(filename); IMemento memento = XMLMemento.loadMemento(in); IMemento[] children = memento.getChildren("map"); int size = children.length; for (int i = 0; i < size; i++) { try { IMemento child = children[i]; String id = child.getString("id"); String pathStr = child.getString("path"); IPath path = new Path(pathStr); userSoundMap.put(id, path); } catch (Exception ex) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Error reading URL map ", ex); } } } } catch (Exception e) { // ignore } finally { if (in != null) { try { in.close(); } catch (Exception e) { // ignore } } } } /** * Play the sound with the given id. (provided that * the user has enabled the sound) * * @param id java.lang.String */ public void playSound(String id) { if (!isSoundEnabled()) return; if (!isSoundEnabled(id)) return; try { Sound sound = sounds.get(id); String category = sound.getCategory(); if (category != null && categories.containsKey(category)) { if (!isCategoryEnabled(category)) return; } URL url = sound.getLocation(); IPath path = getUserSoundPath(id); if (path != null) url = path.toFile().toURL(); playSound(url, getVolume()); } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Error playing audio: " + id, e); } } } /** * Plays the sound at the given url. * * @param url java.net.URL */ protected static void playSound(URL url, final int volume) { try { if (Trace.FINEST) { Trace.trace(Trace.STRING_FINEST, "playSound"); } if (url == null || volume <= 0) return; final Clip clip = getClip(url); if (clip == null) return; if (Trace.FINEST) { Trace.trace(Trace.STRING_FINEST, "playing"); } Thread t = new Thread("Sound Thread") { public void run() { // set gain FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); double value = volume / 20.0; float dB = (float) (Math.log(value==0.0?0.0001:value)/Math.log(10.0)*20.0); gainControl.setValue(dB); if (Trace.FINEST) { Trace.trace(Trace.STRING_FINEST, "start"); } clip.start(); try { sleep(99); } catch (Exception e) { // ignore } while (clip.isActive()) { try { sleep(99); } catch (Exception e) { break; } } clip.stop(); clip.close(); if (Trace.FINEST) { Trace.trace(Trace.STRING_FINEST, "stop"); } } }; t.setDaemon(true); t.start(); } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Error playing audio: " + url, e); } } } /** * Saves the disabled sounds and categories list. */ private void saveDisabledLists() { String filename = ServerUIPlugin.getInstance().getStateLocation().append(DISABLED_FILE).toOSString(); FileOutputStream fout = null; try { XMLMemento memento = XMLMemento.createWriteRoot("disabled"); IMemento cat = memento.createChild("categories"); Iterator iterator = disabledCategories.iterator(); while (iterator.hasNext()) { IMemento child = cat.createChild("category"); String id = (String) iterator.next(); child.putString("id", id); } IMemento sound = memento.createChild("sounds"); iterator = disabledSounds.iterator(); while (iterator.hasNext()) { IMemento child = sound.createChild("sound"); String id = (String) iterator.next(); child.putString("id", id); } fout = new FileOutputStream(filename); memento.save(fout); } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not save disabled information", e); } } finally { if (fout != null) { try { fout.close(); } catch (Exception e) { // ignore } } } } /** * Saves the disabled sound list. */ private void saveSoundMap() { String filename = ServerUIPlugin.getInstance().getStateLocation().append(SOUNDS_FILE).toOSString(); FileOutputStream fout = null; try { XMLMemento memento = XMLMemento.createWriteRoot("sound-map"); Iterator iterator = userSoundMap.keySet().iterator(); while (iterator.hasNext()) { IMemento child = memento.createChild("map"); String id = (String) iterator.next(); child.putString("id", id); IPath path = userSoundMap.get(id); child.putString("path", path.toString()); } fout = new FileOutputStream(filename); memento.save(fout); } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not save URL map information", e); } } finally { if (fout != null) { try { fout.close(); } catch (Exception e) { // ignore } } } } /** * Enable or disable a specific category. * * @param id java.lang.String * @param b boolean */ public void setCategoryEnabled(String id, boolean b) { if (id == null) return; if (disabledCategories == null) loadDisabledLists(); if (b) { if (disabledCategories.contains(id)) { disabledCategories.remove(id); saveDisabledLists(); } } else { if (!disabledCategories.contains(id)) { disabledCategories.add(id); saveDisabledLists(); } } } /** * Enable or disable a specific sound. * * @param id java.lang.String * @param b boolean */ public void setSoundEnabled(String id, boolean b) { if (id == null) return; if (disabledSounds == null) loadDisabledLists(); if (b) { if (disabledSounds.contains(id)) { disabledSounds.remove(id); saveDisabledLists(); } } else { if (!disabledSounds.contains(id)) { disabledSounds.add(id); saveDisabledLists(); } } } /** * Sets whether sound is enabled. * * @param enabled */ public void setSoundsEnabled(boolean enabled) { getPreferenceStore().setValue(PREF_SOUND_ENABLED, enabled); } /** * Sets the current URL for this sound. * * @param id java.lang.String * @param path IPath */ protected void setSoundURL(String id, IPath path) { if (id == null || path == null) return; try { if (userSoundMap == null) loadSoundMap(); userSoundMap.put(id, path); saveSoundMap(); } catch (Exception e) { if (Trace.SEVERE) { Trace.trace(Trace.STRING_SEVERE, "Could not get sound URL: " + id, e); } } } /** * Sets the full user sound map. * * @param map the sound map */ protected void setUserSoundMap(Map<String, IPath> map) { if (map != null) { userSoundMap = map; saveSoundMap(); } } /** * Sets the volume. * * @param volume the volume */ public void setVolume(int volume) { getPreferenceStore().setValue(PREF_VOLUME, volume); } }