PHPスクリプト

自作Mod

AIに音声合成させるためのツール集。

create_dialogue_wav.php

create_dialogue_wav.php (php)

<?php
$context_opts = [];
$http_opts = [];

function exceptions_error_handler($severity, $message, $filename, $lineno) {
    throw new ErrorException($message, 0, $severity, $filename, $lineno);
}

set_error_handler('exceptions_error_handler');

try {
    if ($argc < 2) {
        usage();
        exit;
    }

    $options = getopt('cfm:nv');

    $clean_up_mode = array_key_exists('c', $options);
    $force_create = array_key_exists('f', $options);
    $model_name = array_key_exists('m', $options) ? $options['m'] : null;
    $test_mode = array_key_exists('n', $options);
    $verbose_mode = array_key_exists('v', $options);
    $csv_filename = $argv[$argc - 1];

    if ( !$model_name || !$csv_filename ) {
        usage();
        exit;
    }

    if ( !is_readable($csv_filename) ) {
        throw new Exception($csv_filename . ' is not readable.');
    }

    if ( !is_file($csv_filename) ) {
        throw new Exception($csv_filename . ' is not file.');
    }

    $path_parts = pathinfo($csv_filename);
    $export_dir = $path_parts['filename'] . '_' . $model_name;

    if ( !$test_mode && !is_dir($export_dir) ) {
        mkdir($export_dir);
    }

    $csv_data = parse_csv($csv_filename);

    $context_opts = [
        'http' => [
            'method' => 'GET',
        ]
    ];

    $http_opts = [
        'model_name' => $model_name,
        'model_id' => 0,
        'speaker_id' => 0,
        'sdp_ratio' => 0.2,
        'noise' => 0.6,
        'noisew' => 0.8,
        'length' => 1,
        'language' => 'JP', // JP or EN
        'auto_split' => 'true',
        'split_interval' => 0.5,
        'assist_text_weight' => 1,
        'style' => 'Neutral',
        'style_weight' => 1,
    ];

    $count_total = count($csv_data);

    printf('csv total: %d', $count_total);
    echo PHP_EOL;

    $count_current = 0;

    if ( $clean_up_mode ) {
        $dh = opendir($export_dir);

        if ( !$dh ) {
            throw new Exception('could not open dir - ' . $export_dir);
        }

        $pattern = '/^(.+)\.(wav|lip|fuz)$/';

        $count_deleted = 0;

        while ( $file = readdir($dh) ) {
            if ( $file == '.' ) {
                continue;
            }

            if ( $file == '..' ) {
                continue;
            }

            if ( preg_match($pattern, $file, $matches) ) {
                $count_current++;

                $basename = $matches[1];
                $test_key = $basename . '.wav';

                //echo 'test_key=[' . $test_key . ']' . PHP_EOL;

                if ( !array_key_exists($test_key, $csv_data) ) {
                    if ( $verbose_mode ) {
                        echo $file . PHP_EOL;
                    }

                    if ( !$test_mode ) {
                        $fullpath = $export_dir . '/' . $file;
                        unlink($fullpath);
                    }

                    $count_deleted++;
                }
            }
        }

        closedir($dh);

        printf('deleted: %d / %d', $count_deleted, $count_current);
        echo PHP_EOL;

        //print_r($csv_data);
    } else {
        foreach ($csv_data as $filename => $dialogue) {
            $count_current++;

            $dialogue = proofread($dialogue);

            if ( strlen($dialogue) == 0 ) {
                continue;
            }

            $filename = $export_dir . '/' . $filename;

            $exists = file_exists($filename);

            if ( $test_mode || !$exists ) {
                printf('%d / %d : %s', $count_current, $count_total, $filename);
                echo PHP_EOL;
                printf('%s %s %s', str_repeat(' ', strlen($count_current)), str_repeat(' ', strlen($count_total)), $dialogue);
                echo PHP_EOL;
            }

            if ( !$test_mode && !$exists ) {
                create_wav($dialogue, $filename);
            }
        }
    }
} catch (Exception $e) {
    echo $e->GetMessage() . PHP_EOL;
    echo 'line: ' . $e->GetLine() . PHP_EOL;
}

function usage()
{
    echo 'Usage: create_dialogue_wav.php [-cfnv] -m <model name> <LazyVoiceFinder_export.csv>';
    echo PHP_EOL;
}

