diff options
author | Sven Gothel <[email protected]> | 2023-05-17 08:05:38 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2023-05-17 08:05:38 +0200 |
commit | 270172bcbd91f96d4a38a3d73e23d744f57a25b8 (patch) | |
tree | 97c0dc23eef40fd2663540eb378056d7b81b6192 /src/java/jogamp/common | |
parent | 9301bf1854d91405319801b62c268e2ca09406e6 (diff) |
Promote common av (audio/video) classes (AudioSink, ..) from JOGL to GlueGen for cross module usage in JOAL, JOGL, ...
Supply AudioSink: NullAudioSink and JavaSoundAudioSink by GlueGen,
ALAudioSink is supplied via JOAL.
Diffstat (limited to 'src/java/jogamp/common')
-rw-r--r-- | src/java/jogamp/common/av/JavaSoundAudioSink.java | 267 | ||||
-rw-r--r-- | src/java/jogamp/common/av/NullAudioSink.java | 186 |
2 files changed, 453 insertions, 0 deletions
diff --git a/src/java/jogamp/common/av/JavaSoundAudioSink.java b/src/java/jogamp/common/av/JavaSoundAudioSink.java new file mode 100644 index 0000000..4e4fdec --- /dev/null +++ b/src/java/jogamp/common/av/JavaSoundAudioSink.java @@ -0,0 +1,267 @@ +/** + * Copyright 2013-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.av; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.SourceDataLine; + +import com.jogamp.common.av.AudioSink; + +/*** + * JavaSound Audio Sink + * <p> + * FIXME: Parameterize .. all configs .. best via an init-method, passing requested + * audio capabilities + * </p> + */ +public class JavaSoundAudioSink implements AudioSink { + + // Chunk of audio processed at one time + public static final int BUFFER_SIZE = 1000; + public static final int SAMPLES_PER_BUFFER = BUFFER_SIZE / 2; + private static final boolean staticAvailable; + + // Sample time values + // public static final double SAMPLE_TIME_IN_SECS = 1.0 / DEFAULT_SAMPLE_RATE; + // public static final double BUFFER_TIME_IN_SECS = SAMPLE_TIME_IN_SECS * SAMPLES_PER_BUFFER; + + private javax.sound.sampled.AudioFormat format; + private DataLine.Info info; + private SourceDataLine auline; + private int bufferCount; + private final byte [] sampleData = new byte[BUFFER_SIZE]; + private boolean available = false; + private AudioSink.AudioFormat chosenFormat = null; + + private volatile boolean playRequested = false; + private float volume = 1.0f; + + static { + boolean ok = false; + try { + ok = AudioSystem.getAudioFileTypes().length > 0; + } catch (final Throwable t) { + + } + staticAvailable=ok; + } + + public JavaSoundAudioSink() { + available = false; + if( !staticAvailable ) { + return; + } + available = true; + } + + @Override + public String toString() { + return "JavaSoundSink[avail "+available+", dataLine "+info+", source "+auline+", bufferCount "+bufferCount+ + ", chosen "+chosenFormat+", jsFormat "+format; + } + + @Override + public final float getPlaySpeed() { return 1.0f; } // FIXME + + @Override + public final boolean setPlaySpeed(final float rate) { + return false; // FIXME + } + + @Override + public final float getVolume() { + // FIXME + return volume; + } + + @Override + public final boolean setVolume(final float v) { + // FIXME + volume = v; + return true; + } + + @Override + public int getPreferredSampleRate() { + return DefaultFormat.sampleRate; + } + + @Override + public AudioSink.AudioFormat getPreferredFormat() { + return DefaultFormat; + } + + @Override + public final int getMaxSupportedChannels() { + return 2; + } + + @Override + public final boolean isSupported(final AudioSink.AudioFormat format) { + return true; + } + + @Override + public boolean init(final AudioSink.AudioFormat requestedFormat, final float frameDuration, final int initialQueueSize, final int queueGrowAmount, final int queueLimit) { + if( !staticAvailable ) { + return false; + } + // Create the audio format we wish to use + format = new javax.sound.sampled.AudioFormat(requestedFormat.sampleRate, requestedFormat.sampleSize, requestedFormat.channelCount, requestedFormat.signed, !requestedFormat.littleEndian); + + // Create dataline info object describing line format + info = new DataLine.Info(SourceDataLine.class, format); + + // Clear buffer initially + Arrays.fill(sampleData, (byte) 0); + try{ + // Get line to write data to + auline = (SourceDataLine) AudioSystem.getLine(info); + auline.open(format); + auline.start(); + System.out.println("JavaSound audio sink"); + available=true; + chosenFormat = requestedFormat; + } catch (final Exception e) { + available=false; + } + return true; + } + + @Override + public final AudioFormat getChosenFormat() { + return chosenFormat; + } + + @Override + public boolean isPlaying() { + return playRequested && auline.isRunning(); + } + + @Override + public void play() { + if( null != auline ) { + playRequested = true; + playImpl(); + } + } + private void playImpl() { + if( playRequested && !auline.isRunning() ) { + auline.start(); + } + } + + @Override + public void pause() { + if( null != auline ) { + playRequested = false; + auline.stop(); + } + } + + @Override + public void flush() { + if( null != auline ) { + playRequested = false; + auline.stop(); + auline.flush(); + } + } + + @Override + public final int getEnqueuedFrameCount() { + return 0; // FIXME + } + + @Override + public int getFrameCount() { + return 1; + } + + @Override + public int getQueuedFrameCount() { + return 0; + } + + @Override + public boolean isAvailable() { + return available; + } + + @Override + public void destroy() { + available = false; + chosenFormat = null; + // FIXEM: complete code! + } + + @Override + public AudioFrame enqueueData(final int pts, final ByteBuffer byteBuffer, final int byteCount) { + final byte[] bytes = new byte[byteCount]; + final int p = byteBuffer.position(); + byteBuffer.get(bytes, 0, byteCount); + byteBuffer.position(p); + + int written = 0; + int len; + int bytesLeft = byteCount; + while (bytesLeft > 0) { + len = auline.write(bytes, written, byteCount); + bytesLeft -= len; + written += len; + } + playImpl(); + return new AudioDataFrame(pts, chosenFormat.getBytesDuration(byteCount), byteBuffer, byteCount); + } + + @Override + public int getQueuedByteCount() { + return auline.getBufferSize() - auline.available(); + } + + @Override + public int getFreeFrameCount() { + return auline.available(); + } + + @Override + public int getQueuedTime() { + return getQueuedTimeImpl( getQueuedByteCount() ); + } + private final int getQueuedTimeImpl(final int byteCount) { + final int bytesPerSample = chosenFormat.sampleSize >>> 3; // /8 + return byteCount / ( chosenFormat.channelCount * bytesPerSample * ( chosenFormat.sampleRate / 1000 ) ); + } + + @Override + public final int getPTS() { return 0; } // FIXME +} diff --git a/src/java/jogamp/common/av/NullAudioSink.java b/src/java/jogamp/common/av/NullAudioSink.java new file mode 100644 index 0000000..c1b0d33 --- /dev/null +++ b/src/java/jogamp/common/av/NullAudioSink.java @@ -0,0 +1,186 @@ +/** + * Copyright 2013-2023 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.common.av; + +import java.nio.ByteBuffer; + +import com.jogamp.common.av.AudioSink; + +public class NullAudioSink implements AudioSink { + + private volatile float playSpeed = 1.0f; + private volatile boolean playRequested = false; + private volatile int playingPTS = AudioFrame.INVALID_PTS; + private float volume = 1.0f; + + private AudioFormat chosenFormat; + private boolean available; + + public NullAudioSink() { + available = true; + chosenFormat = null; + } + + @Override + public boolean isAvailable() { + return available; + } + + @Override + public final float getPlaySpeed() { return playSpeed; } + + @Override + public final boolean setPlaySpeed(float rate) { + if( Math.abs(1.0f - rate) < 0.01f ) { + rate = 1.0f; + } + playSpeed = rate; + return true; + } + + @Override + public final float getVolume() { + // FIXME + return volume; + } + + @Override + public final boolean setVolume(final float v) { + // FIXME + volume = v; + return true; + } + + @Override + public int getPreferredSampleRate() { + return DefaultFormat.sampleRate; + } + + @Override + public AudioFormat getPreferredFormat() { + return DefaultFormat; + } + + @Override + public final int getMaxSupportedChannels() { + return 8; + } + + @Override + public final boolean isSupported(final AudioFormat format) { + /** + * If we like to emulate constraints .. + * + if( format.planar || !format.littleEndian ) { + return false; + } + if( format.sampleRate != DefaultFormat.sampleRate ) { + return false; + } + */ + return true; + } + + @Override + public boolean init(final AudioFormat requestedFormat, final float frameDuration, final int initialQueueSize, final int queueGrowAmount, final int queueLimit) { + chosenFormat = requestedFormat; + return true; + } + + @Override + public final AudioFormat getChosenFormat() { + return chosenFormat; + } + + @Override + public boolean isPlaying() { + return playRequested; + } + + @Override + public void play() { + playRequested = true; + } + + @Override + public void pause() { + playRequested = false; + } + + @Override + public void flush() { + } + + @Override + public void destroy() { + available = false; + chosenFormat = null; + } + + @Override + public final int getEnqueuedFrameCount() { + return 0; + } + + @Override + public int getFrameCount() { + return 0; + } + + @Override + public int getQueuedFrameCount() { + return 0; + } + + @Override + public int getQueuedByteCount() { + return 0; + } + + @Override + public int getQueuedTime() { + return 0; + } + + @Override + public final int getPTS() { return playingPTS; } + + @Override + public int getFreeFrameCount() { + return 1; + } + + @Override + public AudioFrame enqueueData(final int pts, final ByteBuffer bytes, final int byteCount) { + if( !available || null == chosenFormat ) { + return null; + } + playingPTS = pts; + return null; + } +} |