using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using FNF.Utility; namespace ScDriver.BouyomiChan { public class ScDeviceDriver : ScBaseDriver, IScBaseDriver { private readonly string DrvName = "BouyomiChan.Driver@echoseika.hgotoh.jp"; private readonly string DrvVersion = "20200430/c"; private readonly string DrvProdName = "BOUYOMICHAN"; private readonly int CidBase = 4000; public SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); private BouyomiChanProxy BouyomiTalkProxy = null; private string[] AvatorNames = { "女性1", "女性2", "男性1", "男性2", "中性", "ロボット", "機械1", "機械2" }; private FNF.Utility.VoiceType VT; public ScDeviceDriver() { ScDrvName = DrvName; ScDrvVersion = DrvVersion; ScDrvProdName = DrvProdName; CidBaseIndex = CidBase; AvatorParams = new Dictionary(); IsAlive = false; try { BouyomiTalkProxy = new BouyomiChanProxy(); if (BouyomiTalkProxy.IsActive) { for (int i = 0; i < AvatorNames.Length; i++) { AvatorParam item = new AvatorParam(); item.AvatorIndex = i; item.AvatorName = AvatorNames[i]; item.VoiceEffects_default = new Dictionary { {EnumVoiceEffect.volume, new EffectValueInfo( 100m, 0.0m, 100.0m, 1.00m)}, {EnumVoiceEffect.speed, new EffectValueInfo( 100m, 50.0m, 200.0m, 1.00m)}, {EnumVoiceEffect.pitch, new EffectValueInfo( 100m, 50.0m, 200.0m, 1.00m)} }; item.VoiceEffects = new Dictionary { {EnumVoiceEffect.volume, new EffectValueInfo( 100m, 0.0m, 100.0m, 1.00m)}, {EnumVoiceEffect.speed, new EffectValueInfo( 100m, 50.0m, 200.0m, 1.00m)}, {EnumVoiceEffect.pitch, new EffectValueInfo( 100m, 50.0m, 200.0m, 1.00m)} }; item.VoiceEmotions_default = new Dictionary(); item.VoiceEmotions = new Dictionary(); AvatorParams.Add(i, item); } } } catch (Exception) { // ThrowException(string.Format(@"{0} {1}", e.Message, e.StackTrace)); } IsAlive = AvatorParams.Count != 0; } ~ScDeviceDriver() { BouyomiTalkProxy?.Dispose(); Dispose(false); } public override void Dispose(bool disposing) { if (Disposed) return; if (disposing) { AvatorParams.Clear(); BouyomiTalkProxy?.Dispose(); GC.SuppressFinalize(this); } Disposed = true; } public void Dispose() { Dispose(true); } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト /// 発声にかかった時間(ミリ秒) public override double Play(int cid, string talkText) { Semaphore.Wait(); Stopwatch sw = new Stopwatch(); int avatorIndex = ConvertAvatorIndex(cid); int speed = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.speed].value); int tone = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.pitch].value); int volume = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.volume].value); AvatorSelect(avatorIndex); ApplyEffectParameters(avatorIndex); ApplyEmotionParameters(avatorIndex); sw.Start(); try { // 再生中なら待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } BouyomiTalkProxy.AddTalkTask2(talkText, speed, tone, volume, VT); // 再生中フラグが立つまで待つ while (BouyomiTalkProxy.NowPlaying == false) { Thread.Sleep(10); } // 再生終了まで待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } } catch (Exception) { // } sw.Stop(); Semaphore.Release(); return sw.ElapsedMilliseconds; } /// /// 指定話者で指定テキストで発声 /// /// 話者CID /// 発声させるテキスト public override void PlayAsync(int cid, string talkText) { Task.Run(() => { Semaphore.Wait(); int avatorIndex = ConvertAvatorIndex(cid); int speed = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.speed].value); int tone = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.pitch].value); int volume = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.volume].value); AvatorSelect(avatorIndex); ApplyEffectParameters(avatorIndex); ApplyEmotionParameters(avatorIndex); try { // 再生中なら待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } BouyomiTalkProxy.AddTalkTask2(talkText, speed, tone, volume, VT); // 再生中フラグが立つまで待つ while (BouyomiTalkProxy.NowPlaying == false) { Thread.Sleep(10); } // 再生終了まで待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } } catch (Exception) { // } Semaphore.Release(); }); } /// /// 指定話者で指定テキストで発声した結果をファイルに保存 /// /// 話者CID /// 発声させるテキスト /// 保存先ファイル名 /// 0.0ミリ秒固定 public override double Save(int cid, string talkText, string saveFilename) { int avatorIndex = ConvertAvatorIndex(cid); int speed = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.speed].value); int tone = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.pitch].value); int volume = Convert.ToInt32(AvatorParams[avatorIndex].VoiceEffects[EnumVoiceEffect.volume].value); AvatorSelect(avatorIndex); ApplyEffectParameters(avatorIndex); ApplyEmotionParameters(avatorIndex); try { // 再生中なら待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } BouyomiTalkProxy.AddTalkTask2(talkText, speed, tone, volume, VT); // 再生中フラグが立つまで待つ while (BouyomiTalkProxy.NowPlaying == false) { Thread.Sleep(10); } // 再生終了まで待つ while (BouyomiTalkProxy.NowPlaying == true) { Thread.Sleep(10); } } catch (Exception) { // } return 0.0; } public override void ResetVoiceEmotion(int cid) { } public override void ResetVoiceEffect(int cid) { int avatorIndex = ConvertAvatorIndex(cid); foreach (var effect in AvatorParams[avatorIndex].VoiceEffects_default) { AvatorParams[avatorIndex].VoiceEffects[effect.Key].value = effect.Value.value; } ApplyEffectParameters(avatorIndex); } private void AvatorSelect(int avatorIndex) { switch (avatorIndex) { case 0: VT = VoiceType.Female1; break; case 1: VT = VoiceType.Female2; break; case 2: VT = VoiceType.Male1; break; case 3: VT = VoiceType.Male2; break; case 4: VT = VoiceType.Imd1; break; case 5: VT = VoiceType.Robot1; break; case 6: VT = VoiceType.Machine1; break; case 7: VT = VoiceType.Machine2; break; } } private void ApplyEmotionParameters(int avatorIndex) { } private void ApplyEffectParameters(int avatorIndex) { } } }