diff src/samer/audio/FileSource.java @ 1:5df24c91468d

Oh my what a mess.
author samer
date Fri, 05 Apr 2019 16:26:00 +0100
parents bf79fb79ee13
children
line wrap: on
line diff
--- a/src/samer/audio/FileSource.java	Tue Jan 17 17:50:20 2012 +0000
+++ b/src/samer/audio/FileSource.java	Fri Apr 05 16:26:00 2019 +0100
@@ -19,6 +19,7 @@
 import  javax.swing.*;
 import  java.io.*;
 import  java.util.*;
+import  java.nio.ByteBuffer;
 
 /**
 	An AudioSource that read from multiple audio files. Can read any
@@ -45,13 +46,13 @@
 {
 	List<File>				list=null;
 	ListIterator<File>	it=null;
-	boolean			loop;
+	boolean			loop, buffered=true;
 	int				channelsToMix;
 	VFile				curFile;
 	InputStream		in=null;
-	AudioFormat		format=null;
+	AudioFormat		format=null, inFormat=null;
 	byte[]  			byte_buf=null;
-	int				chunk=0;
+	int				chunk=0, bytesPerSample=0;
 
 	/**
 	 * Construct a FileSource initialised with current file and loop initialised
@@ -72,15 +73,26 @@
 		Shell.registerViewable(this);
 	}
 
+	// AudioSource interface methods
 	public void dispose() {
 		close();
 		Shell.deregisterViewable(this);
 		curFile.dispose();
 	}
 
+	public int getChannels() { return format.getChannels(); }
+	public float getRate()   { return format.getFrameRate(); }
+
 	/** Returns current file */
 	public File getFile() { return curFile.getFile(); }
-	public void setTargetFormat(AudioFormat f) { format=f; }
+	public void setBuffering(boolean b) { buffered=b; }
+
+	/** The actual format of the stream in. May not be same as target format. **/
+	public AudioFormat getStreamFormat() { return inFormat; }
+
+	/** The requested audio format. **/
+	public AudioFormat getTargetFormat() { return format; }
+	public void        setTargetFormat(AudioFormat f) { format=f; }
 
 	/** If true then loop playlist, otherwise, an Exception will be thrown
 	 * when the end of the playlist is reached. If there is no playlist, then
@@ -153,39 +165,47 @@
 	private synchronized void openCurrent() throws Exception
 	{
 		File file=curFile.getFile();
-		Shell.trace("\nOpening audio file: "+file);
+		Shell.trace("\nFileSource:Opening audio file "+file);
 
 		AudioInputStream		s=AudioSystem.getAudioInputStream(file);
-		AudioFormat				af=s.getFormat();
-		long						frames=file.length()/af.getFrameSize();
+		AudioFormat				fmt1, af=s.getFormat();
 
 		Shell.trace("  format: "+af);
-		Shell.trace("  duration: "+(long)(frames/af.getFrameRate())+" s");
 
 		// convert to target format if required
 		if (format!=null) {
 			if (!format.equals(af)) {
 				Shell.trace("  converting to "+format);
 				if (af.getChannels()>format.getChannels()) {
+					int frameSize = af.getChannels()*format.getSampleSizeInBits()/8;
 					Shell.trace("  channels mix down required.");
-					AudioFormat fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(), 
+					fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(), 
 								format.getSampleSizeInBits(), 
-								af.getChannels(), format.getFrameSize(), format.getFrameRate(), format.isBigEndian());
+								af.getChannels(), frameSize, format.getFrameRate(), format.isBigEndian());
 
 					channelsToMix = af.getChannels();
-					s=convertFormat(s,af,fmt1);
 				} else {
 					channelsToMix = 0;
-					s=convertFormat(s,af,format);
+					fmt1=format;
 				}
+				Shell.trace("  converting via "+fmt1);
+				s=convertFormat(s,af,fmt1);
+				inFormat = fmt1;
+			} else {
+				Shell.trace("  no formation conversion required");
+				channelsToMix = 0;
+				inFormat = af;
 			}
+		} else {
+			Shell.trace("  using stream native format");
+			channelsToMix = 0;
+			inFormat = af;
 		}
+		in=s;
+		if (buffered) in = new BufferedInputStream(in,64*1024);
 
 		// 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,16*1024);
 	}
 
 	/** Returns number of bytes available in current file */
@@ -194,33 +214,44 @@
 	/** Reopen current file, so that next read will be from head of file. */
 	public synchronized void reopen() throws Exception { close(); openCurrent(); }
 