function proofread($text)
{
    // 括弧とその中身は読まない
    $text = preg_replace('/\(.+?\)/', '', $text);

    // グローバル変数を展開するマクロは読まない
    $text = preg_replace('/__[0-9a-zA-Z=\.]+?__/', '', $text);

    // 中黒は無駄に間がおかれるので読まない
    $text = preg_replace('/・/', '', $text);

    // 文末の無駄な_を削除
    $text = preg_replace('/?_/', '?', $text);
    $text = preg_replace('/!_/', '!', $text);

    // 空白は読点に変換して間をおかせる
    $text = str_replace('_', '、', $text);

    // ここまでの処理で発生したゴミを除去
    $text = trim($text);

    // 文末の句点は不要
    $text = preg_replace('/。$/', '', $text);

    // ここまでの処理で発生したゴミを除去
    $text = trim($text);

    return $text;
}

function create_wav($text, $filename)
{
    global $context_opts, $http_opts;

    $http_opts['text'] = $text;

    $url = 'http://127.0.0.1:5000/voice?' . http_build_query($http_opts);

    $context = stream_context_create($context_opts);

    $data = file_get_contents($url, false, $context);

    file_put_contents($filename, $data);
}

function parse_csv($csv_filename)
{
    $csv_data = [];

    $fp = fopen($csv_filename, 'r');

    if ( !$fp ) {
        throw new Exception('could not open csv file - ' . $csv_filename);
    }

    // ヘッダ行を検証する
    $buf = fgets($fp);
    $buf = trim($buf);

    // ファイルの先頭の制御コードを取り除く (BOM?)
    $buf = substr($buf, 3, strlen($buf) - 1);

    // "State","Plugin","Quest Edid","FormId","Topic Edid","File Name","Voice Type","Dialogue 1 - English","Dialogue 2 - English"

    $header_list = explode(',', $buf);
    $header_count = count($header_list);

    $quest_edid_index = array_search('"Quest Edid"', $header_list);
    $form_id_index = array_search('"FormId"', $header_list);
    $topic_edid_index = array_search('"Topic Edid"', $header_list);
    $filename_index = array_search('"File Name"', $header_list);
    $dialogue_index = array_search('"Dialogue 1 - English"', $header_list);

    if ( $dialogue_index === FALSE ) {
        throw new Exception('column not found - "Dialogue 1 - English"');
    }

    if ( $filename_index === FALSE ) {
        throw new Exception('column not found - "File Name"');
    }

    if ( $topic_edid_index === FALSE ) {
        throw new Exception('column not found - "Topic Edid"');
    }

    if ( $form_id_index === FALSE ) {
        throw new Exception('column not found - "FormId"');
    }

    if ( $quest_edid_index === FALSE ) {
        throw new Exception('column not found - "Quest Edid"');
    }

    $no_dialogue = 0;
    $no_filename = 0;
    $no_quest_edid = 0;
    $no_topic_edid = 0;
    $no_form_id = 0;

    $last_form_id = '';

    while ( !feof($fp) ) {
        $ary = fgetcsv($fp, 10000, ',', '"', '');

        // CSVですらない不正な行(ファイルの末尾など)
        if ( !is_array($ary) ) {
            continue;
        }

        // カラムの数が合わない不正な行
        if ( count($ary) != $header_count ) {
            continue;
        }

        // セリフが存在しない行
        $dialogue = trim($ary[$dialogue_index]);

        if ( strlen($dialogue) == 0 ) {
            $no_dialogue++;
            continue;
        }

        // filename
        $filename = $ary[$filename_index];

        if ( strlen($filename) == 0 ) {
            $quest_edid = trim($ary[$quest_edid_index]);

            if ( strlen($quest_edid) == 0 ) {
                $no_quest_edid++;
                continue;
            }

            $topic_edid = trim($ary[$topic_edid_index]);

            if ( strlen($topic_edid) == 0 ) {
                $no_topic_edid++;
                continue;
            }

            $form_id = trim($ary[$form_id_index]);

            if ( strlen($form_id) == 0 ) {
                $no_form_id++;
                continue;
            }

            if ( $form_id == $last_form_id ) {
                $info_count++;
            } else {
                $info_count = 1;
                $last_form_id = $form_id;
            }

            // BaboDialog_BaboDialogueGen_00D80D5F_1
            // BaboDialog_GuardEroGreetin_008D049A_1

            $form_id = '00' . substr($form_id, 2, 6);

            if ( strpos($topic_edid, '[') !== FALSE ) {
                $topic_edid = '';
            } else {
                $topic_edid = substr($topic_edid, 0, 15);
            }

            if ( $topic_edid != '' ) {
                $quest_edid = substr($quest_edid, 0, 10);
            }

            $filename = sprintf('%s_%s_%s_%d.fuz', $quest_edid, $topic_edid, $form_id, $info_count);
        }

        $filename = str_replace('.fuz', '.wav', $filename);

        $csv_data[$filename] = $dialogue;
    }

    fclose($fp);

    if ($no_dialogue >= 1) {
        printf('no_dialogue: %d', $no_dialogue);
        echo PHP_EOL;
    }

    if ($no_filename >= 1) {
        printf('no_filename: %d', $no_filename);
        echo PHP_EOL;
    }

    if ($no_quest_edid >= 1) {
        printf('no_quest_edid: %d', $no_quest_edid);
        echo PHP_EOL;
    }

    if ($no_topic_edid >= 1) {
        printf('no_topic_edid: %d', $no_topic_edid);
        echo PHP_EOL;
    }

    if ($no_form_id >= 1) {
        printf('no_form_id: %d', $no_form_id);
        echo PHP_EOL;
    }

    return $csv_data;
}

