기술/기타

[python] ffmpeg로 wav파일에 header 넣어주기(ffmpeg-python) (feat. microsoft speech cogni

하기싫지만어떡해해야지 2024. 9. 5. 17:14

react에서 user가 녹음한 파일을
백엔드로 받아와서
백엔드에서 발음평가 API를 날려
점수를 받아오는 기능을 구현해야했는데,,
 
내가 이용한 발음평가 API는
Azure AI의 pronunciation assessment였고
cognitive-services-speech-sdk를
다운받아 사용하는 방식이었다
https://ai.azure.com/explore/aiservices/speech/pronunciationassessment?tid=56b5b06f-62d4-4c16-b193-36e8379dae27

Azure AI Studio

ai.azure.com

 

github 코드를 보며
(microsoft라 기대했지만 생각보다 잘 안돼있음^^)
https://docs.microsoft.com/azure/cognitive-services/speech-service/quickstart-python

음성 텍스트 변환 빠른 시작 - Azure AI services

이 빠른 시작에서는 실시간 음성 텍스트 변환에 음성 서비스를 사용하는 방법을 알아봅니다.

learn.microsoft.com

 

0xa (SPXERR_INVALID_HEADER) 에러 발생

API 요청하는 기능을 구현하는데
자꾸 audio 객체를 생성해서
생성한 audio객체를 담아주는 부분에서
0xa (SPXERR_INVALID_HEADER)
이 에러가 뜨면서 전송이 안되는 것이었다

 
이게 아무리 구글링을 해도
wav형태 파일로 보내면 된다는데
난 wav파일이었는데 대체 왜 ,,,,
하며 진짜 엄청나게 삽질을 했는데
 
딱 하나 마음에 예상했던 이슈는
 
wav파일은 header를 담고있는데
이 header에는 기본적인 wav파일의
정보를 담고있다
하지만 내 wav파일은
프론트엔드에서 직접 생성한 파일이므로
이러한 header정보가
누락된 것이 아닐까하는 생각을 했었다
 
공식 github에 샘플로 올라와있는
wav파일을 넣어서 하면 작동이 되는데
내가 프론트에서 받아온 wav파일이 안되는걸 보면
해당 부분의 문제가 확실하다고 판단했다
 

python을 통해 wav파일 헤더 정보 확인하기

그래서 우선 python을 이용해서
내가 만들어준 wav파일에 대한 정보를 확인해봤다
확인하는 코드는 다음과 같다

import wave

try:
    with wave.open(temp_file_path, 'rb') as wav_file:
        print("Channels:", wav_file.getnchannels())
        print("Sample width:", wav_file.getsampwidth())
        print("Frame rate (sample rate):", wav_file.getframerate())
        print("Number of frames:", wav_file.getnframes())
        print("Parameters:", wav_file.getparams())

        if wav_file.getnchannels() != 1:
            raise ValueError("File must be mono")
        if wav_file.getsampwidth() != 2:  # 2 bytes = 16 bits
            raise ValueError("Sample width must be 16-bit")
        if wav_file.getframerate() != 16000:
            raise ValueError("Sample rate must be 16000 Hz")

        print("WAV file is valid.")
except wave.Error as e:
    print(f"Error reading WAV file: {e}")

 
with open으로 wav파일이 있는 경로를 지정해주고
read binary(rb)모드로 파일을 열어준다
 
그리고 파이썬에서 wav파일을 다루는 라이브러리인
wave를 임포트해서 이것저것 정보를 출력해본다
 
아니나 다를까, 내 wav파일의 header에 문제가 있었다
그래서 try문 안에서 wav파일의
여러 정보를 읽어오는도중
에러가 발생했고
except문에서
file does not start with RIFF id
에러가 발생했다
 
WAV 파일은 RIFF(Raster Image File Format)
형식을 따르며,
파일의 시작 부분은 항상 "RIFF"라는
4바이트의 ID로 시작해야하는데
이 부분이 충족되지 않은 것이다
 
한마디로, header가 없거나 그 형태가 이상하다는 소리
 
이러한 header정보가 뭔지
wav에서 왜 header가 필요한지
header가 없으면 왜 에러가 뜨며
오디오 데이터가 정상적으로 인식이 안되는지
조금이라도 알고싶으면
오디오를 컴퓨터가 어떻게 변환해서 표현하는지를
이해하면 좋다
 
나도 이 부분에 전문가는 아니라서
대충 내가 아는 것만 정리를 하면 다음과 같다
(틀릴수도 있으니..)
 

wav파일에 header가 없으면 왜 문제인가? (feat. 컴퓨터는 오디오를 어떻게 해석하는가)

컴퓨터공학이나 선형대수같은 과목을
수강하다보면 한 번쯤 들어봤을텐데
오디오 데이터를 같은
선형적인 데이터(연속적인 데이터)를
컴퓨터가 이해할 수 있는
이진 데이터(불연속적인 데이터)로
변환하는 과정에서는 여러가지 정의해줘야하는
요소들이 있다
sample rate나 channel수 등이
대표적인 예시일 것이다
 
1) sample rate
연속 데이터를 불연속 데이터로 바꾸기 위해서는
그 연속 데이터를 아주 잘게 쪼갠다음
하나로 합쳐주면
불연속 데이터들의 합으로 연속 데이터를
표현이 가능한 것이다
 
