質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Q&A

解決済

2回答

1432閲覧

PerlのWin32::OLEのsapiで非同期音声出力をしたい

Hirakou

総合スコア9

Perl

Perlは多目的に使用される実用性が高い動的プログラミング言語のひとつです。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

0グッド

0クリップ

投稿2021/10/20 06:20

Perlスクリプト(※)でWin32::OLEでSpeech API(sapi)を呼び出し音声出力をすることを考えています。
音声出力が始まったらすぐにスクリプトの次の行の実行をしてほしいのですが、そうなりません。
※ Windows 10 + Strawberry Perl 5.32.1

音声の終了を待たずに次の行の実行に移るにはどうしたら良いでしょうか?

発生している問題・エラーメッセージ

下記のスクリプトをコマンドプロンプトから実行すると、音声が出力されますが、その後の「print "OK";」は、音声の出力が終了しないと実行されません。「... long long sentence.」をしゃべり終わった後で初めて「OK」と表示されます。

該当のソースコード

Perl

1#!/usr/bin/perl 2use utf8; 3use strict; 4use warnings; 5use Win32::OLE; 6 7speak('This is a test message. And this is a very long long sentence.'); 8print "OK"; # 実際はこの後に色々な処理 9exit; 10 11sub speak{ 12 my($text) = @_; 13 my $tts = Win32::OLE->new("Sapi.SpVoice") or die "Sapi.SpVoice failed"; 14 $tts->{Voice} = $tts->GetVoices->Item(1); 15 16 $tts->Speak($text, 1); 17 $tts->WaitUntilDone(-1); 18}

補足情報(FW/ツールのバージョンなど)

https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee125223(v=vs.85)
Microsoft Speech API 5.4 SpeechVoiceSpeakFlags Enum
や、
https://www.tek-tips.com/viewthread.cfm?qid=1262913
を見るとSpeakメソッドの第2引数を1にすると非同期(speak asynchronously)になると書いてあるようなのですが..

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

forkしてプロセスを分けると良さそうです。

#!/usr/bin/perl use utf8; use strict; use warnings; use Win32::OLE; my $pid=fork; if($pid){ # 親プロセス print "OK\n"; }elsif($pid==0){ # 子プロセス speak('This is a test message. And this is a very long long sentence.'); }else{ # 失敗 die "Fail to fork"; } exit; sub speak{ my($text) = @_; my $tts = Win32::OLE->new("Sapi.SpVoice") or die "Sapi.SpVoice failed"; $tts->{Voice} = $tts->GetVoices->Item(1); $tts->Speak($text, 1); $tts->WaitUntilDone(-1); }

投稿2021/10/20 21:27

KojiDoi

総合スコア13671

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hirakou

2021/10/24 06:29

コメントありがとうございます。 Windows版Perlでfork()が実装されて動くとは思いもよりませんでした。(forkがあることを知っていたとしても、今回の問題でforkで別プロセスで音を出す発想には至りませんでした。感謝します。) お教えいただいたスクリプト、Strawberry Perlできちんと意図した挙動(しゃべり始めてすぐに「OK」が表示される)をしてくれました。
guest

0

ベストアンサー

Perl では試していないのですが VBScript で試した感じだと非同期オプションは効いているようでした。

perl

1 $tts->Speak($text, 1);

は、非同期で処理され、

perl

1 $tts->WaitUntilDone(-1);

で、音声出力が終了するまで待機するようになっているようです。
その為、すべて再生するまで「OK」が表示されないという状況のようです。
恐らくは WaitUntilDone を最後に呼ぶようにしたら、音声の終了を待たずに何かしら処理が行えるのでは、と思います。

以下は VBScript での実行例になります。

VBScript

1Dim sapi 2Set sapi = CreateObject("SAPI.SpVoice") 3 4sapi.Speak "おはようございます", 1 5WScript.Echo "おはようございます" 6 7sapi.Speak "こんにちは", 1 8WScript.Echo "こんにちは" 9 10sapi.Speak "おやすみなさい", 1 11WScript.Echo "おやすみなさい" 12 13sapi.WaitUntilDone -1 14

上記のコードを実行すると

おはようございます こんにちは おやすみなさい

が出力された後に、音声出力が始まります。

投稿2021/10/20 14:51

cx20

総合スコア4633

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hirakou

2021/10/24 06:29

アドバイスありがとうございます! 重要な点は、ご指摘の通りWaitUntilDoneの位置でした。 WaitUntilDoneをサブルーチンspeakの中でなく、スクリプト全体の終了直前に移動させることで期待した動作をしてくれました。
cx20

2021/10/24 07:21

そういえば、今更ながらですが、Perl インストールしてみました。 恥ずかしながら、当初、質問文をよく読んでおらず、ActivePerl で試そうとしていました。 しかしながら ActivePerl はダウンロードにアカウント登録が必要なようで、Perl を試すのを諦めてしまいました。 Strawberry Perl ならアカウント登録不要なんですね。良い情報を教えて頂きありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問