Python 실크 코덱 바인딩은 WeChat 음성 코덱을 지원합니다.
필크 : 파이썬 + 실크
관련 프로젝트: weixin-wxposed-silk-voice
pip install pilk
SILK 는 Skype 에서 개발한 음성 코딩 형식입니다. 온라인에서 사용할 수 있는 최신 버전은 2012년에 출시되었습니다.
사양 문서를 포함하여 SILK 원본 코드가 릴리스에 업로드되었습니다.
Tencent 음성 지원은 Silk-v3-디코더를 통해 제공됩니다.
릴리스에는 다시 컴파일된 x64-win 버전의 Silk-v3-디코더도 포함되어 있으며 중국어, 소스 코드를 지원합니다.
여기서 Tencent는 음성을 의미하며 WeChat 음성만을 예로 들어 설명합니다.
b'#!SILK_V3'
로 시작하고 b'xFFxFF'
로 끝나며, 중간에 음성 데이터가 있습니다.b'x02'
삽입하고 끝 부분에서 b'xFFxFF'
제거하고 중간은 변경되지 않은 채로 둡니다.통칭하여 음성 파일이라고 합니다.
음성 데이터는 여러 개의 독립적인 프레임 으로 나누어집니다. 각 프레임 의 처음 2 바이트 는 기본적으로 나머지 프레임 데이터의 크기 를 저장합니다.
이를 바탕으로 음성 파일 의 길이를 구하는 함수를 작성할 수 있습니다. (이 함수는 pilk 에 포함되어 있습니다.)
def get_duration ( silk_path : str , frame_ms : int = 20 ) -> int :
"""获取 silk 文件持续时间,单位:ms"""
with open ( silk_path , 'rb' ) as silk :
tencent = False
if silk . read ( 1 ) == b' x02 ' :
tencent = True
silk . seek ( 0 )
if tencent :
silk . seek ( 10 )
else :
silk . seek ( 9 )
i = 0
while True :
size = silk . read ( 2 )
if len ( size ) != 2 :
break
i += 1
size = size [ 0 ] + size [ 1 ] * 16
silk . seek ( silk . tell () + size )
return i * frame_ms
SILK 형식 사양에 따르면 Frame_ms는 20, 40, 60, 80, 100
이 될 수 있습니다.
자세한 내용은 IDE의 API 문서 주석을 확인하세요.
pilk를 사용하기 전에 오디오 파일 mp3, aac, m4a, flac, wav, ...
와 음성 파일 간의 변환이 PCM 원시 데이터 의 도움으로 완료된다는 것을 알아야 합니다.
구체적인 변환 관계: 오디오 파일 ⇔ PCM ⇔ 음성 파일
오디오(비디오) 파일 ➜ PCM
ffmpeg를 사용하려면 물론 ffmpeg가 먼저 있어야 합니다.
ffmpeg -y -i <音(视)频输入文件> -vn -ar <采样率> -ac 1 -f s16le < PCM输出文件>
-y
: 추가 가능 여부. <PCM 출력 파일>이 이미 존재하는지 묻지 않고 직접 덮어쓴다는 의미입니다.-i
: 할 말이 없음, 수정됨, <오디오(비디오) 입력 파일>이 이어짐-vn
: 비디오 데이터를 처리하지 않음을 나타냅니다. 추가하지 않으면 비디오 데이터가 처리되지 않지만(비디오 데이터를 PCM으로 변환하는 기능은 없습니다) 경고가 인쇄될 수 있습니다.-ar
: 샘플링 속도를 설정합니다. 옵션 값은 [8000, 12000, 16000, 24000, 32000, 44100, 48000]입니다. 여기서는 음질로 직접 이해할 수 있습니다.-ac
: 여기서는 1 이어야 하며 SILK 에 의해 결정되는 채널 수를 설정합니다.-f
: 지정된 형식으로 강제 변환을 의미합니다. 일반적으로 s16le 이어야 합니다. 이는 16-bit short integer Little-Endian data
의미합니다.ffmpeg -y -i mv.mp4 -vn -ar 44100 -ac 1 -f s16le mv.pcm
ffmpeg -y -i music.mp3 -ar 44100 -ac 1 -f s16le music.pcm
PCM ➜ 오디오 파일
ffmpeg -y -f s16le -ar <采样率> -ac <声道数> -i < PCM输入文件> <音频输出文件>
-f
: SILK 에 의해 결정되는 s16le
이어야 합니다.-ar
: 위와 동일-ac
: 의미는 위와 동일하며 값은 임의적입니다.<音频输出文件>
: 확장자는 정확해야 합니다. 형식이 지정되지 않은 경우 ffmpeg는 지정된 출력 파일 확장자를 기반으로 출력해야 하는 형식을 결정합니다.ffmpeg -y -f s16le -ar 16000 -i test.pcm test.mp3
ffmpeg는 python ffmpeg 바인딩으로 대체될 수도 있습니다. PyAV를 직접 학습하는 것이 좋습니다. 따라서 여기서는 자세히 설명하지 않겠습니다.
오디오 파일 ⇔ PCM에 대해 이야기한 후 다음 단계는 pilk를 사용하여 PCM ⇔ 음성 파일을 변환하는 것입니다.
import pilk
# pcm_rate 参数必须和 使用 ffmpeg 转 音频 到 PCM 文件时,使用的 `-ar` 参数一致
# pcm_rate 参数必须和 使用 ffmpeg 转 音频 到 PCM 文件时,使用的 `-ar` 参数一致
# pcm_rate 参数必须和 使用 ffmpeg 转 音频 到 PCM 文件时,使用的 `-ar` 参数一致
duration = pilk . encode ( "test.pcm" , "test.silk" , pcm_rate = 44100 , tencent = True )
print ( "语音时间为:" , duration )
import pilk
# pcm_rate 参数必须和 使用 ffmpeg 转 音频 到 PCM 文件时,使用的 `-ar` 参数一致
duration = pilk . decode ( "test.silk" , "test.pcm" )
print ( "语音时间为:" , duration )
ffmpeg에 의존하려면 pudub를 사용하십시오.
import os , pilk
from pydub import AudioSegment
def convert_to_silk ( media_path : str ) -> str :
"""将输入的媒体文件转出为 silk, 并返回silk路径"""
media = AudioSegment . from_file ( media_path )
pcm_path = os . path . basename ( media_path )
pcm_path = os . path . splitext ( pcm_path )[ 0 ]
silk_path = pcm_path + '.silk'
pcm_path += '.pcm'
media . export ( pcm_path , 's16le' , parameters = [ '-ar' , str ( media . frame_rate ), '-ac' , '1' ]). close ()
pilk . encode ( pcm_path , silk_path , pcm_rate = media . frame_rate , tencent = True )
return silk_path
pyav 사용을 권장함
import os
import av
import pilk
def to_pcm ( in_path : str ) -> tuple [ str , int ]:
"""任意媒体文件转 pcm"""
out_path = os . path . splitext ( in_path )[ 0 ] + '.pcm'
with av . open ( in_path ) as in_container :
in_stream = in_container . streams . audio [ 0 ]
sample_rate = in_stream . codec_context . sample_rate
with av . open ( out_path , 'w' , 's16le' ) as out_container :
out_stream = out_container . add_stream (
'pcm_s16le' ,
rate = sample_rate ,
layout = 'mono'
)
try :
for frame in in_container . decode ( in_stream ):
frame . pts = None
for packet in out_stream . encode ( frame ):
out_container . mux ( packet )
except :
pass
return out_path , sample_rate
def convert_to_silk ( media_path : str ) -> str :
"""任意媒体文件转 silk, 返回silk路径"""
pcm_path , sample_rate = to_pcm ( media_path )
silk_path = os . path . splitext ( pcm_path )[ 0 ] + '.silk'
pilk . encode ( pcm_path , silk_path , pcm_rate = sample_rate , tencent = True )
os . remove ( pcm_path )
return silk_path