안드로이드에서 Java를 이용한 PCM 오디오 형식 변환: MP3 및 WAV 생성

최근 음성 합성 프로젝트를 진행하면서 PCM 형식의 오디오 파일을 MP3나 WAV 형식으로 변환해야 할 필요가 있었습니다. 이 과정을 기록해 둡니다.

Java를 이용한 PCM 오디오 파일을 MP3 형식으로 변환

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * PCM을 MP3로 변환하는 클래스
 *
 * @author 기술 개발자
 * @since 2023-05-15
 */
public class AudioFormatConverter {

    public static void main(String[] args) throws Exception {
        //convertAudioFiles("audio/sample.pcm", "audio/sample.mp3");
        convertPcmToMp3("test.pcm", "test.mp3");
    }

    /**
     * PCM 파일을 MP3 형식으로 변환
     * 
     * @param sourcePath 원본 PCM 파일 경로
     * @param targetPath 대상 MP3 파일 경로
     * @throws IOException 입출력 예외 처리
     */
    public static String convertPcmToMp3(String sourcePath, String targetPath) throws IOException {
        FileInputStream audioInput = new FileInputStream(sourcePath);
        FileOutputStream audioOutput = new FileOutputStream(targetPath);

        // 파일 크기 계산
        byte[] buffer = new byte[4096];
        int bytesRead = audioInput.read(buffer);
        int pcmDataSize = 0;
        
        while (bytesRead != -1) {
            pcmDataSize += bytesRead;
            bytesRead = audioInput.read(buffer);
        }
        audioInput.close();
        
        // 헤더 정보 설정 (16비트 모노, 16000Hz)
        AudioHeader headerInfo = new AudioHeader();
        headerInfo.fileSize = pcmDataSize + (44 - 8);
        headerInfo.formatHeaderLength = 16;
        headerInfo.bitsPerSample = 16;
        headerInfo.channelCount = 1;
        headerInfo.formatTag = 0x0001;
        headerInfo.sampleRate = 16000; // 표준 속도는 8000Hz, 여기서는 2배 빠른 속도
        headerInfo.blockAlignment = (short) (headerInfo.channelCount * headerInfo.bitsPerSample / 8);
        headerInfo.averageBytesPerSecond = headerInfo.blockAlignment * headerInfo.sampleRate;
        headerInfo.dataHeaderLength = pcmDataSize;

        byte[] headerData = headerInfo.getHeader();

        // WAV 헤더는 44바이트여야 함
        assert headerData.length == 44;
        
        // 헤더 작성
        audioOutput.write(headerData, 0, headerData.length);
        
        // 데이터 스트림 작성
        audioInput = new FileInputStream(sourcePath);
        bytesRead = audioInput.read(buffer);
        
        while (bytesRead != -1) {
            audioOutput.write(buffer, 0, bytesRead);
            bytesRead = audioInput.read(buffer);
        }
        
        audioInput.close();
        audioOutput.close();
        System.out.println("PCM에서 MP3 변환 완료!");
        return "성공";
    }
}

Java를 이용한 PCM 오디오 파일을 WAV 형식으로 변환

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * PCM을 WAV로 변환하는 클래스
 *
 * @author 기술 개발자
 * @since 2023-05-15
 */
public class PcmToWavConverter {

    public static void main(String[] args) throws Exception {
        //convertAudioFiles("audio/sample.pcm", "audio/sample.wav");
        convertPcmToWav("test.pcm", "test.wav");
    }
    
