努力したWiki

推敲の足りないメモ書き多数

ユーザ用ツール

サイト用ツール


documents:voiceroid:seikacenter:seikacenter-090

SeikaCenterの関係コード

2018/06/09

  • 長くて見辛いよー、と言われたので関連ソースを別ページに分離

概要

VOICEROID2/+EX/CeVIOをコマンドラインからしゃべらせるのSeikaCenterで使っているソースコードで、VOICEROID操作関係を表示しています。

ソースコード

seikasayコマンドとSeikaCenterのいくつかのクラスだけ表示します。
VOICEROID2/+EX/CeVIOをコマンドラインからしゃべらせるの版と同期が取れているわけではないのでご注意ください。

SeikaSayソース

SeikaCenterAPI.dll 経由でSeikaCenterにデータを渡します。コードの大半はコマンドラインのオプション解析処理です。
SeikaCenterAPI.dllの使用例でもあります。

プロパティ SeikaCenterControl.AvatorList と メソッド SeikaCenterControl.GetAvatorParams() は動的にUIを作るときなんかに使えるかもしれませんねぇ。

Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using SeikaCenter;
 
namespace SeikaSay
{
    class Program
    {
        static void Main(string[] args)
        {
            opts opt = new opts(args); // オプション解析クラスです
 
            if (opt.isActive)
            {
                try
                {
                    SeikaCenterControl scc = new SeikaCenterControl(); // SeikaCenterへ接続するSeikaCenterControlクラスのインスタンスを作成
 
                    if (opt.useAvatorList)
                    {
                        Console.WriteLine("cid   speaker");
                        Console.WriteLine("----- ---------------------------");
 
                        // SeikaCenterControl.AvatorList はSeikaCenterが認識している話者の一覧を返す
                        foreach (var item in scc.AvatorList)
                        {
                            Console.WriteLine("{0,5:d} {1}", item.Key, item.Value);
                        }
                        Console.WriteLine("----- ---------------------------");
                        return;
                    }
 
                    if (opt.useParamList)
                    {
                        Console.WriteLine("cid:{0}",opt.cid);
                        Console.WriteLine("----- ---------------------------");
 
                        // SeikaCenterControl.GetAvatorParams() は指定話者の持つパラメタの情報を返す
                        foreach (var item in scc.GetAvatorParams(opt.cid))
                        {
                            Console.WriteLine("{0} : {1}", item.Key, item.Value);
                        }
                        Console.WriteLine("----- ---------------------------");
                        return;
                    }
 
                    // SeikaCenterControl.Talk()は指定話者に指定の音声効果パラメタを与えて発声させる
                    scc.Talk(opt.cid, opt.talkText, opt.saveFilename, opt.volume, opt.speed, opt.pitch, opt.alpha, opt.intonation, opt.emotions);
                }
                catch (Exception e)
                {
                    Console.WriteLine("ccc:{0}", e.Message + e.StackTrace);
                }
            }
            else
            {
                help();
            }
        }
 
        static void help()
        {
            ...
        }
    }
}

ProductControlBase.cs

クラス Voiceroid2 や VoiceroidEx のベースクラスになります。

ProductControlBase.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NAudio.CoreAudioApi;
using NAudio.Wave;
using System.Text.RegularExpressions;
using System.Threading;
using NAudio.Wave.SampleProviders;
using System.IO;
 
namespace SeikaCenter
{
    class ProductControlBase
    {
        protected StringBuilder TalkTextSb = new StringBuilder();
        protected StringBuilder WavFilePathSb = new StringBuilder();
        protected bool aliveInstance = false;
        protected int selectedAvator = 0;
        protected Dictionary<int, avatorParam> avatorParams;
 
        public enum VoiceEffect
        {
            volume,
            speed,
            pitch,
            alpha,
            intonation
        }
 
        protected class avatorParam
        {
            public int avatorIndex = 0;
            public string avatorName = "";
            public Dictionary<VoiceEffect, EffectValueInfo> voiceEffects = null;
            public Dictionary<VoiceEffect, EffectValueInfo> voiceEffects_default = null;
            public Dictionary<string, EffectValueInfo> voiceEmotions = null;
            public Dictionary<string, EffectValueInfo> voiceEmotions_default = null;
        }
 
        protected class EffectValueInfo
        {
            public decimal value;
            public decimal value_min;
            public decimal value_max;
            public decimal value_step;
 
            public EffectValueInfo(decimal val, decimal min, decimal max, decimal step)
            {
                value = val;
                value_min = min;
                value_max = max;
                value_step = step;
            }
        }
 
        private decimal truncs(decimal val, int digits)
        {
            decimal scale = (decimal)Math.Pow(10, digits);
            decimal ans = val > 0 ? Math.Floor(val * scale) / scale : Math.Ceiling(val * scale) / scale;
 
            return ans;
        }
 
        public string TalkText
        {
            set
            {
                TalkTextSb.Clear();
                TalkTextSb.Append(value);
            }
            get
            {
                return TalkTextSb.ToString();
            }
        }
 
        public string WavFilePath
        {
            set
            {
                WavFilePathSb.Clear();
                if ((""!=value)&&(!Regex.IsMatch(value, @"\.[Ww][Aa][Vv]$")))
                {
                    WavFilePathSb.Append(value + ".wav");
                }
                else
                {
                    WavFilePathSb.Append(value);
                }
            }
            get
            {
                return WavFilePathSb.ToString();
            }
        }
 
        public bool isAlive {
            get
            {
                return aliveInstance;
            }
        }
 
        public string[] AvatorList
        {
            get
            {
                return avatorParams.OrderBy(x => x.Key).Select(x => x.Value.avatorName).ToArray<string>();
            }
        }
 
        public int AvatorsCount
        {
            get
            {
                return avatorParams.Count;
            }
        }
 
        public Dictionary<string,decimal> emotionParams
        {
            get
            {
                return avatorParams[selectedAvator].voiceEmotions.ToDictionary(x => x.Key, y => y.Value.value);
            }
        }
 
        public virtual Dictionary<string, decimal> SelectedAvatorParams
        {
            get
            {
                Dictionary<string, decimal> ans = new Dictionary<string, decimal>();
 
                foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects_default)
                {
                    ans["effect_" + item.Key.ToString() + "_value"] = item.Value.value;
                    ans["effect_" + item.Key.ToString() + "_value_min"] = item.Value.value_min;
                    ans["effect_" + item.Key.ToString() + "_value_max"] = item.Value.value_max;
                    ans["effect_" + item.Key.ToString() + "_value_step"] = item.Value.value_step;
                }
                foreach (KeyValuePair<string, EffectValueInfo> item in avatorParams[selectedAvator].voiceEmotions_default)
                {
                    ans["emotion_" + item.Key.ToString() + "_value"] = item.Value.value;
                    ans["emotion_" + item.Key.ToString() + "_value_min"] = item.Value.value_min;
                    ans["emotion_" + item.Key.ToString() + "_value_max"] = item.Value.value_max;
                    ans["emotion_" + item.Key.ToString() + "_value_step"] = item.Value.value_step;
                }
 
                return ans;
            }
        }
 
