package org.geogebra.desktop.sound; import java.io.FileInputStream; import java.io.InputStream; import java.net.URL; import javax.swing.SwingUtilities; import org.geogebra.common.kernel.geos.GeoFunction; import org.geogebra.common.sound.SoundManager; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.sound.mp3transform.Decoder; /** * Class to handle GeoGebra sound features. Calls to midi and streaming audio * methods are managed from here. * * @author G. Sturr * */ public class SoundManagerD implements SoundManager { private AppD app; private MidiSoundD midiSound; private FunctionSoundD functionSound; private static final int SOUNDTYPE_NONE = -1; private static final int SOUNDTYPE_MIDI = 0; private static final int SOUNDTYPE_FUNCTION = 1; int currentSoundType = SOUNDTYPE_NONE; private boolean isRunning = false; private boolean isPaused = false; /** * Constructor * * @param app */ public SoundManagerD(AppD app) { this.app = app; } // ==================================== // Getters/setters // ==================================== public boolean isRunning() { return isRunning; } public boolean isPaused() { return isPaused; } /** * Retrieves field midiSound. Creates a new instance of MidiSound if none * exists. */ MidiSoundD getMidiSound() { if (midiSound == null) { try { midiSound = new MidiSoundD(app); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return midiSound; } /** * Retrieves field functionSound. Creates a new instance of FunctionSound if * none exists. */ private FunctionSoundD getFunctionSound() { if (functionSound == null) { try { functionSound = new FunctionSoundD(); } catch (Exception e) { Log.error("Problem in getFunctionSound(): " + e.getMessage()); } } return functionSound; } // ==================================== // Sound playing methods // ==================================== /** * Plays a single note using the midi sequencer. * * @param note * @param duration * @param instrument * @param velocity */ @Override public void playSequenceNote(final int note, final double duration, final int instrument, final int velocity) { try { stopCurrentSound(); currentSoundType = SOUNDTYPE_MIDI; getMidiSound().playSequenceNote(note, duration, instrument); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * decoder to play MP3s */ Decoder decoder; /** * Plays an audio file with the .mid / .mp3 * * @param fileName */ @Override public void playFile(final String fileName) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { if (fileName.startsWith("#") || !(fileName.endsWith(".midi") && fileName.endsWith(".mid"))) { InputStream is; if (fileName.startsWith("#")) { // eg PlaySound["#12345"] to play from GeoGebraTube String id = fileName.substring(1); String url = app.getURLforID(id); // #5094 is = new URL(url).openStream(); } else if (fileName.startsWith("http:") || fileName.startsWith("https:")) { is = new URL(fileName).openStream(); } else { // assume local file // eg c:\ // eg file:// is = new FileInputStream(fileName); } if (decoder == null) { decoder = new Decoder(); } Thread thread = new Thread( new PlayMP3Thread(decoder, fileName, is)); thread.start(); return; } // not .mp3, must be .mid stopCurrentSound(); currentSoundType = SOUNDTYPE_MIDI; getMidiSound().playMidiFile(fileName); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Plays a sequence of notes generated by the string noteSring using the * midi sequencer. * * @param noteString * @param instrument */ @Override public void playSequenceFromString(String noteString, int instrument) { try { stopCurrentSound(); currentSoundType = SOUNDTYPE_MIDI; getMidiSound().playSequenceFromJFugueString(noteString, instrument); } catch (Exception e) { e.printStackTrace(); } } /** * Plays a tone generated by the time-valued input function f(t) for t = min * to t = max seconds. Also allows adjustment of the sampling rate and bit * depth. * * @param f * @param min * @param max * @param sampleRate * @param bitDepth */ @Override public void playFunction(final GeoFunction f, final double min, final double max, final int sampleRate, final int bitDepth) { try { stopCurrentSound(); currentSoundType = SOUNDTYPE_FUNCTION; getFunctionSound().playFunction(f, min, max, sampleRate, bitDepth); } catch (Exception e) { e.printStackTrace(); } } /** * Plays a tone generated by the time-valued input function f(t) for t = min * to t = max seconds. * * @param f * @param min * @param max */ @Override public void playFunction(final GeoFunction f, final double min, final double max) { try { stopCurrentSound(); currentSoundType = SOUNDTYPE_FUNCTION; getFunctionSound().playFunction(f, min, max); } catch (Exception e) { Log.error("Problem in playFunction(): " + e.getMessage()); } } // ==================================== // Control methods // ==================================== /** * Stops all sound creation and closes all sound-related resources. */ public void stopCurrentSound() { if (midiSound != null) { midiSound.stop(); } if (functionSound != null) { functionSound.pause(true); } } /** * Pauses/resumes current sound. * * @param doResume * : true = resume play, false = pause */ @Override public void pauseResumeSound(boolean doResume) { if (currentSoundType == SOUNDTYPE_MIDI && midiSound != null) { midiSound.pause(!doResume); } if (currentSoundType == SOUNDTYPE_FUNCTION && functionSound != null) { functionSound.pause(!doResume); } isPaused = !doResume; } }