comparison 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
comparison
equal deleted inserted replaced
0:bf79fb79ee13 1:5df24c91468d
1 /*
2 * FileSource.java
3 *
4 * Copyright (c) 2000, Samer Abdallah, King's College London.
5 * All rights reserved.
6 *
7 * This software is provided AS iS and WITHOUT ANY WARRANTY;
8 * without even the implied warranty of MERCHANTABILITY or
9 * FITNESS FOR A PARTICULAR PURPOSE.
10 */
11
12 package samer.audio;
13
14 import samer.core.*;
15 import samer.core.types.*;
16 import samer.core.util.*;
17 import samer.tools.*;
18 import javax.sound.sampled.*;
19 import javax.swing.*;
20 import java.io.*;
21 import java.util.*;
22 import java.nio.ByteBuffer;
23
24 /**
25 */
26
27
28 public abstract class MultiFileAudioStream extends InputStream
29 {
30 List<File> list=null;
31 ListIterator<File> it=null;
32 int channelsToMix;
33 File curFile=null;
34 InputStream in=null;
35 AudioFormat format=null, inFormat=null;
36 boolean loop=false;
37
38 /**
39 * Construct a FileSource initialised with current file and loop initialised
40 * from the current Environment. No exception is thrown if the current file
41 * cannot be opened, however, you must be sure to set it to a valid file
42 * (or to set a playlist) before trying to read any samples.
43 */
44 public MultiFileAudioStream(AudioFormat target, List<File> files)
45 {
46 list=files;
47 format=target;
48 }
49
50 public AudioFormat getFormat() { return format; }
51
52 /** Set playlist to all WAV files in given directory */
53 public List<File> directory(File dir, String ext) {
54 return Arrays.asList(dir.listFiles(getFileFilter(ext)));
55 }
56
57 private static java.io.FileFilter getFileFilter(final String ext) {
58 return new java.io.FileFilter() {
59 public boolean accept(File file) {
60 return file.getName().toLowerCase().endsWith(ext);
61 }
62 };
63 }
64
65 public synchronized List<File> getPlaylist() { return list; }
66
67 /** Go back to start of playlist. Next block of samples will be from head
68 * of first file in playlist. Will throw an exception if there is no playlist */
69 public synchronized void rewind() throws IOException {
70 it=list.listIterator(); next();
71 }
72
73 /** Move to head of next file in playlist */
74 public synchronized void next() throws IOException
75 {
76 boolean wasOpen=isOpen();
77
78 if (wasOpen) close();
79 if (!it.hasNext()) {
80 if (!loop) throw new EOFException();
81 it=list.listIterator();
82 if (!it.hasNext()) throw new IOException("no files in playlist");
83 }
84 curFile=it.next();
85 if (wasOpen) openCurrent();
86 }
87
88 /** Move to head of previous file in playlist */
89 public synchronized void prev() throws Exception
90 {
91 boolean wasOpen=isOpen();
92 if (wasOpen) close();
93 if (!it.hasPrevious()) {
94 if (!loop) throw new EOFException();
95 it=list.listIterator(list.size());
96 if (!it.hasPrevious()) throw new Exception("no files in playlist");
97 }
98 curFile=it.previous();
99 if (wasOpen) openCurrent();
100 }
101
102 public boolean isOpen() { return in!=null; }
103
104 /** Closes current input stream */
105 public synchronized void close() {
106 try {
107 if (in!=null) {
108 Shell.trace("Closing audio stream...");
109 in.close(); in=null;
110 }
111 }
112 catch (IOException ex) {}
113 }
114
115 /** Opens the playlist starting with the first file. */
116 public synchronized void open() throws Exception { rewind(); if (!isOpen()) openCurrent(); }
117
118 /** Opens the current file. Next read will returns samples
119 * from head of given file. If setFormat() was called previously, then
120 * we will attempt to do format conversion. */
121 private synchronized void openCurrent() throws IOException
122 {
123 try {
124 File file=curFile;
125 Shell.trace("\nFileSource:Opening audio file "+file);
126
127 AudioInputStream s=AudioSystem.getAudioInputStream(file);
128 AudioFormat fmt1, af=s.getFormat();
129
130 Shell.trace(" format: "+af);
131
132 // convert to target format if required
133 if (format!=null) {
134 if (!format.equals(af)) {
135 Shell.trace(" converting to "+format);
136 if (af.getChannels()>format.getChannels()) {
137 int frameSize = af.getChannels()*format.getSampleSizeInBits()/8;
138 Shell.trace(" channels mix down required.");
139 fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(),
140 format.getSampleSizeInBits(),
141 af.getChannels(), frameSize, format.getFrameRate(), format.isBigEndian());
142
143 channelsToMix = af.getChannels();
144 } else {
145 channelsToMix = 0;
146 fmt1=format;
147 }
148 Shell.trace(" converting via "+fmt1);
149 s=convertFormat(s,af,fmt1);
150 inFormat = fmt1;
151 } else {
152 Shell.trace(" no formation conversion required");
153 channelsToMix = 0;
154 inFormat = af;
155 }
156 } else {
157 Shell.trace(" using stream native format");
158 channelsToMix = 0;
159 inFormat = af;
160 }
161 in=s;
162 }
163 catch (Exception ex) { throw new IOException("Failed to open audio file"); }
164 }
165
166 /** Returns number of bytes available in current file */
167 public int available() {
168 try {
169 return in.available();
170 }
171 catch (Exception ex) { return 0; }
172 }
173
174 /** Returns a Task which copies samples as doubles into the given
175 * buffer between the given positions. */
176 public int read(byte [] buf, int off, int len) throws IOException {
177
178 // loop until len samples copied into dbuf
179 int rem=len, pos=off;
180 while (rem>0) {
181 int chunk = in.read(buf, pos, rem);
182 if (chunk > 0) { // append this chunk to output
183 pos+=chunk; rem-=chunk;
184 } else if (it!=null) next(); // next file if there is one
185 else if (!loop) throw new EOFException(); // not looping and no more files
186 else rewind(); // back to first file
187 }
188 return len;
189 }
190
191
192 private static AudioInputStream convertFormat(AudioInputStream sin, AudioFormat fin, AudioFormat fout) throws Exception
193 {
194 Shell.trace("\nconvertFormat:");
195 Shell.trace(" | source: "+fin.toString());
196 Shell.trace(" | target: "+fout.toString());
197
198 if (fin.equals(fout)) return sin;
199 else if (fin.getEncoding()==AudioFormat.Encoding.PCM_SIGNED) {
200 try {
201 Shell.trace(" | Trying direct (PCM) from "+fin.getEncoding().toString());
202 return AudioSystem.getAudioInputStream(fout,sin);
203 }
204 catch (IllegalArgumentException ex) { Shell.trace("Direct conversion failed"); }
205
206 AudioFormat fint = new AudioFormat( // PCM
207 fout.getSampleRate(), fout.getSampleSizeInBits(),
208 fin.getChannels(), true, fout.isBigEndian());
209 Shell.trace(" | Trying PCM conversion via "+fint.toString());
210 return AudioSystem.getAudioInputStream(fout,AudioSystem.getAudioInputStream(fint,sin));
211 } else {
212 // First, check for MP3 - if so, cannot convert number of channels
213 if (fin.getChannels()==fout.getChannels() && fin.getSampleRate()==fout.getSampleRate()) {
214 Shell.trace(" | Trying decoding from "+fin.getEncoding().toString());
215 return AudioSystem.getAudioInputStream(fout,sin);
216 } else {
217 AudioFormat fint = new AudioFormat(
218 fin.getSampleRate(), fout.getSampleSizeInBits(),
219 fin.getChannels(), true, fout.isBigEndian());
220 Shell.trace(" | Trying recursive via "+fint.toString());
221 return convertFormat(AudioSystem.getAudioInputStream(fint,sin),fint,fout);
222 }
223 }
224 }
225 }
226