        public bool ConvStereoToMono { get; set; }
        public bool SkipAudioHeadData { get; set; }
        public decimal SkipAudioHeadTime { get; set; }
 
        public ProductControlBase()
        {
            ConvStereoToMono = false;
            SkipAudioHeadData = false;
            SkipAudioHeadTime = 300;
        }
        public string GetAvatorName(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return "";
 
            return avatorParams[avatorIndex].avatorName;
        }
        public virtual bool SelectAvator(int avatorIndex)
        {
            return false;
        }
        public virtual bool Play(bool async = false)
        {
            return false;
        }
        public virtual bool Save()
        {
            bool ans = false;
            string tempPath = Regex.Replace(WavFilePath, @"\.[Ww][Aa][Vv]$", ".temp.wav");
 
            var cap = new WasapiLoopbackCapture();
            var capWriter = new WaveFileWriter(tempPath, cap.WaveFormat);
 
            try
            {
                cap.ShareMode = AudioClientShareMode.Shared;
 
                cap.DataAvailable += ((s, buf) =>
                {
                    capWriter.Write(buf.Buffer, 0, buf.BytesRecorded);
                });
 
                cap.RecordingStopped += ((s, buf) =>
                {
                    capWriter.Flush();
                    capWriter.Dispose();
                    capWriter = null;
                });
 
                cap.StartRecording();
                ans = Play();
                cap.StopRecording();
 
                while (cap.CaptureState != CaptureState.Stopped)
                {
                    Thread.Sleep(10);
                }
 
                cap.Dispose();
 
                if (File.Exists(WavFilePath)) File.Delete(WavFilePath);
 
                if (!ConvStereoToMono)
                {
                    File.Move(tempPath, WavFilePath);
                }
                else
                {
                    using (var inputReader = new AudioFileReader(tempPath))
                    {
                        if (SkipAudioHeadData)
                        {
                            TimeSpan ms = TimeSpan.FromMilliseconds(Convert.ToDouble(SkipAudioHeadTime));
                            inputReader.CurrentTime = ms;
                        }
 
                        var mono = new StereoToMonoSampleProvider(inputReader);
 
                        mono.LeftVolume = 0.0f; // discard the left channel
                        mono.RightVolume = 1.0f; // keep the right channel
                        WaveFileWriter.CreateWaveFile16(WavFilePath, mono);
                    }
 
                    if (File.Exists(tempPath)) File.Delete(tempPath);
                }
 
                ans = true;
            }
            catch (Exception e)
            {
                if (null != capWriter) capWriter.Dispose();
                if (null != cap) cap.Dispose();
                Console.WriteLine("fs:{0}", e.Message + e.StackTrace);
            }
 
            if (capWriter != null) capWriter.Close();
 
            return ans;
        }
        public virtual bool SetVoiceEffect(VoiceEffect ef, decimal value)
        {
            bool ans = false;
 
            if (avatorParams[selectedAvator].voiceEffects.ContainsKey(ef))
            {
                if (value < avatorParams[selectedAvator].voiceEffects[ef].value_min)
                {
                    avatorParams[selectedAvator].voiceEffects[ef].value = avatorParams[selectedAvator].voiceEffects[ef].value_min;
                }
                else if (value > avatorParams[selectedAvator].voiceEffects[ef].value_max)
                {
                    avatorParams[selectedAvator].voiceEffects[ef].value = avatorParams[selectedAvator].voiceEffects[ef].value_max;
                }
                else
                {
                    avatorParams[selectedAvator].voiceEffects[ef].value = value;
                }
 
                ans = true;
            }
 
            return ans;
        }
        public virtual bool SetVoiceEmotion(string em, decimal value)
        {
            bool ans = false;
 
            if (avatorParams[selectedAvator].voiceEmotions.ContainsKey(em))
            {
                if (value < avatorParams[selectedAvator].voiceEmotions[em].value_min)
                {
                    avatorParams[selectedAvator].voiceEmotions[em].value = avatorParams[selectedAvator].voiceEmotions[em].value_min;
                }
                else if (value > avatorParams[selectedAvator].voiceEmotions[em].value_max)
                {
                    avatorParams[selectedAvator].voiceEmotions[em].value = avatorParams[selectedAvator].voiceEmotions[em].value_max;
                }
                else
                {
                    avatorParams[selectedAvator].voiceEmotions[em].value = value;
                }
 
                ans = true;
            }
 
            return ans;
        }
        public virtual decimal GetVoiceEffect(VoiceEffect ef)
        {
            if (avatorParams[selectedAvator].voiceEffects.ContainsKey(ef))
            {
                throw new Exception("No Effect Data");
            }
 
            return avatorParams[selectedAvator].voiceEffects[ef].value;
        }
        public virtual decimal GetVoiceEmotion(string emo)
        {
            if (avatorParams[selectedAvator].voiceEmotions.ContainsKey(emo))
            {
                throw new Exception("No Emotion Data");
            }
 
            return avatorParams[selectedAvator].voiceEmotions[emo].value;
        }
        public virtual bool ResetVoiceEffect()
        {
            foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects_default)
            {
                avatorParams[selectedAvator].voiceEffects[item.Key].value = item.Value.value;
            }
 
            return true;
        }
        public virtual bool ResetVoiceEmotion()
        {
            foreach (KeyValuePair<string, EffectValueInfo> item in avatorParams[selectedAvator].voiceEmotions_default)
            {
                avatorParams[selectedAvator].voiceEmotions[item.Key].value = item.Value.value;
            }
 
            return true;
        }
    }
}

VoiceroidEx.cs

リクエストがあったので掲載。

VoiceroidEx.cs
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Windows.Grasp;
using Ong.Friendly.FormsStandardControls;
using NAudio.Wave;
using NAudio.CoreAudioApi;
 
namespace SeikaCenter
{
    class VoiceroidEx : ProductControlBase
    {
        Dictionary<int, avatorUIParam> avatorUIParams;
        class avatorUIParam
        {
            public WindowsAppFriend _app = null;
            public WindowControl uiTreeTop = null;
 
            public WindowControl talkTextBox = null;
            public FormsButton playButton = null;
            public FormsButton saveButton = null;
 
            public FormsTextBox volumeText = null;
            public FormsTextBox speedText = null;
            public FormsTextBox pitchText = null;
            public FormsTextBox intonationText = null;
        }
 
