努力したWiki

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

ユーザ用ツール

サイト用ツール


documents:voiceroid:echoseika:echoseika-005

exVoiceの管理リストを作成する

2017/09/04

  • seikaserverに組み込む予定の機能のために、exVoiceの管理データベースを作成するコマンドを作った。
  • SQLiteのデータベースファイルを読めなくても、テキストファイルへ出力可能にした。

ダウンロード

アーカイブの exeとdllは同じ場所に配置してください。

概要

HTTPを使ってVOICEROIDに発声させるサーバプログラム seikaServer でexVoiceを扱うために必要となる、exVoice管理データベースを作成します。 データベースはSQLite(System.Data.SQLite)を使います。 また、タブ区切りのテキストファイル出力もできるようにしました。

プログラムの使い方と説明

.NET Framework 4.6でコンパイルされています。 ※.NET Framework 4.5.2をサポートする SQLite(System.Data.SQLite) が無かったので、4.6に変更せざるを得なかった。

このプログラムでExVoiceをまとめてコピーしたフォルダを指定し、ExVoiceの管理データベースを作成します。

E:\seikaServer>makeexvoiceidx
usage:
  makeExVoiceIdx.exe make DBFileName rootFolder
  makeExVoiceIdx.exe list DBFileName

"make"      : make ExVoice index Database. * SQLite database
"list"      : output ExVoice index list from DBFileName.
DBFileName  : SQLite database file name to create ExVoice index.
rootFolder  : Root of ExVoice folder.

