// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.

package net.avadeaux.klipspringer.codec;

import java.io.IOException;
import java.io.Closeable;
import java.nio.ByteBuffer;
import static java.nio.ByteOrder.*;
import static net.avadeaux.klipspringer.codec.Device.BitLayout.*;

/** Intermediary for passing PCM data to a writer with matching sample rate, bit depth and number of
  * channels, but whose coding may differ in signedness, endianness, or padding (see <a
  * href="https://klipspringer.avadeaux.net/ten-standard-ways-of-representing-binary-numbers/">blog
  * post</a> about various formats). Wraps a byte buffer to provide additional operations on it,
  * without duplicating the operations that work on the wrapped buffer directly. Operates on
  * <em>direct</em> byte buffers (located in external memory), to make bulk data transfer efficient
  * from decoders and device readers implemented using external libraries, and from files read using
  * <a href="https://docs.oracle.com/en/java/javase/16/core/java-nio.html">NIO</a>. It is less
  * efficient when input comes via a {@link javax.sound.sampled.TargetDataLine}, {@link
  * javax.sound.sampled.AudioInputStream}, or plain {@link java.io.InputStream}, which all operate
  * on JVM-internal byte arrays.
  */
public abstract class PcmBuffer implements Closeable {
    static { Library.init(); }

    final ByteBuffer buf;
    final PcmFormat format;
    final int sign;             // 1 in sign bit position only
    final int flip;             // sign if format unsigned, zero otherwise
    final int shift;            // shifted positions (zero unless MSB)
    final int mask;             // 1 in significant bit positions; 0 in higher if LSB, 1 otherwise
    final long handle;          // native struct address

    private boolean closed = false;

    private PcmBuffer(ByteBuffer buf, PcmFormat format, Device.BitLayout lout, int ssbits) {
        this.buf = buf;
        buf.order(format.order());
        this.format = format;
        sign = 1 << format.bips()-1;
        flip = format.signed() ? 0 : sign;
        shift = lout == MSB ? ssbits-format.bips() : 0;
        mask = -1 >>> (lout == LSB ? 32-format.bips() : 0);
        handle = buf.isDirect()
            ? create(buf, format.channels(), format.signed(), format.bigend(), sign, lout == LSBX ? sign : 0, shift)
            : 0;
    }

    /** Gets the output format, used in writing to the wrapped buffer. */
    public PcmFormat format() { return format; }

    /** See {@link Closeable#close()}. */
    public void close() {
        if (!closed) {
            closed = true;
            if (handle != 0) { free(handle); }
        }
    }

    // Overridden by native methods in subclasses.
    abstract void put(long handle, ByteBuffer data, int dataPos, boolean dataSigned, int dataSs, boolean dataBigend, int bufPos, int samples);

    /** Gets the underlying byte buffer. */
    public final ByteBuffer byteBuffer() { return buf; }

    /** Reads a sample as a signed integer at the current position, and advances position. */
    public final int getSample() {
        int p = buf.position();
        buf.position(p+format.ss());
        return getSample(p);
    }

    /** Reads a sample as a signed integer given the position of its first byte. */
    public abstract int getSample(int index);

    /** Writes a signed integer sample value at the current position, and advances position. */
    public final void putSample(int value) {
        int p = buf.position();
        buf.position(p+format.ss());
        putSample(p, value);
    }

    long sa = 0;

    /** Writes a signed integer sample value given the position of its first byte. */
    public abstract void putSample(int index, int value);

    /** Transfers data from a byte buffer into this buffer. The formats must match in quality, but
      * may differ in signedness, endianness and frame size. If the data buffer is not direct, this
      * method copies its contents to a temporary direct buffer before transferring to the internal
      * buffer.
      */
    public final void put(ByteBuffer data, PcmFormat dataFormat) throws IOException {
        if (!buf.isDirect()) { throw new UnsupportedOperationException("put unsupported for nondirect buffer"); }
        if (!data.isDirect()) {
            ByteTransfer.writeDirect(data, new PcmWriter() {
                    public boolean write(ByteBuffer data) throws IOException {
                        put(data, dataFormat);
                        return true;
                    }
                    public void close() { }
                }, dataFormat.fs());
            return;
        }
        if (data.order() != dataFormat.order()) {
            throw new IllegalArgumentException("Byte order mismatch between buffer and format");
        }
        if (!dataFormat.matchesQuality(format)) {
            throw new IllegalArgumentException("Format "+dataFormat+" not compaticle with "+format);
        }
        int dataPos = data.position(), bufPos = buf.position();
        int n = data.remaining(), dss = dataFormat.ss();
        data.position(dataPos + n);
        buf.position(bufPos + n/dss*format.ss());
        put(handle, data, dataPos, dataFormat.signed(), dss, dataFormat.bigend(), bufPos, n/dss);
    }