        public VoiceroidEx(string databaseFilename)
        {
            int index = 0;
 
            avatorParams = new Dictionary<int, avatorParam>();
            avatorUIParams = new Dictionary<int, avatorUIParam>();
 
            Dictionary<Avator,Process> p = GetVoiceroidProcess();
 
            aliveInstance = false;
 
            foreach (KeyValuePair<Avator, Process> v in p)
            {
                try
                {
                    avatorParam item = new avatorParam();
                    avatorUIParam uiItem = new avatorUIParam();
 
                    uiItem._app = new WindowsAppFriend(v.Value);
                    uiItem.uiTreeTop = WindowControl.FromZTop(uiItem._app);
 
                    // Zインデクスはコーディア様の TestAssistantで確認可能
                    // 音声効果タブ切替
                    FormsTabControl a6 = new FormsTabControl(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 0, 0));
                    a6.EmulateTabSelect(2);
 
                    uiItem.talkTextBox = uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 1);
                    uiItem.playButton = new FormsButton(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 3));
                    uiItem.saveButton = new FormsButton(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 1, 0, 1, 0, 1));
                    uiItem.volumeText = new FormsTextBox(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, 8));
                    uiItem.speedText = new FormsTextBox(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, 9));
                    uiItem.pitchText = new FormsTextBox(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, 10));
                    uiItem.intonationText = new FormsTextBox(uiItem.uiTreeTop.IdentifyFromZIndex(2, 0, 0, 0, 0, 0, 0, 11));
 
                    avatorUIParams.Add(index, uiItem);
 
                    item.avatorIndex = index;
                    item.avatorName = string.Format("{0}", v.Key.ProdName());
 
                    item.voiceEffects_default = new Dictionary<VoiceEffect, EffectValueInfo>
                    {
                        {VoiceEffect.volume,     new EffectValueInfo(GetSliderValue(VoiceEffect.volume), 0.0m, 2.0m, 0.01m)},
                        {VoiceEffect.speed,      new EffectValueInfo(GetSliderValue(VoiceEffect.speed), 0.5m, 4.0m, 0.01m)},
                        {VoiceEffect.pitch,      new EffectValueInfo(GetSliderValue(VoiceEffect.pitch), 0.5m, 2.0m, 0.01m)},
                        {VoiceEffect.intonation, new EffectValueInfo(GetSliderValue(VoiceEffect.intonation), 0.0m, 2.0m, 0.01m)}
                    };
                    item.voiceEffects = new Dictionary<VoiceEffect, EffectValueInfo>
                    {
                        {VoiceEffect.volume,     new EffectValueInfo(GetSliderValue(VoiceEffect.volume), 0.0m, 2.0m, 0.01m)},
                        {VoiceEffect.speed,      new EffectValueInfo(GetSliderValue(VoiceEffect.speed), 0.5m, 4.0m, 0.01m)},
                        {VoiceEffect.pitch,      new EffectValueInfo(GetSliderValue(VoiceEffect.pitch), 0.5m, 2.0m, 0.01m)},
                        {VoiceEffect.intonation, new EffectValueInfo(GetSliderValue(VoiceEffect.intonation), 0.0m, 2.0m, 0.01m)}
                    };
 
                    item.voiceEmotions = new Dictionary<string, EffectValueInfo>();
                    item.voiceEmotions_default = new Dictionary<string, EffectValueInfo>();
 
                    avatorParams.Add(index, item);
 
                    index++;
                }
                catch (Exception egg)
                {
                    Console.WriteLine("egg:{0},{1}", egg.Message,egg.StackTrace);
                }
            }
 
            //exVoice準備
            vdb = new ExVoiceIndexDb(databaseFilename);
 
            aliveInstance = avatorParams.Count != 0 ? true : false;
        }
        public override bool SelectAvator(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return false;
 
            selectedAvator = avatorIndex;
 
            return true;
        }
        public override double Play(bool async = false)
        {
            Stopwatch sw = new Stopwatch();
 
            if (avatorUIParams[selectedAvator].playButton == null) return 0.0;
            if (avatorUIParams[selectedAvator].saveButton == null) return 0.0;
            if (avatorUIParams[selectedAvator].talkTextBox == null) return 0.0;
 
            ApplyEffectParameters();
            ApplyEmotionParameters();
 
            GetMMDevice();
 
            sw.Start();
            for (int i=0; i < TalkTextItems.Count; i++)
            {
                TalkTextItem item = TalkTextItems[i];
 
                switch (item.type)
                {
                    case TalkTextItem.vType.text:
 
                        avatorUIParams[selectedAvator].talkTextBox["Text"](item.data);
 
                        if (!avatorUIParams[selectedAvator].saveButton.Enabled)
                        {
                            Console.WriteLine("a:wait..");
                            while (!avatorUIParams[selectedAvator].saveButton.Enabled)
                            {
                                Thread.Sleep(10);
                            }
                            Console.WriteLine("a:finish");
                        }
 
                        avatorUIParams[selectedAvator].playButton.EmulateClick();
                        Thread.Sleep(10); // 1000 -> 10
 
                        if (!async)
                        {
                            if (!avatorUIParams[selectedAvator].saveButton.Enabled)
                            {
                                Console.WriteLine("b:wait..");
                                while (!avatorUIParams[selectedAvator].saveButton.Enabled)
                                {
                                    Thread.Sleep(10);
                                }
                                Console.WriteLine("b:finish");
                            }
                        }
                        break;
 
                    case TalkTextItem.vType.exvoice:
                        try
                        {
                            using (var audioFile = new AudioFileReader(item.data))
                            using (var outputDevice = new WasapiOut(capDev, AudioClientShareMode.Shared, false, 0))
                            {
                                outputDevice.Init(audioFile);
                                outputDevice.Play();
                                while (outputDevice.PlaybackState == PlaybackState.Playing)
                                {
                                    Thread.Sleep(50);
                                }
                            }
                        }
                        catch (Exception)
                        {
                            //
                        }
                        break;
                }
            }
            sw.Stop();
 
            return sw.ElapsedMilliseconds;
        }
 
        private void ApplyEmotionParameters()
        {
            if (avatorParams.Count == 0) return;
        }
        private void ApplyEffectParameters()
        {
            if (avatorParams.Count == 0) return;
 
            FormsTextBox ui1 = null;
 
            foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects)
            {
                switch (item.Key)
                {
                    case VoiceEffect.volume:
                        ui1 = avatorUIParams[selectedAvator].volumeText;
                        break;
 
                    case VoiceEffect.speed:
                        ui1 = avatorUIParams[selectedAvator].speedText;
                        break;
 
                    case VoiceEffect.pitch:
                        ui1 = avatorUIParams[selectedAvator].pitchText;
                        break;
 
                    case VoiceEffect.intonation:
                        ui1 = avatorUIParams[selectedAvator].intonationText;
                        break;
                }
 
                double p = Convert.ToDouble(avatorParams[selectedAvator].voiceEffects[item.Key].value);
 
                if (ui1 != null) ui1.EmulateChangeText(string.Format("{0:0.00}", p));
            }
        }
 
        public decimal GetSliderValue(VoiceEffect ef)
        {
            decimal ans = 0.00m;
            FormsTextBox ui1 = null;
 
            switch (ef)
            {
                case VoiceEffect.volume:
                    ui1 = avatorUIParams[selectedAvator].volumeText;
                    break;
 
                case VoiceEffect.speed:
                    ui1 = avatorUIParams[selectedAvator].speedText;
                    break;
 
                case VoiceEffect.pitch:
                    ui1 = avatorUIParams[selectedAvator].pitchText;
                    break;
 
                case VoiceEffect.intonation:
                    ui1 = avatorUIParams[selectedAvator].intonationText;
                    break;
            }
 
            ans = Convert.ToDecimal(ui1.Text);
 
            return ans;
        }
 
        private Dictionary<Avator, Process> GetVoiceroidProcess()
        {
            Process[] ps = Process.GetProcesses();
            Dictionary<Avator, Process> p = new Dictionary<Avator, Process>();
 
            foreach (Avator item in Enum.GetValues(typeof(Avator)))
            {
                string winTitle1 = item.ProdName();
                string winTitle2 = winTitle1 + "*";
 
                foreach (Process pitem in ps)
                {
                    if ((pitem.MainWindowHandle != IntPtr.Zero) &&
                         ((pitem.MainWindowTitle.Equals(winTitle1)) || (pitem.MainWindowTitle.Equals(winTitle2))))
                    {
                        p.Add(item, pitem);
                        break;
                    }
                }
            }
 
            return p;
        }
    }
 
    enum Avator
    {
        SEIKA,
        YOSHIDA_EX,
        AI_EX,
        SHOUTA_EX,
        MINASE,
        KIRITAN,
        ZUNKO,
        ZUNKO_EX,
        TAMMY_EX,
        YUKARI_EX,
        AKANE,
        AOI,
        UNA,
        GALACO1,
        GALACO2
    }
    static class AvatorEx
    {
        public static string ProdName(this Avator value)
        {
            string[] values =
            {
                "VOICEROID+ 京町セイカ EX",
                "VOICEROID+ 鷹の爪 吉田くん EX",
                "VOICEROID+ 月読アイ EX",
                "VOICEROID+ 月読ショウタ EX",
                "VOICEROID+ 水奈瀬コウ EX",
                "VOICEROID+ 東北きりたん EX",
                "VOICEROID+ 東北ずん子",
                "VOICEROID+ 東北ずん子 EX",
                "VOICEROID+ 民安ともえ EX",
                "VOICEROID+ 結月ゆかり EX",
                "VOICEROID+ 琴葉茜",
                "VOICEROID+ 琴葉葵",
                "音街ウナTalk Ex",
                "ギャラ子Talk",
                "ギャラ子 Talk"
            };
 
            return values[(int)value];
        }
    }
}

