SoXで音声感知録音:その5 話し声を準リアルタイム録音

前の関連記事:SoXで音声感知録音:その4 VAD(Voice Activity Detection)の利用


vadエフェクトはストリーム録音には適していないので、Windowsのタスクスケジューラを使って話し声の抽出を遅延処理するようにします。前回と同様SoXのインストールが事前に必要です。

設定ファイルsettings.bat


録音ファイル形式などを設定するファイルです。
rem データディレクトリに残すファイル数。記録中に処理させる場合は1以上にする。
set RF=1
rem 録音音声ファイル形式の設定(拡張子で判別)。
set EXT=wav
rem 出力音声ファイルの形式
set OUTEXT=flac
rem 出力音声ファイルの圧縮オプション
set OUTOPT=-C 6
rem 記録しない音声ファイルの長さ(秒以下)。(再抽出後は1秒以下)
set SEC=3
rem 2分割して再抽出する音声ファイルの長さ(秒)
set F_LEN=20
rem 1ファイル当たりの録音区間の秒数の設定(秒)。
set DUR=60
rem ディレクトリ名の設定。スクリプトファイルのあるディレクトリの下層に作られる。
rem データディレクトリ名。
set DATA_DIR=sox_data
rem 改名後のデータをいれるディレクトリ名。
set RENAMED_DIR=renamed
rem 処理後のファイルの移動先ディレクトリ名。
set EXTRACTED_DIR=extracted
rem sox.exeへのパスの設定。環境変数PATHに設定しているときは不要。
rem Windows7 64bitの場合。
path %PATH%;C:\Program Files (x86)\sox-14-4-2
rem WindowsXPの場合。
path %PATH%;C:\Program Files\sox-14-4-2
rem sox.exeにパスが通っているかの確認。
set T=0
for /F "delims= " %%t in ('sox --version') do set T=%%t
if %T%==0 (
  cls
  echo sox.exeが見つかりません。
  echo.
  pause
  exit
)
rem SoXの既定の音声デバイスの設定。
set AUDIODRIVER=waveaudio
rem このスクリプトがあるディレクトリ。
set SD=%CD%
rem データディレクトリDATA_DIRのフルパス。
set DATA_DIR_P=%SD%\%DATA_DIR%
rem 改名後のデータをいれるディレクトリRENAMED_DIRのフルパス。
set RENAMED_DIR_P=%SD%\%DATA_DIR%\%RENAMED_DIR%
if not exist "%RENAMED_DIR_P%" (mkdir "%RENAMED_DIR_P%")
rem 処理後のファイルの移動先ディレクトリEXTRACTED_DIRのフルパス。
set EXTRACTED_DIR_P=%SD%\%DATA_DIR%\%EXTRACTED_DIR%
if not exist "%EXTRACTED_DIR_P%" (mkdir "%EXTRACTED_DIR_P%")
rem 再抽出の作業フォルダのフルパス
set TMP_DIR_P=%SD%\%DATA_DIR%\temp
if not exist "%TMP_DIR_P%" (mkdir "%TMP_DIR_P%")
録音条件の設定は3行目から14行目の部分になります。

8行目の圧縮オプションは6行目で指定したファイル形式に依存します。

録音の開始と停止にかかわるバッチファイル


sox_rec.bat

これはsettings.batを読み込んでSoXを起動して録音を開始します。

settings.batを変更していなければバッチファイルがあるフォルダにsox_dataフォルダが作成されてそこに1分間隔ごとに分割されてwavファイルに保存されます。
@echo off
call settings.bat
cd %SD%\%DATA_DIR%
echo SoX on %TIME%
for /F "tokens=1,2 delims=." %%x in ('^(for /F "tokens=1,2,3 delims=:" %%p in ^('echo %DUR%.%date:/=%_%time: =0%'^) do @echo %%p%%q%%r^)') do sox -q -c 1 -d -p | sox -p "%%y_%%x_%%5n.%EXT%" trim 0 %%x : newfile : restart 
sox_start.bat

sox_rec.batで録音開始はできますが終了するのにCtrl+Cを打たないといけないのでバッチファイルから終了できるようにタイトルをつけてsox_rec.batを起動します。
@echo off
echo sox_rec.batをタイトルをつけて起動。
start "sox_rec.bat" sox_rec.bat
sox_end.bat

sox_start.batで開始した録音はこのバッチファイルで終了できます。

