Mercurial > hg > jslab
diff src/samer/audio/MultiFileAudioStream.java @ 1:5df24c91468d
Oh my what a mess.
author | samer |
---|---|
date | Fri, 05 Apr 2019 16:26:00 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/samer/audio/MultiFileAudioStream.java Fri Apr 05 16:26:00 2019 +0100 @@ -0,0 +1,226 @@ +/* + * FileSource.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.util.*; +import java.nio.ByteBuffer; + +/** +*/ + + +public abstract class MultiFileAudioStream extends InputStream +{ + List<File> list=null; + ListIterator<File> it=null; + int channelsToMix; + File curFile=null; + InputStream in=null; + AudioFormat format=null, inFormat=null; + boolean loop=false; + + /** + * Construct a FileSource initialised with current file and loop initialised + * from the current Environment. No exception is thrown if the current file + * cannot be opened, however, you must be sure to set it to a valid file + * (or to set a playlist) before trying to read any samples. + */ + public MultiFileAudioStream(AudioFormat target, List<File> files) + { + list=files; + format=target; + } + + public AudioFormat getFormat() { return format; } + + /** Set playlist to all WAV files in given directory */ + public List<File> directory(File dir, String ext) { + return Arrays.asList(dir.listFiles(getFileFilter(ext))); + } + + private static java.io.FileFilter getFileFilter(final String ext) { + return new java.io.FileFilter() { + public boolean accept(File file) { + return file.getName().toLowerCase().endsWith(ext); + } + }; + } + + public synchronized List<File> getPlaylist() { return list; } + + /** Go back to start of playlist. Next block of samples will be from head + * of first file in playlist. Will throw an exception if there is no playlist */ + public synchronized void rewind() throws IOException { + it=list.listIterator(); next(); + } + + /** Move to head of next file in playlist */ + public synchronized void next() throws IOException + { + boolean wasOpen=isOpen(); + + if (wasOpen) close(); + if (!it.hasNext()) { + if (!loop) throw new EOFException(); + it=list.listIterator(); + if (!it.hasNext()) throw new IOException("no files in playlist"); + } + curFile=it.next(); + if (wasOpen) openCurrent(); + } + + /** Move to head of previous file in playlist */ + public synchronized void prev() throws Exception + { + boolean wasOpen=isOpen(); + if (wasOpen) close(); + if (!it.hasPrevious()) { + if (!loop) throw new EOFException(); + it=list.listIterator(list.size()); + if (!it.hasPrevious()) throw new Exception("no files in playlist"); + } + curFile=it.previous(); + if (wasOpen) openCurrent(); + } + + 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; + } + } + catch (IOException ex) {} + } + + /** Opens the playlist starting with the first file. */ + public synchronized void open() throws Exception { rewind(); 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 IOException + { + try { + File file=curFile; + Shell.trace("\nFileSource:Opening audio file "+file); + + AudioInputStream s=AudioSystem.getAudioInputStream(file); + 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()) { + int frameSize = af.getChannels()*format.getSampleSizeInBits()/8; + Shell.trace(" channels mix down required."); + fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(), + format.getSampleSizeInBits(), + af.getChannels(), frameSize, format.getFrameRate(), format.isBigEndian()); + + channelsToMix = af.getChannels(); + } else { + channelsToMix = 0; + 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; + } + catch (Exception ex) { throw new IOException("Failed to open audio file"); } + } + + /** Returns number of bytes available in current file */ + public int available() { + try { + return in.available(); + } + catch (Exception ex) { return 0; } + } + + /** Returns a Task which copies samples as doubles into the given + * buffer between the given positions. */ + public int read(byte [] buf, int off, int len) throws IOException { + + // loop until len samples copied into dbuf + int rem=len, pos=off; + while (rem>0) { + int chunk = in.read(buf, pos, rem); + if (chunk > 0) { // append this chunk to output + pos+=chunk; rem-=chunk; + } else if (it!=null) next(); // next file if there is one + else if (!loop) throw new EOFException(); // not looping and no more files + else rewind(); // back to first file + } + return len; + } + + + private static AudioInputStream convertFormat(AudioInputStream sin, AudioFormat fin, AudioFormat fout) throws Exception + { + 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 { + 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()); + 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 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 recursive via "+fint.toString()); + return convertFormat(AudioSystem.getAudioInputStream(fint,sin),fint,fout); + } + } + } +} +