--- a/src/core/edu/xtec/jclic/media/JavaSoundAudioPlayer.java +++ b/src/core/edu/xtec/jclic/media/JavaSoundAudioPlayer.java @@ -6,7 +6,7 @@ * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament - * d'Educacio de la Generalitat de Catalunya + * d'Educacio de la Generalitat de Catalunya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ package edu.xtec.jclic.media; import edu.xtec.jclic.bags.MediaBag; +import edu.xtec.util.ResourceManager; import edu.xtec.util.ExtendedByteArrayInputStream; import edu.xtec.util.StreamIO; import java.io.ByteArrayInputStream; @@ -33,10 +34,14 @@ import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineListener; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.UnsupportedAudioFileException; import org.tritonus.applet.AppletMpegSPIWorkaround; import org.tritonus.applet.AppletVorbisSPIWorkaround; +import edu.xtec.util.Options; /** * @@ -44,36 +49,45 @@ * @version 13.08.28 */ public class JavaSoundAudioPlayer implements AudioPlayer{ - + private Clip clip; - protected AudioInputStream ais; + protected AudioInputStream ais=null; protected boolean isMpeg; protected boolean isOgg; protected boolean isWav; - + + private MediaBag mb; + private String mediaFileName; + private Options options; + private PlayThread playThread; + static final int INTERNAL_BUFFER_SIZE=1024; static final int CHECK_BUFFER_SIZE=0x60; static final int BIT_SAMPLE_SIZE=16; static final boolean BIG_ENDIAN=true; - + /** Creates a new instance of JavaSoundAudioPlayer */ public JavaSoundAudioPlayer() { } - + public boolean setDataSource(Object source) throws Exception{ - + this.options = options; close(); - + InputStream is=null; javax.sound.sampled.AudioFileFormat m_audioFileFormat=null; if(source instanceof ExtendedByteArrayInputStream){ + this.mediaFileName=((ExtendedByteArrayInputStream)source).getName(); is=checkInputStream((InputStream)source, ((ExtendedByteArrayInputStream)source).getName()); } else if(source instanceof InputStream){ is=checkInputStream((InputStream)source, null); + this.mediaFileName=null; + is=checkInputStream((InputStream)source, this.mediaFileName); } else if(source instanceof File){ + this.mediaFileName=((File)source).getName(); is=checkInputStream(new java.io.FileInputStream((File)source), ((File)source).getName()); } else{ @@ -84,10 +98,11 @@ url=new java.net.URL((String)source); } if(url!=null){ + this.mediaFileName=source.toString(); is=checkInputStream(url.openStream(), source.toString()); } } - + if(is!=null){ if(isMpeg || (!isWav && !isOgg)){ try { @@ -105,7 +120,7 @@ if(isOgg || (!isMpeg && !isWav)){ try { m_audioFileFormat = AppletVorbisSPIWorkaround.getAudioFileFormat(is); - ais = AppletVorbisSPIWorkaround.getAudioInputStream(is); + ais = AppletVorbisSPIWorkaround.getAudioInputStream(is); isOgg=true; } catch (IOException ex) { @@ -113,14 +128,14 @@ } catch (UnsupportedAudioFileException ex) { isOgg=false; - } + } } if(isWav || (!isMpeg && !isOgg)){ m_audioFileFormat = AudioSystem.getAudioFileFormat(is); - ais = AudioSystem.getAudioInputStream(is); + ais = AudioSystem.getAudioInputStream(is); } } - + if(ais!=null){ AudioFormat af=ais.getFormat(); DataLine.Info info=new DataLine.Info(SourceDataLine.class, af, INTERNAL_BUFFER_SIZE); @@ -133,66 +148,148 @@ sourceFormat.getChannels(), sourceFormat.getChannels() * (BIT_SAMPLE_SIZE / 8), sourceFormat.getSampleRate(), - //sourceFormat.getFrameRate(), + //sourceFormat.getFrameRate(), BIG_ENDIAN); - + if(isMpeg) ais = AppletMpegSPIWorkaround.getAudioInputStream(targetFormat, ais); - else if (isOgg) + else if (isOgg) ais = AppletVorbisSPIWorkaround.getAudioInputStream(targetFormat, ais); else - ais = AudioSystem.getAudioInputStream(targetFormat, ais); - } + ais = AudioSystem.getAudioInputStream(targetFormat, ais); + } } return ais!=null; } - + + private void getAudioInputStream() throws Exception{ + if(ais==null){ + //System.out.println("getAudioInputStream: " + mediaFileName + " mb " + mb); + JavaSoundAudioPlayer jsap=new JavaSoundAudioPlayer(); + jsap.setDataSource(ResourceManager.getResourceAsByteArray(mediaFileName)); + ais=jsap.ais; + if(ais==null) + throw new Exception("Unable to open audio data!"); + } + } + public Clip getClip() throws Exception{ - if(clip==null && ais!=null){ - clip = (Clip)AudioSystem.getLine(new DataLine.Info(Clip.class, ais.getFormat(), INTERNAL_BUFFER_SIZE)); - } - return clip; + if (!Options.CLOSE_STREAMS.booleanValue()) { + if(clip==null && ais!=null){ + clip = (Clip)AudioSystem.getLine(new DataLine.Info(Clip.class, ais.getFormat(), INTERNAL_BUFFER_SIZE)); + } + return clip; + } else { + //System.out.println("getClip() with closestreams " + Options.CLOSE_STREAMS.booleanValue()); + if (ais !=null) close(); + if (ais == null) getAudioInputStream(); + else return null; + + DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat(), INTERNAL_BUFFER_SIZE); + if((clip==null && ais!=null)){ + clip = (Clip)AudioSystem.getLine(info); + clip.addLineListener(new LineListener(){ + public void update(LineEvent event){ + //System.out.println("LineListener " + event.getType() ); + + if(event.getType() == LineEvent.Type.CLOSE){ + } + if(event.getType() == LineEvent.Type.STOP){ + clip.stop(); + close(); + } + if(event.getType() == LineEvent.Type.OPEN){ + if(clip!=null && clip.isRunning()) { + clip.stop(); + } + } + } + }); + return clip; + } + } + return null; } - + public void realize(String fileName, MediaBag mediaBag) throws Exception{ - if(fileName!=null) + if(fileName!=null){ setDataSource(mediaBag.getMediaDataSource(fileName)); - if(ais!=null && getClip()!=null){ - clip.open(ais); + this.mediaFileName=fileName; + } + this.mb=mediaBag; + if (!Options.CLOSE_STREAMS.booleanValue()) { + if(ais!=null && getClip()!=null){ + clip.open(ais); + } } } - + // Modified to avoid unexpected exceptions like // "IllegalStateException: line already closed" // launched by org.classpath.icedtea.pulseaudio.PulseAudioClip.close public void close(){ - if(clip!=null && clip.isOpen()){ - if(clip.isRunning()) - clip.stop(); - clip.close(); - } - clip=null; - ais=null; - } - + if (!Options.CLOSE_STREAMS.booleanValue()) { + if(clip!=null){ + clip.close(); + clip=null; + } + ais=null; + } else { + try { + if(clip!=null){ + //System.out.println("close() clip"); + clip.close(); + clip=null; + } + if(ais!=null){ + //System.out.println("close() ais"); + ais.close(); + ais=null; + } + } catch(java.io.IOException ex){ + System.err.println("close(): close error"); + } + } + } + public void play(){ try{ - stop(); - if(getClip()!=null){ - clip.setFramePosition(0); - clip.start(); + if (!Options.CLOSE_STREAMS.booleanValue()) { + stop(); + if(getClip()!=null){ + clip.setFramePosition(0); + clip.start(); + } + } else { + if(playThread==null){ + playThread=new PlayThread(); + playThread.start(); + } } } catch(Exception ex){ System.err.println("Error playing sound:\n"+ex); } } - + public void stop(){ - if(clip!=null && clip.isActive()) - clip.stop(); + if (!Options.CLOSE_STREAMS.booleanValue()) { + if(clip!=null && clip.isActive()) + clip.stop(); + } else { + if(playThread!=null){ + if(playThread.isAlive()){ + playThread.running=false; + while(playThread!=null){ + Thread.currentThread().yield(); + } + } else{ + playThread=null; + } + } + } } - + protected InputStream checkInputStream(InputStream is, String name) throws Exception{ String s = (name==null ? null : name.toLowerCase()); if(s!=null){ @@ -203,13 +300,13 @@ else if(s.endsWith(".mp3")) isMpeg=true; } - + if(s==null || isWav){ byte[] data; if(!is.markSupported()){ data=StreamIO.readInputStream(is); is=new ByteArrayInputStream(data); - } + } is.mark(CHECK_BUFFER_SIZE); byte[] b=new byte[CHECK_BUFFER_SIZE]; is.read(b); @@ -230,5 +327,76 @@ } } return is; - } + } + class PlayThread extends Thread { + + public boolean running; + private Clip clip2 = null; + + public PlayThread() throws Exception{ + //System.out.println("PlayThread() with closestreams " + Options.CLOSE_STREAMS.booleanValue()); + if (ais !=null) close(); + if (ais == null) getAudioInputStream(); + + DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat(), INTERNAL_BUFFER_SIZE); + if((clip2==null && ais!=null)){ + clip2 = (Clip)AudioSystem.getLine(info); + clip2.addLineListener(new LineListener(){ + public void update(LineEvent event){ + //System.out.println("LineListener " +event.getType() ); + if(event.getType() == LineEvent.Type.CLOSE){ + } + if(event.getType() == LineEvent.Type.STOP){ + clip2.stop(); + close2(); + playThread=null; + running=false; + } + if(event.getType() == LineEvent.Type.OPEN){ + if(clip2!=null && clip2.isRunning()) { + clip2.stop(); + } + } + } + }); + } + } + + public void close2(){ + try { + if(clip2!=null){ + //System.out.println("close2() clip"); + clip2.close(); + clip2=null; + } + if(ais!=null){ + //System.out.println("close2() ais"); + ais.close(); + ais=null; + } + } catch(java.io.IOException ex){ + System.err.println("close2(): close error"); + } + } + + public void run(){ + try{ + if(clip2!=null) { + running=true; + clip2.stop(); + + if (!clip2.isOpen()) { + if(ais!=null){ + clip2.open(ais); + } + } + clip2.setFramePosition(0); + clip2.start(); + Thread.currentThread().yield(); + } + } catch(Exception ex){ + System.err.println("Error playing sound:\n"+ex); + } + } + } } --- a/src/player/edu/xtec/jclic/SingleInstanceJFrame.java +++ b/src/player/edu/xtec/jclic/SingleInstanceJFrame.java @@ -97,6 +97,7 @@ result=arg; } } + options.needViaHack(); return result; } @@ -230,17 +231,22 @@ /** Exit the Application */ private void exitForm(java.awt.event.WindowEvent evt) { - if(socketThread!=null) - socketThread.stopSocketThread(); - if(rc!=null){ - rc.end(); - rc=null; + try + { + if(socketThread!=null) + socketThread.stopSocketThread(); + if(rc!=null){ + rc.end(); + rc=null; + } + while(socketThread!=null){ + Thread.currentThread().yield(); + } } - while(socketThread!=null){ - Thread.yield(); - } - System.exit(0); - } + catch(Exception ex){ + System.exit(0); + } + } protected boolean checkOtherInstance(int port){ boolean result=false; --- a/src/core/edu/xtec/jclic/media/JavaSoundAudioBuffer.java +++ b/src/core/edu/xtec/jclic/media/JavaSoundAudioBuffer.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Timer; import java.util.TimerTask; +import edu.xtec.util.Options; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Line.Info; @@ -171,8 +172,14 @@ // build m_sourceLine dli=new javax.sound.sampled.DataLine.Info(SourceDataLine.class, m_targetLine.getFormat()); - m_sourceLine=(SourceDataLine)AudioSystem.getLine(dli); - m_sourceLine.open(); + if (Options.CLOSE_STREAMS.booleanValue()) { + //System.out.println("JavaSoundAudioBuffer: Default mixer " + (SourceDataLine)AudioSystem.getSourceDataLine(m_targetLine.getFormat(), AudioSystem.getMixer(null). getMixerInfo())); + m_sourceLine=(SourceDataLine)AudioSystem.getSourceDataLine(m_targetLine.getFormat(), AudioSystem.getMixer(null).getMixerInfo()); + } else { + m_sourceLine=(SourceDataLine)AudioSystem.getLine(dli); + } + + m_sourceLine.open(); initialized=true; } @@ -287,6 +294,7 @@ m_sourceLine.drain(); m_sourceLine.stop(); //m_sourceLine.close(); + if (Options.CLOSE_STREAMS.booleanValue()) m_sourceLine.close(); playThread=null; running=false; } --- a/src/core/edu/xtec/jclic/media/JavaSoundActiveMediaPlayer.java +++ b/src/core/edu/xtec/jclic/media/JavaSoundActiveMediaPlayer.java @@ -30,6 +30,7 @@ import javax.sound.midi.MidiSystem; import javax.sound.sampled.*; import javax.swing.Timer; +import edu.xtec.util.Options; /** * @@ -108,11 +109,13 @@ if(midi){ if(sequencer==null) sequencer=MidiSystem.getSequencer(); - } - else{ - if(clip!=null && !clip.isOpen()){ - clip.open(); - } + }else{ + if (!Options.CLOSE_STREAMS.booleanValue()) { + //System.out.println("JavaSoundActiveMediaPlayer: Playing wav"); + if(clip!=null && !clip.isOpen()){ + clip.open(); + } + } } } catch(Exception e){ System.err.println("Error realizing media \""+mc.mediaFileName+"\"\n"+e); @@ -141,8 +144,17 @@ sequencer.start(); } } else if(clip!=null){ - if(!clip.isOpen()) - realize(); + if (Options.CLOSE_STREAMS.booleanValue()) { + //System.out.println("JavaSoundActiveMediaPlayer: open clip"); + clip.stop(); + if(clip!=null) { + if(!clip.isOpen()) + clip.open(); + } + } else { + if(!clip.isOpen()) + realize(); + } setTimeRanges(); attachVisualComponent(); if(mc.loop){ --- a/src/core/edu/xtec/jclic/media/FalseClip.java +++ b/src/core/edu/xtec/jclic/media/FalseClip.java @@ -27,6 +27,7 @@ import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; +import edu.xtec.util.Options; /** * @@ -134,9 +135,12 @@ running=false; getAudioInputStream(); DataLine.Info info = new DataLine.Info(SourceDataLine.class, af); - line = (SourceDataLine)AudioSystem.getLine(info); + //System.out.println("FalseClip: Default mixer " + (SourceDataLine)AudioSystem.getSourceDataLine(af, AudioSystem.getMixer(null).getMixerInfo())); + if (Options.CLOSE_STREAMS.booleanValue()) line = (SourceDataLine)AudioSystem.getSourceDataLine(af, AudioSystem.getMixer(null).getMixerInfo()); + else line = (SourceDataLine)AudioSystem.getLine(info); + if(line!=null) - line.open(af); + if (!Options.CLOSE_STREAMS.booleanValue()) line.open(af); } @Override @@ -145,6 +149,7 @@ running=true; int nBytesRead = 0; try{ + if (Options.CLOSE_STREAMS.booleanValue()) line.open(af); line.start(); while (running && nBytesRead >=0) { nBytesRead = ais.read(buf, 0, buf.length); @@ -157,6 +162,9 @@ line.drain(); else line.stop(); + } catch(javax.sound.sampled.LineUnavailableException ex){ + System.err.println("JavaSound playing error:\n"+ex); + } catch(Exception ex){ System.err.println("JavaSound playing error:\n"+ex); } --- a/src/core/edu/xtec/jclic/media/JMFActiveMediaPlayer.java +++ b/src/core/edu/xtec/jclic/media/JMFActiveMediaPlayer.java @@ -99,8 +99,9 @@ if(!useAudioBuffer){ try{ if(midi){ - if(sequencer==null) + if(sequencer==null) { sequencer=MidiSystem.getSequencer(); + } } else{ if(player==null && dataSource!=null){ --- a/src/utilities/edu/xtec/util/Messages.java +++ b/src/utilities/edu/xtec/util/Messages.java @@ -113,7 +113,16 @@ //pane.setSelectionValues(DESCRIPTIVE_LANGUAGE_CODES); pane.setSelectionValues(getDescriptiveLanguageCodes(null)); pane.setWantsInput(true); - String initialSelection = getDescriptiveLanguageCode(Locale.getDefault().getLanguage()); + String systemlanguage=Locale.getDefault().getLanguage(); + if (System.getenv("LANG").contains("valenci")) + { + System.out.println(" [ Valencian ] "); + systemlanguage="qcv"; + } else { + System.out.println(" [ LANG ] "); + } + String initialSelection=getDescriptiveLanguageCode(systemlanguage); + pane.setInitialSelectionValue(initialSelection); showDlg((Component) options.get(Options.MAIN_PARENT_COMPONENT), pane, "Language selecion"); String sel = (String) pane.getInputValue(); @@ -681,6 +690,7 @@ static { ISO_639_2_CODES.put("ast", "asturianu"); ISO_639_2_CODES.put("vec", "vèneto"); + ISO_639_2_CODES.put("qcv","valenci\u00E0"); } public static String getDescriptiveLanguageCode(String languageCode) { --- a/src/utilities/edu/xtec/util/Options.java +++ b/src/utilities/edu/xtec/util/Options.java @@ -28,6 +28,8 @@ import java.util.Iterator; import java.util.Map; import java.util.Properties; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Mixer; /** * @@ -35,7 +37,9 @@ * @version 13.09.17 */ public class Options extends HashMap { - + + public static Boolean CLOSE_STREAMS=null; + public static String CLOSESTREAMS="closestreams"; public static final String TRUE="true", FALSE="false"; public static final Boolean BTRUE=true, BFALSE=false; public static final String MAC="Mac", WIN="Windows", @@ -87,7 +91,25 @@ if("64".equals(s)) put(ARCH64BIT, true); } - + + public void needViaHack(){ + if (CLOSE_STREAMS == null) { + if (containsKey(CLOSESTREAMS)) CLOSE_STREAMS=new Boolean(getBoolean(CLOSESTREAMS)); + else { + put(CLOSESTREAMS, "false"); + Mixer.Info[] mixers = AudioSystem.getMixerInfo(); + for(int i = 0; i < mixers.length; ++i) { + //System.out.println(mixers[i].getDescription()); + if (mixers[i].getDescription().indexOf("8237") > 0 || mixers[i].getDescription().indexOf("8235") > 0 || mixers[i].getDescription().indexOf("VT1708") > 0 || mixers[i].getDescription().indexOf("8233") > 0) { + put(CLOSESTREAMS, "true"); + break; + } + } + CLOSE_STREAMS=new Boolean(getBoolean(CLOSESTREAMS)); + } + } + //if (CLOSE_STREAMS.booleanValue()) System.out.println("Options: CLOSESTREAMS="+ CLOSE_STREAMS); + } public Properties toProperties(){ Properties prop=new Properties();