PCM 데이터를 사용하는 음성 인식 서비스에서 반환된 원시 오디오를 AMR 형식으로 변환해야 하는 상황이 발생할 수 있습니다. 특히 알리바바의 음성 인식 기술을 활용할 경우, 출력은 일반적으로 16kHz의 PCM 포맷이며, 이를 8kHz AMR로 변환하기 위해서는 중간 단계가 필요합니다. 본문에서는 이를 위한 실용적인 변환 방식과 주의사항을 설명합니다.
전체 과정은 두 단계로 나뉩니다: 먼저 16kHz PCM을 8kHz로 리샘플링하여 정규화된 오디오 데이터를 생성한 후, 해당 데이터를 WAV 포맷으로 변환하고, 마지막으로 OpenCore-AMR 라이브러리를 이용해 AMR로 압축합니다.
첫 번째 단계에서는 16kHz PCM을 8kHz로 다운샘플링해야 합니다. 이 과정에서 잘못된 샘플링 비율을 사용하면 이후의 AMR 인코딩 결과가 왜곡되며, 음질 저하나 재생 불가 문제가 발생합니다. 이 작업은 JSSRC 라이브러리를 활용하며, 직접 소스 코드를 수정하여 16kHz → 8kHz 전용 리샘플러로 구성하였습니다.
다음은 리샘플링된 8kHz PCM을 WAV 파일로 변환하는 코드입니다:
public void pcmToWav(String inputPath, String outputPath, PcmToWavTransformListener listener) {
final long sampleRate = 8000;
final int channelCount = 1;
final long byteRate = 8 * sampleRate * channelCount / 8;
final int bufferSize = 1024;
long totalAudioLen = 0;
try (FileInputStream in = new FileInputStream(inputPath);
FileOutputStream out = new FileOutputStream(outputPath)) {
// 파일 크기 측정
totalAudioLen = in.getChannel().size();
// 리샘플러 초기화 (16kHz → 8kHz)
JSSRCResampler resampler = new JSSRCResampler(in);
// WAV 헤더 작성
writeWaveHeader(out, totalAudioLen, sampleRate, channelCount, byteRate);
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = resampler.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
out.flush();
}
resampler.close();
Log.d("AudioConverter", "WAV 변환 완료: " + outputPath);
if (listener != null) {
listener.onComplete(outputPath);
}
} catch (IOException e) {
e.printStackTrace();
}
}
두 번째 단계에서는 변환된 8kHz WAV 파일을 AMR 형식으로 인코딩합니다. 이 과정에서는 OpenCore-AMR 라이브러리의 AmrEncoder 클래스를 사용합니다. 주의할 점은, WAV 파일의 16비트 리틀엔디안 데이터를 short[] 배열로 올바르게 해석해야 한다는 점입니다.
public void wavToAmr(String inputPath, String outputPath, TransformListener listener) {
try (FileInputStream in = new FileInputStream(inputPath);
FileOutputStream out = new FileOutputStream(outputPath)) {
AmrEncoder.init(0);
out.write(AMR_HEADER); // AMR 파일 헤더 삽입
List<short[]> audioFrames = new ArrayList<>();
byte[] buffer = new byte[320]; // 160개의 샘플에 대응하는 바이트 크기
int readBytes;
while ((readBytes = in.read(buffer)) > 0) {
short[] frame = new short[160];
ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(frame);
audioFrames.add(frame);
}
for (short[] frame : audioFrames) {
byte[] encoded = new byte[32]; // 최대 인코딩 길이
int encodedLength = AmrEncoder.encode(AmrEncoder.Mode.MR515.ordinal(), frame, encoded);
if (encodedLength > 0) {
out.write(encoded, 0, encodedLength);
}
}
out.flush();
Log.d("AudioConverter", "AMR 인코딩 완료: " + outputPath);
if (listener != null) {
listener.onComplete(outputPath);
}
} catch (Exception e) {
e.printStackTrace();
}
}
최종적으로 생성된 .amr 파일은 표준 8kHz AMR 포맷이며, 다양한 음성 처리 시스템에서 호환 가능합니다. 성능과 정확도를 위해 리샘플링 단계의 신뢰성은 매우 중요합니다.
참고 자료: