第三回目です。次で終わりにしたい…

[前回:無料版Davinchi Resolveで字幕生成システムを作る #2 | GUIのapiを使う]

davinchi_voice_recognition.pyを実装していきます。

davinchi_voice_recognition.py

音声認識の処理を行う本体です。

データの取得

前回作成したmy_auto_subtitle.pyから辞書型オブジェクトを文字列にして引数を貰うので、ここで元に戻します。

データにパスが含まれているので、replaceでいい感じに置換します。(OSにより違うかもしれませんが…)

receive_data = sys.argv[1]
receive_data = receive_data.replace('\\\\','//')
tl_data = json.loads(receive_data)

 

素材のサンプリングレートとチャンネルを統一

SileroVADの制約により、音声素材のファイルを16-bit PCM/monoチャンネル/16000Hzに変換します。

from pydub import AudioSegment
# Parameters
AUDIO_TIMELINE_PATH = './result_data/audio_timeline_data.csv'
SAVE_PATH ='C://Users//wapra//Home//workspace//DaVinchiResolve//voice_recognition//result_data//'
BIT_FORMAT = 2 # Audio format (16-bit PCM)
CHANNELS = 1 # Mono audio
SAMPLING_RATE = 16000 # Sample rate

def transform_audio_for_speech_rec(file_path):
    audio = AudioSegment.from_file(file_path)

    # チャンネル数が2の場合はモノに変換
    ch = audio.channels
    if ch != CHANNELS:
        audio = audio.set_channels(CHANNELS)
        print('change to mono audio')

    # サンプル幅16ビット(2バイト)に変換
    sw = audio.sample_width
    if sw != BIT_FORMAT:
        audio = audio.set_sample_width(2)
        print('change audio bit format')

    # サンプリングレートの修正
    sr = audio.frame_rate
    if sr != SAMPLING_RATE:
        audio = audio.set_frame_rate(SAMPLING_RATE)
        print('change sr')
    
    # for audio transform test
    # audio.export(SAVE_PATH + '//temp//temp.wav', format='wav')
    return audio

 

発話区間判定

取得した音声ファイルをpyTorchのテンソルに変換し、DaVinchi ResolveのTLで切り取られた部分のみを素材から抜き出します。

抜き出したデータをSileroVADへ渡して、発話区間のデータを取得します。

# 発話区間判定
def get_voice_activity_time(audio_tensor):
    speech_timestamps = get_speech_timestamps(audio_tensor.float(), vad_model, sampling_rate=SAMPLING_RATE, threshold=0.4)

    speech_sound_list = []
    for idx,st in enumerate(speech_timestamps):
        chunk = audio_tensor[int(st['start']) : int(st['end'])]
        speech_sound_list.append(chunk)
        # for sound data check
        # save_audio(SAVE_PATH + str(idx)+'_save.wav', chunk)

    return speech_sound_list, speech_timestamps

# オーディオデータフォーマット変換
audio = transform_audio_for_speech_rec(tl_data[idx]['file_path'])

# オーディオデータをNumPy配列に変換する
samples = np.array(audio.get_array_of_samples())

# データをint16のままテンソルに変換
audio_tensor = torch.from_numpy(samples).type(torch.int16)

# davinchi resolveのTL上でクロップされた部分のみ取り出し
audio_start = int(tl_data[idx]['clip_start'] / tl_data[idx]['fps'] * SAMPLING_RATE)
audio_end = int(tl_data[idx]['clip_end'] / tl_data[idx]['fps'] * SAMPLING_RATE)
audio_tensor = audio_tensor[audio_start : audio_end]

# 発話区間のチェック
speech_sound_list, speech_timestamps = get_voice_activity_time(audio_tensor)

 

音声認識と字幕用にTL上の書き起こし時間を計算

音声認識は発話区間のみで切り取った音声データをtranscribe関数に入れるだけ。

後は字幕挿入の為に、発話開始時刻と発話時間の長さをひたすら計算しています。

このファイルの最後にresult_dataの中身をファイルへ吐き出して完了です。

for speech_sound, speech_time in zip(speech_sound_list, speech_timestamps):
            audio = audio_from_tensor(speech_sound, SAMPLING_RATE)
            subtitle = transcribe(recognision_model, audio).text
            print(subtitle)

            # 発言時間の計算
            speech_start_frame = int(tl_data[idx]['tl_position_start']) + int((speech_time['start']/SAMPLING_RATE)*tl_data[idx]['fps'])
            speech_duration = (speech_time['end'] - speech_time['start'])/SAMPLING_RATE

            result_data.append(str(speech_start_frame) + ',' + str(speech_duration) + ',' + subtitle)

 

コード全体

import os
import sys
import glob
import json
import csv
import torch
import numpy as np
from pydub import AudioSegment

print('loading voice recognition model')
# for ReazonSpeech
sys.path.append('このスクリプトのある作業ディレクトリ')
from reazonspeech.nemo.asr import load_model, transcribe, audio_from_tensor
from nemo.collections.asr.models import EncDecRNNTBPEModel
recognision_model = load_model(device='cuda')

# for silero vad
vad_model_dir = 'モデルの保存場所'
from utils_vad import (init_jit_model,
                    get_speech_timestamps,
                    save_audio,
                    collect_chunks)
vad_model = init_jit_model(os.path.join(vad_model_dir, 'silero_vad.jit'))
print('finish loading modules')


# Parameters
SAVE_PATH ='音声認識結果の保存ディレクトリ'
BIT_FORMAT = 2 # Audio format (16-bit PCM)
CHANNELS = 1 # Mono audio
SAMPLING_RATE = 16000 # Sample rate

def transform_audio_for_speech_rec(file_path):
    audio = AudioSegment.from_file(file_path)

    # チャンネル数が2の場合はモノに変換
    ch = audio.channels
    if ch != CHANNELS:
        audio = audio.set_channels(CHANNELS)
        print('change to mono audio')

    # サンプル幅16ビット(2バイト)に変換
    sw = audio.sample_width
    if sw != BIT_FORMAT:
        audio = audio.set_sample_width(2)
        print('change audio bit format')

    # サンプリングレートの修正
    sr = audio.frame_rate
    if sr != SAMPLING_RATE:
        audio = audio.set_frame_rate(SAMPLING_RATE)
        print('change sr')
    
    return audio

def get_voice_activity_time(audio_tensor):
    speech_timestamps = get_speech_timestamps(audio_tensor.float(), vad_model, sampling_rate=SAMPLING_RATE, threshold=0.4)

    speech_sound_list = []
    for idx,st in enumerate(speech_timestamps):
        chunk = audio_tensor[int(st['start']) : int(st['end'])]
        speech_sound_list.append(chunk)

    return speech_sound_list, speech_timestamps
  
def main():
    receive_data = sys.argv[1]
    receive_data = receive_data.replace('\\\\','//')
    tl_data = json.loads(receive_data)

    # 音声認識結果の保存用変数
    result_data = []
    result_data.append('speech start time, speech duration, speech 2 txt')

    for idx in tl_data.keys():
        # オーディオデータフォーマット変換
        audio = transform_audio_for_speech_rec(tl_data[idx]['file_path'])
        
        # オーディオデータをNumPy配列に変換する
        samples = np.array(audio.get_array_of_samples())
        
        # データをint16のままテンソルに変換
        audio_tensor = torch.from_numpy(samples).type(torch.int16)

        # davinchi resolveのTL上でクロップされた部分のみ取り出し
        audio_start = int(tl_data[idx]['clip_start'] / tl_data[idx]['fps'] * SAMPLING_RATE)
        audio_end = int(tl_data[idx]['clip_end'] / tl_data[idx]['fps'] * SAMPLING_RATE)
        audio_tensor = audio_tensor[audio_start : audio_end]

        # 発話区間のチェック
        speech_sound_list, speech_timestamps = get_voice_activity_time(audio_tensor)

        for speech_sound, speech_time in zip(speech_sound_list, speech_timestamps):
            audio = audio_from_tensor(speech_sound, SAMPLING_RATE)
            subtitle = transcribe(recognision_model, audio).text
            print(subtitle)

            # 発言時間の計算
            speech_start_frame = int(tl_data[idx]['tl_position_start']) + int((speech_time['start']/SAMPLING_RATE)*tl_data[idx]['fps'])
            speech_duration = (speech_time['end'] - speech_time['start'])/SAMPLING_RATE

            result_data.append(str(speech_start_frame) + ',' + str(speech_duration) + ',' + subtitle)
    
    # 結果保存
    with open(SAVE_PATH + "voice-recognition-result.txt", "w", encoding='utf-8') as f:
        for data in result_data:
            f.write(data + "\n")

main()
sam

sam

流山おおたかの森Techブログの管理人です。 お仕事のご依頼などはmail or Twitter[https://twitter.com/sam_sumario]で連絡頂けると反応出来ます。
Previous post 無料版Davinchi Resolveで字幕生成システムを作る #2 | GUIのapiを使う
Next post OBS Studioのプラグインの作り方 #3 フィルタのプロパティ作成

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です