Voiceroid2.cs

UIコンポーネントの特定方法はかなりいい加減です。将来のアップデートで対応できなくなってしまう可能性があります。

ボイスタブのスタイルで表示されている喜び、怒り、悲しみのパラメタですが、これらは1度レンダリングされたら維持されるものではなくて毎回作られる模様です。よくわかりません。

Voiceroid2.cs
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using Codeer.Friendly;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Windows.Grasp;
using RM.Friendly.WPFStandardControls;
using NAudio.Wave;
using NAudio.CoreAudioApi;
 
namespace SeikaCenter
{
    class Voiceroid2 : ProductControlBase
    {
        WindowsAppFriend _app = null;
        WindowControl uiTreeTop = null;
 
        WPFListView avatorListView_std = null;
        WPFListView avatorListView_usr = null;
        WPFTabControl voicePresetTab = null;
 
        WPFTextBox talkTextBox = null;
        WPFButtonBase playButton = null;
        WPFButtonBase saveButton = null;
 
        Dictionary<int, avatorUIParam> avatorUIParams;
        class avatorUIParam
        {
            public int presetTabIndex = 0;
            public bool withEmotionParams = false;
            public WPFSlider volumeSlider = null;
            public WPFSlider speedSlider = null;
            public WPFSlider pitchSlider = null;
            public WPFSlider intonationSlider = null;
            public WPFSlider happinessSlider = null;
            public WPFSlider hatredSlider = null;
            public WPFSlider sadnessSlider = null;
        }
 
