jaudiotagger工具包获取、设置音频信息

jaudiotagger 是一个用于读取和修改音频文件元数据(metadata)的 Java 库。它支持多种音频格式,包括但不限于:

  • MP3

  • FLAC

  • OGG

  • WAV

  • M4A/AAC

这个库允许你访问并编辑音频文件中的标签信息,例如:

  • 歌曲标题 (Title)

  • 艺术家 (Artist)

  • 专辑 (Album)

  • 封面图片 (Cover Art)

  • 音轨号 (Track Number)

  • 年份 (Year)

引入Jar包

Maven

<dependency>
    <groupId>org</groupId>
    <artifactId>jaudiotagger</artifactId>
    <version>2.0.3</version>
 </dependency>

Gradle

implementation "org:jaudiotagger:2.0.3"

读取、设置.mp3文件的元数据信息

说明

jaudiotagger 包中的 org.jaudiotagger.tag.FieldKey 枚举类中包含了音频文件的各个元数据字段,读写时请使用枚举类进行操作。

示例代码

public class Mp3Test {

    public static void main(String[] args) throws IOException {
        String filePath = "D:\\test.mp3";
        HashMap<FieldKey, String> fieldMap = new HashMap<>(8);
        fieldMap.put(FieldKey.ARTIST, "朝花夕拾");
        fieldMap.put(FieldKey.YEAR, "2025");
        fieldMap.put(FieldKey.TITLE, "朝花夕拾");
        writeMP3FileTagInfo(filePath, fieldMap);
        Map<FieldKey, String> result = readMP3FileTagInfo(filePath, Arrays.asList(FieldKey.ARTIST, FieldKey.TITLE, FieldKey.YEAR));
        System.out.println(result);
        // {ARTIST=朝花夕拾, TITLE=朝花夕拾, YEAR=2025}
    }

    /**
     * 读取MP3文件元数据信息
     * @param filePath 文件路径
     * @param fieldKeys 需要读取的元数据字段
     * @return 元数据信息
     */
    public static Map<FieldKey, String> readMP3FileTagInfo(String filePath, List<FieldKey> fieldKeys) {
        try {
            MP3FileReader fileReader = new MP3FileReader();
            AudioFile read = fileReader.read(new File(filePath));
            Tag fileTag = read.getTag();
            HashMap<FieldKey, String> result = new HashMap<>(8);
            for (FieldKey fieldKey : fieldKeys) {
                result.put(fieldKey, fileTag.getFirst(fieldKey));
            }
            return result;
        } catch (IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置指定元数据信息
     * @param filePath 文件路径
     * @param fieldMap 元数据信息
     */
    public static void writeMP3FileTagInfo(String filePath, Map<FieldKey, String> fieldMap) {
        try {
            MP3FileReader fileReader = new MP3FileReader();
            AudioFile read = fileReader.read(new File(filePath));
            Tag fileTag = read.getTag();
            for (FieldKey fieldKey : fieldMap.keySet()) {
                fileTag.setField(fieldKey, fieldMap.get(fieldKey));
            }
            read.commit();
        } catch (IOException | TagException | ReadOnlyFileException | InvalidAudioFrameException |
                 CannotWriteException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

mp3_author.png

设置.wav文件作者(艺术家)信息

示例代码

public class WavTest {

    public static void main(String[] args) throws IOException {
        // 原始WAV文件路径
        String inputFile = "D:\\test.wav";
        addMetadataToWavInPlace(inputFile, "朝花夕拾");
    }

    /**
     * 直接在原WAV文件末尾添加元数据信息(不创建新文件)
     *
     * @param filePath WAV文件路径
     * @param author   作者信息
     * @throws IOException e
     */
    public static void addMetadataToWavInPlace(String filePath, String author) throws IOException {
        try (RandomAccessFile raf = new RandomAccessFile(filePath, "rw"); ByteArrayOutputStream infoStream = new ByteArrayOutputStream()) {
            // 获取文件长度
            long fileLength = raf.length();
            // 定位到文件末尾
            raf.seek(fileLength);
            // 写入LIST标识符
            infoStream.write("LIST".getBytes());
            // 占位符长度
            byte[] lengthPlaceholder = new byte[4];
            infoStream.write(lengthPlaceholder);
            // 写入INFO标识符
            infoStream.write("INFO".getBytes());
            // 写入IART chunk (艺术家/作者) - 改进编码处理
            byte[] authorBytes = author.getBytes("GBK");
            writeChunk(infoStream, "IART", authorBytes);

            // 获取INFO chunk数据
            byte[] infoData = infoStream.toByteArray();
            // 更新长度字段
            int dataLength = infoData.length - 8;
            ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
            lengthBuffer.order(ByteOrder.LITTLE_ENDIAN);
            lengthBuffer.putInt(dataLength);
            System.arraycopy(lengthBuffer.array(), 0, infoData, 4, 4);
            // 写入INFO chunk到原文件末尾
            raf.write(infoData);
            // 更新RIFF chunk的总长度
            updateRiffChunkSize(raf, fileLength + infoData.length);
        }
    }

    /**
     * 更新RIFF chunk的总长度
     */
    private static void updateRiffChunkSize(RandomAccessFile raf, long newFileSize) throws IOException {
        // RIFF长度字段位置
        raf.seek(4);
        ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
        lengthBuffer.order(ByteOrder.LITTLE_ENDIAN);
        // RIFF长度不包括前8字节
        lengthBuffer.putInt((int) (newFileSize - 8));
        raf.write(lengthBuffer.array());
    }


    private static void writeChunk(ByteArrayOutputStream stream, String id, byte[] data) throws IOException {
        stream.write(id.getBytes());
        // 写入数据长度 - 对于文本字段需要包含null终止符
        int actualLength = data.length + 1; // 加上null终止符
        // 如果加上null后长度为奇数,需要再加一个填充字节
        if (actualLength % 2 != 0) {
            actualLength++;
        }
        ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
        lengthBuffer.order(ByteOrder.LITTLE_ENDIAN);
        lengthBuffer.putInt(actualLength);
        stream.write(lengthBuffer.array());
        // 写入实际数据
        stream.write(data);
        // 写入null终止符
        stream.write(0);

        // 如果数据长度+null终止符后为奇数,需要填充一个字节以保持偶数字节对齐
        if ((data.length + 1) % 2 != 0) {
            // 填充字节
            stream.write(0);
        }
    }
}

执行完成后可以看到正确的设置了作者(艺术家)信息:

wav_author.png