Mercurial > hg > jslab
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 |