Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/DirectAudioDevice.java
38924 views
1
/*
2
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
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.ByteArrayOutputStream;
29
import java.io.IOException;
30
import java.util.Vector;
31
32
import javax.sound.sampled.*;
33
34
// IDEA:
35
// Use java.util.concurrent.Semaphore,
36
// java.util.concurrent.locks.ReentrantLock and other new classes/methods
37
// to improve this class's thread safety.
38
39
40
/**
41
* A Mixer which provides direct access to audio devices
42
*
43
* @author Florian Bomers
44
*/
45
final class DirectAudioDevice extends AbstractMixer {
46
47
// CONSTANTS
48
private static final int CLIP_BUFFER_TIME = 1000; // in milliseconds
49
50
private static final int DEFAULT_LINE_BUFFER_TIME = 500; // in milliseconds
51
52
// INSTANCE VARIABLES
53
54
/** number of opened lines */
55
private int deviceCountOpened = 0;
56
57
/** number of started lines */
58
private int deviceCountStarted = 0;
59
60
// CONSTRUCTOR
61
DirectAudioDevice(DirectAudioDeviceProvider.DirectAudioDeviceInfo portMixerInfo) {
62
// pass in Line.Info, mixer, controls
63
super(portMixerInfo, // Mixer.Info
64
null, // Control[]
65
null, // Line.Info[] sourceLineInfo
66
null); // Line.Info[] targetLineInfo
67
68
if (Printer.trace) Printer.trace(">> DirectAudioDevice: constructor");
69
70
// source lines
71
DirectDLI srcLineInfo = createDataLineInfo(true);
72
if (srcLineInfo != null) {
73
sourceLineInfo = new Line.Info[2];
74
// SourcedataLine
75
sourceLineInfo[0] = srcLineInfo;
76
// Clip
77
sourceLineInfo[1] = new DirectDLI(Clip.class, srcLineInfo.getFormats(),
78
srcLineInfo.getHardwareFormats(),
79
32, // arbitrary minimum buffer size
80
AudioSystem.NOT_SPECIFIED);
81
} else {
82
sourceLineInfo = new Line.Info[0];
83
}
84
85
// TargetDataLine
86
DataLine.Info dstLineInfo = createDataLineInfo(false);
87
if (dstLineInfo != null) {
88
targetLineInfo = new Line.Info[1];
89
targetLineInfo[0] = dstLineInfo;
90
} else {
91
targetLineInfo = new Line.Info[0];
92
}
93
if (Printer.trace) Printer.trace("<< DirectAudioDevice: constructor completed");
94
}
95
96
private DirectDLI createDataLineInfo(boolean isSource) {
97
Vector formats = new Vector();
98
AudioFormat[] hardwareFormatArray = null;
99
AudioFormat[] formatArray = null;
100
101
synchronized(formats) {
102
nGetFormats(getMixerIndex(), getDeviceID(),
103
isSource /* true:SourceDataLine/Clip, false:TargetDataLine */,
104
formats);
105
if (formats.size() > 0) {
106
int size = formats.size();
107
int formatArraySize = size;
108
hardwareFormatArray = new AudioFormat[size];
109
for (int i = 0; i < size; i++) {
110
AudioFormat format = (AudioFormat)formats.elementAt(i);
111
hardwareFormatArray[i] = format;
112
int bits = format.getSampleSizeInBits();
113
boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);
114
boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);
115
if ((isSigned || isUnsigned)) {
116
// will insert a magically converted format here
117
formatArraySize++;
118
}
119
}
120
formatArray = new AudioFormat[formatArraySize];
121
int formatArrayIndex = 0;
122
for (int i = 0; i < size; i++) {
123
AudioFormat format = hardwareFormatArray[i];
124
formatArray[formatArrayIndex++] = format;
125
int bits = format.getSampleSizeInBits();
126
boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);
127
boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);
128
// add convenience formats (automatic conversion)
129
if (bits == 8) {
130
// add the other signed'ness for 8-bit
131
if (isSigned) {
132
formatArray[formatArrayIndex++] =
133
new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,
134
format.getSampleRate(), bits, format.getChannels(),
135
format.getFrameSize(), format.getSampleRate(),
136
format.isBigEndian());
137
}
138
else if (isUnsigned) {
139
formatArray[formatArrayIndex++] =
140
new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
141
format.getSampleRate(), bits, format.getChannels(),
142
format.getFrameSize(), format.getSampleRate(),
143
format.isBigEndian());
144
}
145
} else if (bits > 8 && (isSigned || isUnsigned)) {
146
// add the other endian'ness for more than 8-bit
147
formatArray[formatArrayIndex++] =
148
new AudioFormat(format.getEncoding(),
149
format.getSampleRate(), bits,
150
format.getChannels(),
151
format.getFrameSize(),
152
format.getSampleRate(),
153
!format.isBigEndian());
154
}
155
//System.out.println("Adding "+v.get(v.size()-1));
156
}
157
}
158
}
159
// todo: find out more about the buffer size ?
160
if (formatArray != null) {
161
return new DirectDLI(isSource?SourceDataLine.class:TargetDataLine.class,
162
formatArray, hardwareFormatArray,
163
32, // arbitrary minimum buffer size
164
AudioSystem.NOT_SPECIFIED);
165
}
166
return null;
167
}
168
169
// ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS
170
171
public Line getLine(Line.Info info) throws LineUnavailableException {
172
Line.Info fullInfo = getLineInfo(info);
173
if (fullInfo == null) {
174
throw new IllegalArgumentException("Line unsupported: " + info);
175
}
176
if (fullInfo instanceof DataLine.Info) {
177
178
DataLine.Info dataLineInfo = (DataLine.Info)fullInfo;
179
AudioFormat lineFormat;
180
int lineBufferSize = AudioSystem.NOT_SPECIFIED;
181
182
// if a format is specified by the info class passed in, use it.
183
// otherwise use a format from fullInfo.
184
185
AudioFormat[] supportedFormats = null;
186
187
if (info instanceof DataLine.Info) {
188
supportedFormats = ((DataLine.Info)info).getFormats();
189
lineBufferSize = ((DataLine.Info)info).getMaxBufferSize();
190
}
191
192
if ((supportedFormats == null) || (supportedFormats.length == 0)) {
193
// use the default format
194
lineFormat = null;
195
} else {
196
// use the last format specified in the line.info object passed
197
// in by the app
198
lineFormat = supportedFormats[supportedFormats.length-1];
199
200
// if something is not specified, use default format
201
if (!Toolkit.isFullySpecifiedPCMFormat(lineFormat)) {
202
lineFormat = null;
203
}
204
}
205
206
if (dataLineInfo.getLineClass().isAssignableFrom(DirectSDL.class)) {
207
return new DirectSDL(dataLineInfo, lineFormat, lineBufferSize, this);
208
}
209
if (dataLineInfo.getLineClass().isAssignableFrom(DirectClip.class)) {
210
return new DirectClip(dataLineInfo, lineFormat, lineBufferSize, this);
211
}
212
if (dataLineInfo.getLineClass().isAssignableFrom(DirectTDL.class)) {
213
return new DirectTDL(dataLineInfo, lineFormat, lineBufferSize, this);
214
}
215
}
216
throw new IllegalArgumentException("Line unsupported: " + info);
217
}
218
219
220
public int getMaxLines(Line.Info info) {
221
Line.Info fullInfo = getLineInfo(info);
222
223
// if it's not supported at all, return 0.
224
if (fullInfo == null) {
225
return 0;
226
}
227
228
if (fullInfo instanceof DataLine.Info) {
229
// DirectAudioDevices should mix !
230
return getMaxSimulLines();
231
}
232
233
return 0;
234
}
235
236
237
protected void implOpen() throws LineUnavailableException {
238
if (Printer.trace) Printer.trace("DirectAudioDevice: implOpen - void method");
239
}
240
241
protected void implClose() {
242
if (Printer.trace) Printer.trace("DirectAudioDevice: implClose - void method");
243
}
244
245
protected void implStart() {
246
if (Printer.trace) Printer.trace("DirectAudioDevice: implStart - void method");
247
}
248
249
protected void implStop() {
250
if (Printer.trace) Printer.trace("DirectAudioDevice: implStop - void method");
251
}
252
253
254
// IMPLEMENTATION HELPERS
255
256
int getMixerIndex() {
257
return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getIndex();
258
}
259
260
int getDeviceID() {
261
return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getDeviceID();
262
}
263
264
int getMaxSimulLines() {
265
return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getMaxSimulLines();
266
}
267
268
private static void addFormat(Vector v, int bits, int frameSizeInBytes, int channels, float sampleRate,
269
int encoding, boolean signed, boolean bigEndian) {
270
AudioFormat.Encoding enc = null;
271
switch (encoding) {
272
case PCM:
273
enc = signed?AudioFormat.Encoding.PCM_SIGNED:AudioFormat.Encoding.PCM_UNSIGNED;
274
break;
275
case ULAW:
276
enc = AudioFormat.Encoding.ULAW;
277
if (bits != 8) {
278
if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with ULAW, but bitsPerSample="+bits);
279
bits = 8; frameSizeInBytes = channels;
280
}
281
break;
282
case ALAW:
283
enc = AudioFormat.Encoding.ALAW;
284
if (bits != 8) {
285
if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with ALAW, but bitsPerSample="+bits);
286
bits = 8; frameSizeInBytes = channels;
287
}
288
break;
289
}
290
if (enc==null) {
291
if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with unknown encoding: "+encoding);
292
return;
293
}
294
if (frameSizeInBytes <= 0) {
295
if (channels > 0) {
296
frameSizeInBytes = ((bits + 7) / 8) * channels;
297
} else {
298
frameSizeInBytes = AudioSystem.NOT_SPECIFIED;
299
}
300
}
301
v.add(new AudioFormat(enc, sampleRate, bits, channels, frameSizeInBytes, sampleRate, bigEndian));
302
}
303
304
protected static AudioFormat getSignOrEndianChangedFormat(AudioFormat format) {
305
boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);
306
boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);
307
if (format.getSampleSizeInBits() > 8 && isSigned) {
308
// if this is PCM_SIGNED and 16-bit or higher, then try with endian-ness magic
309
return new AudioFormat(format.getEncoding(),
310
format.getSampleRate(), format.getSampleSizeInBits(), format.getChannels(),
311
format.getFrameSize(), format.getFrameRate(), !format.isBigEndian());
312
}
313
else if (format.getSampleSizeInBits() == 8 && (isSigned || isUnsigned)) {
314
// if this is PCM and 8-bit, then try with signed-ness magic
315
return new AudioFormat(isSigned?AudioFormat.Encoding.PCM_UNSIGNED:AudioFormat.Encoding.PCM_SIGNED,
316
format.getSampleRate(), format.getSampleSizeInBits(), format.getChannels(),
317
format.getFrameSize(), format.getFrameRate(), format.isBigEndian());
318
}
319
return null;
320
}
321
322
323
324
325
// INNER CLASSES
326
327
328
/**
329
* Private inner class for the DataLine.Info objects
330
* adds a little magic for the isFormatSupported so
331
* that the automagic conversion of endianness and sign
332
* does not show up in the formats array.
333
* I.e. the formats array contains only the formats
334
* that are really supported by the hardware,
335
* but isFormatSupported() also returns true
336
* for formats with wrong endianness.
337
*/
338
private static final class DirectDLI extends DataLine.Info {
339
final AudioFormat[] hardwareFormats;
340
341
private DirectDLI(Class clazz, AudioFormat[] formatArray,
342
AudioFormat[] hardwareFormatArray,
343
int minBuffer, int maxBuffer) {
344
super(clazz, formatArray, minBuffer, maxBuffer);
345
this.hardwareFormats = hardwareFormatArray;
346
}
347
348
public boolean isFormatSupportedInHardware(AudioFormat format) {
349
if (format == null) return false;
350
for (int i = 0; i < hardwareFormats.length; i++) {
351
if (format.matches(hardwareFormats[i])) {
352
return true;
353
}
354
}
355
return false;
356
}
357
358
/*public boolean isFormatSupported(AudioFormat format) {
359
* return isFormatSupportedInHardware(format)
360
* || isFormatSupportedInHardware(getSignOrEndianChangedFormat(format));
361
*}
362
*/
363
364
private AudioFormat[] getHardwareFormats() {
365
return hardwareFormats;
366
}
367
}
368
369
/**
370
* Private inner class as base class for direct lines
371
*/
372
private static class DirectDL extends AbstractDataLine implements EventDispatcher.LineMonitor {
373
protected final int mixerIndex;
374
protected final int deviceID;
375
protected long id;
376
protected int waitTime;
377
protected volatile boolean flushing = false;
378
protected final boolean isSource; // true for SourceDataLine, false for TargetDataLine
379
protected volatile long bytePosition;
380
protected volatile boolean doIO = false; // true in between start() and stop() calls
381
protected volatile boolean stoppedWritten = false; // true if a write occurred in stopped state
382
protected volatile boolean drained = false; // set to true when drain function returns, set to false in write()
383
protected boolean monitoring = false;
384
385
// if native needs to manually swap samples/convert sign, this
386
// is set to the framesize
387
protected int softwareConversionSize = 0;
388
protected AudioFormat hardwareFormat;
389
390
private final Gain gainControl = new Gain();
391
private final Mute muteControl = new Mute();
392
private final Balance balanceControl = new Balance();
393
private final Pan panControl = new Pan();
394
private float leftGain, rightGain;
395
protected volatile boolean noService = false; // do not run the nService method
396
397
// Guards all native calls.
398
protected final Object lockNative = new Object();
399
400
// CONSTRUCTOR
401
protected DirectDL(DataLine.Info info,
402
DirectAudioDevice mixer,
403
AudioFormat format,
404
int bufferSize,
405
int mixerIndex,
406
int deviceID,
407
boolean isSource) {
408
super(info, mixer, null, format, bufferSize);
409
if (Printer.trace) Printer.trace("DirectDL CONSTRUCTOR: info: " + info);
410
this.mixerIndex = mixerIndex;
411
this.deviceID = deviceID;
412
this.waitTime = 10; // 10 milliseconds default wait time
413
this.isSource = isSource;
414
415
}
416
417
418
// ABSTRACT METHOD IMPLEMENTATIONS
419
420
// ABSTRACT LINE / DATALINE
421
422
void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException {
423
if (Printer.trace) Printer.trace(">> DirectDL: implOpen("+format+", "+bufferSize+" bytes)");
424
425
// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
426
Toolkit.isFullySpecifiedAudioFormat(format);
427
428
// check for record permission
429
if (!isSource) {
430
JSSecurityManager.checkRecordPermission();
431
}
432
int encoding = PCM;
433
if (format.getEncoding().equals(AudioFormat.Encoding.ULAW)) {
434
encoding = ULAW;
435
}
436
else if (format.getEncoding().equals(AudioFormat.Encoding.ALAW)) {
437
encoding = ALAW;
438
}
439
440
if (bufferSize <= AudioSystem.NOT_SPECIFIED) {
441
bufferSize = (int) Toolkit.millis2bytes(format, DEFAULT_LINE_BUFFER_TIME);
442
}
443
444
DirectDLI ddli = null;
445
if (info instanceof DirectDLI) {
446
ddli = (DirectDLI) info;
447
}
448
449
/* set up controls */
450
if (isSource) {
451
if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)
452
&& !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
453
// no controls for non-PCM formats */
454
controls = new Control[0];
455
}
456
else if (format.getChannels() > 2
457
|| format.getSampleSizeInBits() > 16) {
458
// no support for more than 2 channels or more than 16 bits
459
controls = new Control[0];
460
} else {
461
if (format.getChannels() == 1) {
462
controls = new Control[2];
463
} else {
464
controls = new Control[4];
465
controls[2] = balanceControl;
466
/* to keep compatibility with apps that rely on
467
* MixerSourceLine's PanControl
468
*/
469
controls[3] = panControl;
470
}
471
controls[0] = gainControl;
472
controls[1] = muteControl;
473
}
474
}
475
if (Printer.debug) Printer.debug("DirectAudioDevice: got "+controls.length+" controls.");
476
477
hardwareFormat = format;
478
479
/* some magic to account for not-supported endianness or signed-ness */
480
softwareConversionSize = 0;
481
if (ddli != null && !ddli.isFormatSupportedInHardware(format)) {
482
AudioFormat newFormat = getSignOrEndianChangedFormat(format);
483
if (ddli.isFormatSupportedInHardware(newFormat)) {
484
// apparently, the new format can be used.
485
hardwareFormat = newFormat;
486
// So do endian/sign conversion in software
487
softwareConversionSize = format.getFrameSize() / format.getChannels();
488
if (Printer.debug) {
489
Printer.debug("DirectAudioDevice: softwareConversionSize "
490
+softwareConversionSize+":");
491
Printer.debug(" from "+format);
492
Printer.debug(" to "+newFormat);
493
}
494
}
495
}
496
497
// align buffer to full frames
498
bufferSize = ((int) bufferSize / format.getFrameSize()) * format.getFrameSize();
499
500
id = nOpen(mixerIndex, deviceID, isSource,
501
encoding,
502
hardwareFormat.getSampleRate(),
503
hardwareFormat.getSampleSizeInBits(),
504
hardwareFormat.getFrameSize(),
505
hardwareFormat.getChannels(),
506
hardwareFormat.getEncoding().equals(
507
AudioFormat.Encoding.PCM_SIGNED),
508
hardwareFormat.isBigEndian(),
509
bufferSize);
510
511
if (id == 0) {
512
// TODO: nicer error messages...
513
throw new LineUnavailableException(
514
"line with format "+format+" not supported.");
515
}
516
517
this.bufferSize = nGetBufferSize(id, isSource);
518
if (this.bufferSize < 1) {
519
// this is an error!
520
this.bufferSize = bufferSize;
521
}
522
this.format = format;
523
// wait time = 1/4 of buffer time
524
waitTime = (int) Toolkit.bytes2millis(format, this.bufferSize) / 4;
525
if (waitTime < 10) {
526
waitTime = 1;
527
}
528
else if (waitTime > 1000) {
529
// we have seen large buffer sizes!
530
// never wait for more than a second
531
waitTime = 1000;
532
}
533
bytePosition = 0;
534
stoppedWritten = false;
535
doIO = false;
536
calcVolume();
537
538
if (Printer.trace) Printer.trace("<< DirectDL: implOpen() succeeded");
539
}
540
541
542
void implStart() {
543
if (Printer.trace) Printer.trace(" >> DirectDL: implStart()");
544
545
// check for record permission
546
if (!isSource) {
547
JSSecurityManager.checkRecordPermission();
548
}
549
550
synchronized (lockNative)
551
{
552
nStart(id, isSource);
553
}
554
// check for monitoring/servicing
555
monitoring = requiresServicing();
556
if (monitoring) {
557
getEventDispatcher().addLineMonitor(this);
558
}
559
560
synchronized(lock) {
561
doIO = true;
562
// need to set Active and Started
563
// note: the current API always requires that
564
// Started and Active are set at the same time...
565
if (isSource && stoppedWritten) {
566
setStarted(true);
567
setActive(true);
568
}
569
}
570
571
if (Printer.trace) Printer.trace("<< DirectDL: implStart() succeeded");
572
}
573
574
void implStop() {
575
if (Printer.trace) Printer.trace(">> DirectDL: implStop()");
576
577
// check for record permission
578
if (!isSource) {
579
JSSecurityManager.checkRecordPermission();
580
}
581
582
if (monitoring) {
583
getEventDispatcher().removeLineMonitor(this);
584
monitoring = false;
585
}
586
synchronized (lockNative) {
587
nStop(id, isSource);
588
}
589
// wake up any waiting threads
590
synchronized(lock) {
591
// need to set doIO to false before notifying the
592
// read/write thread, that's why isStartedRunning()
593
// cannot be used
594
doIO = false;
595
setActive(false);
596
setStarted(false);
597
lock.notifyAll();
598
}
599
stoppedWritten = false;
600
601
if (Printer.trace) Printer.trace(" << DirectDL: implStop() succeeded");
602
}
603
604
void implClose() {
605
if (Printer.trace) Printer.trace(">> DirectDL: implClose()");
606
607
// check for record permission
608
if (!isSource) {
609
JSSecurityManager.checkRecordPermission();
610
}
611
612
// be sure to remove this monitor
613
if (monitoring) {
614
getEventDispatcher().removeLineMonitor(this);
615
monitoring = false;
616
}
617
618
doIO = false;
619
long oldID = id;
620
id = 0;
621
synchronized (lockNative) {
622
nClose(oldID, isSource);
623
}
624
bytePosition = 0;
625
softwareConversionSize = 0;
626
if (Printer.trace) Printer.trace("<< DirectDL: implClose() succeeded");
627
}
628
629
// METHOD OVERRIDES
630
631
public int available() {
632
if (id == 0) {
633
return 0;
634
}
635
int a;
636
synchronized (lockNative) {
637
a = nAvailable(id, isSource);
638
}
639
return a;
640
}
641
642
643
public void drain() {
644
noService = true;
645
// additional safeguard against draining forever
646
// this occurred on Solaris 8 x86, probably due to a bug
647
// in the audio driver
648
int counter = 0;
649
long startPos = getLongFramePosition();
650
boolean posChanged = false;
651
while (!drained) {
652
synchronized (lockNative) {
653
if ((id == 0) || (!doIO) || !nIsStillDraining(id, isSource))
654
break;
655
}
656
// check every now and then for a new position
657
if ((counter % 5) == 4) {
658
long thisFramePos = getLongFramePosition();
659
posChanged = posChanged | (thisFramePos != startPos);
660
if ((counter % 50) > 45) {
661
// when some time elapsed, check that the frame position
662
// really changed
663
if (!posChanged) {
664
if (Printer.err) Printer.err("Native reports isDraining, but frame position does not increase!");
665
break;
666
}
667
posChanged = false;
668
startPos = thisFramePos;
669
}
670
}
671
counter++;
672
synchronized(lock) {
673
try {
674
lock.wait(10);
675
} catch (InterruptedException ie) {}
676
}
677
}
678
679
if (doIO && id != 0) {
680
drained = true;
681
}
682
noService = false;
683
}
684
685
public void flush() {
686
if (id != 0) {
687
// first stop ongoing read/write method
688
flushing = true;
689
synchronized(lock) {
690
lock.notifyAll();
691
}
692
synchronized (lockNative) {
693
if (id != 0) {
694
// then flush native buffers
695
nFlush(id, isSource);
696
}
697
}
698
drained = true;
699
}
700
}
701
702
// replacement for getFramePosition (see AbstractDataLine)
703
public long getLongFramePosition() {
704
long pos;
705
synchronized (lockNative) {
706
pos = nGetBytePosition(id, isSource, bytePosition);
707
}
708
// hack because ALSA sometimes reports wrong framepos
709
if (pos < 0) {
710
if (Printer.debug) Printer.debug("DirectLine.getLongFramePosition: Native reported pos="
711
+pos+"! is changed to 0. byteposition="+bytePosition);
712
pos = 0;
713
}
714
return (pos / getFormat().getFrameSize());
715
}
716
717
718
/*
719
* write() belongs into SourceDataLine and Clip,
720
* so define it here and make it accessible by
721
* declaring the respective interfaces with DirectSDL and DirectClip
722
*/
723
public int write(byte[] b, int off, int len) {
724
flushing = false;
725
if (len == 0) {
726
return 0;
727
}
728
if (len < 0) {
729
throw new IllegalArgumentException("illegal len: "+len);
730
}
731
if (len % getFormat().getFrameSize() != 0) {
732
throw new IllegalArgumentException("illegal request to write "
733
+"non-integral number of frames ("
734
+len+" bytes, "
735
+"frameSize = "+getFormat().getFrameSize()+" bytes)");
736
}
737
if (off < 0) {
738
throw new ArrayIndexOutOfBoundsException(off);
739
}
740
if ((long)off + (long)len > (long)b.length) {
741
throw new ArrayIndexOutOfBoundsException(b.length);
742
}
743
synchronized(lock) {
744
if (!isActive() && doIO) {
745
// this is not exactly correct... would be nicer
746
// if the native sub system sent a callback when IO really
747
// starts
748
setActive(true);
749
setStarted(true);
750
}
751
}
752
int written = 0;
753
while (!flushing) {
754
int thisWritten;
755
synchronized (lockNative) {
756
thisWritten = nWrite(id, b, off, len,
757
softwareConversionSize,
758
leftGain, rightGain);
759
if (thisWritten < 0) {
760
// error in native layer
761
break;
762
}
763
bytePosition += thisWritten;
764
if (thisWritten > 0) {
765
drained = false;
766
}
767
}
768
len -= thisWritten;
769
written += thisWritten;
770
if (doIO && len > 0) {
771
off += thisWritten;
772
synchronized (lock) {
773
try {
774
lock.wait(waitTime);
775
} catch (InterruptedException ie) {}
776
}
777
} else {
778
break;
779
}
780
}
781
if (written > 0 && !doIO) {
782
stoppedWritten = true;
783
}
784
return written;
785
}
786
787
protected boolean requiresServicing() {
788
return nRequiresServicing(id, isSource);
789
}
790
791
// called from event dispatcher for lines that need servicing
792
public void checkLine() {
793
synchronized (lockNative) {
794
if (monitoring
795
&& doIO
796
&& id != 0
797
&& !flushing
798
&& !noService) {
799
nService(id, isSource);
800
}
801
}
802
}
803
804
private void calcVolume() {
805
if (getFormat() == null) {
806
return;
807
}
808
if (muteControl.getValue()) {
809
leftGain = 0.0f;
810
rightGain = 0.0f;
811
return;
812
}
813
float gain = gainControl.getLinearGain();
814
if (getFormat().getChannels() == 1) {
815
// trivial case: only use gain
816
leftGain = gain;
817
rightGain = gain;
818
} else {
819
// need to combine gain and balance
820
float bal = balanceControl.getValue();
821
if (bal < 0.0f) {
822
// left
823
leftGain = gain;
824
rightGain = gain * (bal + 1.0f);
825
} else {
826
leftGain = gain * (1.0f - bal);
827
rightGain = gain;
828
}
829
}
830
}
831
832
833
/////////////////// CONTROLS /////////////////////////////
834
835
protected final class Gain extends FloatControl {
836
837
private float linearGain = 1.0f;
838
839
private Gain() {
840
841
super(FloatControl.Type.MASTER_GAIN,
842
Toolkit.linearToDB(0.0f),
843
Toolkit.linearToDB(2.0f),
844
Math.abs(Toolkit.linearToDB(1.0f)-Toolkit.linearToDB(0.0f))/128.0f,
845
-1,
846
0.0f,
847
"dB", "Minimum", "", "Maximum");
848
}
849
850
public void setValue(float newValue) {
851
// adjust value within range ?? spec says IllegalArgumentException
852
//newValue = Math.min(newValue, getMaximum());
853
//newValue = Math.max(newValue, getMinimum());
854
855
float newLinearGain = Toolkit.dBToLinear(newValue);
856
super.setValue(Toolkit.linearToDB(newLinearGain));
857
// if no exception, commit to our new gain
858
linearGain = newLinearGain;
859
calcVolume();
860
}
861
862
float getLinearGain() {
863
return linearGain;
864
}
865
} // class Gain
866
867
868
private final class Mute extends BooleanControl {
869
870
private Mute() {
871
super(BooleanControl.Type.MUTE, false, "True", "False");
872
}
873
874
public void setValue(boolean newValue) {
875
super.setValue(newValue);
876
calcVolume();
877
}
878
} // class Mute
879
880
private final class Balance extends FloatControl {
881
882
private Balance() {
883
super(FloatControl.Type.BALANCE, -1.0f, 1.0f, (1.0f / 128.0f), -1, 0.0f,
884
"", "Left", "Center", "Right");
885
}
886
887
public void setValue(float newValue) {
888
setValueImpl(newValue);
889
panControl.setValueImpl(newValue);
890
calcVolume();
891
}
892
893
void setValueImpl(float newValue) {
894
super.setValue(newValue);
895
}
896
897
} // class Balance
898
899
private final class Pan extends FloatControl {
900
901
private Pan() {
902
super(FloatControl.Type.PAN, -1.0f, 1.0f, (1.0f / 128.0f), -1, 0.0f,
903
"", "Left", "Center", "Right");
904
}
905
906
public void setValue(float newValue) {
907
setValueImpl(newValue);
908
balanceControl.setValueImpl(newValue);
909
calcVolume();
910
}
911
void setValueImpl(float newValue) {
912
super.setValue(newValue);
913
}
914
} // class Pan
915
916
917
918
} // class DirectDL
919
920
921
/**
922
* Private inner class representing a SourceDataLine
923
*/
924
private static final class DirectSDL extends DirectDL
925
implements SourceDataLine {
926
927
// CONSTRUCTOR
928
private DirectSDL(DataLine.Info info,
929
AudioFormat format,
930
int bufferSize,
931
DirectAudioDevice mixer) {
932
super(info, mixer, format, bufferSize, mixer.getMixerIndex(), mixer.getDeviceID(), true);
933
if (Printer.trace) Printer.trace("DirectSDL CONSTRUCTOR: completed");
934
}
935
936
}
937
938
/**
939
* Private inner class representing a TargetDataLine
940
*/
941
private static final class DirectTDL extends DirectDL
942
implements TargetDataLine {
943
944
// CONSTRUCTOR
945
private DirectTDL(DataLine.Info info,
946
AudioFormat format,
947
int bufferSize,
948
DirectAudioDevice mixer) {
949
super(info, mixer, format, bufferSize, mixer.getMixerIndex(), mixer.getDeviceID(), false);
950
if (Printer.trace) Printer.trace("DirectTDL CONSTRUCTOR: completed");
951
}
952
953
// METHOD OVERRIDES
954
955
public int read(byte[] b, int off, int len) {
956
flushing = false;
957
if (len == 0) {
958
return 0;
959
}
960
if (len < 0) {
961
throw new IllegalArgumentException("illegal len: "+len);
962
}
963
if (len % getFormat().getFrameSize() != 0) {
964
throw new IllegalArgumentException("illegal request to read "
965
+"non-integral number of frames ("
966
+len+" bytes, "
967
+"frameSize = "+getFormat().getFrameSize()+" bytes)");
968
}
969
if (off < 0) {
970
throw new ArrayIndexOutOfBoundsException(off);
971
}
972
if ((long)off + (long)len > (long)b.length) {
973
throw new ArrayIndexOutOfBoundsException(b.length);
974
}
975
synchronized(lock) {
976
if (!isActive() && doIO) {
977
// this is not exactly correct... would be nicer
978
// if the native sub system sent a callback when IO really
979
// starts
980
setActive(true);
981
setStarted(true);
982
}
983
}
984
int read = 0;
985
while (doIO && !flushing) {
986
int thisRead;
987
synchronized (lockNative) {
988
thisRead = nRead(id, b, off, len, softwareConversionSize);
989
if (thisRead < 0) {
990
// error in native layer
991
break;
992
}
993
bytePosition += thisRead;
994
if (thisRead > 0) {
995
drained = false;
996
}
997
}
998
len -= thisRead;
999
read += thisRead;
1000
if (len > 0) {
1001
off += thisRead;
1002
synchronized(lock) {
1003
try {
1004
lock.wait(waitTime);
1005
} catch (InterruptedException ie) {}
1006
}
1007
} else {
1008
break;
1009
}
1010
}
1011
if (flushing) {
1012
read = 0;
1013
}
1014
return read;
1015
}
1016
1017
}
1018
1019
/**
1020
* Private inner class representing a Clip
1021
* This clip is realized in software only
1022
*/
1023
private static final class DirectClip extends DirectDL
1024
implements Clip, Runnable, AutoClosingClip {
1025
1026
private volatile Thread thread;
1027
private volatile byte[] audioData = null;
1028
private volatile int frameSize; // size of one frame in bytes
1029
private volatile int m_lengthInFrames;
1030
private volatile int loopCount;
1031
private volatile int clipBytePosition; // index in the audioData array at current playback
1032
private volatile int newFramePosition; // set in setFramePosition()
1033
private volatile int loopStartFrame;
1034
private volatile int loopEndFrame; // the last sample included in the loop
1035
1036
// auto closing clip support
1037
private boolean autoclosing = false;
1038
1039
// CONSTRUCTOR
1040
private DirectClip(DataLine.Info info,
1041
AudioFormat format,
1042
int bufferSize,
1043
DirectAudioDevice mixer) {
1044
super(info, mixer, format, bufferSize, mixer.getMixerIndex(), mixer.getDeviceID(), true);
1045
if (Printer.trace) Printer.trace("DirectClip CONSTRUCTOR: completed");
1046
}
1047
1048
// CLIP METHODS
1049
1050
public void open(AudioFormat format, byte[] data, int offset, int bufferSize)
1051
throws LineUnavailableException {
1052
1053
// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
1054
Toolkit.isFullySpecifiedAudioFormat(format);
1055
1056
byte[] newData = new byte[bufferSize];
1057
System.arraycopy(data, offset, newData, 0, bufferSize);
1058
open(format, newData, bufferSize / format.getFrameSize());
1059
}
1060
1061
// this method does not copy the data array
1062
private void open(AudioFormat format, byte[] data, int frameLength)
1063
throws LineUnavailableException {
1064
1065
// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
1066
Toolkit.isFullySpecifiedAudioFormat(format);
1067
1068
synchronized (mixer) {
1069
if (Printer.trace) Printer.trace("> DirectClip.open(format, data, frameLength)");
1070
if (Printer.debug) Printer.debug(" data="+((data==null)?"null":""+data.length+" bytes"));
1071
if (Printer.debug) Printer.debug(" frameLength="+frameLength);
1072
1073
if (isOpen()) {
1074
throw new IllegalStateException("Clip is already open with format " + getFormat() +
1075
" and frame lengh of " + getFrameLength());
1076
} else {
1077
// if the line is not currently open, try to open it with this format and buffer size
1078
this.audioData = data;
1079
this.frameSize = format.getFrameSize();
1080
this.m_lengthInFrames = frameLength;
1081
// initialize loop selection with full range
1082
bytePosition = 0;
1083
clipBytePosition = 0;
1084
newFramePosition = -1; // means: do not set to a new readFramePos
1085
loopStartFrame = 0;
1086
loopEndFrame = frameLength - 1;
1087
loopCount = 0; // means: play the clip irrespective of loop points from beginning to end
1088
1089
try {
1090
// use DirectDL's open method to open it
1091
open(format, (int) Toolkit.millis2bytes(format, CLIP_BUFFER_TIME)); // one second buffer
1092
} catch (LineUnavailableException lue) {
1093
audioData = null;
1094
throw lue;
1095
} catch (IllegalArgumentException iae) {
1096
audioData = null;
1097
throw iae;
1098
}
1099
1100
// if we got this far, we can instanciate the thread
1101
int priority = Thread.NORM_PRIORITY
1102
+ (Thread.MAX_PRIORITY - Thread.NORM_PRIORITY) / 3;
1103
thread = JSSecurityManager.createThread(this,
1104
"Direct Clip", // name
1105
true, // daemon
1106
priority, // priority
1107
false); // doStart
1108
// cannot start in createThread, because the thread
1109
// uses the "thread" variable as indicator if it should
1110
// continue to run
1111
thread.start();
1112
}
1113
}
1114
if (isAutoClosing()) {
1115
getEventDispatcher().autoClosingClipOpened(this);
1116
}
1117
if (Printer.trace) Printer.trace("< DirectClip.open completed");
1118
}
1119
1120
1121
public void open(AudioInputStream stream) throws LineUnavailableException, IOException {
1122
1123
// $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions
1124
Toolkit.isFullySpecifiedAudioFormat(stream.getFormat());
1125
1126
synchronized (mixer) {
1127
if (Printer.trace) Printer.trace("> DirectClip.open(stream)");
1128
byte[] streamData = null;
1129
1130
if (isOpen()) {
1131
throw new IllegalStateException("Clip is already open with format " + getFormat() +
1132
" and frame lengh of " + getFrameLength());
1133
}
1134
int lengthInFrames = (int)stream.getFrameLength();
1135
if (Printer.debug) Printer.debug("DirectClip: open(AIS): lengthInFrames: " + lengthInFrames);
1136
1137
int bytesRead = 0;
1138
int frameSize = stream.getFormat().getFrameSize();
1139
if (lengthInFrames != AudioSystem.NOT_SPECIFIED) {
1140
// read the data from the stream into an array in one fell swoop.
1141
int arraysize = lengthInFrames * frameSize;
1142
if (arraysize < 0) {
1143
throw new IllegalArgumentException("Audio data < 0");
1144
}
1145
try {
1146
streamData = new byte[arraysize];
1147
} catch (OutOfMemoryError e) {
1148
throw new IOException("Audio data is too big");
1149
}
1150
int bytesRemaining = arraysize;
1151
int thisRead = 0;
1152
while (bytesRemaining > 0 && thisRead >= 0) {
1153
thisRead = stream.read(streamData, bytesRead, bytesRemaining);
1154
if (thisRead > 0) {
1155
bytesRead += thisRead;
1156
bytesRemaining -= thisRead;
1157
}
1158
else if (thisRead == 0) {
1159
Thread.yield();
1160
}
1161
}
1162
} else {
1163
// read data from the stream until we reach the end of the stream
1164
// we use a slightly modified version of ByteArrayOutputStream
1165
// to get direct access to the byte array (we don't want a new array
1166
// to be allocated)
1167
int maxReadLimit = Math.max(16384, frameSize);
1168
DirectBAOS dbaos = new DirectBAOS();
1169
byte[] tmp;
1170
try {
1171
tmp = new byte[maxReadLimit];
1172
} catch (OutOfMemoryError e) {
1173
throw new IOException("Audio data is too big");
1174
}
1175
int thisRead = 0;
1176
while (thisRead >= 0) {
1177
thisRead = stream.read(tmp, 0, tmp.length);
1178
if (thisRead > 0) {
1179
dbaos.write(tmp, 0, thisRead);
1180
bytesRead += thisRead;
1181
}
1182
else if (thisRead == 0) {
1183
Thread.yield();
1184
}
1185
} // while
1186
streamData = dbaos.getInternalBuffer();
1187
}
1188
lengthInFrames = bytesRead / frameSize;
1189
1190
if (Printer.debug) Printer.debug("Read to end of stream. lengthInFrames: " + lengthInFrames);
1191
1192
// now try to open the device
1193
open(stream.getFormat(), streamData, lengthInFrames);
1194
1195
if (Printer.trace) Printer.trace("< DirectClip.open(stream) succeeded");
1196
} // synchronized
1197
}
1198
1199
1200
public int getFrameLength() {
1201
return m_lengthInFrames;
1202
}
1203
1204
1205
public long getMicrosecondLength() {
1206
return Toolkit.frames2micros(getFormat(), getFrameLength());
1207
}
1208
1209
1210
public void setFramePosition(int frames) {
1211
if (Printer.trace) Printer.trace("> DirectClip: setFramePosition: " + frames);
1212
1213
if (frames < 0) {
1214
frames = 0;
1215
}
1216
else if (frames >= getFrameLength()) {
1217
frames = getFrameLength();
1218
}
1219
if (doIO) {
1220
newFramePosition = frames;
1221
} else {
1222
clipBytePosition = frames * frameSize;
1223
newFramePosition = -1;
1224
}
1225
// fix for failing test050
1226
// $$fb although getFramePosition should return the number of rendered
1227
// frames, it is intuitive that setFramePosition will modify that
1228
// value.
1229
bytePosition = frames * frameSize;
1230
1231
// cease currently playing buffer
1232
flush();
1233
1234
// set new native position (if necessary)
1235
// this must come after the flush!
1236
synchronized (lockNative) {
1237
nSetBytePosition(id, isSource, frames * frameSize);
1238
}
1239
1240
if (Printer.debug) Printer.debug(" DirectClip.setFramePosition: "
1241
+" doIO="+doIO
1242
+" newFramePosition="+newFramePosition
1243
+" clipBytePosition="+clipBytePosition
1244
+" bytePosition="+bytePosition
1245
+" getLongFramePosition()="+getLongFramePosition());
1246
if (Printer.trace) Printer.trace("< DirectClip: setFramePosition");
1247
}
1248
1249
// replacement for getFramePosition (see AbstractDataLine)
1250
public long getLongFramePosition() {
1251
/* $$fb
1252
* this would be intuitive, but the definition of getFramePosition
1253
* is the number of frames rendered since opening the device...
1254
* That also means that setFramePosition() means something very
1255
* different from getFramePosition() for Clip.
1256
*/
1257
// take into account the case that a new position was set...
1258
//if (!doIO && newFramePosition >= 0) {
1259
//return newFramePosition;
1260
//}
1261
return super.getLongFramePosition();
1262
}
1263
1264
1265
public synchronized void setMicrosecondPosition(long microseconds) {
1266
if (Printer.trace) Printer.trace("> DirectClip: setMicrosecondPosition: " + microseconds);
1267
1268
long frames = Toolkit.micros2frames(getFormat(), microseconds);
1269
setFramePosition((int) frames);
1270
1271
if (Printer.trace) Printer.trace("< DirectClip: setMicrosecondPosition succeeded");
1272
}
1273
1274
public void setLoopPoints(int start, int end) {
1275
if (Printer.trace) Printer.trace("> DirectClip: setLoopPoints: start: " + start + " end: " + end);
1276
1277
if (start < 0 || start >= getFrameLength()) {
1278
throw new IllegalArgumentException("illegal value for start: "+start);
1279
}
1280
if (end >= getFrameLength()) {
1281
throw new IllegalArgumentException("illegal value for end: "+end);
1282
}
1283
1284
if (end == -1) {
1285
end = getFrameLength() - 1;
1286
if (end < 0) {
1287
end = 0;
1288
}
1289
}
1290
1291
// if the end position is less than the start position, throw IllegalArgumentException
1292
if (end < start) {
1293
throw new IllegalArgumentException("End position " + end + " preceeds start position " + start);
1294
}
1295
1296
// slight race condition with the run() method, but not a big problem
1297
loopStartFrame = start;
1298
loopEndFrame = end;
1299
1300
if (Printer.trace) Printer.trace(" loopStart: " + loopStartFrame + " loopEnd: " + loopEndFrame);
1301
if (Printer.trace) Printer.trace("< DirectClip: setLoopPoints completed");
1302
}
1303
1304
1305
public void loop(int count) {
1306
// note: when count reaches 0, it means that the entire clip
1307
// will be played, i.e. it will play past the loop end point
1308
loopCount = count;
1309
start();
1310
}
1311
1312
// ABSTRACT METHOD IMPLEMENTATIONS
1313
1314
// ABSTRACT LINE
1315
1316
void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException {
1317
// only if audioData wasn't set in a calling open(format, byte[], frameSize)
1318
// this call is allowed.
1319
if (audioData == null) {
1320
throw new IllegalArgumentException("illegal call to open() in interface Clip");
1321
}
1322
super.implOpen(format, bufferSize);
1323
}
1324
1325
void implClose() {
1326
if (Printer.trace) Printer.trace(">> DirectClip: implClose()");
1327
1328
// dispose of thread
1329
Thread oldThread = thread;
1330
thread = null;
1331
doIO = false;
1332
if (oldThread != null) {
1333
// wake up the thread if it's in wait()
1334
synchronized(lock) {
1335
lock.notifyAll();
1336
}
1337
// wait for the thread to terminate itself,
1338
// but max. 2 seconds. Must not be synchronized!
1339
try {
1340
oldThread.join(2000);
1341
} catch (InterruptedException ie) {}
1342
}
1343
super.implClose();
1344
// remove audioData reference and hand it over to gc
1345
audioData = null;
1346
newFramePosition = -1;
1347
1348
// remove this instance from the list of auto closing clips
1349
getEventDispatcher().autoClosingClipClosed(this);
1350
1351
if (Printer.trace) Printer.trace("<< DirectClip: implClose() succeeded");
1352
}
1353
1354
1355
void implStart() {
1356
if (Printer.trace) Printer.trace("> DirectClip: implStart()");
1357
super.implStart();
1358
if (Printer.trace) Printer.trace("< DirectClip: implStart() succeeded");
1359
}
1360
1361
void implStop() {
1362
if (Printer.trace) Printer.trace(">> DirectClip: implStop()");
1363
1364
super.implStop();
1365
// reset loopCount field so that playback will be normal with
1366
// next call to start()
1367
loopCount = 0;
1368
1369
if (Printer.trace) Printer.trace("<< DirectClip: implStop() succeeded");
1370
}
1371
1372
1373
// main playback loop
1374
public void run() {
1375
if (Printer.trace) Printer.trace(">>> DirectClip: run() threadID="+Thread.currentThread().getId());
1376
Thread curThread = Thread.currentThread();
1377
while (thread == curThread) {
1378
// doIO is volatile, but we could check it, then get
1379
// pre-empted while another thread changes doIO and notifies,
1380
// before we wait (so we sleep in wait forever).
1381
synchronized(lock) {
1382
while (!doIO && thread == curThread) {
1383
try {
1384
lock.wait();
1385
} catch (InterruptedException ignored) {
1386
}
1387
}
1388
}
1389
while (doIO && thread == curThread) {
1390
if (newFramePosition >= 0) {
1391
clipBytePosition = newFramePosition * frameSize;
1392
newFramePosition = -1;
1393
}
1394
int endFrame = getFrameLength() - 1;
1395
if (loopCount > 0 || loopCount == LOOP_CONTINUOUSLY) {
1396
endFrame = loopEndFrame;
1397
}
1398
long framePos = (clipBytePosition / frameSize);
1399
int toWriteFrames = (int) (endFrame - framePos + 1);
1400
int toWriteBytes = toWriteFrames * frameSize;
1401
if (toWriteBytes > getBufferSize()) {
1402
toWriteBytes = Toolkit.align(getBufferSize(), frameSize);
1403
}
1404
int written = write(audioData, (int) clipBytePosition, toWriteBytes); // increases bytePosition
1405
clipBytePosition += written;
1406
// make sure nobody called setFramePosition, or stop() during the write() call
1407
if (doIO && newFramePosition < 0 && written >= 0) {
1408
framePos = clipBytePosition / frameSize;
1409
// since endFrame is the last frame to be played,
1410
// framePos is after endFrame when all frames, including framePos,
1411
// are played.
1412
if (framePos > endFrame) {
1413
// at end of playback. If looping is on, loop back to the beginning.
1414
if (loopCount > 0 || loopCount == LOOP_CONTINUOUSLY) {
1415
if (loopCount != LOOP_CONTINUOUSLY) {
1416
loopCount--;
1417
}
1418
newFramePosition = loopStartFrame;
1419
} else {
1420
// no looping, stop playback
1421
if (Printer.debug) Printer.debug("stop clip in run() loop:");
1422
if (Printer.debug) Printer.debug(" doIO="+doIO+" written="+written+" clipBytePosition="+clipBytePosition);
1423
if (Printer.debug) Printer.debug(" framePos="+framePos+" endFrame="+endFrame);
1424
drain();
1425
stop();
1426
}
1427
}
1428
}
1429
}
1430
}
1431
if (Printer.trace) Printer.trace("<<< DirectClip: run() threadID="+Thread.currentThread().getId());
1432
}
1433
1434
// AUTO CLOSING CLIP SUPPORT
1435
1436
/* $$mp 2003-10-01
1437
The following two methods are common between this class and
1438
MixerClip. They should be moved to a base class, together
1439
with the instance variable 'autoclosing'. */
1440
1441
public boolean isAutoClosing() {
1442
return autoclosing;
1443
}
1444
1445
public void setAutoClosing(boolean value) {
1446
if (value != autoclosing) {
1447
if (isOpen()) {
1448
if (value) {
1449
getEventDispatcher().autoClosingClipOpened(this);
1450
} else {
1451
getEventDispatcher().autoClosingClipClosed(this);
1452
}
1453
}
1454
autoclosing = value;
1455
}
1456
}
1457
1458
protected boolean requiresServicing() {
1459
// no need for servicing for Clips
1460
return false;
1461
}
1462
1463
} // DirectClip
1464
1465
/*
1466
* private inner class representing a ByteArrayOutputStream
1467
* which allows retrieval of the internal array
1468
*/
1469
private static class DirectBAOS extends ByteArrayOutputStream {
1470
DirectBAOS() {
1471
super();
1472
}
1473
1474
public byte[] getInternalBuffer() {
1475
return buf;
1476
}
1477
1478
} // class DirectBAOS
1479
1480
1481
private static native void nGetFormats(int mixerIndex, int deviceID,
1482
boolean isSource, Vector formats);
1483
1484
private static native long nOpen(int mixerIndex, int deviceID, boolean isSource,
1485
int encoding,
1486
float sampleRate,
1487
int sampleSizeInBits,
1488
int frameSize,
1489
int channels,
1490
boolean signed,
1491
boolean bigEndian,
1492
int bufferSize) throws LineUnavailableException;
1493
private static native void nStart(long id, boolean isSource);
1494
private static native void nStop(long id, boolean isSource);
1495
private static native void nClose(long id, boolean isSource);
1496
private static native int nWrite(long id, byte[] b, int off, int len, int conversionSize,
1497
float volLeft, float volRight);
1498
private static native int nRead(long id, byte[] b, int off, int len, int conversionSize);
1499
private static native int nGetBufferSize(long id, boolean isSource);
1500
private static native boolean nIsStillDraining(long id, boolean isSource);
1501
private static native void nFlush(long id, boolean isSource);
1502
private static native int nAvailable(long id, boolean isSource);
1503
// javaPos is number of bytes read/written in Java layer
1504
private static native long nGetBytePosition(long id, boolean isSource, long javaPos);
1505
private static native void nSetBytePosition(long id, boolean isSource, long pos);
1506
1507
// returns if the native implementation needs regular calls to nService()
1508
private static native boolean nRequiresServicing(long id, boolean isSource);
1509
// called in irregular intervals
1510
private static native void nService(long id, boolean isSource);
1511
1512
}
1513
1514