/* * File : JavaSoundAudioPlayer.java * Created : 23-jun-2004 11:29 * By : fbusquets * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details (see the LICENSE file). */ package edu.xtec.jclic.media; import edu.xtec.jclic.bags.MediaBag; import edu.xtec.util.ExtendedByteArrayInputStream; import edu.xtec.util.StreamIO; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.Clip; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.UnsupportedAudioFileException; import org.tritonus.applet.AppletMpegSPIWorkaround; import org.tritonus.applet.AppletVorbisSPIWorkaround; /** * * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.08.28 */ public class JavaSoundAudioPlayer implements AudioPlayer{ private Clip clip; protected AudioInputStream ais; protected boolean isMpeg; protected boolean isOgg; protected boolean isWav; 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{ close(); InputStream is=null; javax.sound.sampled.AudioFileFormat m_audioFileFormat=null; if(source instanceof ExtendedByteArrayInputStream){ is=checkInputStream((InputStream)source, ((ExtendedByteArrayInputStream)source).getName()); } else if(source instanceof InputStream){ is=checkInputStream((InputStream)source, null); } else if(source instanceof File){ is=checkInputStream(new java.io.FileInputStream((File)source), ((File)source).getName()); } else{ java.net.URL url=null; if(source instanceof java.net.URL) url=(java.net.URL)source; else if(source instanceof String){ url=new java.net.URL((String)source); } if(url!=null){ is=checkInputStream(url.openStream(), source.toString()); } } if(is!=null){ if(isMpeg || (!isWav && !isOgg)){ try { m_audioFileFormat = AppletMpegSPIWorkaround.getAudioFileFormat(is); ais = AppletMpegSPIWorkaround.getAudioInputStream(is); isMpeg=true; } catch (IOException ex) { throw ex; } catch (UnsupportedAudioFileException ex) { isMpeg=false; } } if(isOgg || (!isMpeg && !isWav)){ try { m_audioFileFormat = AppletVorbisSPIWorkaround.getAudioFileFormat(is); ais = AppletVorbisSPIWorkaround.getAudioInputStream(is); isOgg=true; } catch (IOException ex) { throw ex; } catch (UnsupportedAudioFileException ex) { isOgg=false; } } if(isWav || (!isMpeg && !isOgg)){ m_audioFileFormat = AudioSystem.getAudioFileFormat(is); ais = AudioSystem.getAudioInputStream(is); } } if(ais!=null){ AudioFormat af=ais.getFormat(); DataLine.Info info=new DataLine.Info(SourceDataLine.class, af, INTERNAL_BUFFER_SIZE); if(!AudioSystem.isLineSupported(info)){ AudioFormat sourceFormat = af; AudioFormat targetFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, sourceFormat.getSampleRate(), BIT_SAMPLE_SIZE, sourceFormat.getChannels(), sourceFormat.getChannels() * (BIT_SAMPLE_SIZE / 8), sourceFormat.getSampleRate(), //sourceFormat.getFrameRate(), BIG_ENDIAN); if(isMpeg) ais = AppletMpegSPIWorkaround.getAudioInputStream(targetFormat, ais); else if (isOgg) ais = AppletVorbisSPIWorkaround.getAudioInputStream(targetFormat, ais); else ais = AudioSystem.getAudioInputStream(targetFormat, ais); } } return ais!=null; } 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; } public void realize(String fileName, MediaBag mediaBag) throws Exception{ if(fileName!=null) setDataSource(mediaBag.getMediaDataSource(fileName)); 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; } public void play(){ try{ stop(); if(getClip()!=null){ clip.setFramePosition(0); clip.start(); } } catch(Exception ex){ System.err.println("Error playing sound:\n"+ex); } } public void stop(){ if(clip!=null && clip.isActive()) clip.stop(); } protected InputStream checkInputStream(InputStream is, String name) throws Exception{ String s = (name==null ? null : name.toLowerCase()); if(s!=null){ if(s.endsWith(".wav")) isWav=true; else if(s.endsWith(".ogg")) isOgg=true; 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); is.reset(); if(b[0x00]=='R' && b[0x01]=='I' && b[0x02]=='F' && b[0x03]=='F' && b[0x08]=='W' && b[0x09]=='A' && b[0x0A]=='V' && b[0x0B]=='E' && b[0x0C]=='f' && b[0x0D]=='m' && b[0x0E]=='t' && b[0x0F]==' ' && b[0x14]==0x55 && b[0x15]==0x00){ for(int p=0x11; p<CHECK_BUFFER_SIZE-6; p++){ if(b[p]=='d' && b[p+1]=='a' && b[p+2]=='t' && b[p+3]=='a'){ int offset=p+4+4; is.skip(offset); isWav=false; isMpeg=true; break; } } } } return is; } }