WavStreamStripper.java

// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;

import java.io.ByteArrayOutputStream;

/**
 * Stream mode accepts raw PCM16. When the fed data starts with a RIFF/WAVE header (an app
 * feeding a WAV file), the header is dropped so the PCM is not scored as noise.
 */
public final class WavStreamStripper {
    private static final int MAX_HEADER = 4096;

    private ByteArrayOutputStream pending = new ByteArrayOutputStream();
    private boolean decided;

    /** Returns the PCM part of {@code data[0..len)}, possibly empty while a header is buffered. */
    public byte[] feed(byte[] data, int len) {
        if (decided) {
            return copy(data, len);
        }
        pending.write(data, 0, len);
        byte[] all = pending.toByteArray();
        if (all.length < 12) {
            if (!startsLikeRiff(all)) {
                return finish(all, 0);
            }
            return new byte[0];
        }
        if (!(ascii(all, 0, 4).equals("RIFF") && ascii(all, 8, 4).equals("WAVE"))) {
            return finish(all, 0);
        }
        Wav.Info info = Wav.parse(all, all.length, Long.MAX_VALUE / 2);
        if (info != null) {
            return finish(all, (int) info.dataOffset);
        }
        if (all.length >= MAX_HEADER) {
            return finish(all, 0);
        }
        return new byte[0];
    }

    /** Bytes still buffered while undecided (a stream shorter than a header). */
    public byte[] flush() {
        if (decided) {
            return new byte[0];
        }
        byte[] all = pending.toByteArray();
        boolean riff = all.length >= 4 && ascii(all, 0, 4).equals("RIFF");
        return finish(all, riff ? all.length : 0);
    }

    private byte[] finish(byte[] all, int skip) {
        decided = true;
        pending = null;
        int n = Math.max(0, all.length - skip);
        byte[] out = new byte[n];
        System.arraycopy(all, Math.min(skip, all.length), out, 0, n);
        return out;
    }

    private static boolean startsLikeRiff(byte[] b) {
        String r = "RIFF";
        for (int i = 0; i < Math.min(4, b.length); i++) {
            if (b[i] != r.charAt(i)) {
                return false;
            }
        }
        return true;
    }

    private static byte[] copy(byte[] data, int len) {
        byte[] out = new byte[len];
        System.arraycopy(data, 0, out, 0, len);
        return out;
    }

    private static String ascii(byte[] b, int off, int len) {
        return new String(b, off, len, Codec.US_ASCII);
    }
}