    /** Mixes the samples in a segment of the buffer so that all channels get the same value (the
      * arithmetic mean of the original values.
      */
    public final void monomix(int pos, int frames) {
        if (format.channels() > 1) {
            monomix(handle, pos, frames);
        }
    }

    abstract void monomix(long handle, int pos, int frames);

    /** Constructor that wraps a byte buffer as a PCM buffer for the given output format and
      * layout. Sets the byte order of the byte buffer to match the format.
      */
    public static PcmBuffer of(ByteBuffer buf, PcmFormat format, Device.BitLayout lout) {
        if (lout == LSBX && !format.signed()) {
            throw new IllegalArgumentException("Unisgned format cannot have extended sign");
        }

        switch (format.ss()) {
        case 1:
            return new PcmBuffer1(buf, format, lout);
        case 2:
            return new PcmBuffer2(buf, format, lout);
        case 3:
            return new PcmBuffer3(buf, format, lout);
        case 4:
            return new PcmBuffer4(buf, format, lout);
        default:
            throw new IllegalArgumentException("Illegal sample size in bytes: "+format.ss());
        }
    }

    private native long create(ByteBuffer buf, int channels, boolean signed, boolean bigend, int sign, int xsign, int shift);
    private native void free(long handle);

    // 1 -------------------------------------------------------------------------------------------

    private final static class PcmBuffer1 extends PcmBuffer {
        PcmBuffer1(ByteBuffer buf, PcmFormat format, Device.BitLayout lout) { super(buf, format, lout, 8); }

        public int getSample(int index) {
            int v = (buf.get(index) & 0xff) >>> shift ^ flip;
            return v | -(v & sign);
        }

        public void putSample(int index, int value) {
            buf.put(index, (byte) ((value ^ flip) << shift & mask));
        }

        native void put(long handle, ByteBuffer data, int dataPos, boolean dataSigned, int dataSs, boolean dataBigend, int bufPos, int samples);
        native void monomix(long handle, int pos, int frames);
    }

    // 2 -------------------------------------------------------------------------------------------

    private final static class PcmBuffer2 extends PcmBuffer {
        PcmBuffer2(ByteBuffer buf, PcmFormat format, Device.BitLayout lout) { super(buf, format, lout, 16); }

        public int getSample(int index) {
            int v = (buf.getShort(index) & 0xffff) >>> shift ^ flip;
            return v | -(v & sign);
        }

        public void putSample(int index, int value) {
            buf.putShort(index, (short) ((value ^ flip) << shift & mask));
        }

        native void put(long handle, ByteBuffer data, int dataPos, boolean dataSigned, int dataSs, boolean dataBigend, int bufPos, int samples);
        native void monomix(long handle, int pos, int frames);
    }

    // 3 -------------------------------------------------------------------------------------------

    private final static class PcmBuffer3 extends PcmBuffer {
        final int b0, b2;

        PcmBuffer3(ByteBuffer buf, PcmFormat format, Device.BitLayout lout) {
            super(buf, format, lout, 24);
            if (format.bigend()) {
                b0 = 16;
                b2 = 0;
            } else {
                b0 = 0;
                b2 = 16;
            }
        }

        public int getSample(int index) {
            int v = ((buf.get(index) & 0xff) << b0 | (buf.get(index+1) & 0xff) << 8 | (buf.get(index+2) & 0xff) << b2) >>> shift ^ flip;
            return v | -(v & sign);
        }

        public void putSample(int index, int value) {
            int v = (value ^ flip) << shift & mask;
            buf.put(index, (byte) (v >>> b0)); buf.put(index+1, (byte) (v >>> 8)); buf.put(index+2, (byte) (v >>> b2));
        }

        native void put(long handle, ByteBuffer data, int dataPos, boolean dataSigned, int dataSs, boolean dataBigend, int bufPos, int samples);
        native void monomix(long handle, int pos, int frames);
    }

    // 4 -------------------------------------------------------------------------------------------

    private final static class PcmBuffer4 extends PcmBuffer {
        PcmBuffer4(ByteBuffer buf, PcmFormat format, Device.BitLayout lout) { super(buf, format, lout, 32); }

        public int getSample(int index) {
            int v = buf.getInt(index) >>> shift ^ flip;
            return v | -(v & sign);
        }

        public void putSample(int index, int value) {
            buf.putInt(index, (value ^ flip) << shift & mask);
        }

        native void put(long handle, ByteBuffer data, int dataPos, boolean dataSigned, int dataSs, boolean dataBigend, int bufPos, int samples);
        native void monomix(long handle, int pos, int frames);
    }
}

Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home