미적분에서 배운 적분의 개념으로 이해하면 좋을 것 같다
적분에서 곡선 부분의 면적을 구할 때는
해당 곡선을 아주 작은 사각형(?)으로 쪼개서
그 작은 사각형들의 합 = 곡선의 면적
인 개념과 비슷한 것이다
 
물론 완벽하게 면적이 같다는 아니지만
작은 사각형들의 개수가 많아질수록
실제 곡선의 면적과 가까워지게 된다
 
따라서 wav파일에서 sample rate란
영어를 그대로 이해하면 편하다
 
연속 데이터인 오디오를 작은 값으로 쪼갠 단위가 Sample이고
이 쪼개진 sample들이 모이면 오디오 파일을 복호할 수 있다
 
그럼 rate는 비율이므로
1초에 이 sample이 몇 개가 들어있는지를
나타내는 것이고
이는 우리가 흔히 알고있는 주파수(Hz)로 표현한다
 
보통 wav형식에서는
16kHz나 8kHz를 많이 사용한다
 
2) channel
몇 개의 채널에서 오디오를 표현하는 것인지이다
 
아무튼 wav파일의 헤더에는
이러한 sampleRate, channel 수와 같은
정보들이 담겨있어야하고,
wav파일을 decoding하는 입장에선
이런 정보들이 있어야
이 파일을 제대로 된 방식으로 복호화 할 수 있다
 
그런데 나는 직접 만들어준 wav파일이다보니
이러한 header 정보가 부족해서
cognitive-services-speech-sdk에서
음성을 인식할 때
어떤 sample rate인지
channel이 몇 개인지에 대한 정보가 없어
오디오 복호가 힘드니 에러가 뜬 것으로
추측했다
 
따라서 이걸 어떻게 해결할까 구글링하던 중
ffmpeg 모듈로 wav파일의 형식을 변환하면
된다는 글을 보았고, 해보았더니 실제로 제대로 작동했다
(눈물..)
 
여기서 진짜 삽질을 너무하고 시간낭비를 많이했는데
아무튼 ,, 성공했으니 ,, ㅠㅡㅠ
 
유저가 저장한 wav파일을
발음평가 API에 전송하기 전에
ffmpeg를 통해 형식을 변환해주고
변환된 파일을 API에 전송해주면 된다
 

ffmpeg 설치

이를 위해서 ffmpeg를 로컬에 설치해주고
python 코드로 ffmpeg를 실행시키기 위해서
ffmpeg-python 모듈도 설치해줬다
 
원래라면 ffmpeg를 컴퓨터에 설치하고
terminal 명령어로 실행해줄 수 있지만
ffmpeg-python은 이러한 작업을
python코드를 통해서 가능하게 해준다
https://github.com/kkroening/ffmpeg-python

GitHub - kkroening/ffmpeg-python: Python bindings for FFmpeg - with complex filtering support

Python bindings for FFmpeg - with complex filtering support - kkroening/ffmpeg-python

github.com

 

 
나의 컴퓨터 로컬인 macOS에는
brew로 ffmpeg를 설치해줬다

brew install ffmpeg

 
 
그다음 프로젝트를 진행하고 있는
백엔드의 가상환경에 ffmpeg-python을 설치해줬다

pip3 install ffmpeg-python

 
이렇게 해주면 ffmpeg를 사용할 준비는 완료
 
 

ffmpeg를 사용해 wav형식 변환 python 코드 작성

내가 사용할
microsoft의 speech cognitive는
 
파일 포맷은 wav,
PCM 16비트,
sample rate는 16000Hz,
channel 수는 1개(mono)
 
로 음성 파일을 설정해줘야했다
 
이를 파이썬 코드를 통해 작성해주면

ffmpeg.input(file_path).output(new_file_path, 
                                format='wav', 
                                acodec='pcm_s16le', 
                                ar=16000, 
                                ac=1).run()

 
이렇게 작성해줄 수 있다
input의 파라미터로 형식을 바꿔주고싶은
wav파일의 경로를 넣어주고
output의 파라미터로
형식이 변경된 파일이 저장될 곳을 지정해주면된다
 
위 코드를 bash로 하면

ffmpeg -i input_file.wav -f wav -acodec pcm_s16le -ar 16000 -ac 1 output_file.wav

 
위와 같다
 
 
 
 
아무튼 위 코드를 이용하여 wav파일에
올바른 header정보를 넣어주고
형식을 변경해준 다음
음성인식 API에 날렸더니
 
된다 ,,,,
 

 
wav파일들의 여러 헤더정보가 출력되고
WAV file is valid와 함께...
 
azure ai의 speech cognitive의 
session이 시작되었다는 출력문이 나온다,,,
 
진짜 저거 보고 눈물날뻔했다
 
 
다들 나같은 삽질은 하지말라는 의미
+
내 공부 목적에서
이렇게 게시글을 남긴다
 
많은 사람들에게 도움이 되었으면 ,,,