3 import javax.sound.sampled.AudioFormat;
4 import javax.sound.sampled.AudioSystem;
5 import javax.sound.sampled.DataLine;
6 import javax.sound.sampled.SourceDataLine;
8 import sun.audio.AudioData;
9 import sun.audio.AudioDataStream;
10 import sun.audio.AudioPlayer;
14 * The JSAudioThread adds a layer to JSThread that is specific for audio.
15 * This class utilizes JSAudioLine, which is not fully fleshed out.
17 * Two very simple interfaces,
19 * (new JSAudioThread()).playULawData(byte[] b);
23 * (new JSAudioThread(audioFormat)).playOnce(byte[] b, offset, length);
25 * allow straightforward audio production without having to work with
26 * SourceDataLine directly.
28 * As a JSThread, this class implements myInit(), isLooping(), myLoop(),
29 * whenDone(), onException(), and doFinally().
31 * If the constructor JSAudioThread(JSAudioThreadUser, AudioFormat, byte[]) is
32 * used, then the JSAudioThreadUser must simply implement fillAudioBuffer(),
33 * checkSoundStatus(), and audioThreadExiting() for very simple streaming audio.
34 * JSAudioThread will then take care of all the timing issues.
36 * But the implementer is welcome to override any of the JSThread overrides
37 * themselves in order to customize this further.
39 * The standard streaming case, then is:
41 * audioThread = new JSAudioThread(audioUser, audioFormat, audioByteBuffer);
42 * audioThread.start();
44 * where audioUser provides
46 * checkSoundStatus() (called in isLooping()),
48 * fillAudioBuffer() (called in myLoop()),
52 * audioThreadExiting() (called in doFinally()).
57 public class JSAudioThread extends JSThread {
59 protected Owner owner;
60 protected boolean done;
61 protected int myBufferLength;
62 protected SourceDataLine line;
63 protected int rate, nChannels, bitsPerSample;
65 protected byte[] audioByteBuffer;
66 protected int audioBufferByteLength;
68 private AudioFormat audioFormat;
69 private int myBufferOffset;
70 private int playCount;
72 public JSAudioThread(Owner owner, AudioFormat audioFormat, byte[] audioByteBuffer) {
74 setFormat(audioFormat);
75 setBuffer(audioByteBuffer);
79 * A convenience constructor requiring standard settings of
80 * signed (for 8-bit) and littleEndian (for 16-bit)
84 * @param bitsPerSample
86 * @param audioByteBuffer
88 public JSAudioThread(Owner owner, int rate, int bitsPerSample, int nChannels, byte[] audioByteBuffer) {
90 setFormat(new AudioFormat(rate, bitsPerSample, nChannels, true, false));
91 setBuffer(audioByteBuffer);
95 * primarily available for (new JSAudioThread()).playULawData
98 public JSAudioThread() {
102 * primarily available for (new JSAudioThread()).playOnce
105 public JSAudioThread(AudioFormat audioFormat) {
106 setFormat(audioFormat);
111 * A simple 8-bit uLaw data player
115 public void playULawData(byte[] data) {
116 // this constructor uses default new AudioFormat(ULAW,8000,8,1,1,8000,true)
117 // threading is taken care of by the browser in JavaScript
118 AudioPlayer.player.start(new AudioDataStream(new AudioData(data)));
123 * Just play once through; no additions
127 public void playOnce(byte[] data, int offset, int length) {
129 myBufferOffset = offset;
130 myBufferLength = length;
135 public void setBuffer(byte[] audioByteBuffer) {
136 this.audioByteBuffer = audioByteBuffer;
137 audioBufferByteLength = audioByteBuffer.length;
140 public SourceDataLine getLine() {
144 public AudioFormat getFormat() {
148 public void setFormat(AudioFormat audioFormat) {
149 this.audioFormat = audioFormat;
150 rate = (int) audioFormat.getSampleRate();
151 bitsPerSample = audioFormat.getSampleSizeInBits();
152 nChannels = audioFormat.getChannels();
155 public void resetAudio() {
164 * Standard initialization of a SourceDataLine
168 protected boolean myInit() {
170 DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
173 line = (SourceDataLine) AudioSystem.getLine(info);
174 line.open(audioFormat, audioBufferByteLength);
176 } catch (Exception e) {
184 protected boolean isLooping() {
185 return !done && (--playCount >= 0 || owner != null && owner.checkSoundStatus());
189 protected boolean myLoop() {
191 if ((myBufferLength = (owner == null ? myBufferLength : owner.fillAudioBuffer())) <= 0)
192 return !(done = true);
196 line.write(audioByteBuffer, myBufferOffset, myBufferLength);
197 } catch (Exception e) {
206 protected void whenDone() {
212 protected int getDelayMillis() {
213 // about 25% of the actual play time
214 return 1000 // ms/sec
215 * (myBufferLength * 8 / bitsPerSample) // * samples
216 / rate // * seconds/sample)
217 / nChannels // / number of channels
222 protected void onException(Exception e) {
227 protected void doFinally() {
229 owner.audioThreadExiting();
232 public interface Owner {
236 * @return true if thread should continue; false if not
239 boolean checkSoundStatus();
241 /** fill audio buffer
243 * @return number of bytes to write to audio line
246 int fillAudioBuffer();
249 * called from the finally clause when complete
252 void audioThreadExiting();