ただしロック画面になっているときは終了できません。
@if(0)==(0) echo off
cscript.exe //nologo /E:JScript "%~f0"
goto :EOF
@end
//SoXを停止させる
var sh = new ActiveXObject("WScript.Shell");
sh.AppActivate("sox_rec.bat");
sh.Sendkeys("^c");
WScript.Sleep(1000);
sh.Sendkeys("y{ENTER} ");
sh.Sendkeys("exit{ENTER} ");
sox_restart.bat

sox_start.batで起動したSoXを再起動するバッチファイル。

settings.batを変更したときに使います。
@if(0)==(0) echo off
cscript.exe //nologo /E:JScript "%~f0"
call sox_start.bat
goto :EOF
@end
//SoXを停止させる
var sh = new ActiveXObject("WScript.Shell");
sh.AppActivate("sox_rec.bat");
sh.Sendkeys("^c");
WScript.Sleep(1000);
sh.Sendkeys("y{ENTER} ");
sh.Sendkeys("exit{ENTER} ");

録音した音声ファイルにvadエフェクトをかけるsht_f_del.bat


(2015.8.16追記。この2分割する方法よりも指定した秒数に分割してvadエフェクトをかけるSoXで音声感知録音:その6 話し声を準リアルタイム録音2の方が精度よく話声の抽出ができると思います。)

sox_dataフォルダにあるwavファイルを録音間隔のファイル名に改名してrenamedフォルダに移動させます。

vadエフェクトをかけたあとextractedフォルダに移動させ長さが3秒以下なら削除します。

20秒以上の長さのwavファイルは2分割してさらにvadエフェクトをかけます。

