AudioFiles.java
// SPDX-License-Identifier: Apache-2.0
package com.shengzhiai.yugu.stcompat.internal;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
/** Audio file checks before upload: existence, size, WAV duration, and the part Content-Type. */
public final class AudioFiles {
private AudioFiles() {
}
/** Content type and file extension of an audio file. */
public static final class Kind {
public final String contentType;
public final String extension;
/** Headerless PCM that must be wrapped into WAV before upload. */
public final boolean rawPcm;
Kind(String contentType, String extension, boolean rawPcm) {
this.contentType = contentType;
this.extension = extension;
this.rawPcm = rawPcm;
}
}
/** Detects the type from magic bytes, then the extension, then the requested audioType. */
public static Kind kind(File f, String audioType) throws IOException {
byte[] h = new byte[12];
int n = 0;
InputStream in = new FileInputStream(f);
try {
int r;
while (n < h.length && (r = in.read(h, n, h.length - n)) > 0) {
n += r;
}
} finally {
in.close();
}
if (n >= 12 && ascii(h, 0, 4).equals("RIFF") && ascii(h, 8, 4).equals("WAVE")) {
return new Kind("audio/wav", "wav", false);
}
if (n >= 4 && ascii(h, 0, 4).equals("OggS")) {
return new Kind("audio/ogg", "ogg", false);
}
if (n >= 5 && ascii(h, 0, 5).equals("#!AMR")) {
return new Kind("audio/amr", "amr", false);
}
if (n >= 4 && ascii(h, 0, 4).equals("fLaC")) {
return new Kind("audio/flac", "flac", false);
}
if (n >= 3 && ascii(h, 0, 3).equals("FLV")) {
return new Kind("video/x-flv", "flv", false);
}
if (n >= 8 && ascii(h, 4, 4).equals("ftyp")) {
return new Kind("audio/mp4", "m4a", false);
}
if (n >= 3 && ascii(h, 0, 3).equals("ID3")) {
return new Kind("audio/mpeg", "mp3", false);
}
if (n >= 2 && (h[0] & 0xFF) == 0xFF && (h[1] & 0xF6) == 0xF0) {
return new Kind("audio/aac", "aac", false);
}
if (n >= 2 && (h[0] & 0xFF) == 0xFF && (h[1] & 0xE0) == 0xE0) {
return new Kind("audio/mpeg", "mp3", false);
}
String ext = extension(f.getName());
String type = audioType == null ? "" : audioType.trim().toLowerCase(Locale.ROOT);
if (ext.equals("pcm") || ext.equals("raw") || type.equals("pcm") || type.equals("raw")) {
return new Kind("audio/wav", "wav", true);
}
if (ext.equals("mp3") || type.equals("mp3")) {
return new Kind("audio/mpeg", "mp3", false);
}
if (ext.equals("wav") || ext.isEmpty() && type.equals("wav")) {
// a .wav name without a RIFF header is raw PCM
return new Kind("audio/wav", "wav", true);
}
return new Kind("application/octet-stream", ext.isEmpty() ? "bin" : ext, false);
}
/**
* errId of the local pre-check, 0 when the file may be uploaded: 60001 missing or unreadable,
* 60002 empty, 60005 WAV shorter than 1 s, 60009 larger than 50 MB or WAV longer than 300 s.
*/
public static int precheck(File f, boolean wav) {
if (f == null || !f.isFile() || !f.canRead()) {
return CompatErrIds.AUDIO_FILE_MISSING;
}
long size = f.length();
if (size == 0) {
return CompatErrIds.AUDIO_EMPTY;
}
if (size > CompatConfig.MAX_AUDIO_BYTES) {
return CompatErrIds.AUDIO_TOO_LARGE;
}
if (wav) {
Wav.Info info;
try {
info = Wav.parse(f);
} catch (IOException e) {
return CompatErrIds.AUDIO_FILE_MISSING;
}
if (info != null) {
if (info.dataLength == 0) {
return CompatErrIds.AUDIO_EMPTY;
}
if (info.durationSeconds() < 1.0) {
return CompatErrIds.AUDIO_TOO_SHORT;
}
if (info.durationSeconds() > CompatConfig.MAX_AUDIO_SECONDS) {
return CompatErrIds.AUDIO_TOO_LARGE;
}
}
}
return 0;
}
static String extension(String name) {
int dot = name.lastIndexOf('.');
return dot < 0 ? "" : name.substring(dot + 1).toLowerCase(Locale.ROOT);
}
private static String ascii(byte[] b, int off, int len) {
return new String(b, off, len, Codec.US_ASCII);
}
}