5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.sun.media.sound;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.Arrays;
30
31 import javax.sound.sampled.AudioFormat;
32 import javax.sound.sampled.AudioInputStream;
33 import javax.sound.sampled.AudioSystem;
34 import javax.sound.sampled.DataLine;
35 import javax.sound.sampled.LineEvent;
36 import javax.sound.sampled.LineUnavailableException;
37 import javax.sound.sampled.SourceDataLine;
38
39 /**
40 * SourceDataLine implementation for the SoftMixingMixer.
41 *
42 * @author Karl Helgason
43 */
44 public final class SoftMixingSourceDataLine extends SoftMixingDataLine
59 private byte[] cycling_buffer;
60
61 private int cycling_read_pos = 0;
62
63 private int cycling_write_pos = 0;
64
65 private int cycling_avail = 0;
66
67 private long cycling_framepos = 0;
68
69 private AudioFloatInputStream afis;
70
71 private static class NonBlockingFloatInputStream extends
72 AudioFloatInputStream {
73 AudioFloatInputStream ais;
74
75 NonBlockingFloatInputStream(AudioFloatInputStream ais) {
76 this.ais = ais;
77 }
78
79 public int available() throws IOException {
80 return ais.available();
81 }
82
83 public void close() throws IOException {
84 ais.close();
85 }
86
87 public AudioFormat getFormat() {
88 return ais.getFormat();
89 }
90
91 public long getFrameLength() {
92 return ais.getFrameLength();
93 }
94
95 public void mark(int readlimit) {
96 ais.mark(readlimit);
97 }
98
99 public boolean markSupported() {
100 return ais.markSupported();
101 }
102
103 public int read(float[] b, int off, int len) throws IOException {
104 int avail = available();
105 if (len > avail) {
106 int ret = ais.read(b, off, avail);
107 Arrays.fill(b, off + ret, off + len, 0);
108 return len;
109 }
110 return ais.read(b, off, len);
111 }
112
113 public void reset() throws IOException {
114 ais.reset();
115 }
116
117 public long skip(long len) throws IOException {
118 return ais.skip(len);
119 }
120
121 }
122
123 SoftMixingSourceDataLine(SoftMixingMixer mixer, DataLine.Info info) {
124 super(mixer, info);
125 }
126
127 public int write(byte[] b, int off, int len) {
128 if (!isOpen())
129 return 0;
130 if (len % framesize != 0)
131 throw new IllegalArgumentException(
132 "Number of bytes does not represent an integral number of sample frames.");
133 if (off < 0) {
134 throw new ArrayIndexOutOfBoundsException(off);
135 }
136 if ((long)off + (long)len > (long)b.length) {
137 throw new ArrayIndexOutOfBoundsException(b.length);
138 }
139
140 byte[] buff = cycling_buffer;
141 int buff_len = cycling_buffer.length;
142
143 int l = 0;
144 while (l != len) {
145 int avail;
146 synchronized (cycling_buffer) {
185 // FloatControl.Type.PAN
186 // FloatControl.Type.MASTER_GAIN
187 // FloatControl.Type.BALANCE
188
189 private boolean _active = false;
190
191 private AudioFormat outputformat;
192
193 private int out_nrofchannels;
194
195 private int in_nrofchannels;
196
197 private float _rightgain;
198
199 private float _leftgain;
200
201 private float _eff1gain;
202
203 private float _eff2gain;
204
205 protected void processControlLogic() {
206 _active = active;
207 _rightgain = rightgain;
208 _leftgain = leftgain;
209 _eff1gain = eff1gain;
210 _eff2gain = eff2gain;
211 }
212
213 protected void processAudioLogic(SoftAudioBuffer[] buffers) {
214 if (_active) {
215 float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
216 float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
217 int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();
218
219 int readlen = bufferlen * in_nrofchannels;
220 if (readbuffer == null || readbuffer.length < readlen) {
221 readbuffer = new float[readlen];
222 }
223 int ret = 0;
224 try {
225 ret = afis.read(readbuffer);
226 if (ret != in_nrofchannels)
227 Arrays.fill(readbuffer, ret, readlen, 0);
228 } catch (IOException e) {
229 }
230
231 int in_c = in_nrofchannels;
232 for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
257 }
258 }
259 }
260
261 if (_eff2gain > 0.0001) {
262 float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
263 .array();
264 for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
265 eff2[i] += readbuffer[ix] * _eff2gain;
266 }
267 if (in_nrofchannels == 2) {
268 for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
269 eff2[i] += readbuffer[ix] * _eff2gain;
270 }
271 }
272 }
273
274 }
275 }
276
277 public void open() throws LineUnavailableException {
278 open(format);
279 }
280
281 public void open(AudioFormat format) throws LineUnavailableException {
282 if (bufferSize == -1)
283 bufferSize = ((int) (format.getFrameRate() / 2))
284 * format.getFrameSize();
285 open(format, bufferSize);
286 }
287
288 public void open(AudioFormat format, int bufferSize)
289 throws LineUnavailableException {
290
291 LineEvent event = null;
292
293 if (bufferSize < format.getFrameSize() * 32)
294 bufferSize = format.getFrameSize() * 32;
295
296 synchronized (control_mutex) {
297
298 if (!isOpen()) {
299 if (!mixer.isOpen()) {
300 mixer.open();
301 mixer.implicitOpen = true;
302 }
303
304 event = new LineEvent(this, LineEvent.Type.OPEN, 0);
305
306 this.bufferSize = bufferSize - bufferSize
307 % format.getFrameSize();
308 this.format = format;
309 this.framesize = format.getFrameSize();
310 this.outputformat = mixer.getFormat();
311 out_nrofchannels = outputformat.getChannels();
312 in_nrofchannels = format.getChannels();
313
314 open = true;
315
316 mixer.getMainMixer().openLine(this);
317
318 cycling_buffer = new byte[framesize * bufferSize];
319 cycling_read_pos = 0;
320 cycling_write_pos = 0;
321 cycling_avail = 0;
322 cycling_framepos = 0;
323
324 InputStream cycling_inputstream = new InputStream() {
325
326 public int read() throws IOException {
327 byte[] b = new byte[1];
328 int ret = read(b);
329 if (ret < 0)
330 return ret;
331 return b[0] & 0xFF;
332 }
333
334 public int available() throws IOException {
335 synchronized (cycling_buffer) {
336 return cycling_avail;
337 }
338 }
339
340 public int read(byte[] b, int off, int len)
341 throws IOException {
342
343 synchronized (cycling_buffer) {
344 if (len > cycling_avail)
345 len = cycling_avail;
346 int pos = cycling_read_pos;
347 byte[] buff = cycling_buffer;
348 int buff_len = buff.length;
349 for (int i = 0; i < len; i++) {
350 b[off++] = buff[pos];
351 pos++;
352 if (pos == buff_len)
353 pos = 0;
354 }
355 cycling_read_pos = pos;
356 cycling_avail -= len;
357 cycling_framepos += len / framesize;
358 }
359 return len;
370 if (Math.abs(format.getSampleRate()
371 - outputformat.getSampleRate()) > 0.000001)
372 afis = new AudioFloatInputStreamResampler(afis,
373 outputformat);
374
375 } else {
376 if (!format.matches(getFormat())) {
377 throw new IllegalStateException(
378 "Line is already open with format " + getFormat()
379 + " and bufferSize " + getBufferSize());
380 }
381 }
382
383 }
384
385 if (event != null)
386 sendEvent(event);
387
388 }
389
390 public int available() {
391 synchronized (cycling_buffer) {
392 return cycling_buffer.length - cycling_avail;
393 }
394 }
395
396 public void drain() {
397 while (true) {
398 int avail;
399 synchronized (cycling_buffer) {
400 avail = cycling_avail;
401 }
402 if (avail != 0)
403 return;
404 try {
405 Thread.sleep(1);
406 } catch (InterruptedException e) {
407 return;
408 }
409 }
410 }
411
412 public void flush() {
413 synchronized (cycling_buffer) {
414 cycling_read_pos = 0;
415 cycling_write_pos = 0;
416 cycling_avail = 0;
417 }
418 }
419
420 public int getBufferSize() {
421 synchronized (control_mutex) {
422 return bufferSize;
423 }
424 }
425
426 public AudioFormat getFormat() {
427 synchronized (control_mutex) {
428 return format;
429 }
430 }
431
432 public int getFramePosition() {
433 return (int) getLongFramePosition();
434 }
435
436 public float getLevel() {
437 return AudioSystem.NOT_SPECIFIED;
438 }
439
440 public long getLongFramePosition() {
441 synchronized (cycling_buffer) {
442 return cycling_framepos;
443 }
444 }
445
446 public long getMicrosecondPosition() {
447 return (long) (getLongFramePosition() * (1000000.0 / (double) getFormat()
448 .getSampleRate()));
449 }
450
451 public boolean isActive() {
452 synchronized (control_mutex) {
453 return active;
454 }
455 }
456
457 public boolean isRunning() {
458 synchronized (control_mutex) {
459 return active;
460 }
461 }
462
463 public void start() {
464
465 LineEvent event = null;
466
467 synchronized (control_mutex) {
468 if (isOpen()) {
469 if (active)
470 return;
471 active = true;
472 event = new LineEvent(this, LineEvent.Type.START,
473 getLongFramePosition());
474 }
475 }
476
477 if (event != null)
478 sendEvent(event);
479 }
480
481 public void stop() {
482 LineEvent event = null;
483
484 synchronized (control_mutex) {
485 if (isOpen()) {
486 if (!active)
487 return;
488 active = false;
489 event = new LineEvent(this, LineEvent.Type.STOP,
490 getLongFramePosition());
491 }
492 }
493
494 if (event != null)
495 sendEvent(event);
496 }
497
498 public void close() {
499
500 LineEvent event = null;
501
502 synchronized (control_mutex) {
503 if (!isOpen())
504 return;
505 stop();
506
507 event = new LineEvent(this, LineEvent.Type.CLOSE,
508 getLongFramePosition());
509
510 open = false;
511 mixer.getMainMixer().closeLine(this);
512 }
513
514 if (event != null)
515 sendEvent(event);
516
517 }
518
519 public boolean isOpen() {
520 synchronized (control_mutex) {
521 return open;
522 }
523 }
524
525 }
|
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.media.sound;
27
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.util.Arrays;
31
32 import javax.sound.sampled.AudioFormat;
33 import javax.sound.sampled.AudioInputStream;
34 import javax.sound.sampled.AudioSystem;
35 import javax.sound.sampled.DataLine;
36 import javax.sound.sampled.LineEvent;
37 import javax.sound.sampled.LineUnavailableException;
38 import javax.sound.sampled.SourceDataLine;
39
40 /**
41 * SourceDataLine implementation for the SoftMixingMixer.
42 *
43 * @author Karl Helgason
44 */
45 public final class SoftMixingSourceDataLine extends SoftMixingDataLine
60 private byte[] cycling_buffer;
61
62 private int cycling_read_pos = 0;
63
64 private int cycling_write_pos = 0;
65
66 private int cycling_avail = 0;
67
68 private long cycling_framepos = 0;
69
70 private AudioFloatInputStream afis;
71
72 private static class NonBlockingFloatInputStream extends
73 AudioFloatInputStream {
74 AudioFloatInputStream ais;
75
76 NonBlockingFloatInputStream(AudioFloatInputStream ais) {
77 this.ais = ais;
78 }
79
80 @Override
81 public int available() throws IOException {
82 return ais.available();
83 }
84
85 @Override
86 public void close() throws IOException {
87 ais.close();
88 }
89
90 @Override
91 public AudioFormat getFormat() {
92 return ais.getFormat();
93 }
94
95 @Override
96 public long getFrameLength() {
97 return ais.getFrameLength();
98 }
99
100 @Override
101 public void mark(int readlimit) {
102 ais.mark(readlimit);
103 }
104
105 @Override
106 public boolean markSupported() {
107 return ais.markSupported();
108 }
109
110 @Override
111 public int read(float[] b, int off, int len) throws IOException {
112 int avail = available();
113 if (len > avail) {
114 int ret = ais.read(b, off, avail);
115 Arrays.fill(b, off + ret, off + len, 0);
116 return len;
117 }
118 return ais.read(b, off, len);
119 }
120
121 @Override
122 public void reset() throws IOException {
123 ais.reset();
124 }
125
126 @Override
127 public long skip(long len) throws IOException {
128 return ais.skip(len);
129 }
130
131 }
132
133 SoftMixingSourceDataLine(SoftMixingMixer mixer, DataLine.Info info) {
134 super(mixer, info);
135 }
136
137 @Override
138 public int write(byte[] b, int off, int len) {
139 if (!isOpen())
140 return 0;
141 if (len % framesize != 0)
142 throw new IllegalArgumentException(
143 "Number of bytes does not represent an integral number of sample frames.");
144 if (off < 0) {
145 throw new ArrayIndexOutOfBoundsException(off);
146 }
147 if ((long)off + (long)len > (long)b.length) {
148 throw new ArrayIndexOutOfBoundsException(b.length);
149 }
150
151 byte[] buff = cycling_buffer;
152 int buff_len = cycling_buffer.length;
153
154 int l = 0;
155 while (l != len) {
156 int avail;
157 synchronized (cycling_buffer) {
196 // FloatControl.Type.PAN
197 // FloatControl.Type.MASTER_GAIN
198 // FloatControl.Type.BALANCE
199
200 private boolean _active = false;
201
202 private AudioFormat outputformat;
203
204 private int out_nrofchannels;
205
206 private int in_nrofchannels;
207
208 private float _rightgain;
209
210 private float _leftgain;
211
212 private float _eff1gain;
213
214 private float _eff2gain;
215
216 @Override
217 protected void processControlLogic() {
218 _active = active;
219 _rightgain = rightgain;
220 _leftgain = leftgain;
221 _eff1gain = eff1gain;
222 _eff2gain = eff2gain;
223 }
224
225 @Override
226 protected void processAudioLogic(SoftAudioBuffer[] buffers) {
227 if (_active) {
228 float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
229 float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
230 int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();
231
232 int readlen = bufferlen * in_nrofchannels;
233 if (readbuffer == null || readbuffer.length < readlen) {
234 readbuffer = new float[readlen];
235 }
236 int ret = 0;
237 try {
238 ret = afis.read(readbuffer);
239 if (ret != in_nrofchannels)
240 Arrays.fill(readbuffer, ret, readlen, 0);
241 } catch (IOException e) {
242 }
243
244 int in_c = in_nrofchannels;
245 for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
270 }
271 }
272 }
273
274 if (_eff2gain > 0.0001) {
275 float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
276 .array();
277 for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
278 eff2[i] += readbuffer[ix] * _eff2gain;
279 }
280 if (in_nrofchannels == 2) {
281 for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
282 eff2[i] += readbuffer[ix] * _eff2gain;
283 }
284 }
285 }
286
287 }
288 }
289
290 @Override
291 public void open() throws LineUnavailableException {
292 open(format);
293 }
294
295 @Override
296 public void open(AudioFormat format) throws LineUnavailableException {
297 if (bufferSize == -1)
298 bufferSize = ((int) (format.getFrameRate() / 2))
299 * format.getFrameSize();
300 open(format, bufferSize);
301 }
302
303 @Override
304 public void open(AudioFormat format, int bufferSize)
305 throws LineUnavailableException {
306
307 LineEvent event = null;
308
309 if (bufferSize < format.getFrameSize() * 32)
310 bufferSize = format.getFrameSize() * 32;
311
312 synchronized (control_mutex) {
313
314 if (!isOpen()) {
315 if (!mixer.isOpen()) {
316 mixer.open();
317 mixer.implicitOpen = true;
318 }
319
320 event = new LineEvent(this, LineEvent.Type.OPEN, 0);
321
322 this.bufferSize = bufferSize - bufferSize
323 % format.getFrameSize();
324 this.format = format;
325 this.framesize = format.getFrameSize();
326 this.outputformat = mixer.getFormat();
327 out_nrofchannels = outputformat.getChannels();
328 in_nrofchannels = format.getChannels();
329
330 open = true;
331
332 mixer.getMainMixer().openLine(this);
333
334 cycling_buffer = new byte[framesize * bufferSize];
335 cycling_read_pos = 0;
336 cycling_write_pos = 0;
337 cycling_avail = 0;
338 cycling_framepos = 0;
339
340 InputStream cycling_inputstream = new InputStream() {
341
342 @Override
343 public int read() throws IOException {
344 byte[] b = new byte[1];
345 int ret = read(b);
346 if (ret < 0)
347 return ret;
348 return b[0] & 0xFF;
349 }
350
351 @Override
352 public int available() throws IOException {
353 synchronized (cycling_buffer) {
354 return cycling_avail;
355 }
356 }
357
358 @Override
359 public int read(byte[] b, int off, int len)
360 throws IOException {
361
362 synchronized (cycling_buffer) {
363 if (len > cycling_avail)
364 len = cycling_avail;
365 int pos = cycling_read_pos;
366 byte[] buff = cycling_buffer;
367 int buff_len = buff.length;
368 for (int i = 0; i < len; i++) {
369 b[off++] = buff[pos];
370 pos++;
371 if (pos == buff_len)
372 pos = 0;
373 }
374 cycling_read_pos = pos;
375 cycling_avail -= len;
376 cycling_framepos += len / framesize;
377 }
378 return len;
389 if (Math.abs(format.getSampleRate()
390 - outputformat.getSampleRate()) > 0.000001)
391 afis = new AudioFloatInputStreamResampler(afis,
392 outputformat);
393
394 } else {
395 if (!format.matches(getFormat())) {
396 throw new IllegalStateException(
397 "Line is already open with format " + getFormat()
398 + " and bufferSize " + getBufferSize());
399 }
400 }
401
402 }
403
404 if (event != null)
405 sendEvent(event);
406
407 }
408
409 @Override
410 public int available() {
411 synchronized (cycling_buffer) {
412 return cycling_buffer.length - cycling_avail;
413 }
414 }
415
416 @Override
417 public void drain() {
418 while (true) {
419 int avail;
420 synchronized (cycling_buffer) {
421 avail = cycling_avail;
422 }
423 if (avail != 0)
424 return;
425 try {
426 Thread.sleep(1);
427 } catch (InterruptedException e) {
428 return;
429 }
430 }
431 }
432
433 @Override
434 public void flush() {
435 synchronized (cycling_buffer) {
436 cycling_read_pos = 0;
437 cycling_write_pos = 0;
438 cycling_avail = 0;
439 }
440 }
441
442 @Override
443 public int getBufferSize() {
444 synchronized (control_mutex) {
445 return bufferSize;
446 }
447 }
448
449 @Override
450 public AudioFormat getFormat() {
451 synchronized (control_mutex) {
452 return format;
453 }
454 }
455
456 @Override
457 public int getFramePosition() {
458 return (int) getLongFramePosition();
459 }
460
461 @Override
462 public float getLevel() {
463 return AudioSystem.NOT_SPECIFIED;
464 }
465
466 @Override
467 public long getLongFramePosition() {
468 synchronized (cycling_buffer) {
469 return cycling_framepos;
470 }
471 }
472
473 @Override
474 public long getMicrosecondPosition() {
475 return (long) (getLongFramePosition() * (1000000.0 / (double) getFormat()
476 .getSampleRate()));
477 }
478
479 @Override
480 public boolean isActive() {
481 synchronized (control_mutex) {
482 return active;
483 }
484 }
485
486 @Override
487 public boolean isRunning() {
488 synchronized (control_mutex) {
489 return active;
490 }
491 }
492
493 @Override
494 public void start() {
495
496 LineEvent event = null;
497
498 synchronized (control_mutex) {
499 if (isOpen()) {
500 if (active)
501 return;
502 active = true;
503 event = new LineEvent(this, LineEvent.Type.START,
504 getLongFramePosition());
505 }
506 }
507
508 if (event != null)
509 sendEvent(event);
510 }
511
512 @Override
513 public void stop() {
514 LineEvent event = null;
515
516 synchronized (control_mutex) {
517 if (isOpen()) {
518 if (!active)
519 return;
520 active = false;
521 event = new LineEvent(this, LineEvent.Type.STOP,
522 getLongFramePosition());
523 }
524 }
525
526 if (event != null)
527 sendEvent(event);
528 }
529
530 @Override
531 public void close() {
532
533 LineEvent event = null;
534
535 synchronized (control_mutex) {
536 if (!isOpen())
537 return;
538 stop();
539
540 event = new LineEvent(this, LineEvent.Type.CLOSE,
541 getLongFramePosition());
542
543 open = false;
544 mixer.getMainMixer().closeLine(this);
545 }
546
547 if (event != null)
548 sendEvent(event);
549
550 }
551
552 @Override
553 public boolean isOpen() {
554 synchronized (control_mutex) {
555 return open;
556 }
557 }
558 }
|