From 742cf0cd053f968cbf291ed367d4568c12d8bde2 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sun, 21 May 2023 16:44:17 +0200 Subject: AudioFormat/AudioSink: Use float in seconds for duration to avoid losing precision when dealing with stats, averages etc --- src/java/com/jogamp/common/av/AudioFormat.java | 46 ++++++++++++++++---------- src/java/com/jogamp/common/av/AudioSink.java | 24 +++++++++++--- src/java/com/jogamp/common/av/TimeFrameI.java | 8 +++++ 3 files changed, 56 insertions(+), 22 deletions(-) (limited to 'src/java/com/jogamp/common') diff --git a/src/java/com/jogamp/common/av/AudioFormat.java b/src/java/com/jogamp/common/av/AudioFormat.java index 870ccd2..2802f31 100644 --- a/src/java/com/jogamp/common/av/AudioFormat.java +++ b/src/java/com/jogamp/common/av/AudioFormat.java @@ -79,48 +79,58 @@ public class AudioFormat { // /** - * Returns the byte size of the given milliseconds + * Returns the byte size of the given duration in seconds * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. + *
+     *  final float bytesPerSample = sampleSize/8;
+     *  return Math.round( duration * channelCount * bytesPerSample * sampleRate );
+     * 
*

* Time -> Byte Count *

+ * @param duration duration in seconds */ - public final int getDurationsByteSize(final int millisecs) { - final int bytesPerSample = sampleSize >>> 3; // /8 - return Math.round( millisecs * ( (float)channelCount * (float)bytesPerSample * ( sampleRate / 1000f ) ) ); + public final int getDurationsByteSize(final float duration) { + final float bytesPerSample = sampleSize >>> 3; // /8 + return Math.round( duration * channelCount * bytesPerSample * sampleRate ); } /** - * Returns the duration in milliseconds of the given byte count + * Returns the duration in seconds of the given byte count * according to {@link #sampleSize}, {@link #channelCount} and {@link #sampleRate}. + *
+     *  final float bytesPerSample = sampleSize/8;
+     *  return byteCount / ( channelCount * bytesPerSample * sampleRate )
+     * 
*

* Byte Count -> Time *

+ * @param byteCount size in bytes */ - public final int getBytesDuration(final int byteCount) { - final int bytesPerSample = sampleSize >>> 3; // /8 - return Math.round( byteCount / ( (float)channelCount * (float)bytesPerSample * ( sampleRate / 1000f ) ) ); + public final float getBytesDuration(final int byteCount) { + final float bytesPerSample = sampleSize >>> 3; // /8 + return byteCount / ( channelCount * bytesPerSample * sampleRate ); } /** - * Returns the duration in milliseconds of the given sample count per frame and channel + * Returns the duration in seconds of the given sample count per frame and channel * according to the {@link #sampleRate}, i.e. *
-     *    round( ( 1000f * sampleCount ) / sampleRate )
+     *    (float)sampleCount / sampleRate
      * 
*

* Sample Count -> Time *

* @param sampleCount sample count per frame and channel */ - public final int getSamplesDuration(final int sampleCount) { - return Math.round( ( 1000f * sampleCount ) / sampleRate ); + public final float getSamplesDuration(final int sampleCount) { + return (float)sampleCount / sampleRate; } /** - * Returns the rounded frame count of the given milliseconds and frame duration. + * Returns the rounded frame count of the given duration and frame duration, both in seconds. *
-     *     Math.max( 1, millisecs / frameDuration + 0.5f )
+     *     Math.max(1, Math.round( duration / frameDuration ))
      * 
*

* Note: frameDuration can be derived by sample count per frame and channel @@ -129,13 +139,13 @@ public class AudioFormat { *

* Frame Time -> Frame Count *

- * @param millisecs time in milliseconds - * @param frameDuration duration per frame in milliseconds. + * @param duration duration in seconds + * @param frameDuration duration per frame in seconds, i.e. 1/frame_rate * @see #getSamplesDuration(int) * @see #getBytesDuration(int) */ - public final int getFrameCount(final int millisecs, final int frameDuration) { - return Math.max(1, (int) ( (float)millisecs / (float)frameDuration + 0.5f )); + public final int getFrameCount(final float duration, final float frameDuration) { + return Math.max(1, Math.round( duration / frameDuration )); } /** diff --git a/src/java/com/jogamp/common/av/AudioSink.java b/src/java/com/jogamp/common/av/AudioSink.java index 704c2a6..b4738b3 100644 --- a/src/java/com/jogamp/common/av/AudioSink.java +++ b/src/java/com/jogamp/common/av/AudioSink.java @@ -65,9 +65,18 @@ public interface AudioSink { public static abstract class AudioFrame extends TimeFrameI { protected int byteSize; + /** + * Ctor w/ zero duration, {@link #INVALID_PTS} and zero byte size + */ public AudioFrame() { this.byteSize = 0; } + /** + * Create a new instance + * @param pts frame pts in milliseconds + * @param duration frame duration in milliseconds + * @param byteCount size in bytes + */ public AudioFrame(final int pts, final int duration, final int byteCount) { super(pts, duration); this.byteSize=byteCount; @@ -89,6 +98,13 @@ public interface AudioSink { public static class AudioDataFrame extends AudioFrame { protected final ByteBuffer data; + /** + * Create a new instance + * @param pts frame pts in milliseconds + * @param duration frame duration in milliseconds + * @param bytes audio data + * @param byteCount size in bytes + */ public AudioDataFrame(final int pts, final int duration, final ByteBuffer bytes, final int byteCount) { super(pts, duration, byteCount); if( byteCount > bytes.remaining() ) { @@ -343,13 +359,13 @@ public interface AudioSink { public int getQueuedByteCount(); /** - * Returns the current queued frame time in milliseconds for playing. + * Returns the current queued frame time in seconds for playing. *

* {@link #init(AudioFormat, float, int, int, int)} must be called first. *

* @see #init(AudioFormat, float, int, int, int) */ - public int getQueuedTime(); + public float getQueuedTime(); /** * Returns average frame duration last assessed @ {@link #enqueueData(int, ByteBuffer, int)} when queue was full. @@ -357,7 +373,7 @@ public interface AudioSink { * avgFrameDuration = {@link #getQueuedTime()} / {@link #getQueuedFrameCount()} * */ - public int getAvgFrameDuration(); + public float getAvgFrameDuration(); /** * Return the current audio presentation timestamp (PTS) in milliseconds. @@ -381,7 +397,7 @@ public interface AudioSink { *

* {@link #init(AudioFormat, float, int, int, int)} must be called first. *

- * @param pts presentation time stamp for the newly enqueued {@link AudioFrame} + * @param pts presentation time stamp in milliseconds for the newly enqueued {@link AudioFrame} * @param bytes audio data for the newly enqueued {@link AudioFrame} * @returns the enqueued internal {@link AudioFrame}. * @see #init(AudioFormat, float, int, int, int) diff --git a/src/java/com/jogamp/common/av/TimeFrameI.java b/src/java/com/jogamp/common/av/TimeFrameI.java index a528c09..400618f 100644 --- a/src/java/com/jogamp/common/av/TimeFrameI.java +++ b/src/java/com/jogamp/common/av/TimeFrameI.java @@ -56,10 +56,18 @@ public class TimeFrameI { protected int pts; protected int duration; + /** + * Ctor w/ zero duration and {@link #INVALID_PTS}. + */ public TimeFrameI() { pts = INVALID_PTS; duration = 0; } + /** + * Create a new instance + * @param pts frame pts in milliseconds + * @param duration frame duration in milliseconds + */ public TimeFrameI(final int pts, final int duration) { this.pts = pts; this.duration = duration; -- cgit v1.2.3