    /**
     * PCM 파일을 WAV 형식으로 변환
     * 
     * @param sourcePath 원본 PCM 파일 경로
     * @param targetPath 대상 WAV 파일 경로
     * @throws IOException 입출력 예외 처리
     */
    public static void convertPcmToWav(String sourcePath, String targetPath) throws IOException {
        FileInputStream audioInput = new FileInputStream(sourcePath);
        FileOutputStream audioOutput = new FileOutputStream(targetPath);

        // 파일 크기 계산
        byte[] buffer = new byte[4096];
        int bytesRead = audioInput.read(buffer);
        int pcmDataSize = 0;
        
        while (bytesRead != -1) {
            pcmDataSize += bytesRead;
            bytesRead = audioInput.read(buffer);
        }
        audioInput.close();
        
        // 헤더 정보 설정 (16비트 스테레오, 8000Hz)
        AudioHeader headerInfo = new AudioHeader();
        headerInfo.fileSize = pcmDataSize + (44 - 8);
        headerInfo.formatHeaderLength = 16;
        headerInfo.bitsPerSample = 16;
        headerInfo.channelCount = 2;
        headerInfo.formatTag = 0x0001;
        headerInfo.sampleRate = 8000;
        headerInfo.blockAlignment = (short) (headerInfo.channelCount * headerInfo.bitsPerSample / 8);
        headerInfo.averageBytesPerSecond = headerInfo.blockAlignment * headerInfo.sampleRate;
        headerInfo.dataHeaderLength = pcmDataSize;

        byte[] headerData = headerInfo.getHeader();

        // WAV 헤더는 44바이트여야 함
        assert headerData.length == 44;
        
        // 헤더 작성
        audioOutput.write(headerData, 0, headerData.length);
        
        // 데이터 스트림 작성
        audioInput = new FileInputStream(sourcePath);
        bytesRead = audioInput.read(buffer);
        
        while (bytesRead != -1) {
            audioOutput.write(buffer, 0, bytesRead);
            bytesRead = audioInput.read(buffer);
        }
        
        audioInput.close();
        audioOutput.close();
        System.out.println("변환 완료!");
    }
}
<strong>AudioHeader 클래스<br></br></strong>
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * WAV 오디오 헤더 생성 클래스
 *
 * @author 기술 개발자
 * @since 2023-05-15
 */
public class AudioHeader {
    public final char riffIdentifier[] = {'R', 'I', 'F', 'F'};
    public int fileSize;
    public char waveTag[] = {'W', 'A', 'V', 'E'};
    public char formatHeaderId[] = {'f', 'm', 't', ' '};
    public int formatHeaderLength;
    public short formatCode;
    public short channelCount;
    public int sampleRate;
    public int averageBytesPerSecond;
    public short blockAlignment;
    public short bitsPerSample;
    public char dataHeaderId[] = {'d', 'a', 't', 'a'};
    public int dataHeaderLength;

    public byte[] getHeader() throws IOException {
        ByteArrayOutputStream headerStream = new ByteArrayOutputStream();
        writeChars(headerStream, riffIdentifier);
        writeInteger(headerStream, fileSize);
        writeChars(headerStream, waveTag);
        writeChars(headerStream, formatHeaderId);
        writeInteger(headerStream, formatHeaderLength);
        writeShort(headerStream, formatCode);
        writeShort(headerStream, channelCount);
        writeInteger(headerStream, sampleRate);
        writeInteger(headerStream, averageBytesPerSecond);
        writeShort(headerStream, blockAlignment);
        writeShort(headerStream, bitsPerSample);
        writeChars(headerStream, dataHeaderId);
        writeInteger(headerStream, dataHeaderLength);
        
        headerStream.flush();
        byte[] headerBytes = headerStream.toByteArray();
        headerStream.close();
        return headerBytes;
    }

    private void writeShort(ByteArrayOutputStream stream, int value) throws IOException {
        byte[] shortBytes = new byte[2];
        shortBytes[1] = (byte) ((value << 16) >> 24);
        shortBytes[0] = (byte) ((value << 24) >> 24);
        stream.write(shortBytes);
    }

    private void writeInteger(ByteArrayOutputStream stream, int number) throws IOException {
        byte[] intBytes = new byte[4];
        intBytes[3] = (byte) (number >> 24);
        intBytes[2] = (byte) ((number << 8) >> 24);
        intBytes[1] = (byte) ((number << 16) >> 24);
        intBytes[0] = (byte) ((number << 24) >> 24);
        stream.write(intBytes);
    }

    private void writeChars(ByteArrayOutputStream stream, char[] characters) {
        for (char c : characters) {
            stream.write(c);
        }
    }
}

태그: Android java PCM MP3 WAV

7월 5일 01:08에 게시됨