Mercurial > hg > jslab
view src/samer/audio/FileSource2.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 source
/* * FileSource2.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; /** An AudioSource that read from multiple audio files. 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. An EOFException is thrown if an attempt is made to read beyond the end of the last file. FileSource2 is a Viewable called "playlist", and an Agent with commands next, view. */ public class FileSource2 extends Viewable implements AudioSource, Agent { List<File> list=null; ListIterator<File> it=null; int channelsToMix; File curFile=null; InputStream in=null; AudioFormat targetFormat=null; byte[] byte_buf=null; int chunk=0, bytesPerSample=0; /** * Construct a FileSource2. */ public FileSource2(AudioFormat targetFormat, List<File> files) { super("playlist"); setAgent(this); Shell.registerViewable(this); this.targetFormat=targetFormat; this.list=files; it=list.listIterator(); } // AudioSource interface methods public void dispose() { close(); Shell.deregisterViewable(this); } public int getChannels() { return targetFormat.getChannels(); } public float getRate() { return targetFormat.getFrameRate(); } /** Returns current file */ public File getFile() { return curFile; } /** The requested audio format. **/ public AudioFormat getTargetFormat() { return targetFormat; } /** Set playlist to all WAV files in given directory */ public static List<File> directory(File dir, String ext) { return Arrays.asList(dir.listFiles(getFileFilter(ext))); } public synchronized List<File> getPlaylist() { return list; } /** Move to head of next file in playlist */ public synchronized void next() throws Exception { boolean wasOpen=isOpen(); if (wasOpen) close(); if (!it.hasNext()) throw new EOFException(); curFile=it.next(); 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; byte_buf=null; } } catch (IOException ex) {} } /** Opens the playlist starting with the first file. */ public synchronized void open() throws Exception { it=list.listIterator(); next(); 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 { File file=curFile; Shell.trace("\nFileSource:Opening audio file "+file); AudioInputStream ain=AudioSystem.getAudioInputStream(file); // convert to target format if required if (targetFormat==null) { AudioFormat fin=ain.getFormat(); ain=convertFormat(new AudioFormat( fin.getSampleRate(), 16, fin.getChannels(), true, false), ain); } else { ain=convertFormat(targetFormat, ain); } bytesPerSample=ain.getFormat().getSampleSizeInBits()/8; if (bytesPerSample!=2) throw new Exception("Must be 16 bits per sample"); in = new BufferedInputStream(ain,64*1024); // If we have a reader task, then update the byte buffer if (chunk>0) setByteBuffer(); } /** Returns number of bytes available in current file */ public int available() throws Exception { return in.available(); } private void setBlockSize(int s) { chunk=s; } private void setByteBuffer() { 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) { setBlockSize(len); if (in!=null) setByteBuffer(); return new AnonymousTask() { public void run() throws Exception { synchronized (FileSource2.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; Util.shortToDouble(byte_buf,dbuf,pos,count); pos+=count; rem-=count; } else if (it!=null) next(); // next file if there is one else throw new EOFException(); // not looping and no more files } } } }; } /** 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) { setBlockSize(len); if (in!=null) setByteBuffer(); return new AnonymousTask() { public synchronized void run() throws Exception { synchronized (FileSource2.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) { int count=bytesRead/bytesPerSample; Util.shortToFloat(byte_buf,dbuf,pos,count); pos+=count; rem-=count; } else if (it!=null) next(); else throw new EOFException(); } } } }; } // Agent public void getCommands(Agent.Registry r) { r.add("next").add("view"); } public void execute(String cmd, Environment env) throws Exception { if (cmd.equals("next")) next(); else if (cmd.equals("view")) { if (list!=null) { Shell.expose( new JScrollPane(new JList(list.toArray())), "playlist"); } } } // Viewable public Viewer getViewer() { return new FileSourceViewer(); } class FileSourceViewer extends DefaultViewer implements Agent { public FileSourceViewer() { super(FileSource2.this); //add(curFile); add(Shell.createButtonsFor(this)); } public void getCommands(Agent.Registry r) { r.add("next").add("view"); } public void execute(String cmd, Environment env) throws Exception { FileSource2.this.execute(cmd,env); } }; 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); } }; } private static AudioInputStream convertVia(AudioFormat fout, AudioInputStream sin, AudioFormat fint) throws Exception { Shell.trace(" | Trying recursive via "+fint.toString()); AudioInputStream sint=AudioSystem.getAudioInputStream(fint,sin); Shell.trace(" | Obtained "+sint.getFormat().toString()); return convertFormat(fout, sint); } private static AudioInputStream convertFormat(AudioFormat fout, AudioInputStream sin) throws Exception { AudioFormat fin=sin.getFormat(); if (fin.equals(fout)) return sin; Shell.trace("\nconvertFormat:"); Shell.trace(" | source: "+fin.toString()); Shell.trace(" | target: "+fout.toString()); if (fin.getEncoding()!=AudioFormat.Encoding.PCM_SIGNED) { // first get into PCM encoding, then try recursive try { return convertVia( fout, sin, new AudioFormat( fin.getSampleRate(), fout.getSampleSizeInBits(), fin.getChannels(), true, fout.isBigEndian())); } catch (IllegalArgumentException ex) { Shell.trace("Direct conversion failed"); } return convertVia( fout, sin, new AudioFormat( fin.getSampleRate(), fin.getSampleSizeInBits(), fin.getChannels(), true, fout.isBigEndian())); } if (fin.getChannels()!=fout.getChannels() || fin.getSampleSizeInBits()!=fout.getSampleSizeInBits()) { // convert these before doing any sample rate conversion return convertVia(fout, sin, new AudioFormat( fin.getSampleRate(), fout.getSampleSizeInBits(), fout.getChannels(), true, fout.isBigEndian())); } // the only thing left is sample rate return AudioSystem.getAudioInputStream(fout,sin); } }