vadエフェクトをかけたあとの分割ファイルを再結合して合計の長さが1秒以下のものは削除します。
@if(0)==(0) echo off
call settings.bat
echo %DATA_DIR%フォルダにある%EXT%ファイルを改名して%RENAMED_DIR%フォルダに移動させています。
for /F %%p in ('cscript.exe //nologo //E:JScript "%~f0"') do set FCNT=%%p
rem 削除した全ファイル数
set DCNT=0
rem 再抽出したファイル数
set REEXT=0
rem 再抽出後に削除したファイル数
set DCNT2=0
cd "%EXTRACTED_DIR_P%"
setlocal enabledelayedexpansion
for %%f in ("%RENAMED_DIR_P%\*.%EXT%") do (
  rem 変換後のファイル名
  set F_NAME=%%~nf.wav
  echo 話声抽出後の!F_NAME!の長さが%SEC%秒以下なら削除します。
  echo 既削除数!DCNT!/%FCNT%(再抽出後既削除数!DCNT2!/!REEXT!^)
  sox "%%f" !F_NAME! vad reverse vad reverse
  del /Q "%%f"
  rem sox --iで測定できないときのためにTをリセットする。
  set T=0
  for /F "tokens=1 delims=." %%t in ('sox --i -D !F_NAME!') do set T=%%t
  if !T! LEQ %SEC% (
    del /Q !F_NAME!
    set /A DCNT=!DCNT!+1
    echo !F_NAME!の長さが%SEC%秒以下なので削除しました。
  )
  rem %F_LEN%秒より長いファイルは半割して再抽出
  if !T! GTR %F_LEN% (
  set /A REEXT=!REEXT!+1
    echo !F_NAME!は%F_LEN%秒より長いので再抽出します
    set /A T2=!T!/2
    sox !F_NAME! "%TMP_DIR_P%\001.wav" trim 0 !T2! vad reverse vad reverse
    sox !F_NAME! "%TMP_DIR_P%\002.wav" trim !T2! vad reverse vad reverse
    del /Q !F_NAME!
    sox "%TMP_DIR_P%\001.wav" "%TMP_DIR_P%\002.wav" !F_NAME!
    del /Q "%TMP_DIR_P%\001.wav"
    del /Q "%TMP_DIR_P%\002.wav"
    for /F "tokens=1 delims=." %%t in ('sox --i -D !F_NAME!') do set T=%%t
    if !T! LEQ 1 (
      del /Q !F_NAME!
      set /A DCNT=!DCNT!+1
   set /A DCNT2=!DCNT2!+1
      echo 話声再抽出後の!F_NAME!の長さが1秒以下なので削除しました。
    )
  )
  rem wavファイルを変換。
  if exist !F_NAME! (
    sox !F_NAME! %OUTOPT% %%~nf.%OUTEXT%
    del /Q !F_NAME!
  )
)
rem 作業フォルダの内容の削除
del /Q "%TMP_DIR_P%\*.*"
rem renamedフォルダの内容の削除
del /Q "%RENAMED_DIR_P%\*.*"
endlocal
GOTO :EOF
@end
var sh = new ActiveXObject("WScript.Shell");
var fso = new ActiveXObject("Scripting.FileSystemObject");
var ext = sh.ExpandEnvironmentStrings("%EXT%");//改名対象拡張子。
var data_dir = sh.ExpandEnvironmentStrings("%DATA_DIR_P%");//データディレクトリdata_dirフルパス。
var renamed_dir = sh.ExpandEnvironmentStrings("%RENAMED_DIR_P%");//改名後のデータをいれるディレクトリrenamed_dirのフルパス。
var fldr = fso.GetFolder(data_dir); //データフォルダオブジェクトの取得。
var fc = new Enumerator(fldr.Files); //ファイルコレクションをEnumeratorオブジェクトにして取得。
var f, new_name, j;
var f_arr = [];//処理対象ファイルオブジェクトを格納する配列。
//RegExpオブジェクトを作成。
var re = new RegExp("^(\\d{4})(\\d{2})(\\d{2})_(\\d{2})(\\d{2})(\\d{2})_(\\d+)_(\\d+)\\." + ext + "$");
//ファイルコレクションの各ファイルに対して。
for (; !fc.atEnd() ; fc.moveNext()) {
    f = fc.item(); //ファイルオブジェクトの取得。
    //ファイル名の判別
    if (re.test(f.Name)) {
        f_arr.push(f);//処理対象となるファイルオブジェクトを配列に格納。ファイル名昇順に取得できているはず。
    } else {
        f.Delete();//処理対象とならないファイルは削除。
    }
}
var fn = f_arr.length - sh.ExpandEnvironmentStrings("%RF%")//処理するファイル数。
for (var i = 0; i < fn ; i++) {
    f = f_arr[i];//配列からファイルオブジェクトを取得。
    new_name = new_fname(re.exec(f.Name));//正規表現結果配列からファイル名を取得。
    try {
        j = 0;//「年月日_時分-年月日_時分」を示す要素番号。
        f.Move(renamed_dir + "\\" + new_name[j] + "." + ext);//改名して移動。
    } catch(e) {
        j = 1;//「年月日_時分秒-年月日_時分秒」を示す要素番号。
        f.Move(renamed_dir + "\\" + new_name[j] + "." + ext);//改名して移動。
    } 
}
WScript.Echo(fn);//バッチファイルに処理対象ファイル数を返す。
//正規表現結果配列から新しいファイル名を返す関数。戻り値は配列[年月日_時分-年月日_時分, 年月日_時分秒-年月日_時分秒]。
function new_fname(r) {
    //ファイル名から記録開始日時のDateオブジェクトを得る。1月を0として開始。
    var start_dt = new Date(r[1], r[2] - 1, r[3], r[4], r[5], r[6]);
    var int_sec = r[7] * 1000; //記録区間長(ミリ秒)
    var int_cnt = r[8]; //記録区間番号
    var start_ms = start_dt.getTime() + int_sec * (int_cnt - 1); //区間開始日時経過ミリ秒
    //経過ミリ秒を数字羅列に変換
    var end_n = date_num(start_ms + int_sec);//区間開始日時。
    var start_n = date_num(start_ms);//区間終了日時。
    var hhmm = start_n[0] + "-" + end_n[0];//新しいファイル名年月日_時分-年月日_時分。
    var hhmmss = start_n[0] + start_n[1] + "-" + end_n[0] + end_n[1];//新しいファイル名年月日_時分秒-年月日_時分秒。
    return [hhmm, hhmmss];
    //経過ミリ秒を年月日の数字羅列に変換する関数。戻り値は配列[年月日_時分, 秒]。
    function date_num(ms) {
        var dt = new Date();//Dateオブジェクトの生成。
        dt.setTime(ms);//経過ミリ秒からDateオブジェクトを取得。
        return [dt.getFullYear() + fm(dt.getMonth() + 1) + fm(dt.getDate()) + "_" + fm(dt.getHours()) + fm(dt.getMinutes()), fm(dt.getSeconds())];
        function fm(m) {
            return ("0" + m).slice(-2);//固定長2桁を返す。
        }
    }
}
このsht_f_del.batをWindowsのタスクマネージャで定期的に実行させれば準リアルタイムに話し声だけの録音ができたことになります。

次の関連記事:SoXで音声ファイルを分割してvadエフェクトをかけるバッチファイル

PR

0 件のコメント:

コメントを投稿