        public Voiceroid2(string databaseFilename)
        {
            avatorParams = new Dictionary<int, avatorParam>();
            avatorUIParams = new Dictionary<int, avatorUIParam>();
 
            Process p = GetVoiceroidEditorProcess();
 
            aliveInstance = false;
 
            if (p != null)
            {
                try
                {
                    _app = new WindowsAppFriend(p);
                    uiTreeTop = WindowControl.FromZTop(_app);
 
                    //if (uiTreeTop.LogicalTree().Count!=0)
                    //{
                    //    var items = uiTreeTop.LogicalTree();
                    //    for (int i=0; i<items.Count; i++)
                    //    {
                    //        Console.WriteLine("*** :{0}", items[i]);
                    //    }
                    //}
 
                    //判明しているGUI要素特定
                    var editUis = uiTreeTop.GetFromTypeFullName("AI.Talk.Editor.TextEditView")[0].LogicalTree();
 
                    talkTextBox = new WPFTextBox(editUis[4]);
                    playButton = new WPFButtonBase(editUis[6]);
                    saveButton = new WPFButtonBase(editUis[24]);
 
                    var tabs = uiTreeTop.GetFromTypeFullName("AI.Framework.Wpf.Controls.TitledTabControl");
                    var tab1 = new WPFTabControl(tabs[1]); // チューニングのタブコントロール
                    voicePresetTab = new WPFTabControl(tabs[0]); // ボイスプリセットのタブコントロール
 
                    //標準タブにいる各話者毎のGUI要素データを取得
                    tab1.EmulateChangeSelectedIndex(1);
                    voicePresetTab.EmulateChangeSelectedIndex(0);
                    avatorListView_std = new WPFListView( uiTreeTop.GetFromTypeFullName("System.Windows.Controls.ListView")[0] );
                    ScanPreset(avatorListView_std, 0, 0);
 
                    //ユーザータブにいる各話者プリセット毎のGUI要素データを取得
                    tab1.EmulateChangeSelectedIndex(1);
                    voicePresetTab.EmulateChangeSelectedIndex(1);
                    avatorListView_usr = new WPFListView(uiTreeTop.GetFromTypeFullName("System.Windows.Controls.ListView")[1]);
                    ScanPreset(avatorListView_usr, avatorListView_std.ItemCount, 1);
 
 
                    //exVoice準備
                    vdb = new ExVoiceIndexDb(databaseFilename);
 
                    aliveInstance = true;
                }
                catch(Exception ev2)
                {
                    Console.WriteLine("ev2:{0}", ev2.Message + ev2.StackTrace);
                    aliveInstance = false;
                }
            }
        }
        private void ScanPreset(WPFListView avatorListView, int indexbase, int presettabIndex)
        {
            for (int i = 0; i < avatorListView.ItemCount; i++)
            {
                avatorParam item = new avatorParam();
                avatorUIParam uiItem = new avatorUIParam();
 
                avatorListView.EmulateChangeSelectedIndex(i);
 
                //ListViewから話者名を取れなかったのでダサい方法で対処
                var params1 = uiTreeTop.GetFromTypeFullName("AI.Framework.Wpf.Controls.TextBoxEx");
                var nameTextBox = new WPFTextBox(params1[8]);
 
                //スライダーの配列を取得
                var params2 = uiTreeTop.GetFromTypeFullName("System.Windows.Controls.Slider");
                uiItem.volumeSlider = new WPFSlider(params2[7]);
                uiItem.speedSlider = new WPFSlider(params2[8]);
                uiItem.pitchSlider = new WPFSlider(params2[9]);
                uiItem.intonationSlider = new WPFSlider(params2[10]);
 
                //アイテムがあるリストボックスが見つかるならスタイル(喜び・怒り・悲しみ)がある
                var params3 = uiTreeTop.GetFromTypeFullName("System.Windows.Controls.ListBox");
                if (params3.Length > 0)
                {
                    if (params2.Length > 13)
                    {
                        uiItem.happinessSlider = new WPFSlider(params2[13]);
                        uiItem.hatredSlider = new WPFSlider(params2[14]);
                        uiItem.sadnessSlider = new WPFSlider(params2[15]);
                        uiItem.withEmotionParams = true;
                    }
                }
 
                uiItem.presetTabIndex = presettabIndex;
                avatorUIParams.Add(indexbase + i, uiItem);
 
                item.avatorIndex = i;
                item.avatorName = nameTextBox.Text;
 
                item.voiceEffects_default = new Dictionary<VoiceEffect, EffectValueInfo>
                        {
                            {VoiceEffect.volume,     new EffectValueInfo(GetSliderValue(VoiceEffect.volume), 0.0m, 2.0m, 0.01m)},
                            {VoiceEffect.speed,      new EffectValueInfo(GetSliderValue(VoiceEffect.speed), 0.5m, 4.0m, 0.01m)},
                            {VoiceEffect.pitch,      new EffectValueInfo(GetSliderValue(VoiceEffect.pitch), 0.5m, 2.0m, 0.01m)},
                            {VoiceEffect.intonation, new EffectValueInfo(GetSliderValue(VoiceEffect.intonation), 0.0m, 2.0m, 0.01m)}
                        };
                item.voiceEffects = new Dictionary<VoiceEffect, EffectValueInfo>
                        {
                            { VoiceEffect.volume,     new EffectValueInfo(GetSliderValue(VoiceEffect.volume), 0.0m, 2.0m, 0.01m)},
                            { VoiceEffect.speed,      new EffectValueInfo(GetSliderValue(VoiceEffect.speed), 0.5m, 4.0m, 0.01m)},
                            { VoiceEffect.pitch,      new EffectValueInfo(GetSliderValue(VoiceEffect.pitch), 0.5m, 2.0m, 0.01m)},
                            { VoiceEffect.intonation, new EffectValueInfo(GetSliderValue(VoiceEffect.intonation), 0.0m, 2.0m, 0.01m)}
                        };
 
                item.voiceEmotions = new Dictionary<string, EffectValueInfo>();
                item.voiceEmotions_default = new Dictionary<string, EffectValueInfo>();
                if (uiItem.withEmotionParams)
                {
                    item.voiceEmotions_default.Add("喜び", new EffectValueInfo(GetSliderValue("喜び"), 0.00m, 1.00m, 0.01m));
                    item.voiceEmotions_default.Add("怒り", new EffectValueInfo(GetSliderValue("怒り"), 0.00m, 1.00m, 0.01m));
                    item.voiceEmotions_default.Add("悲しみ", new EffectValueInfo(GetSliderValue("悲しみ"), 0.00m, 1.00m, 0.01m));
                    item.voiceEmotions.Add("喜び", new EffectValueInfo(GetSliderValue("喜び"), 0.00m, 1.00m, 0.01m));
                    item.voiceEmotions.Add("怒り", new EffectValueInfo(GetSliderValue("怒り"), 0.00m, 1.00m, 0.01m));
                    item.voiceEmotions.Add("悲しみ", new EffectValueInfo(GetSliderValue("悲しみ"), 0.00m, 1.00m, 0.01m));
                }
 
                avatorParams.Add(indexbase + i, item);
            }
        }
        public override bool SelectAvator(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return false;
 
            selectedAvator = avatorIndex;
            switch (avatorUIParams[avatorIndex].presetTabIndex)
            {
                case 0:
                default:
                    if (avatorListView_std == null) return false;
                    voicePresetTab.EmulateChangeSelectedIndex(0);
                    avatorListView_std.EmulateChangeSelectedIndex(avatorParams[avatorIndex].avatorIndex);
                    break;
                case 1:
                    if (avatorListView_usr == null) return false;
                    voicePresetTab.EmulateChangeSelectedIndex(1);
                    avatorListView_usr.EmulateChangeSelectedIndex(avatorParams[avatorIndex].avatorIndex);
                    break;
            }
 
            return true;
        }
        public override double Play(bool async = false)
        {
            Stopwatch sw = new Stopwatch();
 
            if (playButton == null) return 0.0;
            if (saveButton == null) return 0.0;
            if (talkTextBox == null) return 0.0;
 
            ApplyEffectParameters();
            ApplyEmotionParameters();
 
            GetMMDevice();
 
            sw.Start();
            for (int i=0; i<TalkTextItems.Count; i++)
            {
                TalkTextItem item = TalkTextItems[i];
 
                switch (item.type)
                {
                    case TalkTextItem.vType.text:
 
                        talkTextBox.EmulateChangeText(item.data);
 
                        if (!saveButton.IsEnabled)
                        {
                            Console.WriteLine("a:wait..");
                            while (!saveButton.IsEnabled)
                            {
                                Thread.Sleep(10);
                            }
                            Console.WriteLine("a:finish");
                        }
 
                        playButton.EmulateClick();
                        Thread.Sleep(10);
 
                        if (!async)
                        {
                            if (!saveButton.IsEnabled)
                            {
                                Console.WriteLine("b:wait..");
                                while (!saveButton.IsEnabled)
                                {
                                    Thread.Sleep(10);
                                }
                                Console.WriteLine("b:finish");
                            }
                        }
                        break;
 
                    case TalkTextItem.vType.exvoice:
                        try
                        {
                            using (var audioFile = new AudioFileReader(item.data))
                            using (var outputDevice = new WasapiOut(capDev, AudioClientShareMode.Shared, false, 0))
                            {
                                outputDevice.Init(audioFile);
                                outputDevice.Play();
                                while (outputDevice.PlaybackState == PlaybackState.Playing)
                                {
                                    Thread.Sleep(50);
                                }
                            }
                        }
                        catch (Exception)
                        {
                            //
                        }
                        break;
                }
            }
            sw.Stop();
 
            return sw.ElapsedMilliseconds;
        }
        private void ApplyEmotionParameters()
        {
            if (avatorParams.Count == 0) return;
 
            if (avatorUIParams[selectedAvator].withEmotionParams)
            {
                WPFSlider ui1 = null;
                AppVar ui2 = null;
                AppVar[] faders = uiTreeTop.GetFromTypeFullName("System.Windows.Controls.Slider");
 
                foreach (KeyValuePair<string, EffectValueInfo> item in avatorParams[selectedAvator].voiceEmotions)
                {
                    switch (item.Key)
                    {
                        case "喜び":
                            ui1 = avatorUIParams[selectedAvator].happinessSlider;
                            ui2 = faders.Length > 14 ? faders[13] : null;
                            break;
 
                        case "怒り":
                            ui1 = avatorUIParams[selectedAvator].hatredSlider;
                            ui2 = faders.Length > 15 ? faders[14] : null;
                            break;
 
                        case "悲しみ":
                            ui1 = avatorUIParams[selectedAvator].sadnessSlider;
                            ui2 = faders.Length == 16 ? faders[15] : null;
                            break;
                    }
 
                    double p = Convert.ToDouble(avatorParams[selectedAvator].voiceEmotions[item.Key].value);
 
                    if (ui1 != null) ui1.EmulateChangeValue(p);
                    if (ui2 != null) ui2["Value"](p);
                }
            }
        }
        private void ApplyEffectParameters()
        {
            if (avatorParams.Count == 0) return;
 
            WPFSlider ui1 = null;
 
            foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects)
            {
                switch (item.Key)
                {
                    case VoiceEffect.volume:
                        ui1 = avatorUIParams[selectedAvator].volumeSlider;
                        break;
 
                    case VoiceEffect.speed:
                        ui1 = avatorUIParams[selectedAvator].speedSlider;
                        break;
 
                    case VoiceEffect.pitch:
                        ui1 = avatorUIParams[selectedAvator].pitchSlider;
                        break;
 
                    case VoiceEffect.intonation:
                        ui1 = avatorUIParams[selectedAvator].intonationSlider;
                        break;
                }
 
                double p = Convert.ToDouble(avatorParams[selectedAvator].voiceEffects[item.Key].value);
 
                if (ui1 != null) ui1.EmulateChangeValue(p);
            }
        }
        public bool withEmotionParams(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return false;
 
            return avatorUIParams[avatorIndex].withEmotionParams;
        }
        public decimal GetSliderValue(VoiceEffect ef)
        {
            decimal ans = 0.00m;
            WPFSlider ui1 = null;
 
            switch (ef)
            {
                case VoiceEffect.volume:
                    ui1 = avatorUIParams[selectedAvator].volumeSlider;
                    break;
 
                case VoiceEffect.speed:
                    ui1 = avatorUIParams[selectedAvator].speedSlider;
                    break;
 
                case VoiceEffect.pitch:
                    ui1 = avatorUIParams[selectedAvator].pitchSlider;
                    break;
 
                case VoiceEffect.intonation:
                    ui1 = avatorUIParams[selectedAvator].intonationSlider;
                    break;
            }
 
            ans = Convert.ToDecimal(ui1.Value);
 
            return ans;
        }
        public decimal GetSliderValue(string emo)
        {
            WPFSlider ui2 = null;
            AppVar[] faders = uiTreeTop.GetFromTypeFullName("System.Windows.Controls.Slider");
 
            switch (emo)
            {
                case "喜び":
                    ui2 = faders.Length > 14 ? new WPFSlider(faders[13]) : null;
                    break;
 
                case "怒り":
                    ui2 = faders.Length > 15 ? new WPFSlider(faders[14]) : null;
                    break;
 
                case "悲しみ":
                    ui2 = faders.Length == 16 ? new WPFSlider(faders[15]) : null;
                    break;
            }
 
            if (ui2 == null)
            {
                throw new Exception("Effect Slider not found");
            }
 
            return Convert.ToDecimal(ui2.Value);
        }
        private Process GetVoiceroidEditorProcess()
        {
            string winTitle1 = "VOICEROID2";
            string winTitle2 = winTitle1 + "*";
 
            int RetryCount = 3;
            int RetryWaitms = 500;
            Process p = null;
 
            for (int i = 0; i < 3; i++)
            {
                Process[] ps = Process.GetProcesses();
 
                foreach (Process pitem in ps)
                {
                    if ((pitem.MainWindowHandle != IntPtr.Zero) &&
                         ((pitem.MainWindowTitle.Equals(winTitle1)) || (pitem.MainWindowTitle.Equals(winTitle2))))
                    {
                        p = pitem;
                        if (i < (RetryCount - 1)) Thread.Sleep(RetryWaitms);
                    }
                }
            }
 
            return p;
        }
 
    }
}

