Mercurial > hg > jslab
diff src/samer/audio/StreamSource.java @ 1:5df24c91468d
Oh my what a mess.
author | samer |
---|---|
date | Fri, 05 Apr 2019 16:26:00 +0100 |
parents | |
children | 15b93db27c04 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/samer/audio/StreamSource.java Fri Apr 05 16:26:00 2019 +0100 @@ -0,0 +1,202 @@ +/* + * StreamSource.java + * + * Copyright (c) 2000, Samer Abdallah, King's College London. + * All rights reserved. + * + * This software is provided AS iS and WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + */ + +package samer.audio; + +import samer.core.*; +import samer.core.types.*; +import samer.core.util.*; +import samer.tools.*; +import javax.sound.sampled.*; +import javax.swing.*; +import java.io.*; +import java.net.URL; +import java.util.*; + +/** + An AudioSource that read from an input stream. Can read any + format for which the appropriate JavaSound plug-in is installed on + your system, eg WAV, AU, MP3, OGG. The implementation of + AudioSource.reader() returns a Task which gets samples from the audio + files, going through the play list one by one. If the loop flag + is true, then samples are returned indefinitely by looping through the + playlist; otherwise, an EOFException is thrown if an attempt is made + to read beyond the end of the last file. + + Properties read from current environment: + <dl> + <dt>current<dd>Current file (String) + <dt>loop<dd>Loop playlist? (Boolean) [true] + </dl> + +*/ + +public class StreamSource implements AudioSource +{ + int channelsToMix; + AudioInputStream source; + InputStream in=null; + AudioFormat format=null; + byte[] byte_buf=null; + int chunk=0; + + /** + * Construct a StreamSource initialised with current file and loop initialised + * from the current Environment. No exception is thrown if the current file + */ + public StreamSource(AudioInputStream s) throws Exception { source=s; } + public StreamSource(InputStream s) throws Exception { source=AudioSystem.getAudioInputStream(s); } + public StreamSource(File file) throws Exception { source=AudioSystem.getAudioInputStream(file); } + public StreamSource(URL url) throws Exception { source=AudioSystem.getAudioInputStream(url); } + + public void dispose() { close(); } + + /** Returns current file */ + public void setTargetFormat(AudioFormat f) { format=f; } + + public AudioFormat getFormat() { return format; } + + public boolean isOpen() { return in!=null; } + + /** Closes current input stream */ + public synchronized void close() { + try { + if (in!=null) { + Shell.trace("Closing audio stream..."); + in.close(); in=null; byte_buf=null; + } + } + catch (IOException ex) {} + } + + /** Opens the playlist starting with the first file. */ + public synchronized void open() throws Exception { if (!isOpen()) openCurrent(); } + + /** Opens the current file. Next read will returns samples + * from head of given file. If setFormat() was called previously, then + * we will attempt to do format conversion. */ + private synchronized void openCurrent() throws Exception + { + AudioInputStream s=source; + AudioFormat fmt1, af=s.getFormat(); + + Shell.trace(" format: "+af); + + // convert to target format if required + if (format!=null) { + if (!format.equals(af)) { + Shell.trace(" converting to "+format); + if (af.getChannels()>format.getChannels()) { + Shell.trace(" channels mix down required."); + fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(), + format.getSampleSizeInBits(), + af.getChannels(), format.getFrameSize(), format.getFrameRate(), format.isBigEndian()); + + channelsToMix = af.getChannels(); + } else { + channelsToMix = 0; + fmt1=format; + } + s=convertFormat(s,af,fmt1); + } + } + + // If we have a reader task, then update the byte buffer + if (chunk>0) setByteBuffer(); + + // Shell.trace("stream format: "+s.getFormat()); + in = new BufferedInputStream(s,64*1024); + } + + /** Returns number of bytes available in current file */ + public int available() throws Exception { return in.available(); } + + private void setChunkSize(int s) { chunk=s; } + private void setByteBuffer() { + byte_buf = new byte[2*chunk*(channelsToMix>0 ? channelsToMix : 1)]; + } + + /** Returns a Task which copies samples as doubles into the given + * buffer between the given positions. */ + public Task reader(final double [] dbuf, final int off, final int len) { + setChunkSize(len); + if (in!=null) setByteBuffer(); + + return new AnonymousTask() { + public void run() throws Exception { + int n = 0; + + synchronized (StreamSource.this) { + int blen = byte_buf.length; + while (n < blen) { + int count = in.read(byte_buf, n, blen - n); + if (count > 0) n+=count; + else throw new EOFException(); + } + } + if (channelsToMix>0) Util.shortToDoubleMixDown(byte_buf,dbuf,off,len,channelsToMix); + else Util.shortToDouble(byte_buf,dbuf,off,len); + } + }; + } + + /** Returns a Task which copies samples as floats into the given + * buffer between the given positions. */ + public Task reader(final float [] dbuf, final int off, final int len) { + setChunkSize(len); + if (in!=null) setByteBuffer(); + + return new AnonymousTask() { + public synchronized void run() throws Exception { + int n = 0; + + synchronized (StreamSource.this) { + int blen = byte_buf.length; + while (n < blen) { + int count = in.read(byte_buf, n, blen - n); + if (count > 0) n+=count; + else throw new EOFException(); + } + } + if (channelsToMix>0) Util.shortToFloatMixDown(byte_buf,dbuf,off,len,channelsToMix); + else Util.shortToFloat(byte_buf,dbuf,off,len); + } + }; + } + + private static AudioInputStream convertFormat(AudioInputStream sin, AudioFormat fin, AudioFormat fout) throws Exception + { + if (fin.equals(fout)) return sin; + else if (fin.getEncoding()==AudioFormat.Encoding.PCM_SIGNED) { + try { return AudioSystem.getAudioInputStream(fout,sin); } + catch (IllegalArgumentException ex) { Shell.trace("Direct conversion failed"); } + + AudioFormat fint = new AudioFormat( // PCM + fout.getSampleRate(), fout.getSampleSizeInBits(), + fin.getChannels(), true, fout.isBigEndian()); + Shell.trace("Trying PCM conversion via "+fint.toString()); + return AudioSystem.getAudioInputStream(fout,AudioSystem.getAudioInputStream(fint,sin)); + } else { + // First, check for MP3 - if so, cannot convert number of channels + if (fin.getChannels()==fout.getChannels() && fin.getSampleRate()==fout.getSampleRate()) { + Shell.trace("Trying direct decoding from "+fin.getEncoding().toString()); + return AudioSystem.getAudioInputStream(fout,sin); + } else { + AudioFormat fint = new AudioFormat( + fin.getSampleRate(), fout.getSampleSizeInBits(), + fin.getChannels(), true, fout.isBigEndian()); + Shell.trace("Trying conversion via "+fint.toString()); + return convertFormat(AudioSystem.getAudioInputStream(fint,sin),fint,fout); + } + } + } +} +