Mercurial > hg > jslab
comparison src/samer/audio/StreamSource.java @ 1:5df24c91468d
Oh my what a mess.
author | samer |
---|---|
date | Fri, 05 Apr 2019 16:26:00 +0100 |
parents | |
children | 15b93db27c04 |
comparison
equal
deleted
inserted
replaced
0:bf79fb79ee13 | 1:5df24c91468d |
---|---|
1 /* | |
2 * StreamSource.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.net.URL; | |
22 import java.util.*; | |
23 | |
24 /** | |
25 An AudioSource that read from an input stream. Can read any | |
26 format for which the appropriate JavaSound plug-in is installed on | |
27 your system, eg WAV, AU, MP3, OGG. The implementation of | |
28 AudioSource.reader() returns a Task which gets samples from the audio | |
29 files, going through the play list one by one. If the loop flag | |
30 is true, then samples are returned indefinitely by looping through the | |
31 playlist; otherwise, an EOFException is thrown if an attempt is made | |
32 to read beyond the end of the last file. | |
33 | |
34 Properties read from current environment: | |
35 <dl> | |
36 <dt>current<dd>Current file (String) | |
37 <dt>loop<dd>Loop playlist? (Boolean) [true] | |
38 </dl> | |
39 | |
40 */ | |
41 | |
42 public class StreamSource implements AudioSource | |
43 { | |
44 int channelsToMix; | |
45 AudioInputStream source; | |
46 InputStream in=null; | |
47 AudioFormat format=null; | |
48 byte[] byte_buf=null; | |
49 int chunk=0; | |
50 | |
51 /** | |
52 * Construct a StreamSource initialised with current file and loop initialised | |
53 * from the current Environment. No exception is thrown if the current file | |
54 */ | |
55 public StreamSource(AudioInputStream s) throws Exception { source=s; } | |
56 public StreamSource(InputStream s) throws Exception { source=AudioSystem.getAudioInputStream(s); } | |
57 public StreamSource(File file) throws Exception { source=AudioSystem.getAudioInputStream(file); } | |
58 public StreamSource(URL url) throws Exception { source=AudioSystem.getAudioInputStream(url); } | |
59 | |
60 public void dispose() { close(); } | |
61 | |
62 /** Returns current file */ | |
63 public void setTargetFormat(AudioFormat f) { format=f; } | |
64 | |
65 public AudioFormat getFormat() { return format; } | |
66 | |
67 public boolean isOpen() { return in!=null; } | |
68 | |
69 /** Closes current input stream */ | |
70 public synchronized void close() { | |
71 try { | |
72 if (in!=null) { | |
73 Shell.trace("Closing audio stream..."); | |
74 in.close(); in=null; byte_buf=null; | |
75 } | |
76 } | |
77 catch (IOException ex) {} | |
78 } | |
79 | |
80 /** Opens the playlist starting with the first file. */ | |
81 public synchronized void open() throws Exception { if (!isOpen()) openCurrent(); } | |
82 | |
83 /** Opens the current file. Next read will returns samples | |
84 * from head of given file. If setFormat() was called previously, then | |
85 * we will attempt to do format conversion. */ | |
86 private synchronized void openCurrent() throws Exception | |
87 { | |
88 AudioInputStream s=source; | |
89 AudioFormat fmt1, af=s.getFormat(); | |
90 | |
91 Shell.trace(" format: "+af); | |
92 | |
93 // convert to target format if required | |
94 if (format!=null) { | |
95 if (!format.equals(af)) { | |
96 Shell.trace(" converting to "+format); | |
97 if (af.getChannels()>format.getChannels()) { | |
98 Shell.trace(" channels mix down required."); | |
99 fmt1 = new AudioFormat( format.getEncoding(), format.getSampleRate(), | |
100 format.getSampleSizeInBits(), | |
101 af.getChannels(), format.getFrameSize(), format.getFrameRate(), format.isBigEndian()); | |
102 | |
103 channelsToMix = af.getChannels(); | |
104 } else { | |
105 channelsToMix = 0; | |
106 fmt1=format; | |
107 } | |
108 s=convertFormat(s,af,fmt1); | |
109 } | |
110 } | |
111 | |
112 // If we have a reader task, then update the byte buffer | |
113 if (chunk>0) setByteBuffer(); | |
114 | |
115 // Shell.trace("stream format: "+s.getFormat()); | |
116 in = new BufferedInputStream(s,64*1024); | |
117 } | |
118 | |
119 /** Returns number of bytes available in current file */ | |
120 public int available() throws Exception { return in.available(); } | |
121 | |
122 private void setChunkSize(int s) { chunk=s; } | |
123 private void setByteBuffer() { | |
124 byte_buf = new byte[2*chunk*(channelsToMix>0 ? channelsToMix : 1)]; | |
125 } | |
126 | |
127 /** Returns a Task which copies samples as doubles into the given | |
128 * buffer between the given positions. */ | |
129 public Task reader(final double [] dbuf, final int off, final int len) { | |
130 setChunkSize(len); | |
131 if (in!=null) setByteBuffer(); | |
132 | |
133 return new AnonymousTask() { | |
134 public void run() throws Exception { | |
135 int n = 0; | |
136 | |
137 synchronized (StreamSource.this) { | |
138 int blen = byte_buf.length; | |
139 while (n < blen) { | |
140 int count = in.read(byte_buf, n, blen - n); | |
141 if (count > 0) n+=count; | |
142 else throw new EOFException(); | |
143 } | |
144 } | |
145 if (channelsToMix>0) Util.shortToDoubleMixDown(byte_buf,dbuf,off,len,channelsToMix); | |
146 else Util.shortToDouble(byte_buf,dbuf,off,len); | |
147 } | |
148 }; | |
149 } | |
150 | |
151 /** Returns a Task which copies samples as floats into the given | |
152 * buffer between the given positions. */ | |
153 public Task reader(final float [] dbuf, final int off, final int len) { | |
154 setChunkSize(len); | |
155 if (in!=null) setByteBuffer(); | |
156 | |
157 return new AnonymousTask() { | |
158 public synchronized void run() throws Exception { | |
159 int n = 0; | |
160 | |
161 synchronized (StreamSource.this) { | |
162 int blen = byte_buf.length; | |
163 while (n < blen) { | |
164 int count = in.read(byte_buf, n, blen - n); | |
165 if (count > 0) n+=count; | |
166 else throw new EOFException(); | |
167 } | |
168 } | |
169 if (channelsToMix>0) Util.shortToFloatMixDown(byte_buf,dbuf,off,len,channelsToMix); | |
170 else Util.shortToFloat(byte_buf,dbuf,off,len); | |
171 } | |
172 }; | |
173 } | |
174 | |
175 private static AudioInputStream convertFormat(AudioInputStream sin, AudioFormat fin, AudioFormat fout) throws Exception | |
176 { | |
177 if (fin.equals(fout)) return sin; | |
178 else if (fin.getEncoding()==AudioFormat.Encoding.PCM_SIGNED) { | |
179 try { return AudioSystem.getAudioInputStream(fout,sin); } | |
180 catch (IllegalArgumentException ex) { Shell.trace("Direct conversion failed"); } | |
181 | |
182 AudioFormat fint = new AudioFormat( // PCM | |
183 fout.getSampleRate(), fout.getSampleSizeInBits(), | |
184 fin.getChannels(), true, fout.isBigEndian()); | |
185 Shell.trace("Trying PCM conversion via "+fint.toString()); | |
186 return AudioSystem.getAudioInputStream(fout,AudioSystem.getAudioInputStream(fint,sin)); | |
187 } else { | |
188 // First, check for MP3 - if so, cannot convert number of channels | |
189 if (fin.getChannels()==fout.getChannels() && fin.getSampleRate()==fout.getSampleRate()) { | |
190 Shell.trace("Trying direct decoding from "+fin.getEncoding().toString()); | |
191 return AudioSystem.getAudioInputStream(fout,sin); | |
192 } else { | |
193 AudioFormat fint = new AudioFormat( | |
194 fin.getSampleRate(), fout.getSampleSizeInBits(), | |
195 fin.getChannels(), true, fout.isBigEndian()); | |
196 Shell.trace("Trying conversion via "+fint.toString()); | |
197 return convertFormat(AudioSystem.getAudioInputStream(fint,sin),fint,fout); | |
198 } | |
199 } | |
200 } | |
201 } | |
202 |