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);
+			}
+		}
+	}
+}
+