package org.multibit.hd.ui.audio; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import org.multibit.commons.concurrent.SafeExecutors; import org.multibit.hd.core.config.SoundConfiguration; import org.spongycastle.util.io.Streams; import javax.sound.sampled.*; import java.awt.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.concurrent.ExecutorService; /** * <p>Factory to provide the following to UI:</p> * <ul> * <li>Provision of standard sounds</li> * </ul> * * @since 0.0.1 * */ public class Sounds { private static final Map<String, byte[]> allSounds = Maps.newHashMap(); /** * The name and key of the MP3 file containing the "payment received" sound */ private static final String PAYMENT_RECEIVED = "receive-bitcoin"; private static final ExecutorService executorService = SafeExecutors.newSingleThreadExecutor("play-received"); /** * Utilities have no public constructor */ private Sounds() { } public static void initialise() { allSounds.put(PAYMENT_RECEIVED, load(PAYMENT_RECEIVED)); } /** * Make a standard beep sound (useful for audio feedback of failure) * @param sound The Sound configuration to use */ public static void playBeep(SoundConfiguration sound) { if (sound.isAlertSound()) { Toolkit.getDefaultToolkit().beep(); } } /** * Plays the "payment received" sound in a new thread * @param sound The Sound configuration to use */ public static void playPaymentReceived(SoundConfiguration sound) { if (sound.isReceiveSound()) { executorService.submit(new Runnable() { @Override public void run() { play(PAYMENT_RECEIVED); } }); } } /** * @param name The name of the sound file (no extension) */ private static void play(String name) { Preconditions.checkState(allSounds.containsKey(name), "'" + name + "' must be present (did you initialise?)"); byte[] sound = allSounds.get(name); try { final Clip clip = AudioSystem.getClip(); clip.addLineListener(new LineListener() { @Override public void update(LineEvent event) { if (event.getType() == LineEvent.Type.STOP) clip.close(); } }); clip.open(AudioSystem.getAudioInputStream(new ByteArrayInputStream(sound))); clip.start(); } catch (UnsupportedAudioFileException e) { throw new IllegalStateException(e.getMessage()); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } catch (LineUnavailableException e) { throw new IllegalStateException(e.getMessage()); } } /** * @param name The name of the sound file (no extension) * * @return A byte[] containing the sound */ private static byte[] load(String name) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (InputStream is = Sounds.class.getResourceAsStream("/assets/sounds/" + name + ".wav")) { Streams.pipeAll(is, baos); } catch (IOException e) { throw new IllegalStateException(e.getMessage()); } return baos.toByteArray(); } }