28package com.jogamp.openal.util;
30import java.nio.ByteBuffer;
32import jogamp.openal.Debug;
34import com.jogamp.common.ExceptionUtils;
35import com.jogamp.common.av.AudioFormat;
36import com.jogamp.common.av.AudioSink;
37import com.jogamp.common.av.AudioSink.AudioFrame;
38import com.jogamp.common.av.PTS;
39import com.jogamp.common.av.TimeFrameI;
40import com.jogamp.common.os.Clock;
41import com.jogamp.common.util.LFRingbuffer;
42import com.jogamp.common.util.PropertyAccess;
43import com.jogamp.common.util.Ringbuffer;
44import com.jogamp.common.util.TSPrinter;
45import com.jogamp.openal.AL;
46import com.jogamp.openal.ALC;
47import com.jogamp.openal.ALCConstants;
48import com.jogamp.openal.ALCcontext;
49import com.jogamp.openal.ALCdevice;
50import com.jogamp.openal.ALConstants;
51import com.jogamp.openal.ALException;
52import com.jogamp.openal.ALExt;
53import com.jogamp.openal.ALExt.ALEVENTPROCSOFT;
54import com.jogamp.openal.ALExtConstants;
55import com.jogamp.openal.sound3d.AudioSystem3D;
56import com.jogamp.openal.sound3d.Context;
57import com.jogamp.openal.sound3d.Device;
58import com.jogamp.openal.sound3d.Source;
72 private static final boolean DEBUG_TRACE;
73 private static final TSPrinter logout;
74 private static final ALC alc;
75 private static final AL al;
76 private static final ALExt alExt;
77 private static final boolean staticsInitialized;
79 private final Device device;
80 private boolean hasSOFTBufferSamples;
81 private boolean hasEXTMcFormats;
82 private boolean hasEXTFloat32;
83 private boolean hasEXTDouble;
84 private boolean hasALC_thread_local_context;
85 private boolean hasAL_SOFT_events;
86 private boolean useAL_SOFT_events;
87 private int sourceCount;
89 private float defaultLatency;
91 private float latency;
92 private final AudioFormat nativeFormat;
93 private int userMaxChannels = 8;
94 private AudioFormat preferredFormat;
98 private float playSpeed = 1.0f;
99 private float volume = 1.0f;
101 static class ALAudioFrame
extends AudioFrame {
102 private final int alBuffer;
104 ALAudioFrame(
final int alBuffer) {
105 this.alBuffer = alBuffer;
107 public ALAudioFrame(
final int alBuffer,
final int pts,
final int duration,
final int dataSize) {
108 super(pts, duration, dataSize);
109 this.alBuffer = alBuffer;
113 public final int getALBuffer() {
return alBuffer; }
117 return "ALAudioFrame[pts " + pts +
" ms, l " + duration +
" ms, " + byteSize +
" bytes, buffer "+alBuffer+
"]";
121 private int[] alBufferNames =
null;
123 private int queueSize = 0;
125 private float avgFrameDuration = 0f;
127 private Ringbuffer<ALAudioFrame> alFramesFree =
null;
128 private Ringbuffer<ALAudioFrame> alFramesPlaying =
null;
129 private volatile int alBufferBytesQueued = 0;
130 private volatile int last_buffered_pts = TimeFrameI.INVALID_PTS;
131 private volatile boolean playRequested =
false;
132 private final PTS pts =
new PTS( () -> {
return playRequested ? playSpeed : 0f; } );
133 private volatile int enqueuedFrameCount;
136 private AudioFormat chosenFormat;
137 private int alChannelLayout;
138 private int alSampleType;
139 private int alFormat;
140 private volatile boolean available;
144 Debug.initSingleton();
145 DEBUG_TRACE = PropertyAccess.isPropertyDefined(
"joal.debug.AudioSink.trace",
true);
146 if( DEBUG || DEBUG_TRACE ) {
147 logout = TSPrinter.stderr();
159 return staticsInitialized;
162 private static Device createDevice(
final String name) {
165 throw new ALException(getThreadName()+
": ALAudioSink: Error opening OpenAL device '"+name+
"'");
184 this(createDevice(deviceName));
197 if( !staticsInitialized ) {
200 nativeFormat = DefaultFormat;
203 if(
null == alDevice ) {
204 device = createDevice(
null);
206 throw new ALException(getThreadName()+
": ALAudioSink: Couldn't open default device: "+device);
210 if( !device.
open() ) {
211 throw new ALException(getThreadName()+
": ALAudioSink: Error device not open or couldn't be opened "+device);
215 context =
new Context(device,
null);
217 throw new ALException(getThreadName()+
": ALAudioSink: Error creating OpenAL context "+context);
228 useAL_SOFT_events = hasAL_SOFT_events;
230 int checkErrIter = 1;
232 int defaultSampleRate = DefaultFormat.sampleRate;
234 final int[] value = { 0 };
238 logout.println(
"ALAudioSink.queryDefaultSampleRate: failed, using default "+defaultSampleRate);
241 defaultSampleRate = value[0];
243 logout.println(
"ALAudioSink.queryDefaultSampleRate: OK "+defaultSampleRate);
251 logout.println(
"ALAudioSink.queryMonoSourceCount: failed");
254 sourceCount = value[0];
259 defaultLatency = 20f/1000f;
261 logout.println(
"ALAudioSink.queryDefaultRefreshRate: failed");
264 defaultLatency = 1f/value[0];
266 logout.println(
"ALAudioSink.queryDefaultRefreshRate: OK "+value[0]+
" Hz = "+(1000f*defaultLatency)+
" ms");
270 nativeFormat =
new AudioFormat(defaultSampleRate, DefaultFormat.sampleSize, getMaxSupportedChannels(
false),
271 DefaultFormat.signed, DefaultFormat.fixedP, DefaultFormat.planar, DefaultFormat.littleEndian);
272 preferredFormat = nativeFormat;
274 final int[] alcvers = { 0, 0 };
278 System.out.println(
"ALAudioSink: Null device OpenALC:");
281 System.out.println(
" Version: "+alcvers[0]+
"."+alcvers[1]);
284 System.out.println(
"ALAudioSink: Device "+device+
" OpenALC:");
287 System.out.println(
" Version: "+alcvers[0]+
"."+alcvers[1]);
289 System.out.println(
"ALAudioSink: hasSOFTBufferSamples "+hasSOFTBufferSamples);
290 System.out.println(
"ALAudioSink: hasEXTMcFormats "+hasEXTMcFormats);
291 System.out.println(
"ALAudioSink: hasEXTFloat32 "+hasEXTFloat32);
292 System.out.println(
"ALAudioSink: hasEXTDouble "+hasEXTDouble);
293 System.out.println(
"ALAudioSink: hasALC_thread_local_context "+hasALC_thread_local_context);
294 System.out.println(
"ALAudioSink: hasAL_SOFT_events "+hasAL_SOFT_events);
295 System.out.println(
"ALAudioSink: maxSupportedChannels "+getMaxSupportedChannels(
false));
296 System.out.println(
"ALAudioSink: nativeAudioFormat "+nativeFormat);
297 System.out.println(
"ALAudioSink: defaultMixerRefreshRate "+(1000f*defaultLatency)+
" ms, "+(1f/defaultLatency)+
" Hz");
302 logout.println(
"ALAudioSink: Using device: " + device);
357 public final boolean release(
final boolean throwException) {
358 return context.
release(throwException);
360 private final void destroyContext() {
366 final int ctxHash = context !=
null ? context.hashCode() : 0;
367 final int alFramesEnqueued = alFramesPlaying !=
null ? alFramesPlaying.size() : 0;
368 final int alFramesFree_ = alFramesFree!=
null ? alFramesFree.size() : 0;
369 return String.format(
"ALAudioSink[playReq %b, device '%s', ctx 0x%x, alSource %d"+
370 ", chosen %s, al[chan %s, type %s, fmt 0x%x, tlc %b, soft[buffer %b, events %b/%b]"+
371 ", latency %.2f/%.2f ms, sources %d], playSpeed %.2f, "+
372 "play[used %d, apts %d], queued[free %d, apts %d, %.1f ms, %d bytes, avg %.2f ms/frame, max %d ms]]",
373 playRequested, device.
getName(), ctxHash, alSource.
getID(), chosenFormat,
375 alFormat, hasALC_thread_local_context, hasSOFTBufferSamples, useAL_SOFT_events, hasAL_SOFT_events,
376 1000f*latency, 1000f*defaultLatency, sourceCount, playSpeed,
377 alFramesEnqueued,
getPTS().getLast(),
383 final int alFramesEnqueued = alFramesPlaying !=
null ? alFramesPlaying.size() : 0;
384 final int alFramesFree_ = alFramesFree!=
null ? alFramesFree.size() : 0;
385 return String.format(
"play[used %d, apts %d], queued[free %d, apts %d, %.1f ms, %d bytes, avg %.2f ms/frame, max %d ms]",
386 alFramesEnqueued,
getPTS().getLast(),
402 if( !staticsInitialized ) {
410 if( !staticsInitialized ) {
413 return preferredFormat;
418 userMaxChannels = Math.min(8, Math.max(1, cc));
420 preferredFormat =
new AudioFormat(nativeFormat.sampleRate,
421 nativeFormat.sampleSize, getMaxSupportedChannels(
true),
422 nativeFormat.signed, nativeFormat.fixedP,
423 nativeFormat.planar, nativeFormat.littleEndian);
425 System.out.println(
"ALAudioSink: channelLimit "+userMaxChannels+
", preferredFormat "+preferredFormat);
429 private final int getMaxSupportedChannels(
final boolean considerLimit) {
430 if( !staticsInitialized ) {
434 if( hasEXTMcFormats || hasSOFTBufferSamples ) {
439 return considerLimit ? Math.min(userMaxChannels, cc) : cc;
444 if( !staticsInitialized ) {
447 if( format.planar != preferredFormat.planar ||
448 format.littleEndian != preferredFormat.littleEndian ||
449 format.sampleRate > preferredFormat.sampleRate ||
450 format.channelCount > preferredFormat.channelCount )
453 logout.println(getThreadName()+
": ALAudioSink.isSupported: NO.0 "+format);
458 hasSOFTBufferSamples, hasEXTMcFormats,
459 hasEXTFloat32, hasEXTDouble);
462 logout.println(getThreadName()+
": ALAudioSink.isSupported: OK "+format+
", alFormat "+toHexString(alFormat));
467 logout.println(getThreadName()+
": ALAudioSink.isSupported: NO.1 "+format);
474 public final boolean init(
final AudioFormat requestedFormat,
final int frameDurationHint,
final int queueSize)
476 if( !staticsInitialized ) {
480 final int alSampleType =
ALHelpers.
getALSampleType(requestedFormat.sampleSize, requestedFormat.signed, requestedFormat.fixedP);
484 hasSOFTBufferSamples, hasEXTMcFormats,
485 hasEXTFloat32, hasEXTDouble);
492 logout.println(getThreadName()+
": ALAudioSink.init1: Not supported: "+requestedFormat+
", "+
toString());
496 return initImpl(requestedFormat, alChannelLayout, alSampleType, alFormat, frameDurationHint/1000f, queueSize);
518 public final boolean init(
final int alChannelLayout,
final int alSampleType,
final int alFormat,
519 final int sampleRate,
final int sampleSize,
final int frameDurationHint,
final int queueSize)
521 final AudioFormat requestedFormat =
ALHelpers.
getAudioFormat(alChannelLayout, alSampleType, alFormat, sampleRate, sampleSize);
522 if(
null == requestedFormat ) {
524 logout.println(getThreadName()+
": ALAudioSink.init2: Invalid AL channelLayout "+toHexString(alChannelLayout)+
525 ", sampleType "+toHexString(alSampleType)+
", format "+toHexString(alFormat)+
" or sample[rate "+sampleRate+
", size "+sampleSize+
"]; "+
toString());
529 return initImpl(requestedFormat, alChannelLayout, alSampleType, alFormat, frameDurationHint/1000f, queueSize);
532 private final synchronized boolean initImpl(
final AudioFormat requestedFormat,
533 final int alChannelLayout,
final int alSampleType,
final int alFormat,
534 float frameDurationHintS,
final int queueSize) {
535 this.alChannelLayout = alChannelLayout;
536 this.alSampleType = alSampleType;
537 this.alFormat = alFormat;
555 throw new ALException(
"init() must be called w/o makeCurrent: lockCount "+context+
", "+
this);
557 boolean releaseContext =
true;
563 frameDurationHintS = frameDurationHintS >= 1f/1000f ? frameDurationHintS : AudioSink.DefaultFrameDuration/1000f;
566 final int defRefreshRate = Math.round( 1f / defaultLatency );
567 final int expMixerRefreshRate = Math.round( 1f / frameDurationHintS );
569 if( frameDurationHintS < defaultLatency ) {
571 logout.println(getThreadName()+
": ALAudioSink.init: Re-create context as latency exp "+
572 (1000f*frameDurationHintS)+
" ms ("+expMixerRefreshRate+
" Hz) < default "+(1000f*defaultLatency)+
" ms ("+defRefreshRate+
" Hz)");
574 if( !context.
recreate(
new int[] { ALCConstants.ALC_REFRESH, expMixerRefreshRate } ) ) {
575 logout.println(getThreadName()+
": ALAudioSink: Error creating OpenAL context "+context);
579 logout.println(getThreadName()+
": ALAudioSink.init: Keep context, latency exp "+
580 (1000f*frameDurationHintS)+
" ms ("+expMixerRefreshRate+
" Hz) >= default "+(1000f*defaultLatency)+
" ms ("+defRefreshRate+
" Hz)");
585 final int[] value = { 0 };
587 if( AudioSystem3D.checkError(device,
"read ALC_FREQUENCY", DEBUG,
false) || 0 == value[0] ) {
588 latency = defaultLatency;
590 logout.println(
"ALAudioSink.queryRefreshRate: failed, claiming default "+(1000f*latency)+
" ms");
593 latency = 1f/value[0];
595 logout.println(
"ALAudioSink.queryRefreshRate: OK "+value[0]+
" Hz = "+(1000f*latency)+
" ms");
599 if( !createSource() ) {
601 releaseContext =
false;
607 final int frameCount = requestedFormat.getFrameCount(
608 queueSize > 0 ? queueSize/1000f : AudioSink.DefaultQueueSize/1000f, frameDurationHintS);
609 alBufferNames =
new int[frameCount];
611 if( AudioSystem3D.checkALError(
"alGenBuffers",
true,
false) ) {
612 alBufferNames =
null;
615 releaseContext =
false;
618 final ALAudioFrame[] alFrames =
new ALAudioFrame[frameCount];
619 for(
int i=0; i<frameCount; i++) {
620 alFrames[i] =
new ALAudioFrame(alBufferNames[i]);
622 alFramesFree =
new LFRingbuffer<ALAudioFrame>(alFrames);
623 alFramesPlaying =
new LFRingbuffer<ALAudioFrame>(ALAudioFrame[].
class, frameCount);
624 this.queueSize = queueSize > 0 ? queueSize : AudioSink.DefaultQueueSize;
626 alFramesFree.dump(System.err,
"Avail-init");
627 alFramesPlaying.dump(System.err,
"Playi-init");
630 if( hasAL_SOFT_events && useAL_SOFT_events ) {
632 alExt.
alEventControlSOFT(1,
new int[] { ALExtConstants.AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT }, 0,
true);
635 if( releaseContext ) {
639 chosenFormat = requestedFormat;
640 avgFrameDuration = latency;
642 logout.println(getThreadName()+
": ALAudioSink.init: OK "+requestedFormat+
", "+
toString());
652 private void destroyBuffers() {
653 if( !staticsInitialized ) {
656 if(
null != alBufferNames ) {
659 }
catch (
final Throwable t) {
661 logout.println(
"Caught "+t.getClass().getName()+
": "+t.getMessage());
665 alFramesFree.clear();
667 alFramesPlaying.clear();
668 alFramesPlaying =
null;
669 alBufferBytesQueued = 0;
671 alBufferNames =
null;
675 private void destroySource() {
681 private boolean createSource() {
694 if(
null != context ) {
696 if( hasAL_SOFT_events ) {
699 ALExtConstants.AL_EVENT_TYPE_DISCONNECTED_SOFT
721 @SuppressWarnings(
"unused")
723 public void callback(
final int eventType,
final int object,
final int param,
724 final String message,
final ALCcontext context) {
727 logout.println(
"ALAudioSink.Event: type "+toHexString(eventType)+
", obj "+toHexString(
object)+
", param "+param+
728 ", msg '"+message+
"', userParam "+k);
731 alSource.
getID() ==
object )
733 synchronized( eventReleasedBuffersLock ) {
736 logout.println(
"ALAudioSink.Event: type "+toHexString(eventType)+
", obj "+toHexString(
object)+
737 ", eventReleasedBuffers +"+param+
" -> "+(eventReleasedBuffers + param)+
738 ", msg '"+message+
"', userParam "+k);
740 eventReleasedBuffers += param;
741 eventReleasedBuffersLock.notifyAll();
746 private final Object eventReleasedBuffersLock =
new Object();
747 private volatile int eventReleasedBuffers = 0;
749 private final int waitForReleasedEvent(
final long t0,
final boolean wait,
final int releaseBufferCountReq) {
750 if( alBufferBytesQueued == 0 ) {
753 final int enqueuedBuffers = alFramesPlaying.size();
756 int releasedBuffers = 0;
758 synchronized( eventReleasedBuffersLock ) {
759 while( wait && alBufferBytesQueued > 0 && eventReleasedBuffers < releaseBufferCountReq ) {
762 eventReleasedBuffersLock.wait();
763 }
catch (
final InterruptedException e) { }
767 final int releasedBuffersByEvent = eventReleasedBuffers;
769 releasedBuffers = Math.min(releasedBuffersByEvent, releasedBuffersByQuery);
770 eventReleasedBuffers = 0;
772 slept += Clock.currentMillis() - t0;
773 if( wait || releasedBuffers > 0 ) {
774 final String warnInfo = releasedBuffers != releasedBuffersByEvent ?
" ** Warning ** " :
"";
775 logout.println(
"ALAudioSink.DeqEvent["+wait_cycles+
"]: released "+releasedBuffers+warnInfo+
776 " [enqeueud "+enqueuedBuffers+
", event "+
777 releasedBuffersByEvent+
", query "+releasedBuffersByQuery+
"], req "+releaseBufferCountReq+
", slept "+
778 slept+
" ms, free total "+alFramesFree.size());
782 }
while ( wait && alBufferBytesQueued > 0 && releasedBuffers < releaseBufferCountReq );
783 return releasedBuffers;
785 private final int waitForReleasedPoll(
final boolean wait,
final int releaseBufferCountReq) {
786 if( alBufferBytesQueued == 0 ) {
789 final long sleepLimes = Math.round( releaseBufferCountReq * 1000.0*avgFrameDuration );
792 boolean onceBusyDebug =
true;
793 int releasedBuffers = 0;
796 if( wait && releasedBuffers < releaseBufferCountReq ) {
799 final int sleep = Math.max(2, Math.min(300, Math.round( (releaseBufferCountReq-releasedBuffers) * 1000f*avgFrameDuration) ) ) - 1;
800 if( slept + sleep + 1 <= sleepLimes ) {
802 logout.println(
"ALAudioSink: DeqPoll["+wait_cycles+
"].1:"+
803 "released "+releasedBuffers+
"/"+releaseBufferCountReq+
", sleep "+sleep+
"/"+slept+
"/"+sleepLimes+
804 " ms, "+
getPerfString()+
", state "+ALHelpers.alSourceStateString(getSourceState(
false)));
808 Thread.sleep( sleep );
810 }
catch (
final InterruptedException e) {
817 if( onceBusyDebug ) {
818 logout.println(
"ALAudioSink: DeqPoll["+wait_cycles+
"].2:"+
819 "released "+releasedBuffers+
"/"+releaseBufferCountReq+
", sleep "+sleep+
"->1/"+slept+
"/"+sleepLimes+
820 " ms, "+
getPerfString()+
", state "+ALHelpers.alSourceStateString(getSourceState(
false)));
821 onceBusyDebug =
false;
828 }
catch (
final InterruptedException e) {
834 }
while ( wait && alBufferBytesQueued > 0 && releasedBuffers < releaseBufferCountReq );
835 return releasedBuffers;
842 private final int dequeueBuffer(
final boolean wait,
final int releaseBufferCountReq) {
843 final long t0 = Clock.currentMillis();
844 final int releasedBufferCount;
845 if( hasAL_SOFT_events && useAL_SOFT_events ) {
846 releasedBufferCount = waitForReleasedEvent(t0, wait, releaseBufferCountReq);
848 releasedBufferCount = waitForReleasedPoll(wait, releaseBufferCountReq);
850 final long t1 = Clock.currentMillis();
851 if( releasedBufferCount > 0 ) {
852 final int[] buffers =
new int[releasedBufferCount];
855 for (
int i=0; i<releasedBufferCount; i++ ) {
856 final ALAudioFrame releasedBuffer = alFramesPlaying.get();
857 if(
null == releasedBuffer ) {
858 throw new InternalError(
"Internal Error: "+
this);
860 if( releasedBuffer.alBuffer != buffers[i] ) {
861 alFramesFree.dump(System.err,
"Avail-deq02-post");
862 alFramesPlaying.dump(System.err,
"Playi-deq02-post");
863 throw new InternalError(
"Buffer name mismatch: dequeued: "+buffers[i]+
", released "+releasedBuffer+
", "+
this);
865 alBufferBytesQueued -= releasedBuffer.getByteSize();
867 pts.set(t1, releasedBuffer.getPTS() );
872 if( !alFramesFree.put(releasedBuffer) ) {
873 throw new InternalError(
"Internal Error: "+
this);
876 logout.println(
"<< [al "+buffers[i]+
", q "+releasedBuffer.alBuffer+
"] <- "+
getPerfString()+
" @ "+getThreadName());
881 logout.println(
"ALAudioSink.Dequeued: "+(t1-t0)+
882 "ms , released "+releasedBufferCount+
"/"+releaseBufferCountReq+
", "+
getPerfString()+
883 ", state "+ALHelpers.alSourceStateString(getSourceState(
false)));
886 return releasedBufferCount;
889 private final void dequeueForceAll() {
891 logout.println(
"< _FLUSH_ <- "+
getPerfString()+
" @ "+getThreadName());
893 int processedBufferCount = 0;
899 while ( !alFramesPlaying.isEmpty() ) {
900 final ALAudioFrame releasedBuffer = alFramesPlaying.get();
901 if(
null == releasedBuffer ) {
902 throw new InternalError(
"Internal Error: "+
this);
904 alBufferBytesQueued -= releasedBuffer.getByteSize();
905 if( !alFramesFree.put(releasedBuffer) ) {
906 throw new InternalError(
"Internal Error: "+
this);
909 alBufferBytesQueued = 0;
910 last_buffered_pts = TimeFrameI.INVALID_PTS;
911 pts.set(0, TimeFrameI.INVALID_PTS);
913 logout.println(
"<< _FLUSH_ [al "+processedBufferCount+
", err "+toHexString(alErr)+
"] <- "+
getPerfString()+
" @ "+getThreadName());
914 ExceptionUtils.dumpStack(System.err);
920 if( !available ||
null == chosenFormat ) {
929 dequeueBuffer(
false , 1 );
937 public final AudioFrame
enqueueData(
final int pts,
final ByteBuffer bytes,
final int byteCount) {
938 if( !available ||
null == chosenFormat ) {
941 final ALAudioFrame alFrame;
948 final int sourceState0 = DEBUG ? getSourceState(
false) : 0;
949 final float neededDuration = chosenFormat.getBytesDuration(byteCount);
950 int enqueuedBuffers = alFramesPlaying.size();
951 final char avgUpdateC;
954 if( enqueuedBuffers > 2 ) {
955 final float queuedDuration = chosenFormat.getBytesDuration(alBufferBytesQueued);
956 avgFrameDuration = queuedDuration / enqueuedBuffers;
963 if( alFramesFree.isEmpty() || enqueuedBuffers > 2 ) {
965 logout.printf(
"ALAudioSink.DequeuSoft"+avgUpdateC+
": %.2f ms, queued %d, %s%n",
968 dequeueBuffer(
false , 1 );
970 enqueuedBuffers = alFramesPlaying.size();
973 if( alFramesFree.isEmpty() && isPlayingImpl() ) {
975 final int releaseBuffersHardReq = Math.max(1, enqueuedBuffers / 3 );
977 logout.printf(
"ALAudioSink.DequeuHard"+avgUpdateC+
": %.2f ms, req %d, queued %d, %s%n",
978 1000f*neededDuration, releaseBuffersHardReq, enqueuedBuffers,
getPerfString());
980 dequeueBuffer(
true , releaseBuffersHardReq );
984 alFrame = alFramesFree.get();
985 if(
null == alFrame ) {
986 alFramesFree.dump(System.err,
"Avail");
987 throw new InternalError(
"Internal Error: avail.get null "+alFramesFree+
", "+
this);
990 alFrame.setDuration(Math.round(1000f*neededDuration));
991 alFrame.setByteSize(byteCount);
992 if( !alFramesPlaying.put( alFrame ) ) {
993 throw new InternalError(
"Internal Error: "+
this);
995 last_buffered_pts = pts;
996 final int[] alBufferNames =
new int[] { alFrame.alBuffer };
997 if( hasSOFTBufferSamples ) {
998 final int samplesPerChannel = chosenFormat.getBytesSampleCount(byteCount) / chosenFormat.channelCount;
1001 samplesPerChannel, alChannelLayout, alSampleType, bytes);
1003 al.
alBufferData(alFrame.alBuffer, alFormat, bytes, byteCount, chosenFormat.sampleRate);
1007 alBufferBytesQueued += byteCount;
1008 enqueuedFrameCount++;
1011 logout.println(
">> "+alFrame.alBuffer+
" -> "+
getPerfString()+
" @ "+getThreadName());
1014 final int sourceState1 = getSourceState(
false);
1016 final int sourceState2 = getSourceState(
false);
1017 if( sourceState0 != sourceState1 || sourceState0 != sourceState2 || sourceState1 != sourceState2 ) {
1018 logout.printf(
"ALAudioSink.Enqueued : %.2f ms, %s, state* %s -> %s -> %s; %s bytes%n",
1022 logout.printf(
"ALAudioSink.Enqueued : %.2f ms, %s, state %s; %d bytes%n",
1036 if( !available ||
null == chosenFormat ) {
1039 if( playRequested ) {
1042 return isPlayingImpl();
1050 private final boolean isPlayingImpl() {
1051 if( playRequested ) {
1052 return ALConstants.AL_PLAYING == getSourceState(
false);
1057 private final int getSourceState(
final boolean ignoreError) {
1059 final String msg = getThreadName()+
": getSourceState: invalid "+alSource;
1062 logout.println(msg);
1064 return ALConstants.AL_NONE;
1066 throw new ALException(msg);
1069 final int[] val = { ALConstants.AL_NONE };
1071 if( AudioSystem3D.checkALError(
"alGetSourcei",
true,
false) ) {
1072 final String msg = getThreadName()+
": Error while querying SOURCE_STATE. "+
this;
1075 logout.println(msg);
1077 return ALConstants.AL_NONE;
1079 throw new ALException(msg);
1087 if( !available ||
null == chosenFormat ) {
1090 playRequested =
true;
1101 private final void playImpl() {
1110 if( !available ||
null == chosenFormat ) {
1113 if( playRequested ) {
1125 private final void pauseImpl() {
1126 if( isPlayingImpl() ) {
1127 playRequested =
false;
1132 private final void stopImpl(
final boolean ignoreError) {
1136 if( ALConstants.AL_STOPPED != getSourceState(ignoreError) ) {
1137 playRequested =
false;
1139 if( AudioSystem3D.checkALError(
"alSourcePause",
true,
false) ) {
1140 final String msg =
"Error while stopping. "+
this;
1143 logout.println(getThreadName()+
": "+msg);
1146 throw new ALException(getThreadName()+
": Error while stopping. "+
this);
1157 if( !available ||
null == chosenFormat ) {
1162 if( Math.abs(1.0f - rate) < 0.01f ) {
1165 if( 0.5f <= rate && rate <= 2.0f ) {
1181 private static final float clipAudioVolume(
final float v) {
1184 }
else if( Math.abs(1.0f - v) < 0.01f ) {
1191 if( !available ||
null == chosenFormat ) {
1196 v = clipAudioVolume(v);
1197 if( 0.0f <= v && v <= 1.0f ) {
1210 if( !available ||
null == chosenFormat ) {
1219 if( alBufferNames.length != alFramesFree.size() || alFramesPlaying.size() != 0 ) {
1220 throw new InternalError(
"XXX: "+
this);
1232 return enqueuedFrameCount;
1237 return null != alBufferNames ? alBufferNames.length : 0;
1242 if( !available ||
null == chosenFormat ) {
1245 return alFramesPlaying.size();
1250 if( !available ||
null == chosenFormat ) {
1253 return alFramesFree.size();
1258 if( !available ||
null == chosenFormat ) {
1261 return alBufferBytesQueued;
1266 if( !available ||
null == chosenFormat ) {
1269 return chosenFormat.getBytesDuration(alBufferBytesQueued);
1274 return avgFrameDuration;
1283 private static final String toHexString(
final int v) {
return "0x"+Integer.toHexString(v); }
1284 private static final String getThreadName() {
return Thread.currentThread().getName(); }
Implementing equals(Object) based on the native address and hashCode() on the HashUtil#getAddrHash32_...
ALContextKey(final ALCcontext context)
Creates an instance using the current context as key.
A generic exception for OpenAL errors used throughout the binding as a substitute for RuntimeExceptio...
The AudioSystem3D class provides a set of methods for creating and manipulating a 3D audio environmen...
static final ALC getALC()
Return OpenAL global ALC.
static boolean checkError(final Device device, final String prefix, final boolean verbose, final boolean throwException)
Returns true if an OpenAL ALC or AL error occurred, otherwise false.
static final AL getAL()
Return OpenAL global AL.
static boolean checkALError(final String prefix, final boolean verbose, final boolean throwException)
Returns true if an OpenAL AL error occurred, otherwise false.
static boolean isAvailable()
Returns the available state of this instance.
static final ALExt getALExt()
Return OpenAL global ALExt.
This class provides a Sound3D Context associated with a specified device.
boolean release(final boolean throwException)
Releases control of this audio context from the current thread, if implementation utilizes context lo...
boolean isValid()
Returns whether getALContext() is valid, i.e.
boolean makeCurrent(final boolean throwException)
Makes the audio context current on the calling thread.
int getLockCount()
Return the lock count of this context, i.e.
boolean recreate(final int[] attributes)
Recreates the internal ALCcontext instance, i.e.
void destroy()
destroys this context freeing its resources.
ALCcontext getALContext()
Returns the OpenAL ALCcontext.
final boolean hasALC_thread_local_context
This class provides a handle to a specific audio device.
String getName()
Returns the device name.
void close()
closes the device, freeing its resources.
ALCdevice getALDevice()
Returns the OpenAL ALCdevice.
boolean open()
Opens the device if not yet opened.
boolean isValid()
Returns whether getALDevice() is open and valid, i.e.
This class is used to represent sound-producing objects in the Sound3D environment.
boolean isValid()
Returns whether getID() is valid, i.e.
int getBuffersProcessed()
Gets the number of buffers already processed on this source.
void unqueueBuffers(final Buffer[] buffers)
Unqueues one or more buffers on a source.
void setPitch(final float pitch)
Sets the pitch of the audio on this source.
boolean create()
Creates a new OpenAL source ID if isValid() == false.
void stop()
Stops the audio in this Source.
void delete()
Delete this source, freeing its resources.
void setGain(final float gain)
Sets the gain of the audio on this source.
void pause()
pauses the audio in this Source.
int getID()
Return the OpenAL source ID, -1 if invalid.
void play()
Beginning playing the audio in this source.
void queueBuffers(final Buffer[] buffers)
Queues one or more buffers on a source.
final boolean release(final boolean throwException)
final AudioFormat getChosenFormat()
final AudioFrame enqueueData(final int pts, final ByteBuffer bytes, final int byteCount)
final int getALChannelLayout()
Return this instance's OpenAL channel layout, set after init(AudioFormat, float, int).
final boolean init(final AudioFormat requestedFormat, final int frameDurationHint, final int queueSize)
final int getFreeFrameCount()
ALAudioSink(final Device alDevice)
Create a new instance with an optional given ALCdevice.
final boolean hasEXTDouble()
Return whether OpenAL extension AL_EXT_DOUBLE is available.
float getAvgFrameDuration()
static final ALC getALC()
Return OpenAL global ALC.
final boolean setPlaySpeed(float rate)
float getDefaultLatency()
final int getEnqueuedFrameCount()
final boolean hasALCThreadLocalContext()
Return whether OpenAL extension ALC_EXT_thread_local_context is available.
final float getPlaySpeed()
final boolean isSupported(final AudioFormat format)
final boolean isPlaying()
final float getQueuedDuration()
final Context getContext()
Return this instance's OpenAL Context.
ALAudioSink(final String deviceName)
Create a new instance with a new named ALCdevice.
static final AL getAL()
Return OpenAL global AL.
final AudioFormat getNativeFormat()
final boolean makeCurrent(final boolean throwException)
final boolean isAvailable()
final boolean init(final int alChannelLayout, final int alSampleType, final int alFormat, final int sampleRate, final int sampleSize, final int frameDurationHint, final int queueSize)
Initializes the sink using the given OpenAL audio parameter and streaming details.
final AudioFormat getPreferredFormat()
final int getALSampleType()
Return this instance's OpenAL sample type, set after init(AudioFormat, float, int).
final void setUseSOFTEvents(final boolean v)
Enable or disable AL_SOFT_events, default is enabled if hasSOFTEvents().
final boolean hasSOFTBufferSamples()
Return whether OpenAL extension AL_SOFT_buffer_samples is available.
final boolean hasSOFTEvents()
Return whether OpenAL extension AL_SOFT_events is available.
ALAudioSink()
Create a new instance with a new default ALCdevice.
final boolean getUseSOFTEvents(final boolean v)
Returns whether AL_SOFT_events is enabled, default if hasSOFTEvents().
static boolean isInitialized()
Returns true if OpenAL has been loaded and static fields ALC, AL and ALExt have been initialized succ...
final Device getDevice()
Return this instance's OpenAL Device.
final int getQueuedByteCount()
static final ALExt getALExt()
Return OpenAL global ALExt.
final boolean hasEXTFloat32()
Return whether OpenAL extension AL_EXT_FLOAT32 is available.
final String getPerfString()
final int getALFormat()
Return this instance's OpenAL format, set after init(AudioFormat, float, int).
final int getFrameCount()
final int getQueuedFrameCount()
final Source getSource()
Return this instance's OpenAL Source.
final int getLastBufferedPTS()
final boolean setVolume(float v)
final boolean hasEXTMcFormats()
Return whether OpenAL extension AL_EXT_MCFORMATS is available.
final void setChannelLimit(final int cc)
static int getALFormat(final AudioFormat audioFormat, final AL al, final ALExt alExt)
Returns a compatible AL buffer format given the AudioFormat, which determines the AL channel layout a...
static final String alChannelLayoutName(final int alChannelLayout)
Returns the readable name of the given AL channel layout.
static final String AL_SOFT_events
static final String AL_EXT_DOUBLE
static final String AL_SOFT_buffer_samples
openal-soft >= 1.18.0
static final String alSampleTypeName(final int alSampleType)
Returns the readable name of the given AL sample type.
static final String AL_EXT_MCFORMATS
static final String alSourceStateString(final int sourceState)
Returns given ALConstants#AL_SOURCE_STATE AL#alGetSourcei(int, int, int[], int)} value as a string.
static final String AL_EXT_FLOAT32
static AudioFormat getAudioFormat(final int alChannelLayout, final int alSampleType, final int alFormat, final int sampleRate, final int sampleSize)
Returns a compatible AudioFormat based on given OpenAL channel-layout, sample-type and format,...
static final int getDefaultALChannelLayout(final int channelCount)
Returns the default AL channel layout matching the given channel count, or ALConstants#AL_NONE.
static final int getALSampleType(final int sampleSize, final boolean signed, final boolean fixedP)
Returns the AL sample type matching the given audio type attributes, or ALConstants#AL_NONE.
static final int ALC_MAJOR_VERSION
Define "ALC_MAJOR_VERSION" with expression '0x1000', CType: int.
static final int ALC_MINOR_VERSION
Define "ALC_MINOR_VERSION" with expression '0x1001', CType: int.
static final int ALC_FREQUENCY
Define "ALC_FREQUENCY" with expression '0x1007', CType: int.
static final int ALC_EXTENSIONS
Define "ALC_EXTENSIONS" with expression '0x1006', CType: int.
static final int ALC_MONO_SOURCES
Define "ALC_MONO_SOURCES" with expression '0x1010', CType: int.
static final int ALC_REFRESH
Define "ALC_REFRESH" with expression '0x1008', CType: int.
String alcGetString(ALCdevice device, int param)
Entry point (through function pointer) to C language function: const ALCchar * alcGetString(ALCdevi...
void alcGetIntegerv(ALCdevice device, int param, int size, IntBuffer values)
Entry point (through function pointer) to C language function: void alcGetIntegerv(ALCdevice * dev...
static final int AL_PLAYING
Define "AL_PLAYING" with expression '0x1012', CType: int.
static final int AL_VERSION
Define "AL_VERSION" with expression '0xB002', CType: int.
static final int AL_EXTENSIONS
Define "AL_EXTENSIONS" with expression '0xB004', CType: int.
static final int AL_NONE
Define "AL_NONE" with expression '0', CType: int.
static final int AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT
Define "AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT" with expression '0x19A5', CType: int.
static final int AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT
Define "AL_EVENT_TYPE_BUFFER_COMPLETED_SOFT" with expression '0x19A4', CType: int.
JavaCallback interface: ALEVENTPROCSOFT -> void (*ALEVENTPROCSOFT)(ALenum eventType,...
void alEventControlSOFT(int count, IntBuffer types, boolean enable)
Entry point (through function pointer) to C language function: void alEventControlSOFT(ALsizei coun...
void alEventCallbackSOFT(ALEVENTPROCSOFT callback, ALCcontext userParam)
Entry point (through function pointer) to C language function: void alEventCallbackSOFT(ALEVENTPROC...
void alBufferSamplesSOFT(int buffer, int samplerate, int internalformat, int samples, int channels, int type, Buffer data)
Entry point (through function pointer) to C language function: void alBufferSamplesSOFT(ALuint buff...
void alBufferData(int buffer, int format, Buffer data, int size, int samplerate)
Entry point (through function pointer) to C language function: void alBufferData(ALuint buffer,...
int alGetError()
Entry point (through function pointer) to C language function: ALenum alGetError()
void alGenBuffers(int n, IntBuffer buffers)
Entry point (through function pointer) to C language function: void alGenBuffers(ALsizei n,...
boolean alIsExtensionPresent(String extname)
Entry point (through function pointer) to C language function: ALboolean alIsExtensionPresent(const...
void alGetSourcei(int source, int param, IntBuffer value)
Entry point (through function pointer) to C language function: void alGetSourcei(ALuint source,...
void alSourcei(int source, int param, int value)
Entry point (through function pointer) to C language function: void alSourcei(ALuint source,...
void alDeleteBuffers(int n, IntBuffer buffers)
Entry point (through function pointer) to C language function: void alDeleteBuffers(ALsizei n,...
String alGetString(int param)
Entry point (through function pointer) to C language function: const ALchar * alGetString(ALenum pa...