xTranslator_dic_to_DBVO.php

xTranslator_dic_to_DBVO.php (php)

<?php
try {
    if ( $argc != 2 ) {
        throw new Exception('Usage: xTranslator_dic_to_DBVO.php <input filename>');
    }

    $in_filename = $argv[1];

    parse_xml($in_filename);
} catch (Exception $e) {
    echo $e->GetMessage() . PHP_EOL;
}

function parse_xml($in_filename)
{
    $fp = fopen($in_filename, 'r');

    if ( !$fp ) {
        throw new Exception('could not open input file - ' . $in_filename);
    }

    $buf = '';

    while ( !feof($fp) ) {
        $buf .= fgets($fp);
    }

    fclose($fp);


    if ( !preg_match_all('!<String.+?</String>!ms', $buf, $matches, PREG_SET_ORDER) ) {
        throw new Exception('no match');
    }


    $fp1 = null;
    $fp2 = null;
    $fp3 = null;

    try {
        $out_filename = 'voicevox.csv';

        $fp1 = fopen($out_filename, 'w');

        if ( !$fp1 ) {
            throw new Exception('could not open output file - ' . $out_filename);
        }

        $out_filename = 'LazyVoiceFinder_export.csv';

        $fp2 = fopen($out_filename, 'w');

        if ( !$fp2 ) {
            throw new Exception('could not open output file - ' . $out_filename);
        }

        fputs($fp2, '"State","Plugin","FormId","Topic Edid","File Name","Voice Type","Dialogue 1 - en","Dialogue 2 - en"' . PHP_EOL);

        $out_filename = 'DBVO.json';

        $fp3 = fopen($out_filename, 'w');

        if ( !$fp3 ) {
            throw new Exception('could not open output file - ' . $out_filename);
        }

        fputs($fp3, '{' . PHP_EOL);
    } catch (Exception $e) {
        if ($fp1) {
            fclose($fp1);
        }

        if ($fp2) {
            fclose($fp2);
        }

        if ($fp3) {
            fclose($fp3);
        }

        throw $e;
    }

    $history = [];
    $dbvo_dic_buf = '';

    foreach ($matches as $regs) {
        if ( strpos($regs[0], '<REC>DIAL:FULL</REC>') === false ) {
            continue;
        }

        if ( !preg_match('!<Source>(.+?)</Source>!ms', $regs[0], $regs2) ) {
            continue;
        }

        $source = $regs2[1];

        if ( in_array($source, $history) ) {
            continue;
        }

        $history[] = $source;

        if ( !preg_match('!<Dest>(.+?)</Dest>!ms', $regs[0], $regs2) ) {
            continue;
        }

        $dest = $regs2[1];

        // OK_Followerの翻訳辞書は日英なので入れ替える
        //list($source, $dest) = [$dest, $source];

        $dest = str_replace("\r", '', $dest);
        $dest = str_replace("\n", '', $dest);

        $source = str_replace("\r", '', $source);
        $source = str_replace("\n", '', $source);

        $dest = str_replace(''', "'", $dest);
        $dest = str_replace('"', '', $dest);
        $dest = str_replace('<', '<', $dest);
        $dest = str_replace('>', '>', $dest);

        $dest = preg_replace('/<Alias=Player> */', 'ドバコ', $dest);
        $dest = preg_replace('/<[^>]+>/', '', $dest);

        $dest = str_replace('(', '(', $dest);
        $dest = str_replace(')', ')', $dest);

        $dest = preg_replace('/\.\.\.+/', '…', $dest);

        $dest = trim($dest);

        $dest = preg_replace('/[\.……]+$/u', '', $dest);

        $dest = trim($dest);

        if ( strlen($dest) == 0 ) {
            continue;
        }

        $dest = preg_replace('/^\((.+)\) *\((.+)\)$/', '$1', $dest);

        $dest = preg_replace('/^\((.+)\)$/', '$1', $dest);
        $dest = preg_replace('/^\[(.+)\]$/', '$1', $dest);
        $dest = preg_replace('/^\*(.+)\*$/', '$1', $dest);

        $dest = preg_replace('/^(.+)\(.+?\)/', '$1', $dest);
        $dest = preg_replace('/^(.+)\[.+?\]/', '$1', $dest);
        $dest = preg_replace('/^(.+)\*.+?\*/', '$1', $dest);

        $dest = preg_replace('/^\(.+?\)(.+)$/', '$1', $dest);
        $dest = preg_replace('/^\[.+?\](.+)$/', '$1', $dest);
        $dest = preg_replace('/^\*.+?\*(.+)$/', '$1', $dest);

        $dest = trim($dest);

        if ( strlen($dest) == 0 ) {
            continue;
        }

        $dest = preg_replace('/?/u', '?', $dest);
        $dest = preg_replace('/!/u', '!', $dest);

        $dest_old = $dest;

        // ほぼ確実
        $dest = str_replace('マスタードバコ', 'ご主人様', $dest);
        $dest = str_replace('ウェンチ', '娼婦', $dest);
        $dest = str_replace('主が', 'あるじが', $dest);
        $dest = str_replace('主に', 'あるじに', $dest);

        $dest = preg_replace('/Deadly Wenches */u', '娼婦', $dest);

        $dest = str_replace('見えますが、', '見えるが、', $dest);
        $dest = str_replace('しれませんが、', 'しれないが、', $dest);
        $dest = str_replace('ありませんが、', 'ないが、', $dest);
        $dest = str_replace('はい、', 'そうだ、', $dest);
        $dest = str_replace('なのですから、', 'なのだから、', $dest);
        $dest = str_replace('続けていますが、', '続けているが、', $dest);

        $dest = str_replace('そうですね…', 'そうだな…', $dest);
        $dest = str_replace('わかりました…', 'わかった…', $dest);
        $dest = str_replace('やめています…', 'やめている…', $dest);
        $dest = str_replace('ありませんでした…', 'なかった…', $dest);

        $dest = str_replace('はどのようにして', 'はどうやって', $dest);
        $dest = str_replace('どのようにして', 'どうして', $dest);
        $dest = str_replace('いたのですが', 'いたのだが', $dest);
        $dest = str_replace('ごめんなさい', 'すまない', $dest);
        $dest = str_replace('わかりません', 'わからない', $dest);
        $dest = str_replace('気がします', '気がする', $dest);
        $dest = str_replace('していただければ', 'してもらえれば', $dest);

        $dest = fixString('何ですか', '何だ', $dest);
        $dest = fixString('何をしましたか', '何をしたんだ', $dest);
        $dest = fixString('信じています', '信じている', $dest);
        $dest = fixString('元気ですか', '元気か', $dest);
        $dest = fixString('助けます', '助けよう', $dest);
        $dest = fixString('奴隷です', '奴隷だ', $dest);
        $dest = fixString('好きです', '好きだ', $dest);
        $dest = fixString('好きですか', '好きか', $dest);
        $dest = fixString('必要ですか', '必要だ', $dest);
        $dest = fixString('思いました', '思った', $dest);
        $dest = fixString('思います', '思う', $dest);
        $dest = fixString('思いますか', '思う', $dest);
        $dest = fixString('思えません', '思えない', $dest);
        $dest = fixString('思っています', '思っている', $dest);
        $dest = fixString('持っています', '持っている', $dest);
        $dest = fixString('楽しんでいますか', '楽しんでいるのか', $dest);
        $dest = fixString('構いません', '構わない', $dest);
        $dest = fixString('決めます', '決める', $dest);
        $dest = fixString('着ています', '着ている', $dest);
        $dest = fixString('知っていますか', '知っている', $dest);
        $dest = fixString('知りたいのです', '知りたいんだ', $dest);
        $dest = fixString('知れませんが', '知れないが', $dest);
        $dest = fixString('言いません', '言わない', $dest);
        $dest = fixString('言うんですか', '言うのか', $dest);
        $dest = fixString('誰ですか', '誰なんだ', $dest);
        $dest = fixString('従ったのです', '従ったんだ', $dest);

        // まず大丈夫
        $dest = fixString('いくらですか', 'いくらだ', $dest);
        $dest = fixString('いるの', 'いる', $dest);
        $dest = fixString('いるのです', 'いるんだ', $dest);
        $dest = fixString('おかしいですか', 'おかしいか', $dest);
        $dest = fixString('おきます', 'おこう', $dest);
        $dest = fixString('ことですか', 'ことか', $dest);
        $dest = fixString('したんですか', 'したのか', $dest);
        $dest = fixString('していません', 'していない', $dest);
        $dest = fixString('しましたか', 'したのか', $dest);
        $dest = fixString('するつもりですか', 'するつもりなんだ', $dest);
        $dest = fixString('たいですか', 'たいか', $dest);
        $dest = fixString('たかったのです', 'たかったんだ', $dest);
        $dest = fixString('できます', 'できる', $dest);
        $dest = fixString('どうですか', 'どうだ', $dest);
        $dest = fixString('なのでしょうか', 'なのか', $dest);
        $dest = fixString('なんですか', 'なのか', $dest);
        $dest = fixString('るのでしょうか', 'るんだ', $dest);
        $dest = fixString('わかるよ', 'わかる', $dest);

        // 誤変換の可能性がある
        $dest = fixString('つもりですか', 'つもりか', $dest);
        $dest = fixString('いますか', 'いるんだ', $dest);
        $dest = fixString('ください', 'ほしい', $dest);
        $dest = fixString('ありません', 'ない', $dest);
        $dest = fixString('あります', 'ある', $dest);
        $dest = fixString('でした', 'だった', $dest);
        $dest = fixString('でしょう', 'だろう', $dest);
        $dest = fixString('のでしょうか', 'んだ', $dest);
        $dest = fixString('しました', 'した', $dest);
        $dest = fixString('しれません', 'しれない', $dest);
        $dest = fixString('いいよ', 'いいだろう', $dest);
        $dest = fixString('だけです', 'だけだ', $dest);

        // 誤変換の可能性が非常に高い
        $dest = fixString('します', 'しよう', $dest);
        $dest = fixString('のですか', 'んだ', $dest);
        $dest = fixString('ですか', 'なのか', $dest);
        $dest = fixString('です', 'だ', $dest);

        if ($dest != $dest_old) {
            echo $dest_old . PHP_EOL;
            echo $dest . PHP_EOL;
        }

        fputs($fp1, 'ずんだもん,' . $dest . PHP_EOL);

        $s = sprintf('"オーバーライド","fallout4.esm","00043A15","[000E0BFF]","%s.fuz","synthgen1male01","%s",""', $source, $dest);
        fputs($fp2, $s . PHP_EOL);

        if ( $dbvo_dic_buf ) {
            fputs($fp3, $dbvo_dic_buf . ',' . PHP_EOL);
        }

        $dbvo_dic_buf = "\t" . '"' . $dest . '": "' . $source . '"';
    }

    if ( $dbvo_dic_buf ) {
        fputs($fp3, $dbvo_dic_buf . PHP_EOL);
    }

    fputs($fp3, '}' . PHP_EOL);

    fclose($fp1);
    fclose($fp2);
    fclose($fp3);
}

function fixString($replace_from, $replace_to, $haystack)
{
    $haystack = preg_replace('/' . $replace_from . '$/u', $replace_to, $haystack);
    $haystack = preg_replace('/' . $replace_from . '([、。…!\?])/u', $replace_to . '$1', $haystack);

    return $haystack;
}

create_DBVO_wav.php

create_DBVO_wav.php (php)

<?php
define('WAV_DIR', 'wav');

$context_opts = [];
$http_opts = [];

try {
    if ($argc < 2) {
        throw new Exception('Usage: create_DBVO_wav.php [-cfnv] <DBVO local pack json>');
    }

    $options = getopt('cfnv');

    $clean_up_mode = array_key_exists('c', $options);
    $force_create = array_key_exists('f', $options);
    $test_mode = array_key_exists('n', $options);
    $verbose_mode = array_key_exists('v', $options);

    $json_filename = $argv[$argc - 1];

    if ( !is_readable($json_filename) ) {
        throw new Exception($json_filename . ' is not readable.');
    }

    //$json_data = json_decode( file_get_contents($json_filename) );
    $json_data = json_decode_flip( file_get_contents($json_filename) );

    if ( !$json_data ) {
        throw new Exception('json decode failed.');
    }

    //print_r($json_data);
    //exit;

    if ( !is_dir(WAV_DIR) ) {
        mkdir(WAV_DIR);
    }

    $context_opts = [
        'http' => [
            'method' => 'GET',
        ]
    ];

    $http_opts = [
        'model_name' => '2B',
        'model_id' => 0,
        'speaker_id' => 0,
        'sdp_ratio' => 0.2,
        'noise' => 0.6,
        'noisew' => 0.8,
        'length' => 1,
        'language' => 'JP',
        'auto_split' => 'true',
        'split_interval' => 0.5,
        'assist_text_weight' => 1,
        'style' => 'Neutral',
        'style_weight' => 1,
    ];

    $basename_list = [];
    $counter = 0;

    //foreach ($json_data as $japanese => $english) {
    foreach ($json_data as $english => $japanese) {
        $counter++;

        $japanese = preg_replace('/\(.+?\)/', '', $japanese);
        $japanese = preg_replace('/__[0-9a-zA-Z=\.]+?__/', '', $japanese);
        $japanese = preg_replace('/?_/', '?', $japanese);
        $japanese = preg_replace('/!_/', '!', $japanese);
        $japanese = str_replace('_', '、', $japanese);

        $japanese = trim($japanese);

        $japanese = preg_replace('/。$/', '', $japanese);

        $japanese = trim($japanese);

        if ( strlen($japanese) == 0 ) {
            continue;
        }

        if ( $verbose_mode ) {
            echo $counter . ' ' . $english . PHP_EOL;
        }

        $english = str_replace(''', "'", $english);
        $english = str_replace('"', '_', $english);
        $english = str_replace('<', '<', $english);
        $english = str_replace('>', '>', $english);

        $english = preg_replace('/<Alias=Player> */', 'Dovaco', $english);
        $english = preg_replace('/<[^>]+>/', '', $english);
        $english = preg_replace('/(^.+) +\(.+?\)$/', '$1', $english);

        $english = str_replace(' ', '_', $english);
        $english = str_replace('?', '_', $english);
        $english = str_replace('*', '_', $english);
        $english = str_replace('[', '_', $english);
        $english = str_replace(']', '_', $english);
        $english = str_replace('%', '_', $english);
        $english = str_replace(':', '_', $english);

        $filename = WAV_DIR . '/' . $english . '.wav';

        if ( $clean_up_mode ) {
            $basename_list[] = $english;
        } else {
            if ( $verbose_mode ) {
                echo $counter . ' ' . $filename . PHP_EOL;
                echo $counter . ' ' . $japanese . PHP_EOL;
            }

            if ( $force_create || !file_exists($filename) ) {
                if ( !$verbose_mode ) {
                    echo $filename . PHP_EOL;
                    echo $japanese . PHP_EOL;
                }

                if ( !$test_mode ) {
                    create_wav($japanese, $filename);
                }
            }
        }
    }

    if ( $clean_up_mode ) {
        $dh = opendir(WAV_DIR);

        if ( !$dh ) {
            throw new Exception('could not open dir - ' . WAV_DIR);
        }

        $pattern = '/^(.+)\.(wav|lip|fuz)$/';

        while ( $file = readdir($dh) ) {
            if ( $file == '.' ) {
                continue;
            }

            if ( $file == '..' ) {
                continue;
            }

            if ( preg_match($pattern, $file, $matches) ) {
                $basename = $matches[1];

                if ( !in_array($basename, $basename_list) ) {
                    echo $file . PHP_EOL;
                    $fullpath = WAV_DIR . '/' . $file;
                    unlink($fullpath);
                }
            }
        }

        closedir($dh);
    }
} catch (Exception $e) {
    echo $e->GetMessage() . PHP_EOL;
}

