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