/// <summary> /// 音声変換時のデータコールバックメソッド /// </summary> /// <param name="reason">呼び出し要因</param> /// <param name="job_id">ジョブID</param> /// <param name="tick">時刻[ms]</param> /// <param name="user_data">ユーザーデータ(SpeechJobDataへのポインタ)</param> /// <returns>ゼロを返す</returns> private static int RawBufferCallback(AitalkCore.EventReason reason, int job_id, long tick, IntPtr user_data) { GCHandle gc_handle = GCHandle.FromIntPtr(user_data); SpeechJobData job_data = gc_handle.Target as SpeechJobData; if (job_data == null) { return(0); } // 変換できた分だけGetData()で読み取ってjob_dataのバッファに格納する int buffer_capacity = job_data.BufferCapacity; byte[] buffer = new byte[2 * buffer_capacity]; AitalkCore.Result result; int read_samples; do { result = AitalkCore.GetData(job_id, buffer, buffer_capacity, out read_samples); if (result != AitalkCore.Result.Success) { break; } job_data.Output.AddRange(new ArraySegment <byte>(buffer, 0, 2 * read_samples)); }while ((buffer_capacity - 1) <= read_samples); if (reason == AitalkCore.EventReason.RawBufferClose) { job_data.CloseEvent.Set(); } return(0); }
/// <summary> /// 音声変換時のイベントコールバックメソッド /// </summary> /// <param name="reason">呼び出し要因</param> /// <param name="job_id">ジョブID</param> /// <param name="tick">時刻[ms]</param> /// <param name="name">イベントの値</param> /// <param name="user_data">ユーザーデータ(SpeechJobDataへのポインタ)</param> /// <returns>ゼロを返す</returns> private static int TtsEventCallback(AitalkCore.EventReason reason, int job_id, long tick, string name, IntPtr user_data) { GCHandle gc_handle = GCHandle.FromIntPtr(user_data); SpeechJobData job_data = gc_handle.Target as SpeechJobData; if (job_data == null) { return(0); } switch (reason) { case AitalkCore.EventReason.PhoneticLabel: case AitalkCore.EventReason.Bookmark: case AitalkCore.EventReason.AutoBookmark: job_data.EventData.Add(new TtsEventData(tick, name, reason)); break; } return(0); }
/// <summary> /// 読み仮名を読み上げてWAVEファイルをストリームに出力する。 /// なお、ストリームへの書き込みは変換がすべて終わった後に行われる。 /// </summary> /// <param name="kana">読み仮名</param> /// <param name="wave_stream">WAVEファイルの出力先ストリーム</param> /// <param name="timeout">タイムアウト[ms]。0以下はタイムアウト無しで待ち続ける。</param> public static void KanaToSpeech(string kana, Stream wave_stream, int timeout = 0) { UpdateParameter(); // コールバックメソッドとの同期オブジェクトを用意する SpeechJobData job_data = new SpeechJobData(); job_data.BufferCapacity = 176400; job_data.Output = new List <byte>(); job_data.EventData = new List <TtsEventData>(); job_data.CloseEvent = new EventWaitHandle(false, EventResetMode.ManualReset); GCHandle gc_handle = GCHandle.Alloc(job_data); try { // 変換を開始する AitalkCore.JobParam job_param; job_param.ModeInOut = AitalkCore.JobInOut.KanaToWave; job_param.UserData = GCHandle.ToIntPtr(gc_handle); AitalkCore.Result result; result = AitalkCore.TextToSpeech(out int job_id, ref job_param, kana); if (result != AitalkCore.Result.Success) { throw new AitalkException("音声変換が開始できませんでした。", result); } // 変換の終了を待つ // timeoutで与えられた時間だけ待つ bool respond; respond = job_data.CloseEvent.WaitOne((0 < timeout) ? timeout : -1); // 変換を終了する result = AitalkCore.CloseSpeech(job_id); if (respond == false) { throw new AitalkException("音声変換がタイムアウトしました。"); } else if (result != AitalkCore.Result.Success) { throw new AitalkException("音声変換が正常に終了しませんでした。", result); } } finally { gc_handle.Free(); } // TTSイベントをJSONに変換する // 変換後の文字列にヌル終端がてら4の倍数の長さになるようパディングを施す MemoryStream event_stream = new MemoryStream(); var serializer = new DataContractJsonSerializer(typeof(List <TtsEventData>)); serializer.WriteObject(event_stream, job_data.EventData); int padding = 4 - ((int)event_stream.Length % 4); for (int cnt = 0; cnt < padding; cnt++) { event_stream.WriteByte(0x0); } byte[] event_json = event_stream.ToArray(); // データをWAVE形式で出力する // phonチャンクとしてTTSイベントを埋め込む byte[] data = job_data.Output.ToArray(); var writer = new BinaryWriter(wave_stream); writer.Write(new byte[4] { (byte)'R', (byte)'I', (byte)'F', (byte)'F' }); writer.Write(44 + event_json.Length + data.Length); writer.Write(new byte[4] { (byte)'W', (byte)'A', (byte)'V', (byte)'E' }); writer.Write(new byte[4] { (byte)'f', (byte)'m', (byte)'t', (byte)' ' }); writer.Write(16); writer.Write((short)0x1); writer.Write((short)1); writer.Write(VoiceSampleRate); writer.Write(2 * VoiceSampleRate); writer.Write((short)2); writer.Write((short)16); writer.Write(new byte[4] { (byte)'p', (byte)'h', (byte)'o', (byte)'n' }); writer.Write(event_json.Length); writer.Write(event_json); writer.Write(new byte[4] { (byte)'d', (byte)'a', (byte)'t', (byte)'a' }); writer.Write(data.Length); writer.Write(data); }