function create_wav($text, $filename)
{
    global $context_opts, $http_opts;

    $http_opts['text'] = $text;

    $url = 'http://127.0.0.1:5000/voice?' . http_build_query($http_opts);

    $context = stream_context_create($context_opts);

    $data = file_get_contents($url, false, $context);

    file_put_contents($filename, $data);
}

function json_decode_flip($json)
{
    $array = explode("\n", $json);
    $result = [];

    $counter = 0;

    foreach ($array as $line) {
        $line = trim($line);

        if ( $line == '' || $line == '{' || $line == '}' ) {
            continue;
        }

        $ary = explode('": "', $line);

        $value = ltrim($ary[0], '"');
        $key = rtrim($ary[1], '",');

        //echo $counter . ' ' . $key . PHP_EOL;
        //echo $counter . ' ' . $value . PHP_EOL;

        $result[$key] = $value;

        $counter++;
    }

    return $result;
}

create_lip.php

create_lip.php (php)

<?php
define('FACEFXWRAPPER', 'E:\SteamLibrary\steamapps\common\Skyrim Special Edition\Tools\Audio\FaceFXWrapper.exe');
define('ARG_TYPE', 'Skyrim');
define('ARG_LANG', 'USEnglish');
define('FONIXDATAPATH', 'E:\SteamLibrary\steamapps\common\Skyrim Special Edition\Data\Sound\Voice\Processing\FonixData.cdf');