Cevio.cs

リクエストがあったので掲載。外部連携インタフェースが用意されているのはありがたい。

CeVIOを使う場合、2018/06/04時点では32bit版のバイナリとして作る必要がある。

CeVIO専用なら遅延バインドする必要はない。CeVIOが存在しない環境が考えられるなら遅延バインドしないといけない。
遅延バインド時はAppDomainを使って外部連携インタフェースのアセンブリを解放可能にしておく必要がある。

Cevio.cs
using System;
using System.Collections.Generic;
using System.Reflection;
using NAudio.Wave;
using NAudio.CoreAudioApi;
using System.Threading;
using System.Diagnostics;
 
namespace SeikaCenter
{
    class Cevio : ProductControlBase
    {
        CevioProxy cevioTalker = null;
        string Version { get; }
 
        public Cevio(string databaseFilename)
        {
            avatorParams = new Dictionary<int, avatorParam>();
 
            try
            {
                AppDomain appDomain = AppDomain.CreateDomain("SeikaCenterCevioPlugin");
                Type t2 = typeof(CevioProxy);
                cevioTalker = (CevioProxy)appDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, t2.FullName);
 
                dynamic talkers = cevioTalker.AvailableCasts;
 
                if (talkers.Length !=0)
                {
                    for (int i = 0; i < talkers.Length; i++)
                    {
                        avatorParam item = new avatorParam();
 
                        cevioTalker.Cast = talkers[i];
 
                        item.avatorIndex = i;
                        item.avatorName = talkers[i];
 
                        item.voiceEffects_default = new Dictionary<VoiceEffect, EffectValueInfo>
                            {
                                {VoiceEffect.volume,     new EffectValueInfo( (decimal)cevioTalker.Volume, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.speed,      new EffectValueInfo( (decimal)cevioTalker.Speed, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.pitch,      new EffectValueInfo( (decimal)cevioTalker.Tone, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.alpha,      new EffectValueInfo( (decimal)cevioTalker.Alpha, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.intonation, new EffectValueInfo( (decimal)cevioTalker.ToneScale, 0.0m, 100.0m, 1.00m)}
                            };
                        item.voiceEffects = new Dictionary<VoiceEffect, EffectValueInfo>
                            {
                                {VoiceEffect.volume,     new EffectValueInfo( (decimal)cevioTalker.Volume, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.speed,      new EffectValueInfo( (decimal)cevioTalker.Speed, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.pitch,      new EffectValueInfo( (decimal)cevioTalker.Tone, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.alpha,      new EffectValueInfo( (decimal)cevioTalker.Alpha, 0.0m, 100.0m, 1.00m)},
                                {VoiceEffect.intonation, new EffectValueInfo( (decimal)cevioTalker.ToneScale, 0.0m, 100.0m, 1.00m)}
                            };
 
                        item.voiceEmotions_default = new Dictionary<string, EffectValueInfo>();
                        item.voiceEmotions = new Dictionary<string, EffectValueInfo>();
                        Dictionary<string, uint> emoparams = cevioTalker.Components;
                        if (0 < emoparams.Count)
                        {
                            foreach (KeyValuePair<string, uint> emo in emoparams)
                            {
                                item.voiceEmotions_default.Add(emo.Key, new EffectValueInfo((decimal)(emo.Value), 0.00m, 100.0m, 1.00m));
                                item.voiceEmotions.Add(emo.Key, new EffectValueInfo((decimal)(emo.Value), 0.00m, 100.0m, 1.00m));
                            }
                        }
 
                        avatorParams.Add(i, item);
                    }
 
                    //exVoice準備
                    vdb = new ExVoiceIndexDb(databaseFilename);
 
                    aliveInstance = true;
                }
            }
            catch(Exception e)
            {
                Console.WriteLine("{0},{1}", e.Message,e.StackTrace);
                aliveInstance = false;
            }
        }
 
        public override bool SelectAvator(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return false;
 
            selectedAvator = avatorIndex;
            cevioTalker.Cast = avatorParams[avatorIndex].avatorName;
 
            return true;
        }
        public override double Play(bool async = false)
        {
            Stopwatch sw = new Stopwatch();
 
            ApplyEffectParameters();
            ApplyEmotionParameters();
 
            GetMMDevice();
 
            sw.Start();
            for (int i = 0; i < TalkTextItems.Count; i++)
            {
                TalkTextItem item = TalkTextItems[i];
 
                switch (item.type)
                {
                    case TalkTextItem.vType.text:
                        try
                        {
                            cevioTalker.Speak(item.data, async);
                        }
                        catch (Exception)
                        {
                            //
                        }
 
                        break;
 
                    case TalkTextItem.vType.exvoice:
                        try
                        {
                            using (var audioFile = new AudioFileReader(item.data))
                            using (var outputDevice = new WasapiOut(capDev, AudioClientShareMode.Shared, false, 0))
                            {
                                outputDevice.Init(audioFile);
                                outputDevice.Play();
                                while (outputDevice.PlaybackState == PlaybackState.Playing)
                                {
                                    Thread.Sleep(50);
                                }
                            }
                        }
                        catch (Exception)
                        {
                            //
                        }
                        break;
                }
            }
            sw.Stop();
 
            return sw.ElapsedMilliseconds;
        }
        //public override bool Save()
        //{
        //    bool ans = false;
 
        //    ApplyEffectParameters();
        //    ApplyEmotionParameters();
        //    try
        //    {
        //        cevioTalker.OutputWaveToFile(TalkText, WavFilePath);
        //        ans = true;
        //    }
        //    catch (Exception e2e)
        //    {
        //        Console.WriteLine("e2e:{0},{1},{2}", e2e.Message, e2e.InnerException == null ? "" : e2e.InnerException.Message, e2e.StackTrace);
 
        //        ans = false;
        //    }
 
        //    return ans;
        //}
 
        private void ApplyEmotionParameters()
        {
            if (avatorParams.Count != 0)
            {
                foreach (KeyValuePair<string, EffectValueInfo> item in avatorParams[selectedAvator].voiceEmotions)
                {
                    cevioTalker.SetComponent(item.Key, (uint)(item.Value.value));
                }
            }
        }
        private void ApplyEffectParameters()
        {
            if (avatorParams.Count != 0)
            {
                if (!KeepEmotionSetting)
                {
                    foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects_default)
                    {
                        switch (item.Key)
                        {
                            case VoiceEffect.volume:
                                cevioTalker.Volume = (uint)(item.Value.value);
                                break;
 
                            case VoiceEffect.speed:
                                cevioTalker.Speed = (uint)(item.Value.value);
                                break;
 
                            case VoiceEffect.pitch:
                                cevioTalker.Tone = (uint)(item.Value.value);
                                break;
 
                            case VoiceEffect.alpha:
                                cevioTalker.Alpha = (uint)(item.Value.value);
                                break;
 
                            case VoiceEffect.intonation:
                                cevioTalker.ToneScale = (uint)(item.Value.value);
                                break;
                        }
                    }
                }
                foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects)
                {
                    switch (item.Key)
                    {
                        case VoiceEffect.volume:
                            cevioTalker.Volume = (uint)(item.Value.value);
                            break;
 
                        case VoiceEffect.speed:
                            cevioTalker.Speed = (uint)(item.Value.value);
                            break;
 
                        case VoiceEffect.pitch:
                            cevioTalker.Tone = (uint)(item.Value.value);
                            break;
 
                        case VoiceEffect.alpha:
                            cevioTalker.Alpha = (uint)(item.Value.value);
                            break;
 
                        case VoiceEffect.intonation:
                            cevioTalker.ToneScale = (uint)(item.Value.value);
                            break;
                    }
                }
            }
        }
    }
    // このクラス内で遅延バインドさせる
    public class CevioProxy : MarshalByRefObject
    {
        dynamic cevioTalker;
        double timeout = 3 * 60 * 1000;
 
        public CevioProxy()
        {
            Type t2 = Type.GetTypeFromProgID("CeVIO.Talk.RemoteService.Talker");  // 遅延バインドで使用します
            cevioTalker = Activator.CreateInstance(t2);
        }
 
        public string[] AvailableCasts
        {
            get
            {
                List<string> x = new List<string>();
 
                dynamic ac = cevioTalker.AvailableCasts;
                for (int i = 0; i < ac.Length; i++)
                {
                    x.Add(ac[i]);
                }
                return x.ToArray();
            }
        }
 
        public string Cast
        {
            get
            {
                return cevioTalker.Cast;
            }
            set
            {
                cevioTalker.Cast = value;
            }
        }
 
        public uint Volume
        {
            get
            {
                return cevioTalker.Volume;
            }
            set
            {
                cevioTalker.Volume = value;
            }
        }
 
        public uint Speed
        {
            get
            {
                return cevioTalker.Speed;
            }
            set
            {
                cevioTalker.Speed = value;
            }
        }
 
        public uint Tone
        {
            get
            {
                return cevioTalker.Tone;
            }
            set
            {
                cevioTalker.Tone = value;
            }
        }
 
        public uint Alpha
        {
            get
            {
                return cevioTalker.Alpha;
            }
            set
            {
                cevioTalker.Alpha = value;
            }
        }
 
        public uint ToneScale
        {
            get
            {
                return cevioTalker.ToneScale;
            }
            set
            {
                cevioTalker.ToneScale = value;
            }
        }
 
        public Dictionary<string, uint> Components
        {
            get
            {
                var emo = cevioTalker.Components;
                Dictionary<string, uint> param = new Dictionary<string, uint>();
 
                for (int i = 0; i < emo.Length; i++)
                {
                    param.Add(emo[i].Name, emo[i].Value);
                }
                return param;
            }
        }
 
        public void SetComponent(string emoName, uint emoValue)
        {
            cevioTalker.Components[emoName].Value = emoValue;
        }
 
        public string GetComponent(string emoName)
        {
            return cevioTalker.Components[emoName].Value;
        }
 
        public void Speak(string text, bool async = false)
        {
            dynamic status = cevioTalker.Speak(text);  //SpeakingState status = cevioTalker.Speak(TalkText);
 
            if (!async)
            {
                status.Wait(timeout);
            }
            cevioTalker.Stop();
        }
    }
 
}

Sapi5.cs

リクエストがあったので掲載。発声に際しては実行するスレッドに注意。

Sapi5.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using SpeechLib;
using System.Text.RegularExpressions;
 
namespace SeikaCenter
{
    internal class Sapi5 : ProductControlBase
    {
        SpVoice sapi = null;
        ISpeechObjectTokens speakerList = null;
 
        public Sapi5()
        {
            avatorParams = new Dictionary<int, avatorParam>();
 
            try
            {
                sapi = new SpVoice();
                speakerList = sapi.GetVoices("", "");
 
                for (int i = 0; i < speakerList.Count; i++)
                {
                    avatorParam item = new avatorParam();
 
                    item.avatorIndex = i;
                    item.avatorName = speakerList.Item(i).GetAttribute("Name");
                    item.voiceEffects_default = new Dictionary<VoiceEffect, EffectValueInfo>
                    {
                        {VoiceEffect.volume,     new EffectValueInfo(100.0m, 0.0m, 100.0m, 1.00m)},
                        {VoiceEffect.speed,      new EffectValueInfo(0.00m, -10.0m, 10.0m, 1.00m)}
                    };
                    item.voiceEffects = new Dictionary<VoiceEffect, EffectValueInfo>(item.voiceEffects_default);
                    item.voiceEmotions = new Dictionary<string, EffectValueInfo>();
                    item.voiceEmotions_default = new Dictionary<string, EffectValueInfo>();
 
                    avatorParams.Add(i, item);
                }
 
                aliveInstance = true;
            }
            catch (Exception ef5)
            {
                Console.WriteLine("ef5:{0},{1},{2}", ef5.Message, ef5.InnerException == null ? "" : ef5.InnerException.Message, ef5.StackTrace);
                aliveInstance = false;
            }
        }
 
        public override bool SelectAvator(int avatorIndex)
        {
            if ((avatorIndex < 0) || (avatorIndex > avatorParams.Count)) return false;
 
            selectedAvator = avatorIndex;
            sapi.Voice = speakerList.Item(avatorIndex);
 
            return true;
        }
        public override bool Play(bool async = false)
        {
            bool ans = false;
 
            ApplyEmotionParameters();
            ApplyEffectParameters();
 
            try
            {
                if (!async)
                {
                    Thread t = new Thread(() => {
                        sapi.Speak(TalkText);
                    });
                    t.SetApartmentState(ApartmentState.STA);
                    t.Start();
                    t.Join();
                }
                else
                {
                    Thread t = new Thread(() => {
                        sapi.Speak(TalkText, SpeechVoiceSpeakFlags.SVSFlagsAsync);
                    });
                    t.SetApartmentState(ApartmentState.STA);
                    t.Start();
                }
 
                ans = true;
            }
            catch (Exception efe)
            {
                Console.WriteLine("efe:{0},{1},{2}", efe.Message, efe.InnerException == null ? "" : efe.InnerException.Message, efe.StackTrace);
 
                ans = false;
            }
 
            ResetVoiceEffect();
            ResetVoiceEmotion();
            ApplyEffectParameters();
            ApplyEmotionParameters();
 
            return ans;
        }
        public override bool Save()
        {
            bool ans = false;
            string WavFileName = WavFilePath;
 
            ApplyEmotionParameters();
            ApplyEffectParameters();
 
            try
            {
                SpFileStream ss = new SpFileStream();
                ss.Open(WavFileName, SpeechStreamFileMode.SSFMCreateForWrite);
 
                sapi.AudioOutputStream = ss;
 
                Thread t = new Thread(() => {
                    sapi.Speak(TalkText);
                });
                t.SetApartmentState(ApartmentState.STA);
                t.Start();
                t.Join();
                ss.Close();
                ans = true;
            }
            catch (Exception)
            {
                //
            }
 
            ResetVoiceEffect();
            ResetVoiceEmotion();
            ApplyEffectParameters();
            ApplyEmotionParameters();
 
            return ans;
        }
 
        private void ApplyEmotionParameters()
        {
        }
        private void ApplyEffectParameters()
        {
            if (avatorParams.Count != 0)
            {
                foreach (KeyValuePair<VoiceEffect, EffectValueInfo> item in avatorParams[selectedAvator].voiceEffects)
                {
                    switch (item.Key)
                    {
                        case VoiceEffect.volume:
                            sapi.Volume = (int)item.Value.value;
                            break;
 
                        case VoiceEffect.speed:
                            sapi.Rate = (int)item.Value.value;
                            break;
                    }
                }
            }
        }
 
    }
}
documents/voiceroid/seikacenter/seikacenter-090.txt · 最終更新: 2018/07/13 15:45 by k896951

ページ用ツール