This program uses System.Data.SQLite. (https://system.data.sqlite.org/)
For details on copyright, please check the following URL.
https://system.data.sqlite.org/index.html/doc/trunk/www/copyright.wiki

E:\seikaServer>

“make”の指示で管理データベースを作成します。作成するデータベースファイル名と、exVoiceが格納されているルートフォルダ名を指定します。
以下の例では、E:\seikaServer\ExVoice フォルダ以下にある“SEIKA”,“YUKARI”,“ZUNKO” の3製品のexVoiceについて、管理データベース E:\seikaServer\ExVoiceIndex.db を作成します。

E:\seikaServer>dir ExVoice
 ドライブ E のボリューム ラベルは CCER です
 ボリューム シリアル番号は 7ECE-5660 です

 E:\seikaServer\ExVoice のディレクトリ

2017/09/03  19:43    <DIR>          .
2017/09/03  19:43    <DIR>          ..
2017/08/19  18:58    <DIR>          SEIKA
2017/08/19  19:39    <DIR>          YUKARI
2017/08/19  19:20    <DIR>          ZUNKO
               0 個のファイル                   0 バイト
               5 個のディレクトリ  985,539,518,464 バイトの空き領域

E:\seikaServer>makeexvoiceidx make .\ExVoiceIndex.db E:\seikaserver\ExVoice
mikiさん, E:\seikaserver\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiさん1.wav
mikiさん, E:\seikaserver\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiさん2.wav
mikiちゃん, E:\seikaserver\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiちゃん1.wav
mikiちゃん, E:\seikaserver\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiちゃん2.wav
:
:
:
火曜, E:\seikaserver\ExVoice\ZUNKO\日時\曜日\火曜.wav
火曜日, E:\seikaserver\ExVoice\ZUNKO\日時\曜日\火曜日.wav
金曜, E:\seikaserver\ExVoice\ZUNKO\日時\曜日\金曜.wav
金曜日, E:\seikaserver\ExVoice\ZUNKO\日時\曜日\金曜日.wav
10月, E:\seikaserver\ExVoice\ZUNKO\日時\月\10月.wav
11月, E:\seikaserver\ExVoice\ZUNKO\日時\月\11月.wav
12月, E:\seikaserver\ExVoice\ZUNKO\日時\月\12月(1).wav
12月, E:\seikaserver\ExVoice\ZUNKO\日時\月\12月(2).wav
1月, E:\seikaserver\ExVoice\ZUNKO\日時\月\1月.wav
2月, E:\seikaserver\ExVoice\ZUNKO\日時\月\2月.wav
3月, E:\seikaserver\ExVoice\ZUNKO\日時\月\3月.wav
4月, E:\seikaserver\ExVoice\ZUNKO\日時\月\4月.wav
5月, E:\seikaserver\ExVoice\ZUNKO\日時\月\5月.wav
6月, E:\seikaserver\ExVoice\ZUNKO\日時\月\6月.wav
7月, E:\seikaserver\ExVoice\ZUNKO\日時\月\7月.wav
8月, E:\seikaserver\ExVoice\ZUNKO\日時\月\8月.wav
9月, E:\seikaserver\ExVoice\ZUNKO\日時\月\9月.wav
E:\seikaServer>dir ExVoiceIndex.db
 ドライブ E のボリューム ラベルは CCER です
 ボリューム シリアル番号は 7ECE-5660 です

 E:\seikaServer のディレクトリ

2017/09/04  02:54           245,760 ExVoiceIndex.db
               1 個のファイル             245,760 バイト
               0 個のディレクトリ  985,539,518,464 バイトの空き領域

E:\seikaServer>

データベースにはテーブル“ExVoiceIndex”が作成され、以下のように格納されています。 exVoiceによってはファイル名の命名規則が変則的で、例えば“E:\seikaServer\ExVoice\YUKARI\Voice_Percussions\Snare(タッタッターン)1.wav”は、発声内容が“Snare”ではなく“タッタッターン”です。そのため、WordとWordSub、の2項目に別けています。

データベースの格納状況

“list”の指示で管理データベースからタブ区切りテキストを出力できます。リダイレクトでファイルに出力して使ってください。 以下は開発者の環境で出力した結果です。

E:\seikaServer>makeexvoiceidx list ExVoiceIndex.db > list.txt

list.txt の内容はタブ区切りのテキストで以下のようになります。

list.txt
Id	CvName	Category	Word	WordSub	Index	Path
1	SEIKA	AHS関連キャラ呼びかけ	mikiさん		1	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiさん1.wav
2	SEIKA	AHS関連キャラ呼びかけ	mikiさん		2	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiさん2.wav
3	SEIKA	AHS関連キャラ呼びかけ	mikiちゃん		1	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiちゃん1.wav
4	SEIKA	AHS関連キャラ呼びかけ	mikiちゃん		2	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\mikiちゃん2.wav
5	SEIKA	AHS関連キャラ呼びかけ	いろはさん		1	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\いろはさん1.wav
6	SEIKA	AHS関連キャラ呼びかけ	いろはさん		2	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\いろはさん2.wav
7	SEIKA	AHS関連キャラ呼びかけ	いろはちゃん		1	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\いろはちゃん1.wav
8	SEIKA	AHS関連キャラ呼びかけ	いろはちゃん		2	E:\seikaServer\ExVoice\SEIKA\AHS関連キャラ呼びかけ\いろはちゃん2.wav
:
:
:
1374	ZUNKO	青森	東津軽郡		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\東津軽郡.wav
1375	ZUNKO	青森	西津軽		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\西津軽.wav
1376	ZUNKO	青森	西津軽郡		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\西津軽郡.wav
1377	ZUNKO	青森	青森		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\青森.wav
1378	ZUNKO	青森	青森市		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\青森市.wav
1379	ZUNKO	青森	青森県		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\青森県.wav
1380	ZUNKO	青森	黒石		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\黒石.wav
1381	ZUNKO	青森	黒石市		1	E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\黒石市.wav
項目 説明
CvName E:\seikaServer\ExVoice の直下のフォルダ。 E:\seikaServer\ExVoice\ZUNKO が存在すればCvNameは“ZUNKO”になる。
Category wavファイルの格納されている親フォルダ名。E:\seikaServer\ExVoice\ZUNKO\地域\地域\青森\東津軽郡.wav ならCategoryは“青森”になる。
Word 発声内容。 E:\seikaServer\ExVoice\SEIKA\精華町言葉\あんじょう(丁寧に、上手に).wav ならWordは“あんじょう”になる。
WordSub 発声内容もしくは説明。 E:\seikaServer\ExVoice\YUKARI\Voice_Percussions\Snare(タッタッターン)1.wav なら WordSubは“タッタッターン”になる。
Index 同じ発声内容につけられた識別番号。
Path wavファイルへのパス。

注意

今回の例で言うと、E:\seikaServer\ExVoice の直下に作るフォルダ名は、echoseika.exeの“-cv”オプションで指定する内容と同じものにしてください。
“VOICEROID+ 結月ゆかり”のexVoice格納場所は “YUKARI” になり、“VOICEROID+ 結月ゆかり EX”のexVoice格納場所は “YUKARI_EX”になります。

で、何に使うの?

たとえば、結月ゆかりさんに“へっぷし!風邪ひいたかしら”というテキストを発声させるとき、

  • “へっぷし!風邪ひいたかしら”をVOICEROIDですべて発声
  • “へっぷし!”をExVoice(E:\seikaServer\ExVoice\YUKARI\くしゃみ\くしゃみ2.WAV)、“風邪ひいたかしら” をVOICEROIDで再生

させられたら面白いかな、と。

“{m2:くしゃみ}風邪ひいたかしら”みたいなテキストを喰わせて、VOICEROIDの発声音声とexVoiceの音声データをマージして1つにできたらちょっと面白いよね、位な理由です。

ソース

makeExVoiceIdxのソースは以下の通り。

makeExVoiceIdx.cs
using System;
using System.Data.SQLite;
using System.IO;
using System.Text.RegularExpressions;
 
namespace makeExVoiceIdx
{
    class makeExVoiceIdx
    {
        static void Main(string[] args)
        {
            if (args.Length>=2)
            {
                switch(args[0])
                {
                    case "make":
                        if (args.Length==3)
                        {
                            ExVoiceFolderExplorer(args[1], args[2]);
                            return;
                        }
                        Console.WriteLine("error: DBFileName or rootFolder not found.");
                        break;
 
                    case "list":
                        if (args.Length == 2)
                        {
                            ExVoiceList(args[1]);
                            return;
                        }
                        Console.WriteLine("error: DBFileName not found.");
                        break;
 
                    default:
                        break;
                }
            }
            Console.WriteLine("usage:");
            Console.WriteLine("  makeExVoiceIdx.exe make DBFileName rootFolder");
            Console.WriteLine("  makeExVoiceIdx.exe list DBFileName");
            Console.WriteLine("");
            Console.WriteLine("\"make\"      : make ExVoice index Database. * SQLite database");
            Console.WriteLine("\"list\"      : output ExVoice index list from DBFileName.");
            Console.WriteLine("DBFileName  : SQLite database file name to create ExVoice index.");
            Console.WriteLine("rootFolder  : Root of ExVoice folder.");
            Console.WriteLine("");
            Console.WriteLine("This program uses System.Data.SQLite. (https://system.data.sqlite.org/)");
            Console.WriteLine("For details on copyright, please check the following URL.");
            Console.WriteLine("https://system.data.sqlite.org/index.html/doc/trunk/www/copyright.wiki");
        }
 
        static private void ExVoiceList(string databaseFileName)
        {
            ExVoiceIndexDb db;
 
            try
            {
                db = new ExVoiceIndexDb(databaseFileName, false);
 
                db.BeginTran();
                db.ListRecs();
                db.CommitTran();
            }
            catch (Exception e)
            {
                Console.WriteLine("{0}, {1}", e.Message, e.InnerException != null ? e.InnerException.Message : "");
            }
        }
        static private void ExVoiceFolderExplorer(string databaseFileName, string rootFolder)
        {
            ExVoiceIndexDb db;
            DirectoryInfo di;
 
            try
            {
                db = new ExVoiceIndexDb(databaseFileName);
                di = new DirectoryInfo(rootFolder);
 
                db.BeginTran();
 
                foreach (DirectoryInfo cvitem in di.GetDirectories())
                {
                    ExVoiceFileExplorer(ref db, cvitem.Name.ToUpper(), cvitem.FullName);
                }
 
                db.CommitTran();
            }
            catch(Exception e)
            {
                Console.WriteLine("{0}, {1}", e.Message, e.InnerException!=null ? e.InnerException.Message : "");
            }
        }
        static private void ExVoiceFileExplorer(ref ExVoiceIndexDb db, string cvname, string DirectoryName)
        {
            DirectoryInfo di = new DirectoryInfo(DirectoryName);
 
            foreach (FileInfo item in di.GetFiles(@"*.wav"))
            {
                ExVoiceIndexDb.ExVoiceIndex rec = new ExVoiceIndexDb.ExVoiceIndex();
                rec.CvName = cvname;
                rec.VoiceWord = Path.GetFileNameWithoutExtension(item.FullName);
                rec.VoiceCategory = di.Name;
                rec.VoiceFilePath = item.FullName;
                db.InsertRec(rec);
 
                Console.WriteLine("{0}, {1}", rec.VoiceWord, rec.VoiceFilePath);
            }
 
            foreach(DirectoryInfo subItem in di.GetDirectories())
            {
                ExVoiceFileExplorer(ref db, cvname, subItem.FullName);
            }
        }
    }
 
    class ExVoiceIndexDb
    {
        private SQLiteConnection conn;
        private SQLiteTransaction tran;
        private SQLiteCommand insertCmd;
        private SQLiteCommand selectCmd;
        private int CommitCounter = 0;
        private const int CommitMaxCount = 100; 
 
        public class ExVoiceIndex
        {
            public string CvName { get; set; }
            public string VoiceWord {
                get
                {
                    return voiceWord;
                }
                set
                {
                    Match orgIdx1 = Regex.Match(value, @"_\d+$");
                    Match orgIdx2 = Regex.Match(value, @"[\((]\d+[\))]$");
                    Match orgIdx3 = Regex.Match(value, @"\d+$");
                    voiceWordOrgNum = null;
                    voiceWordSub = null;
 
                    if (orgIdx1.Success)
                    {
                        voiceWord = value.Substring(0, value.Length - orgIdx1.Length);
                        voiceWordOrgNum = int.Parse(orgIdx1.Value.Substring(1));
                    }
                    else if (orgIdx2.Success)
                    {
                        voiceWord = value.Substring(0, value.Length - orgIdx2.Length);
                        voiceWordOrgNum = int.Parse(orgIdx2.Value.Substring(1, orgIdx2.Length - 2));
                    }
                    else if (orgIdx3.Success)
                    {
                        voiceWord = value.Substring(0, value.Length - orgIdx3.Length);
                        voiceWordOrgNum = int.Parse(orgIdx3.Value);
                    }
                    else
                    {
                        voiceWord = value;
                        voiceWordOrgNum = null;
                    }
 
                    Match afterIdx1 = Regex.Match(voiceWord, @"[\((][^\(\)()]+[\))]$");
 
                    if (afterIdx1.Success)
                    {
                        voiceWord = voiceWord.Substring(0, voiceWord.Length - afterIdx1.Length);
                        voiceWordSub = afterIdx1.Value.Substring(1, afterIdx1.Value.Length - 2);
                    }
 
                    voiceWord = voiceWord.Trim();
                }
            }
            public string VoiceWordSub {
                get
                {
                    return voiceWordSub;
                }
                set
                {
                    voiceWordSub = value;
                }
            }
 
            public string VoiceCategory { get; set; }
            public string VoiceFilePath { get; set; }
            public int? VoiceWordOrgNum
            {
                get
                {
                    return voiceWordOrgNum;
                }
                set
                {
                    voiceWordOrgNum = value;
                }
            }
            public int VoiceWordGlobalNum { get; set; }
 
            private string voiceWord;
            private int? voiceWordOrgNum;
            private string voiceWordSub;
        }
 
        public ExVoiceIndexDb(string databaseFileName, bool create = true)
        {
            conn = new SQLiteConnection(string.Format(@"Data Source={0}", databaseFileName));
            conn.Open();
 
            SQLiteCommand cmd = conn.CreateCommand();
 
            if (create)
            {
                try
                {
                    cmd.CommandText = "DROP TABLE ExVoiceIndex;";
                    cmd.ExecuteNonQuery();
                }
                catch
                {
                    // エラースキップしたいだけ
                }
 
                cmd.CommandText = "CREATE TABLE ExVoiceIndex("
                + "  Id            integer primary key AUTOINCREMENT"
                + " ,CvName        nvarchar(30) not null"
                + " ,VoiceWord     nvarchar(100) not null"
                + " ,VoiceWordSub  nvarchar(100)"
                + " ,VoiceCategory nvarchar(100) not null"
                + " ,VoiceWordOrgNum     integer"
                + " ,VoiceWordGlobalNum  integer not null"
                + " ,VoiceFilePath nvarchar(300) not null"
                + ");";
                cmd.ExecuteNonQuery();
 
                cmd.CommandText = "CREATE INDEX ExVoiceIndex_Idx01 on ExVoiceIndex("
                + "  CvName"
                + " ,VoiceWord"
                + ");";
                cmd.ExecuteNonQuery();
                cmd.Dispose();
            }
 
            insertCmd = conn.CreateCommand();
            insertCmd.CommandText =
              "INSERT INTO ExVoiceIndex("
            + " CvName,"
            + " VoiceWord,"
            + " VoiceWordSub,"
            + " VoiceCategory,"
            + " VoiceWordOrgNum,"
            + " VoiceWordGlobalNum,"
            + " VoiceFilePath)"
            + "VALUES("
            + " @CvName,"
            + " @VoiceWord,"
            + " @VoiceWordSub,"
            + " @VoiceCategory,"
            + " @VoiceWordOrgNum,"
            + " (SELECT COUNT(*)+1 FROM ExVoiceIndex WHERE CvName=@CvName and VoiceWord=@VoiceWord),"
            + " @VoiceFilePath)";
            insertCmd.Parameters.Add(new SQLiteParameter("@CvName"));
            insertCmd.Parameters.Add(new SQLiteParameter("@VoiceWord"));
            insertCmd.Parameters.Add(new SQLiteParameter("@VoiceWordSub"));
            insertCmd.Parameters.Add(new SQLiteParameter("@VoiceCategory"));
            insertCmd.Parameters.Add(new SQLiteParameter("@VoiceWordOrgNum"));
            insertCmd.Parameters.Add(new SQLiteParameter("@VoiceFilePath"));
 
            selectCmd = conn.CreateCommand();
            selectCmd.CommandText =
              "SELECT "
            + " \"Id\","
            + " \"CvName\","
            + " \"VoiceWord\","
            + " \"VoiceWordSub\","
            + " \"VoiceCategory\","
            + " \"VoiceWordOrgNum\","
            + " \"VoiceWordGlobalNum\","
            + " \"VoiceFilePath\""
            + " FROM ExVoiceIndex"
            +" ORDER BY 2,5,3,4";
        }
        ~ExVoiceIndexDb()
        {
            insertCmd.Dispose();
            conn.Dispose();
        }
        public void BeginTran()
        {
            tran = conn.BeginTransaction();
        }
        public void CommitTran()
        {
            tran.Commit();
        }
        public bool InsertRec(ExVoiceIndex rec)
        {
            bool ans;
            try
            {
                insertCmd.Parameters["@CvName"].Value = rec.CvName;
                insertCmd.Parameters["@VoiceWord"].Value = rec.VoiceWord;
                insertCmd.Parameters["@VoiceWordSub"].Value = rec.VoiceWordSub;
                insertCmd.Parameters["@VoiceCategory"].Value = rec.VoiceCategory;
                insertCmd.Parameters["@VoiceWordOrgNum"].Value = rec.VoiceWordOrgNum;
                insertCmd.Parameters["@VoiceFilePath"].Value = rec.VoiceFilePath;
                insertCmd.ExecuteNonQuery();
                ans = true;
 
                CommitCounter++;
                if (CommitCounter > CommitMaxCount)
                {
                    CommitCounter = 0;
                    tran.Commit();
                    tran.Dispose();
                    tran = conn.BeginTransaction();
                }
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
                ans = false;
            }
            return ans;
        }
        public bool ListRecs()
        {
            bool ans;
            try
            {
                Console.WriteLine("Id\tCvName\tCategory\tWord\tWordSub\tIndex\tPath");
 
                using (SQLiteDataReader r = selectCmd.ExecuteReader())
                {
                    while(r.Read())
                    {
                        string id = r["Id"].ToString();
                        string cvname = r["CvName"].ToString();
                        string category = r["VoiceCategory"].ToString();
                        string word = r["VoiceWord"].ToString();
                        string wordSub = r["VoiceWordSub"].ToString();
                        string index = r["VoiceWordGlobalNum"].ToString();
                        string path = r["VoiceFilePath"].ToString();
 
                        Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", id, cvname, category, word, wordSub, index, path);
                    }
                }
                ans = true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                ans = false;
            }
            return ans;
        }
 
    }
 
}

コメント

コメントを入力. Wiki文法が有効です:
画像の文字が読めなければ、文字を読んだ.wavファイルをダウンロードして下さい。
 
documents/voiceroid/echoseika/echoseika-005.txt · 最終更新: 2018/06/24 01:55 by k896951

ページ用ツール