-	private void setChunkSize(int s) { chunk=s; }
+	private void setBlockSize(int s) { chunk=s; }
 	private void setByteBuffer() {
-		byte_buf = new byte[2*chunk*(channelsToMix>0 ? channelsToMix : 1)];
+		bytesPerSample = 2*(channelsToMix>0 ? channelsToMix : 1);
+		byte_buf       = new byte[chunk*bytesPerSample];
+	}
+
+	public int readInto(ByteBuffer buf, int offset, int len) throws IOException {
+		return in.read(buf.array(), offset, len);
 	}
 
 	/** 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);
+		setBlockSize(len);
 		if (in!=null) setByteBuffer();
 
 		return new AnonymousTask() {
 			public void run() throws Exception {
-				int n = 0;
+				synchronized (FileSource.this) {
+					// loop until len samples copied into dbuf
+					int rem=len, pos=off;
+					while (rem>0) {
+						int bytesRead = in.read(byte_buf, 0, rem*bytesPerSample);
+						if (bytesRead > 0) { // append this chunk to output
+							int count=bytesRead/bytesPerSample;
+							if (channelsToMix>0) {
+								Util.shortToDoubleMixDown(byte_buf,dbuf,pos,count,channelsToMix);
+							} else {
+								Util.shortToDouble(byte_buf,dbuf,pos,count);
+							}
+							pos+=count; rem-=count;
+						} else if (it!=null) next(); // next file if there is one
+						else if (!loop) throw new EOFException(); // not looping and no more files
+						else reopen(); // back to first file
 
-				synchronized (FileSource.this) {
-					int blen = byte_buf.length;
-					while (n < blen) {
-						int count = in.read(byte_buf, n, blen - n);
-						if (count > 0) n+=count;
-						else if (it!=null) next();
-						else if (!loop) throw new EOFException();
-						else reopen();
+						// if (rem>0) Shell.trace("Read "+bytesRead+" bytes, need "+rem+" more samples.");
 					}
 				}
-				if (channelsToMix>0) Util.shortToDoubleMixDown(byte_buf,dbuf,off,len,channelsToMix);
-				else Util.shortToDouble(byte_buf,dbuf,off,len);
 			}
 		};
 	}
@@ -228,25 +259,29 @@
 	/** 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);
+		setBlockSize(len);
 		if (in!=null) setByteBuffer();
 
 		return new AnonymousTask() {
 			public synchronized void run() throws Exception {
-				int n = 0;
-
 				synchronized (FileSource.this) {
-					int blen = byte_buf.length;
-					while (n < blen) {
-						int count = in.read(byte_buf, n, blen - n);
-						if (count > 0) n+=count;
-						else if (it!=null) next();
+					// loop until len samples copied into dbuf
+					int rem=len, pos=off;
+					while (rem>0) {
+						int bytesRead = in.read(byte_buf, 0, rem*bytesPerSample);
+						if (bytesRead > 0) {
+							int count=bytesRead/bytesPerSample;
+							if (channelsToMix>0) {
+								Util.shortToFloatMixDown(byte_buf,dbuf,pos,count,channelsToMix);
+							} else {
+								Util.shortToFloat(byte_buf,dbuf,pos,count);
+							}
+							pos+=count; rem-=count;
+						} else if (it!=null) next();
 						else if (!loop) throw new EOFException();
 						else reopen();
 					}
 				}
-				if (channelsToMix>0) Util.shortToFloatMixDown(byte_buf,dbuf,off,len,channelsToMix);
-				else Util.shortToFloat(byte_buf,dbuf,off,len);
 			}
 		};
 	}
@@ -318,26 +353,33 @@
 
 	private static AudioInputStream convertFormat(AudioInputStream sin, AudioFormat fin, AudioFormat fout) throws Exception
 	{
-		if (fin==fout) return sin;
+		Shell.trace("\nconvertFormat:");
+		Shell.trace("  | source: "+fin.toString());
+		Shell.trace("  | target: "+fout.toString());
+
+		if (fin.equals(fout)) return sin;
 		else if (fin.getEncoding()==AudioFormat.Encoding.PCM_SIGNED) {
-			try { return AudioSystem.getAudioInputStream(fout,sin); }
+			try { 
+				Shell.trace("  | Trying direct (PCM) from "+fin.getEncoding().toString());
+				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());
+			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());
+				Shell.trace("  | Trying 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());
+				Shell.trace("  | Trying recursive via "+fint.toString());
 				return convertFormat(AudioSystem.getAudioInputStream(fint,sin),fint,fout);
 			}
 		}