/* * (c) 2000-2009 Carlos G�mez Rodr�guez, todos los derechos reservados / all rights reserved. * Licencia en license/bsd.txt / License in license/bsd.txt */ package eu.irreality.age; import java.io.*; import java.net.URL; import java.nio.ByteOrder; import java.util.*; import java.util.logging.Level; //for MOD import micromod.*; import micromod.resamplers.*; import micromod.output.*; import micromod.output.converters.*; import javax.sound.midi.MidiSystem; import javax.sound.midi.MidiUnavailableException; import javax.sound.midi.Receiver; import javax.sound.midi.ShortMessage; import javax.sound.sampled.*; import javazoom.jlgui.basicplayer.BasicController; import javazoom.jlgui.basicplayer.BasicPlayer; import javazoom.jlgui.basicplayer.BasicPlayerEvent; import javazoom.jlgui.basicplayer.BasicPlayerException; import javazoom.jlgui.basicplayer.BasicPlayerListener; import eu.irreality.age.debug.Debug; import eu.irreality.age.filemanagement.URLUtils; //Viene a ser la API de sonido del AGE. public class AGESoundClient implements SoundClient { //MIDI private javax.sound.midi.Sequencer seqr; private javax.sound.midi.Sequence curseq; private java.util.Hashtable midiPreloaded = new java.util.Hashtable(); private boolean on = true; public boolean isOn() { return on; } public void activate() { on = true; } public void deactivate() { stopAllSound(); on = false; } //gets a MIDI sequence which has been preloaded. //return null if it isn't preloaded. private javax.sound.midi.Sequence getPreloadedSequence ( URL u ) { //try //{ return (javax.sound.midi.Sequence) midiPreloaded.get ( u ); //} //catch ( java.io.IOException ioe ) //of getCanonicalPath() //{ // return null; //} } //call midiInit before using midiXXX functions. Initializes the sequencer. public void midiInit ( ) throws javax.sound.midi.MidiUnavailableException { //pulseaudio-friendly mode: midiClose(); if ( seqr == null ) { seqr = javax.sound.midi.MidiSystem.getSequencer(); seqr.open(); } } //loads a MIDI file into memory (hashtable) so that you don't have to open it each time you play it. public void midiPreload ( URL midfile ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException { javax.sound.midi.Sequence seq = javax.sound.midi.MidiSystem.getSequence ( midfile ); midiPreloaded.put ( midfile , seq ); } //unloads a MIDI file from memory. (does nothing if file invalid) public void midiUnload ( URL midfile ) throws java.io.IOException { midiPreloaded.remove ( midfile ); } public void midiUnload ( String s ) throws java.io.IOException { midiUnload ( URLUtils.stringToURL ( s ) ); } //loads a MIDI file (or not if preloaded) and starts playing it. If the manager was playing another file, playback stops. public void midiStart ( URL midfile ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException, MidiUnavailableException { if ( !isOn() ) return; //if ( seqr == null ) midiInit(); curseq = getPreloadedSequence ( midfile ); if ( curseq == null ) curseq = javax.sound.midi.MidiSystem.getSequence ( midfile ); //if ( !seqr.isOpen() ) seqr.open(); if ( seqr.isRunning() ) seqr.stop(); seqr.setLoopCount ( 0 ); seqr.setSequence ( curseq ); //for fade-out, did not work /* synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); if (synthesizer.getDefaultSoundbank() == null) { seqr.getTransmitter().setReceiver(MidiSystem.getReceiver()); } else { seqr.getTransmitter().setReceiver(synthesizer.getReceiver()); } */ seqr.start(); } //loads a MIDI file (or not if preloaded) and starts playing it. If the manager was playing another file, playback stops. public void midiStart ( String f ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException, MidiUnavailableException { if ( !isOn() ) return; midiStart ( URLUtils.stringToURL ( f ) ); } //loads a MIDI file into memory (hashtable) so that you don't have to open it each time you play it. public void midiPreload ( String f ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException { midiPreload ( URLUtils.stringToURL ( f ) ); } //sets the current file in the sequencer to the given file. public void midiOpen ( URL midfile ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException { curseq = getPreloadedSequence ( midfile ); if ( curseq == null ) curseq = javax.sound.midi.MidiSystem.getSequence ( midfile ); } //sets the current file in the sequencer to the given file. public void midiOpen ( String f ) throws javax.sound.midi.InvalidMidiDataException , java.io.IOException { midiOpen ( URLUtils.stringToURL ( f ) ); } public void midiLoop ( int loopCount ) throws javax.sound.midi.InvalidMidiDataException { if ( !isOn() ) return; if ( seqr.isRunning() ) seqr.stop(); seqr.setSequence ( curseq ); seqr.setLoopCount( loopCount ); seqr.start(); } //starts looping the current file indefinitely. public void midiLoop ( ) throws javax.sound.midi.InvalidMidiDataException { if ( !isOn() ) return; midiLoop ( javax.sound.midi.Sequencer.LOOP_CONTINUOUSLY ); } //starts playing the current file. Only works if we did a midiOpen() on the file. public void midiStart ( ) throws javax.sound.midi.InvalidMidiDataException { if ( !isOn() ) return; //if ( seqr == null ) midiInit(); //if ( !seqr.isOpen() ) seqr.open(); if ( seqr.isRunning() ) seqr.stop(); seqr.setSequence ( curseq ); seqr.setLoopCount ( 0 ); seqr.start(); } //stops playback. public void midiStop ( ) { //if ( seqr == null ) midiInit(); seqr.stop(); } //sets the current file in the sequencer to none, and closes the sequencer. public void midiClose ( ) { curseq = null; if ( seqr != null && seqr.isOpen() ) seqr.close(); seqr = null; } //DOES NOT WORK javax.sound.midi.Synthesizer synthesizer; javax.sound.midi.Synthesizer synthDevice; private static final int CHANGE_VOLUME = 7; //DOES NOT WORK public void midiResetGain( double gain ) { // make sure the value for gain is valid (between 0 and 1) if( gain < 0.0d ) gain = 0.0d; if( gain > 1.0d ) gain = 1.0d; int midiVolume = (int) ( gain /* * SoundSystemConfig.getMasterGain() */ /** (float) Math.abs( fadeOutGain ) * fadeInGain */ * 127.0d ); System.err.println("Vol " + midiVolume); if( synthesizer != null ) { javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels(); System.err.println("Channels: " + channels.length); for( int c = 0; channels != null && c < channels.length; c++ ) { System.err.println("cc " + midiVolume); channels[c].controlChange( CHANGE_VOLUME, midiVolume ); } } else if( synthDevice != null ) { try { ShortMessage volumeMessage = new ShortMessage(); for( int i = 0; i < 16; i++ ) { volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, CHANGE_VOLUME, midiVolume ); synthDevice.getReceiver().send( volumeMessage, -1 ); } } catch( Exception e ) { System.err.println( "Error resetting gain on MIDI device" ); e.printStackTrace(); } } else if( seqr != null && seqr instanceof Synthesizer ) { synthesizer = (javax.sound.midi.Synthesizer) seqr; javax.sound.midi.MidiChannel[] channels = synthesizer.getChannels(); for( int c = 0; channels != null && c < channels.length; c++ ) { channels[c].controlChange( CHANGE_VOLUME, midiVolume ); } } else { try { Receiver receiver = MidiSystem.getReceiver(); ShortMessage volumeMessage= new ShortMessage(); for( int c = 0; c < 16; c++ ) { volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, c, CHANGE_VOLUME, midiVolume ); receiver.send( volumeMessage, -1 ); } } catch( Exception e ) { System.err.println( "Error resetting gain on MIDI device" ); e.printStackTrace(); } } } /* * Did not work: * public boolean setVolume(double value) { try { Receiver receiver = MidiSystem.getReceiver(); ShortMessage volumeMessage= new ShortMessage(); for (int i = 0; i < 16; i++) { volumeMessage.setMessage(ShortMessage.CONTROL_CHANGE, i, 7, (int)(value * 127.0)); receiver.send(volumeMessage, -1); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } */ //DOES NOT WORK public void midiFadeOut() { double volume = 0.6; for (;;) { if (((volume - 0.05) < 0)) { break; } midiResetGain(volume); System.err.println("Gain = " + volume); try { Thread.sleep(150); } catch (Exception exception) { } volume -= 0.025; } if (synthesizer != null) { synthesizer.close(); synthesizer = null; } if (seqr != null) { if (seqr.isOpen()) { seqr.stop(); } seqr.close(); } } /* Associated with prev. two: * public void startMidi() { String midiDir = getMidiFileName() + getMidiSaveDir(); try { if (sequencer != null) { fadeOut(); } sequencer = null; sequence = null; File file = new File(midiDir); if (file.exists()) { sequence = MidiSystem.getSequence(file); } sequencer = MidiSystem.getSequencer(); sequencer.setSequence(sequence); synthesizer = MidiSystem.getSynthesizer(); synthesizer.open(); if (synthesizer.getDefaultSoundbank() == null) { sequencer.getTransmitter().setReceiver(MidiSystem.getReceiver()); } else { sequencer.getTransmitter().setReceiver(synthesizer.getReceiver()); } sequencer.open(); sequencer.start(); } catch (Exception exception) { exception.printStackTrace(); } } */ /*end MIDI*/ /*begin AUDIO*/ //AUDIO //a Clip is a DataLine whose audio gets preloaded. //javax.sound.midi.Sequencer seqr; <- no hay equivalente, Lines las abrimos ya sabiendo AudioFormat //javax.sound.sampled.AudioInputStream curStream; private java.util.Hashtable audioPreloaded = new java.util.Hashtable(); public void audioPreload ( URL u ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { javax.sound.sampled.AudioInputStream aii = javax.sound.sampled.AudioSystem.getAudioInputStream ( u ); javax.sound.sampled.AudioFormat af = aii.getFormat(); javax.sound.sampled.AudioFormat finalFormat = af; //will not be af if format needs to be decoded javax.sound.sampled.AudioInputStream finalStream = aii; //same as with the format if ( u.getPath().toLowerCase().endsWith(".ogg") || u.getPath().toLowerCase().endsWith(".mp3") ) { //boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN); AudioFormat baseFormat = aii.getFormat(); finalFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(), 16, baseFormat.getChannels(), baseFormat.getChannels() * 2, baseFormat.getSampleRate(), false); //bigEndian); // Get AudioInputStream that will be decoded by underlying VorbisSPI finalStream = AudioSystem.getAudioInputStream(finalFormat, aii); //} } //get a Clip javax.sound.sampled.Clip cl = ( javax.sound.sampled.Clip ) javax.sound.sampled.AudioSystem.getLine ( new javax.sound.sampled.DataLine.Info ( javax.sound.sampled.Clip.class , finalFormat ) ); //load the AudioInputStream gotten from the file into the Clip cl.open ( finalStream ); //put Clip into hashtable audioPreloaded.put ( u , cl ); } //unloads an audio clip from memory. (does nothing if file invalid) public void audioUnload ( java.io.File afile ) throws java.io.IOException { audioPreloaded.remove ( afile.getCanonicalPath() ); } public void audioUnload ( String s ) throws java.io.IOException { audioUnload ( new File ( s ) ); } public void audioPreload ( String s ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { audioPreload ( URLUtils.stringToURL ( s ) ); } private javax.sound.sampled.Clip getPreloadedClip ( URL u ) { //try //{ return (javax.sound.sampled.Clip) audioPreloaded.get ( u ); //} //catch ( java.io.IOException ioe ) //of getCanonicalPath() //{ // return null; //} } public void audioStartPreloaded ( URL u ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { if ( !isOn() ) return; audioStartPreloaded ( u , 0 ); } public void audioStartPreloaded ( URL u , int loopTimes ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { if ( !isOn() ) return; javax.sound.sampled.Clip cl = getPreloadedClip ( u ); if ( cl == null ) audioPreload ( u ); cl = getPreloadedClip ( u ); //cl not null [or exception should have been thrown by audioPreload] cl.setFramePosition(0); cl.loop(loopTimes); //cl.start(); } public void audioStopPreloaded ( URL u ) { javax.sound.sampled.Clip cl = getPreloadedClip ( u ); if ( cl == null ) return; //cl not null cl.stop(); } private Map basicPlayers = Collections.synchronizedMap(new HashMap()); public void audioStartUnpreloaded ( final URL u ) throws IOException { if ( !isOn() ) return; audioStartUnpreloaded(u,0); } /** * Restarts a sound that was playing. This is used by loops to restart the sound once it hits its end. * @param bp BasicPlayer where the sould was playing and must be restarted. * @param u URL of the sound to be restarted. */ private void restartSound ( BasicPlayer bp , URL u ) { try { /* System.err.println("Seek:"); bp.seek(0); System.err.println("Play:"); bp.play(); */ if ( !isOn() ) return; bp.stop(); double theGain = getCurrentGain(u); //bp.open(u.openStream()); bp.open(u); //to fix mark/reset not supported? if not, just wrap open with bufferedinputstream bp.play(); bp.setGain(theGain); } catch ( BasicPlayerException bpe ) { bpe.printStackTrace(); } //catch ( IOException ioe ) //{ // ioe.printStackTrace(); //} } /** * Plays the sound file located at the given URL, looping it loopTimes times (i.e., playing it loopTimes+1 times in total). * A negative value of loopTimes stands for infinity. * @param u * @param loopTimes * @throws IOException */ public void audioStartUnpreloaded ( final URL u , final int loopTimes ) throws IOException { if ( !isOn() ) return; /* Log theLog = LogFactory.getLog(BasicPlayer.class); if ( theLog instanceof Jdk14Logger ) { Jdk14Logger logToDisable = (Jdk14Logger)theLog; logToDisable. } */ try { java.util.logging.Logger log = java.util.logging.Logger.getLogger("javazoom.jlgui.basicplayer.BasicPlayer"); log.setLevel(Level.SEVERE); } catch ( SecurityException se ) { System.err.println("Restricted security environment, will not take logs of audio issues."); } final BasicPlayer bp = new BasicPlayer(); try { InputStream theStream = u.openStream(); //if this if-else structure gives any problem, we can also directly open the URL with the basicplayer. This avoids the mark/reset problem. if ( theStream.markSupported() ) { bp.open(theStream); } else //in applets that read remote URLs, mark is not supported so we need to add an extra layer. { BufferedInputStream bib = new BufferedInputStream(theStream); bp.open(bib); } } catch ( BasicPlayerException bpe ) { bpe.printStackTrace(); throw new IOException(bpe); } basicPlayers.put(u,bp); bp.addBasicPlayerListener( new BasicPlayerListener() { private int loopCount = loopTimes; public void opened(Object arg0, Map arg1) { } public void progress(int arg0, long arg1, byte[] arg2, Map arg3) {} public void setController(BasicController arg0) {} public void stateUpdated(BasicPlayerEvent arg0) { if ( arg0.getCode() == BasicPlayerEvent.EOM ) { if ( loopCount != 0 && basicPlayers.get(u) == bp ) //if a stop method has been called, the basic player will have been unregistered from the table, then we have to stop playing { if ( loopCount < 0 ) //infinite loop { restartSound(bp,u); } else if ( loopCount > 0 ) { loopCount--; restartSound(bp,u); } } else //we don't need to restart the sound (either because the loop parameter was set to zero, or we already executed all loops, or a stop operation was executed) { basicPlayers.remove(u); resetCurrentGain(u); } } } } ); try { bp.play(); } catch ( BasicPlayerException bpe ) { bpe.printStackTrace(); throw new IOException(bpe); } } public void audioStopUnpreloaded ( URL u ) { BasicPlayer bp = (BasicPlayer) basicPlayers.get(u); if ( bp != null ) { try { bp.stop(); } catch ( BasicPlayerException bpe ) { bpe.printStackTrace(); } basicPlayers.remove(u); } } public void audioStart ( URL u , int loopTimes ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { javax.sound.sampled.Clip cl = getPreloadedClip ( u ); if ( cl == null ) audioStartUnpreloaded ( u , loopTimes ); else audioStartPreloaded ( u , loopTimes ); } public void audioStart ( URL u ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { audioStart ( u , 0 ); } public void audioStart ( String s ) throws UnsupportedAudioFileException, LineUnavailableException, IOException { audioStart ( s , 0 ); } public void audioStart ( String s , int loopTimes ) throws javax.sound.sampled.UnsupportedAudioFileException , javax.sound.sampled.LineUnavailableException , java.io.IOException { audioStart ( URLUtils.stringToURL ( s ) , loopTimes ); } public void audioStop ( String s ) { audioStopUnpreloaded ( URLUtils.stringToURL ( s ) ); } public void audioFadeIn ( String s , int loopTimes , double seconds , double delay ) throws UnsupportedAudioFileException, LineUnavailableException, IOException { audioFadeIn ( URLUtils.stringToURL ( s ) , loopTimes , seconds , delay ); } public void audioFadeOut ( String s , double seconds ) { audioFadeOut ( URLUtils.stringToURL ( s ) , seconds ); } /** * input: time (from 0.0 to 1.0) * output: gain (from 1.0 to 0.0) * contract: should output 0.0 or less for 1.0 or more * @param time */ private double fadeOutFunction ( double time ) { return expFade(time); } private double fadeInFunction ( double time ) { return 1-expFade(time); } private double cosineFade ( double time ) { double angle = time * Math.PI/2; if ( time >= 1.0 ) return 0.0; else return Math.cos(angle); } private double expFade ( double time ) { if ( time >= 1.0 ) return 0.0; else return 1.0 / ((6*time+1.0)*(6*time+1.0)); } public void audioFadeOut ( final URL u , final double seconds ) { final BasicPlayer bp = (BasicPlayer) basicPlayers.get(u); if ( bp != null ) { Thread thr = new Thread() { public void run() { double gain = 1.0; double iters = 100.0; //number of iters of fade-out double itersDone = 0.0; //iterations done int sleepTime = (int)(seconds * 1000.0 / iters); while ( gain > 0.0 ) { itersDone += 1.0; gain = fadeOutFunction ( itersDone / (iters-1) ); try { bp.setGain(gain); //System.err.println("Gain now " + gain); //System.err.println("Gain min " + bp.getMinimumGain()); } catch (BasicPlayerException e1) { e1.printStackTrace(); } try { sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } } audioStopUnpreloaded(u); } }; thr.start(); } } private Hashtable gainsForURLs = new Hashtable(); /** * Because BasicPlayer API won't give us the gain in a reliable format (if we do setGain, then getGainValue returns different units!) * @param u */ private void setCurrentGain ( final URL u , final double gain ) { gainsForURLs.put(u,new Double(gain)); } /** * Because BasicPlayer API won't give us the gain in a reliable format (if we do setGain, then getGainValue returns different units!) */ private double getCurrentGain ( final URL u ) { Double d = (Double) gainsForURLs.get(u); if ( d == null ) return 1.0; //default gain else return d.doubleValue(); } /** * Because BasicPlayer API won't give us the gain in a reliable format (if we do setGain, then getGainValue returns different units!) */ private void resetCurrentGain ( final URL u ) { gainsForURLs.remove(u); } public void audioSetGain ( final URL u , final double gain ) { final BasicPlayer bp = (BasicPlayer) basicPlayers.get(u); try { setCurrentGain ( u , gain ); bp.setGain(gain); } catch (BasicPlayerException e1) { e1.printStackTrace(); } } public void audioSetGain ( String s , double gain ) { audioSetGain ( URLUtils.stringToURL ( s ) , gain ); } public void audioFadeIn ( final URL u , final int loopTimes , final double seconds , final double delay ) throws UnsupportedAudioFileException, LineUnavailableException, IOException { Thread thr = new Thread() { public void run() { try { sleep ( (int) delay * 1000 ); } catch (InterruptedException e2) { e2.printStackTrace(); } try { audioStart ( u , loopTimes ); } catch (UnsupportedAudioFileException e2) { e2.printStackTrace(); } catch (LineUnavailableException e2) { e2.printStackTrace(); } catch (IOException e2) { e2.printStackTrace(); } final BasicPlayer bp = (BasicPlayer) basicPlayers.get(u); double gain = 0.0; double iters = 100.0; //number of iters of fade-in double itersDone = 0.0; //iterations done int sleepTime = (int)(seconds * 1000.0 / iters); while ( gain < 1.0 ) { itersDone += 1.0; gain = fadeInFunction ( itersDone / (iters-1) ); try { bp.setGain(gain); //System.err.println("Gain now " + gain); //System.err.println("Gain min " + bp.getMinimumGain()); } catch (BasicPlayerException e1) { e1.printStackTrace(); } try { sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } } } }; thr.start(); } // for MOD PlayerThread pt; //iRepeat: number of times to repeat (or -1 for infinite) public void playMOD( File f, int iRepeat ) throws Exception { if ( !isOn() ) return; playMOD(new DataInputStream(new FileInputStream(f)),iRepeat); } public void playMOD ( URL u , int iRepeat ) throws Exception { if ( !isOn() ) return; playMOD(new DataInputStream(u.openStream()),iRepeat); } private void playMOD ( DataInput theInput , int iRepeat ) throws Exception { if ( !isOn() ) return; MODThread mt; JavaSoundOutputDevice out = new JavaSoundOutputDevice(new SS16LEAudioFormatConverter(), 44100, 1000); Module module = ModuleLoader.read(theInput); MicroMod microMod = new MicroMod(module, out, new LinearResampler()); mt = new MODThread(microMod, out, iRepeat); pt = mt; pt.setVolume(0x10000); mt.start(); return; } public void playMOD ( String s , int iRepeat ) throws Exception { if ( !isOn() ) return; playMOD ( new File(s) , iRepeat ); } public void stopMOD() throws Exception { if (pt != null) pt.stopPlaying(); } /*inner*/ class MODThread extends Thread implements PlayerThread { int soundId; boolean running; boolean stopped; JavaSoundOutputDevice out; MicroMod mm; int iRepeat; MODThread(MicroMod mm, JavaSoundOutputDevice out, int iRepeat) { this.mm = mm; this.out = out; this.iRepeat = iRepeat; running = false; stopped = false; } public synchronized void setVolume(int vol) { Line l = out.getLine(); FloatControl ctl = (FloatControl) l.getControl(FloatControl.Type.MASTER_GAIN); double gain = (double) vol / (double) 0x10000; float dB = (float) (Math.log(gain) / Math.log(10.0) * 20); if (ctl != null) ctl.setValue(dB); } public synchronized void stopPlaying() { if (!stopped) { running = false; stopped = true; out.stop(); out.close(); //donePlaying(); } } synchronized void donePlaying() { pt = null; } public void run() { out.start(); for (int i = 0; !stopped && (iRepeat == -1 || i < iRepeat); i++) { running = true; mm.setCurrentPatternPos(0); while (running && mm.getSequenceLoopCount() == 0) { synchronized (this) { Debug.println("Real Time."); mm.doRealTimePlayback(); try { Thread.sleep(20); } catch (InterruptedException e) {} } } } synchronized (this) { if (!stopped) { running = false; out.stop(); out.close(); donePlaying(); } /* if (iNotify != 0) { Glk.GlkEvent e = new Glk.GlkEvent(); e.type = Glk.EVTYPE_SOUND_NOTIFY; e.win = null; e.val1 = soundId; e.val2 = iNotify; Glk.addEvent(e); } */ } } } /** * Method to stop all current sound, useful e.g. when closing an AGE window. */ public void stopAllSound() { //stop basic player sound for (Iterator iterator = basicPlayers.values().iterator(); iterator.hasNext();) { BasicPlayer bp = (BasicPlayer) iterator.next(); try { bp.stop(); } catch ( BasicPlayerException bpe ) { bpe.printStackTrace(); } } basicPlayers.clear(); //stop midi if ( seqr != null && seqr.isOpen() ) seqr.stop(); if ( seqr != null ) midiClose(); //stop MOD try { stopMOD(); } catch ( Exception e ) { e.printStackTrace(); } } } //for MOD interface PlayerThread extends Runnable { public abstract void setVolume(int vol); public abstract void stopPlaying() throws Exception; }