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 javax.sound.sampled.AudioFormat;
29 import javax.sound.sampled.AudioSystem;
30 import javax.sound.sampled.Control;
31 import javax.sound.sampled.DataLine;
32 import javax.sound.sampled.LineEvent;
33 import javax.sound.sampled.LineUnavailableException;
34
35
36 /**
37 * AbstractDataLine
38 *
39 * @author Kara Kytle
40 */
41 abstract class AbstractDataLine extends AbstractLine implements DataLine {
42
43 // DEFAULTS
44
45 // default format
46 private final AudioFormat defaultFormat;
47
48 // default buffer size in bytes
49 private final int defaultBufferSize;
50
51 // the lock for synchronization
52 protected final Object lock = new Object();
53
54 // STATE
55
130 } else {
131 if (Printer.debug) Printer.debug(" dataline already open");
132
133 // if the line is already open and the requested format differs from the
134 // current settings, throw an IllegalStateException
135 //$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
136 if (!format.matches(getFormat())) {
137 throw new IllegalStateException("Line is already open with format " + getFormat() +
138 " and bufferSize " + getBufferSize());
139 }
140 //$$fb 2002-07-26: allow changing the buffersize of already open lines
141 if (bufferSize > 0) {
142 setBufferSize(bufferSize);
143 }
144 }
145
146 if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
147 }
148 }
149
150
151 public final void open(AudioFormat format) throws LineUnavailableException {
152 open(format, AudioSystem.NOT_SPECIFIED);
153 }
154
155
156 /**
157 * This implementation always returns 0.
158 */
159 public int available() {
160 return 0;
161 }
162
163
164 /**
165 * This implementation does nothing.
166 */
167 public void drain() {
168 if (Printer.trace) Printer.trace("AbstractDataLine: drain");
169 }
170
171
172 /**
173 * This implementation does nothing.
174 */
175 public void flush() {
176 if (Printer.trace) Printer.trace("AbstractDataLine: flush");
177 }
178
179
180 public final void start() {
181 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
182 synchronized(mixer) {
183 if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
184
185 // $$kk: 06.06.99: if not open, this doesn't work....???
186 if (isOpen()) {
187
188 if (!isStartedRunning()) {
189 mixer.start(this);
190 implStart();
191 running = true;
192 }
193 }
194 }
195
196 synchronized(lock) {
197 lock.notifyAll();
198 }
199
200 if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
201 }
202
203
204 public final void stop() {
205
206 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
207 synchronized(mixer) {
208 if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
209
210 // $$kk: 06.06.99: if not open, this doesn't work.
211 if (isOpen()) {
212
213 if (isStartedRunning()) {
214
215 implStop();
216 mixer.stop(this);
217
218 running = false;
219
220 // $$kk: 11.10.99: this is not exactly correct, but will probably work
221 if (started && (!isActive())) {
222 setStarted(false);
223 }
228 synchronized(lock) {
229 lock.notifyAll();
230 }
231
232 if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
233 }
234
235 // $$jb: 12.10.99: The official API for this is isRunning().
236 // Per the denied RFE 4297981,
237 // the change to isStarted() is technically an unapproved API change.
238 // The 'started' variable is false when playback of data stops.
239 // It is changed throughout the implementation with setStarted().
240 // This state is what should be returned by isRunning() in the API.
241 // Note that the 'running' variable is true between calls to
242 // start() and stop(). This state is accessed now through the
243 // isStartedRunning() method, defined below. I have not changed
244 // the variable names at this point, since 'running' is accessed
245 // in MixerSourceLine and MixerClip, and I want to touch as little
246 // code as possible to change isStarted() back to isRunning().
247
248 public final boolean isRunning() {
249 return started;
250 }
251
252 public final boolean isActive() {
253 return active;
254 }
255
256
257 public final long getMicrosecondPosition() {
258
259 long microseconds = getLongFramePosition();
260 if (microseconds != AudioSystem.NOT_SPECIFIED) {
261 microseconds = Toolkit.frames2micros(getFormat(), microseconds);
262 }
263 return microseconds;
264 }
265
266
267 public final AudioFormat getFormat() {
268 return format;
269 }
270
271
272 public final int getBufferSize() {
273 return bufferSize;
274 }
275
276 /**
277 * This implementation does NOT change the buffer size
278 */
279 public final int setBufferSize(int newSize) {
280 return getBufferSize();
281 }
282
283 /**
284 * This implementation returns AudioSystem.NOT_SPECIFIED.
285 */
286 public final float getLevel() {
287 return (float)AudioSystem.NOT_SPECIFIED;
288 }
289
290
291 // HELPER METHODS
292
293 /**
294 * running is true after start is called and before stop is called,
295 * regardless of whether data is actually being presented.
296 */
297 // $$jb: 12.10.99: calling this method isRunning() conflicts with
298 // the official API that was once called isStarted(). Since we
299 // use this method throughout the implementation, I am renaming
300 // it to isStartedRunning(). This is part of backing out the
301 // change denied in RFE 4297981.
302
303 final boolean isStartedRunning() {
304 return running;
305 }
306
307 /**
308 * This method sets the active state and generates
309 * events if it changes.
310 */
311 final void setActive(boolean active) {
312
313 if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
314
315 //boolean sendEvents = false;
316 //long position = getLongFramePosition();
317
318 synchronized (this) {
319
320 //if (Printer.debug) Printer.debug(" AbstractDataLine: setActive: this.active: " + this.active);
321 //if (Printer.debug) Printer.debug(" active: " + active);
322
323 if (this.active != active) {
324 this.active = active;
325 //sendEvents = true;
326 }
327 }
328
329 //if (Printer.debug) Printer.debug(" this.active: " + this.active);
330 //if (Printer.debug) Printer.debug(" sendEvents: " + sendEvents);
331
332
333 // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
334 // putting them in is technically an API change.
335 // do not generate ACTIVE / INACTIVE events for now
336 // if (sendEvents) {
337 //
338 // if (active) {
339 // sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
340 // } else {
341 // sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
342 // }
343 //}
344 }
345
346 /**
347 * This method sets the started state and generates
348 * events if it changes.
349 */
350 final void setStarted(boolean started) {
351
352 if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
353
354 boolean sendEvents = false;
355 long position = getLongFramePosition();
356
357 synchronized (this) {
358
359 //if (Printer.debug) Printer.debug(" AbstractDataLine: setStarted: this.started: " + this.started);
360 //if (Printer.debug) Printer.debug(" started: " + started);
361
362 if (this.started != started) {
363 this.started = started;
364 sendEvents = true;
365 }
366 }
367
368 //if (Printer.debug) Printer.debug(" this.started: " + this.started);
369 //if (Printer.debug) Printer.debug(" sendEvents: " + sendEvents);
370
371 if (sendEvents) {
372
373 if (started) {
374 sendEvents(new LineEvent(this, LineEvent.Type.START, position));
375 } else {
376 sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
377 }
378 }
379 if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
380 }
381
382
383 /**
384 * This method generates a STOP event and sets the started state to false.
385 * It is here for historic reasons when an EOM event existed.
386 */
387 final void setEOM() {
388
389 if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
390 //$$fb 2002-04-21: sometimes, 2 STOP events are generated.
391 // better use setStarted() to send STOP event.
392 setStarted(false);
393 if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
394 }
395
396
397
398
399 // OVERRIDES OF ABSTRACT LINE METHODS
400
401 /**
402 * Try to open the line with the current format and buffer size values.
403 * If the line is not open, these will be the defaults. If the
404 * line is open, this should return quietly because the values
405 * requested will match the current ones.
406 */
407 public final void open() throws LineUnavailableException {
408
409 if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
410
411 // this may throw a LineUnavailableException.
412 open(format, bufferSize);
413 if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
414 }
415
416
417 /**
418 * This should also stop the line. The closed line should not be running or active.
419 * After we close the line, we reset the format and buffer size to the defaults.
420 */
421 public final void close() {
422 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
423 synchronized (mixer) {
424 if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
425
426 if (isOpen()) {
427
428 // stop
429 stop();
430
431 // set the open state to false and send events
432 setOpen(false);
433
434 // close resources for this line
435 implClose();
436
437 // release mixer resources for this line
438 mixer.close(this);
439
440 // reset format and buffer size to the defaults
441 format = defaultFormat;
442 bufferSize = defaultBufferSize;
443 }
444 }
445 if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
446 }
447
448
449 // IMPLEMENTATIONS OF ABSTRACT LINE ABSTRACE METHODS
450
451
452 // ABSTRACT METHODS
453
454 abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
455 abstract void implClose();
456
457 abstract void implStart();
458 abstract void implStop();
459 }
|
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 javax.sound.sampled.AudioFormat;
29 import javax.sound.sampled.AudioSystem;
30 import javax.sound.sampled.Control;
31 import javax.sound.sampled.DataLine;
32 import javax.sound.sampled.LineEvent;
33 import javax.sound.sampled.LineUnavailableException;
34
35 /**
36 * AbstractDataLine
37 *
38 * @author Kara Kytle
39 */
40 abstract class AbstractDataLine extends AbstractLine implements DataLine {
41
42 // DEFAULTS
43
44 // default format
45 private final AudioFormat defaultFormat;
46
47 // default buffer size in bytes
48 private final int defaultBufferSize;
49
50 // the lock for synchronization
51 protected final Object lock = new Object();
52
53 // STATE
54
129 } else {
130 if (Printer.debug) Printer.debug(" dataline already open");
131
132 // if the line is already open and the requested format differs from the
133 // current settings, throw an IllegalStateException
134 //$$fb 2002-04-02: fix for 4661602: Buffersize is checked when re-opening line
135 if (!format.matches(getFormat())) {
136 throw new IllegalStateException("Line is already open with format " + getFormat() +
137 " and bufferSize " + getBufferSize());
138 }
139 //$$fb 2002-07-26: allow changing the buffersize of already open lines
140 if (bufferSize > 0) {
141 setBufferSize(bufferSize);
142 }
143 }
144
145 if (Printer.trace) Printer.trace("< AbstractDataLine.open(format, bufferSize) completed");
146 }
147 }
148
149 public final void open(AudioFormat format) throws LineUnavailableException {
150 open(format, AudioSystem.NOT_SPECIFIED);
151 }
152
153 /**
154 * This implementation always returns 0.
155 */
156 @Override
157 public int available() {
158 return 0;
159 }
160
161 /**
162 * This implementation does nothing.
163 */
164 @Override
165 public void drain() {
166 if (Printer.trace) Printer.trace("AbstractDataLine: drain");
167 }
168
169 /**
170 * This implementation does nothing.
171 */
172 @Override
173 public void flush() {
174 if (Printer.trace) Printer.trace("AbstractDataLine: flush");
175 }
176
177 @Override
178 public final void start() {
179 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
180 synchronized(mixer) {
181 if (Printer.trace) Printer.trace("> "+getClass().getName()+".start() - AbstractDataLine");
182
183 // $$kk: 06.06.99: if not open, this doesn't work....???
184 if (isOpen()) {
185
186 if (!isStartedRunning()) {
187 mixer.start(this);
188 implStart();
189 running = true;
190 }
191 }
192 }
193
194 synchronized(lock) {
195 lock.notifyAll();
196 }
197
198 if (Printer.trace) Printer.trace("< "+getClass().getName()+".start() - AbstractDataLine");
199 }
200
201 @Override
202 public final void stop() {
203
204 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
205 synchronized(mixer) {
206 if (Printer.trace) Printer.trace("> "+getClass().getName()+".stop() - AbstractDataLine");
207
208 // $$kk: 06.06.99: if not open, this doesn't work.
209 if (isOpen()) {
210
211 if (isStartedRunning()) {
212
213 implStop();
214 mixer.stop(this);
215
216 running = false;
217
218 // $$kk: 11.10.99: this is not exactly correct, but will probably work
219 if (started && (!isActive())) {
220 setStarted(false);
221 }
226 synchronized(lock) {
227 lock.notifyAll();
228 }
229
230 if (Printer.trace) Printer.trace("< "+getClass().getName()+".stop() - AbstractDataLine");
231 }
232
233 // $$jb: 12.10.99: The official API for this is isRunning().
234 // Per the denied RFE 4297981,
235 // the change to isStarted() is technically an unapproved API change.
236 // The 'started' variable is false when playback of data stops.
237 // It is changed throughout the implementation with setStarted().
238 // This state is what should be returned by isRunning() in the API.
239 // Note that the 'running' variable is true between calls to
240 // start() and stop(). This state is accessed now through the
241 // isStartedRunning() method, defined below. I have not changed
242 // the variable names at this point, since 'running' is accessed
243 // in MixerSourceLine and MixerClip, and I want to touch as little
244 // code as possible to change isStarted() back to isRunning().
245
246 @Override
247 public final boolean isRunning() {
248 return started;
249 }
250
251 @Override
252 public final boolean isActive() {
253 return active;
254 }
255
256 @Override
257 public final long getMicrosecondPosition() {
258
259 long microseconds = getLongFramePosition();
260 if (microseconds != AudioSystem.NOT_SPECIFIED) {
261 microseconds = Toolkit.frames2micros(getFormat(), microseconds);
262 }
263 return microseconds;
264 }
265
266 @Override
267 public final AudioFormat getFormat() {
268 return format;
269 }
270
271 @Override
272 public final int getBufferSize() {
273 return bufferSize;
274 }
275
276 /**
277 * This implementation does NOT change the buffer size
278 */
279 public final int setBufferSize(int newSize) {
280 return getBufferSize();
281 }
282
283 /**
284 * This implementation returns AudioSystem.NOT_SPECIFIED.
285 */
286 @Override
287 public final float getLevel() {
288 return (float)AudioSystem.NOT_SPECIFIED;
289 }
290
291 // HELPER METHODS
292
293 /**
294 * running is true after start is called and before stop is called,
295 * regardless of whether data is actually being presented.
296 */
297 // $$jb: 12.10.99: calling this method isRunning() conflicts with
298 // the official API that was once called isStarted(). Since we
299 // use this method throughout the implementation, I am renaming
300 // it to isStartedRunning(). This is part of backing out the
301 // change denied in RFE 4297981.
302
303 final boolean isStartedRunning() {
304 return running;
305 }
306
307 /**
308 * This method sets the active state and generates
309 * events if it changes.
310 */
311 final void setActive(boolean active) {
312
313 if (Printer.trace) Printer.trace("> AbstractDataLine: setActive(" + active + ")");
314
315 //boolean sendEvents = false;
316 //long position = getLongFramePosition();
317
318 synchronized (this) {
319
320 if (this.active != active) {
321 this.active = active;
322 //sendEvents = true;
323 }
324 }
325
326 // $$kk: 11.19.99: take ACTIVE / INACTIVE / EOM events out;
327 // putting them in is technically an API change.
328 // do not generate ACTIVE / INACTIVE events for now
329 // if (sendEvents) {
330 //
331 // if (active) {
332 // sendEvents(new LineEvent(this, LineEvent.Type.ACTIVE, position));
333 // } else {
334 // sendEvents(new LineEvent(this, LineEvent.Type.INACTIVE, position));
335 // }
336 //}
337 }
338
339 /**
340 * This method sets the started state and generates
341 * events if it changes.
342 */
343 final void setStarted(boolean started) {
344
345 if (Printer.trace) Printer.trace("> AbstractDataLine: setStarted(" + started + ")");
346
347 boolean sendEvents = false;
348 long position = getLongFramePosition();
349
350 synchronized (this) {
351
352 if (this.started != started) {
353 this.started = started;
354 sendEvents = true;
355 }
356 }
357
358 if (sendEvents) {
359
360 if (started) {
361 sendEvents(new LineEvent(this, LineEvent.Type.START, position));
362 } else {
363 sendEvents(new LineEvent(this, LineEvent.Type.STOP, position));
364 }
365 }
366 if (Printer.trace) Printer.trace("< AbstractDataLine: setStarted completed");
367 }
368
369 /**
370 * This method generates a STOP event and sets the started state to false.
371 * It is here for historic reasons when an EOM event existed.
372 */
373 final void setEOM() {
374
375 if (Printer.trace) Printer.trace("> AbstractDataLine: setEOM()");
376 //$$fb 2002-04-21: sometimes, 2 STOP events are generated.
377 // better use setStarted() to send STOP event.
378 setStarted(false);
379 if (Printer.trace) Printer.trace("< AbstractDataLine: setEOM() completed");
380 }
381
382 // OVERRIDES OF ABSTRACT LINE METHODS
383
384 /**
385 * Try to open the line with the current format and buffer size values.
386 * If the line is not open, these will be the defaults. If the
387 * line is open, this should return quietly because the values
388 * requested will match the current ones.
389 */
390 @Override
391 public final void open() throws LineUnavailableException {
392
393 if (Printer.trace) Printer.trace("> "+getClass().getName()+".open() - AbstractDataLine");
394
395 // this may throw a LineUnavailableException.
396 open(format, bufferSize);
397 if (Printer.trace) Printer.trace("< "+getClass().getName()+".open() - AbstractDataLine");
398 }
399
400 /**
401 * This should also stop the line. The closed line should not be running or active.
402 * After we close the line, we reset the format and buffer size to the defaults.
403 */
404 @Override
405 public final void close() {
406 //$$fb 2001-10-09: Bug #4517739: avoiding deadlock by synchronizing to mixer !
407 synchronized (mixer) {
408 if (Printer.trace) Printer.trace("> "+getClass().getName()+".close() - in AbstractDataLine.");
409
410 if (isOpen()) {
411
412 // stop
413 stop();
414
415 // set the open state to false and send events
416 setOpen(false);
417
418 // close resources for this line
419 implClose();
420
421 // release mixer resources for this line
422 mixer.close(this);
423
424 // reset format and buffer size to the defaults
425 format = defaultFormat;
426 bufferSize = defaultBufferSize;
427 }
428 }
429 if (Printer.trace) Printer.trace("< "+getClass().getName()+".close() - in AbstractDataLine");
430 }
431
432 abstract void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException;
433 abstract void implClose();
434
435 abstract void implStart();
436 abstract void implStop();
437 }
|