try {
    $wav_dir = '.';

    if ($argc >= 2) {
        $wav_dir = $argv[$argc - 1];
    }

    parse_dir($wav_dir);
} catch (Exception $e) {
    echo $e->GetMessage() . PHP_EOL;
}

function parse_dir($dir)
{
    $dh = opendir($dir);

    if ( !$dh ) {
        throw new Exception('could not open dir - ' . $dir);
    }

    $pattern = '/\.wav$/';

    while ( $file = readdir($dh) ) {
        if ( $file == '.' ) {
            continue;
        }

        if ( $file == '..' ) {
            continue;
        }

        if ( preg_match($pattern, $file) ) {
            $fullpath = $dir . '/' . $file;
            create_lip($fullpath);
        }
    }

    closedir($dh);
}

function create_lip($wav_filename)
{
    $lip_filename = str_replace('.wav', '.lip', $wav_filename);

    if ( file_exists($lip_filename) ) {
        return;
    }

    $wav_filename_new = $wav_filename;
    $lip_filename_new = $lip_filename;

    if ( strpos($wav_filename, '!') ) {
        $wav_filename_new = str_replace('!', '_', $wav_filename_new);
        $lip_filename_new = str_replace('.wav', '.lip', $wav_filename_new);

        copy($wav_filename, $wav_filename_new);
    }

    echo $wav_filename_new . PHP_EOL;
    echo $lip_filename_new . PHP_EOL;

    // FaceFXWrapper [Type] [Lang] [FonixDataPath] [ResampledWavPath] [LipPath] [Text]
    $cmd = sprintf(
        '"%s" %s %s %s %s %s ""',
        FACEFXWRAPPER,
        escapeshellarg(ARG_TYPE),
        escapeshellarg(ARG_LANG),
        escapeshellarg(FONIXDATAPATH),
        escapeshellarg($wav_filename_new),
        escapeshellarg($lip_filename_new)
    );

    exec($cmd);

    if ( !file_exists($lip_filename_new) ) {
        return;
    }

    if ( $lip_filename_new != $lip_filename ) {
        rename($lip_filename_new, $lip_filename);
        unlink($wav_filename_new);
    }
}

タイトルとURLをコピーしました