注意 (追記 2016年11月18日)
この記事は IkaLog CLI版 を対象にしていますが、現在のバージョンではセリフの追加が簡単になっていますので、参考程度にお読みください。
IkaLog+棒読みちゃんで、セリフをしゃべらせるイベントを増やす方法が何となく分かったので書いてみます。
- 素人がソースを読んで勝手にいじって書いています。
- 用語の間違いや勘違いをしてる事があるかもしれませんが、その辺りを含めてイカよろしくおねがいします。
やったこと
- IkaLog + 棒読みちゃんで喋らせている内容は、IkaLog\ikalog\outputs\commentator.py というファイルに、イベントと対応するセリフを記入するようになっているっぽいです。
- 見たところ、on_game_なんとか とあるのがイベントで、CommentatorDictionary()にセリフな感じです。
- イベントは、stat.inkさん向けのデータを作ってる statink.py に書いてあるのが使えそうな気がしたので、on_game_special_weapon と on_game_low_ink をコピペしてみました。
ファイルの最後にイベントを追加し、対応するセリフも追加、他のセリフもいくつか変更してみました。
注意事項
個人的にガチマッチに行かないので、キルデスから塗りポイントにセリフを入れ替えました。ガチマッチに行かれる方は、変更箇所をオリジナルに戻してください。
※この部分を
'individual_kill_death': [
{'text': 'こんかいのバトルで {score} ポイント もらえました', 'emotion': 'none'},
↓ に書き換えてください。
{'text': '{kill}キル{death}デス', 'emotion': 'none'}, ],
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # IkaLog # ====== # Copyright (C) 2015 ExceptionError # Copyright (C) 2015 Takeshi HASEGAWA # Copyright (C) 2015 AIZAWA Hina # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import json import random import select import socket import sys import traceback import threading from ikalog.constants import * from ikalog.utils import * class CommentatorDictionary(object): ''' 実況アプリ用辞書 emotionは適当に決めてください: http://mikumikumouth.net/developer.html#emotion一覧 ''' # 個人的にガチマッチに行かないので、キルデスから塗りポイントにセリフを入れ替えました。 # このままガチマッチに使うとスコアが0ポイントになってしまうと思うので、駄目な人はオリジナルに戻してください。 # 'individual_kill_death': [ # {'text': '{kill}キル{death}デス', 'emotion': 'none'}, # ], _config = { 'initialize': [ {'text': '読み上げテストです', 'emotion': 'salute'}, ], 'lobby_matching': [ {'text': 'しばらく待ちましょう', 'emotion': 'response'}, ], 'lobby_matched': [ {'text': 'メンバーが揃いました', 'emotion': 'hakushu'}, ], 'start': [ { 'text': '{map}で{rule}が始まります', 'emotion': 'greeting', }, ], 'go_sign': [ {'text': 'しあいかいし! みんな よろしくね', 'emotion': 'meirei'}, ], 'killed': [ {'text': '撃破したわ', 'emotion': 'warai'}, {'text': 'やったわ!', 'emotion': 'wktk'}, ], 'dead': [ {'text': 'こうどうふのお', 'emotion': 'no'}, {'text': 'しまった!', 'emotion': 'cry'}, {'text': 'やあぁらあぁれえぇたあぁ', 'emotion': 'bikkuri'}, ], 'death_reason_identified': [ {'text': '{reason} で やられた!', 'emotion': 'none'} ], 'death_reason_oob': [ {'text': 'はぁいはい ど;おせそどこですよぉ', 'emotion': 'bikkuri'}, ], 'finish': [ {'text': 'しあいしゅうりょお!', 'emotion': 'greeting'}, ], 'individual_result_win': [ {'text': 'かちました! チームのみんな ありがとう', 'emotion': 'happy'}, {'text': 'かてました! チームのみんな ありがとう', 'emotion': 'shy'}, ], 'individual_result_lose': [ {'text': 'まけちゃった…', 'emotion': 'no'}, {'text': "ざんねんです む'ねんです", 'emotion': 'cry'}, ], 'individual_result_unknown': [ {'text': 'よくわからない', 'emotion': 'question'}, {'text': 'どういうこと?', 'emotion': 'tsukkomi'}, ], 'individual_kill_death': [ {'text': 'こんかいのバトルで {score} ポイント もらえました', 'emotion': 'none'}, ], 'session_end': [ {'text': 'おつかれさまでした', 'emotion': 'byebye'}, ], 'session_abort': [ {'text': 'ゲームを見失いました', 'emotion': 'byebye'}, ], 'ranked_we_lead': [ {'text': 'カウントリードした', 'emotion': 'hakushu'}, {'text': 'このまま行けば勝てるぞ', 'emotion': 'wktk'}, ], 'ranked_they_lead': [ {'text': 'カウントリードされた', 'emotion': 'cry'}, {'text': 'このままでは負けるぞ', 'emotion': 'no'}, ], 'splatzone_we_got': [ {'text': 'エリア確保した', 'emotion': 'hakushu'}, {'text': 'エリア確保!', 'emotion': 'wktk'}, ], 'splatzone_we_lost': [ {'text': 'カウントストップされた', 'emotion': 'bikkuri'}, {'text': 'エリアを失った', 'emotion': 'notsay'}, ], 'splatzone_they_got': [ {'text': 'エリア確保された', 'emotion': 'bikkuri'}, {'text': 'エリアを取られた', 'emotion': 'notsay'}, ], 'splatzone_they_lost': [ {'text': 'カウントストップした', 'emotion': 'warai'}, {'text': '食い止めた!', 'emotion': 'happy'}, ], 'tower_we_got': [ {'text': 'ヤグラゲット', 'emotion': 'hakushu'}, {'text': 'ヤグラわ我らのもの', 'emotion': 'response'}, {'text': 'ソイヤ!', 'emotion': 'wktk'}, ], 'tower_we_lost': [ {'text': 'ヤグラがやられた', 'emotion': 'bikkuri'}, {'text': 'ヤグラを失った', 'emotion': 'notsay'}, ], 'tower_they_got': [ {'text': 'ヤグラをとられた', 'emotion': 'cry'}, ], 'tower_they_lost': [ {'text': 'ヤグラをとめた', 'emotion': 'hakushu'}, {'text': 'ヤグラをストップした', 'emotion': 'warai'}, {'text': '食い止めた!', 'emotion': 'happy'}, ], 'rainmaker_we_got': [ {'text': 'ほこゲット', 'emotion': 'hakushu'}, {'text': 'ホコわ我らのもの', 'emotion': 'wktk'}, ], 'rainmaker_we_lost': [ {'text': 'ホコがやられた', 'emotion': 'bikkuri'}, {'text': 'ホコを失った', 'emotion': 'notsay'}, ], 'rainmaker_they_got': [ {'text': 'ホコをとられた', 'emotion': 'cry'}, ], 'rainmaker_they_lost': [ {'text': 'ホコをとめた', 'emotion': 'hakushu'}, {'text': 'ホコをストップした', 'emotion': 'warai'}, {'text': '食い止めた!', 'emotion': 'happy'}, ], 'my_special_weapon': [ {'text': 'でっかいからって ;いぃ;いきにならないでよ', 'emotion': 'happy'}, ], 'low_ink': [ {'text': 'インクっ', 'emotion': 'bikkuri'}, ], } def __init__(self, config): for key in self._config.keys(): if key in config: self._config[key] = config[key] def data(self, key): return random.choice(self._config.get(key, [''])).copy() def get_config(self): return self._config class Commentator(object): ''' 実況者基底クラス ''' custom_read = { '52gal': 'ごーにーガロン', '52gal_deco': 'ごーにーガロンデコ', '96gal': 'きゅーろくガロン', '96gal_deco': 'きゅーろくガロンデコ', 'nzap85': 'エヌザップ85', 'nzap89': 'エヌザップ89', 'bamboo14mk1': 'ひとよん式竹筒じゅう・こう', 'bamboo14mk2': 'ひとよん式竹筒じゅう・おつ', 'bamboo14mk3': 'ひとよん式竹筒じゅう・へい', 'liter3k': 'リッター3ケー', 'liter3k_custom': 'リッター3ケーカスタム', 'liter3k_scope': '3ケースコープ', 'liter3k_scope_custom': '3ケースコープカスタム', 'promodeler_rg': 'プロモデラーアールジー', 'promodeler_mg': 'プロモデラーエムジー', 'squiclean_a': 'スクイックリンアルファ', 'squiclean_b': 'スクイックリンベータ', 'squiclean_g': 'スクイックリンガンマ', 'rapid_elite': 'ラピッドブラスターエリート', 'rapid_elite_deco': 'ラピッドブラスターエリートデコ', 'unknown': '未知の武器', } def __init__(self, dictionary={}): self._enabled = True self._dict = CommentatorDictionary(dictionary) def set_config(self, config): dictionary = config.get(self.config_key(), {}) self._dict = CommentatorDictionary(dictionary) def get_config(self, config): commentator = self._dict.get_config() config[self.config_key()] = commentator return config def _read(self, message): if (not self._enabled): return try: self._do_read(message) except ConnectionRefusedError: error = '「{message}」を読み上げることができませんでした'.format(message=message['text']) IkaUtils.dprint(error) def _do_read(self, message): return def _get_message(self, key): return self._dict.data(key) def _read_event(self, key): self._read(self._get_message(key)) def on_lobby_matching(self, context): self._read_event('lobby_matching') def on_lobby_matched(self, context): self._read_event('lobby_matched') def on_game_start(self, context): map_text = IkaUtils.map2text(context['game']['map'], unknown='スプラトゥーン') rule_text = IkaUtils.rule2text(context['game']['rule'], unknown='ゲーム') data = self._get_message('start') data['text'] = data['text'].format(map=map_text, rule=rule_text) self._read(data) def on_game_go_sign(self, context): self._read_event('go_sign') def on_game_killed(self, context, params): self._read_event('killed') def on_game_dead(self, context): self._read_event('dead') def on_game_death_reason_identified(self, context): reason = context['game']['last_death_reason'] if reason in oob_reasons: data = self._get_message('death_reason_oob') else: label = self._death_reason_label(reason) data = self._get_message('death_reason_identified') data['text'] = data['text'].format(reason=label) self._read(data) def _death_reason_label(self, reason): if reason in self.custom_read: return self.custom_read[reason] return IkaUtils.death_reason2text( reason, self.custom_read['unknown'], 'ja') def on_game_finish(self, context): self._read_event('finish') def on_game_individual_result(self, context): map = IkaUtils.map2text(context['game']['map']) rule = IkaUtils.rule2text(context['game']['rule']) won = IkaUtils.getWinLoseText( context['game']['won'], win_text=self._get_message('individual_result_win'), lose_text=self._get_message('individual_result_lose'), unknown_text=self._get_message('individual_result_unknown') ) self._read(won) me = IkaUtils.getMyEntryFromContext(context) kill = me['kills'] death = me['deaths'] score = 0 if not me['score'] is None: score = me['score'] data = self._get_message('individual_kill_death') data['text'] = data['text'].format(kill=kill, death=death, score=score) self._read(data) def on_game_session_end(self, context): self._read_event('session_end') def on_game_session_abort(self, context): self._read_event('session_abort') def on_game_ranked_we_lead(self, context): self._read_event('ranked_we_lead') def on_game_ranked_they_lead(self, context): self._read_event('ranked_they_lead') def on_game_splatzone_we_got(self, context): self._read_event('splatzone_we_got') def on_game_splatzone_we_lost(self, context): self._read_event('splatzone_we_lost') def on_game_splatzone_they_got(self, context): self._read_event('splatzone_they_got') def on_game_splatzone_they_lost(self, context): self._read_event('splatzone_they_lost') def on_game_rainmaker_we_got(self, context): self._read_event('rainmaker_we_got') def on_game_rainmaker_we_lost(self, context): self._read_event('rainmaker_we_lost') def on_game_rainmaker_they_got(self, context): self._read_event('rainmaker_they_got') def on_game_rainmaker_they_lost(self, context): self._read_event('rainmaker_they_lost') def on_game_tower_we_got(self, context): self._read_event('tower_we_got') def on_game_tower_we_lost(self, context): self._read_event('tower_we_lost') def on_game_tower_they_got(self, context): self._read_event('tower_they_got') def on_game_tower_they_lost(self, context): self._read_event('tower_they_lost') # # スペシャル判定を追加 statink.py から引用 def _get_offset_msec(self, context): if (context['engine'].get('msec') and context['game'].get('start_offset_msec')): return (context['engine']['msec'] - context['game']['start_offset_msec']) return None # def on_game_special_weapon(self, context): special_weapon = context['game'].get('special_weapon', None) if not (special_weapon in special_weapons.keys()): IkaUtils.dprint('%s: special_weapon %s is invalid.' % (self, special_weapon)) return event_msec = self._get_offset_msec(context) if event_msec: #自分のスペシャルか判定 if context['game'].get('special_weapon_is_mine', False): self._read_event('my_special_weapon') def on_game_low_ink(self, context): self._read_event('low_ink')
こんな感じになりました。
相変わらずへたっぴプレイなのですが…みくみくまうす連動も動きました。
動画配信は、これを元にクロマキー合成するそうです。
みくみくまうすの連動のみで、みくみくまうすが棒読みちゃんを制御してくれるみたいです。
最初、2つとも有効にしてたのですが、同じ読み上げを2回するので???ってなりました。
本当に遅ればせながら棒読みちゃんを試してみたのですが、想像以上にしっかり喋るので驚きました。
声の感じをいじったり、セリフを考えたりするのも楽しいです。
VOICEROIDというのがあるみたいですが、そど子の中の人である声優の井澤詩織さんのバージョンをリリースして欲しいです。
最後になりましたが、IkaLogさん stat.inkさん 棒読みちゃんさん みくみくまうすさん ありがとうございます。
0 件のコメント:
コメントを投稿