Asterisk で音声返答付きモーニングコール機能を実装する
2025/11/23 19:35
前提条件
- Ubuntu 24.04.3 LTS
- FFmpeg インストール済み (version 6.1.1-3ubuntu5)
- Asterisk で通話可能になっている
- 文字コードは UTF-8 で統一
準備1 音声を生成するためのもの (OpenJTalk + MMDAgent)
インストール
$ sudo apt install open-jtalk open-jtalk-mecab-naist-jdic hts-voice-nitech-jp-atr503-m001 $ : MMDAgent から音声モデルをいただきます $ wget https://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/MMDAgent_Example-1.8/MMDAgent_Example-1.8.zip $ unzip MMDAgent_Example-1.8.zip $ sudo mv MMDAgent_Example-1.8/Voice/mei /usr/share/hts-voice/ $ : 発声が出来るか確認 $ echo "10分" | open_jtalk -m /usr/share/hts-voice/mei/mei_normal.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow /tmp/voice.wav -g 15 && aplay /tmp/voice.wav
文字列を Asterisk 用音声に変換するスクリプトを用意
パスを /usr/local/bin/asterisk-utils/jtalk2wav.pl とします。実行権限を付加します。標準状態の Asterisk で .wav ファイルを再生するには モノラル / サンプリング周波数8000Hz である必要があるようです。
#!/usr/bin/perl
use strict;
use warnings;
my $VOICE = '/usr/share/hts-voice/mei/mei_normal.htsvoice';
my $DICT = '/var/lib/mecab/dic/open-jtalk/naist-jdic';
my $OTHER_OPT = '';
my($text, $output_wav) = @ARGV;
my $TEMP_WAV = "/var/tmp/$$.wav";
open(my $pipe, '|-', "/usr/bin/open_jtalk -m $VOICE -x $DICT -ow $TEMP_WAV $OTHER_OPT") or die;
print $pipe $text;
close $pipe;
system("ffmpeg -i $TEMP_WAV -af 'aformat=sample_rates=8000' -loglevel quiet -y $output_wav");
unlink($TEMP_WAV);
exit;
準備2 受付・キャンセル用スクリプトの用意
パスを /usr/local/bin/asterisk-utils/morning-call.pl とします。実行権限を付加します。
#!/usr/bin/perl -w
use v5.12;
use FindBin;
chdir $FindBin::Bin or die;
use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);
my $OUT_SPOOL = '/var/spool/asterisk/outgoing';
my $TEMP_DIR = '/var/tmp';
my $SOUND_EXT = '279'; #モーニングコール音源につながる内線番号
my($action, $time, $caller, $answer_wav);
GetOptions('answer-wav=s' => \$answer_wav,
'action=s' => \$action,
'time=s' => \$time,
'caller=s' => \$caller,
) or die 'Invalid options';
my $answer_text = '';
if( $action =~ /REGISTER/i && $time =~ /^(\d{2})(\d{2})$/ && $1 <= 23 && $2 <= 59){
my($h, $m) = ($1, $2);
cancel($caller);
my ($date, $today_or_tomorrow) = get_date_at_the_time($time);
register($date, $time, $caller);
$h =~ s/^0//;
$m = $m==0 ? 'ちょうど'
:$m==30? '半'
:( int($m). "分" );
$answer_text = "モーニングコールを $today_or_tomorrowの $h時$mにセットしました";
}elsif( $action =~ /CANCEL/i ) {
cancel($caller);
$answer_text = "モーニングコールをキャンセルしました";
}else{
$answer_text = "無効な指定です";
}
system("./jtalk2wav.pl '$answer_text' $answer_wav") if $answer_wav;
say $answer_text;
exit;
#コールをセット.
sub register {
my($date, $time, $caller_ext) = @_;
my $file = "$TEMP_DIR/morning-call-$caller_ext-$date-$time.txt";
open(my $fh, '>', $file) or die("Cannot create $file");
my $body = << " EOM";
Channel: PJSIP/$caller_ext
CallerID: Good_Morning!!
MaxRetries: 0
RetryTime: 300
WaitTime: 30
Context: ipdenwa
Extension: $SOUND_EXT
Archive: yes
EOM
$body =~ s/^\s+//gm;
print $fh $body;
system("touch --date='$date $time' $file");
system("mv $file $OUT_SPOOL");
}
#指定内線番号の登録をすべて解除.
sub cancel {
my($caller_ext) = @_;
unlink glob("$OUT_SPOOL/morning-call-$caller_ext-*.txt");
}
#指定された時刻に応じて ( 今日or明日の日付(ISO8601), 文字列「今日」or「明日」 ) をリストで返す.
sub get_date_at_the_time {
#時刻は3~4桁の数値で指定
my($time) = @_;
my $date;
my $today_or_tomorrow;
my $now_time = int(`date +%H%M`);
#現在時刻が 指定時刻を過ぎていたら
if( $time <= $now_time ){
$today_or_tomorrow = '明日';
$date = `date --iso-8601 --date 1day`;
}else{
$today_or_tomorrow = '今日';
$date = `date --iso-8601`;
}
chomp $date;
return ($date, $today_or_tomorrow);
}
準備3 モーニングコール音源の用意
モーニングコールを受けた際に再生する音楽や音声などを、モノラル / サンプリング周波数8000Hz の .wav ファイルで用意します。お好みで CD から音楽をリッピングしたり、前述の OpenJTalk で音声を合成したりしてください。
そのファイルを私は /var/lib/asterisk/sounds/asadayo.wav に置きました。
準備4 extensions.conf への追記
/etc/asterisk/extensions.conf にモーニングコールに関する動作を追記します。私の環境では内線番号には200番台を振っています。それにならって、モーニングコールのセットには「277 + 時刻4桁」を、キャンセルには「278」を、またモーニングコール音源を再生する番号には「279」を割り当てました。
[house] は私の環境における内線のためのコンテキストです。適宜読み替えてください。
[house] (...中略...) ; 277xxxx モーニングコール 登録 exten => _277XXXX,1,NoOp(REGISTER-MORNINGCALL) same => n,Answer same => n,Playback(beep) same => n,Set(ANSWER_WAV_BASENAME=/var/tmp/morning-call-answer-$CALLERID(num)) same => n,System(/usr/bin/rm $ANSWER_WAV_BASENAME.wav) same => n,System(/usr/local/bin/asterisk-utils/morning-call.pl --action register --time '$EXTEN:3' --caller '$CALLERID(num)' --answer-wav $ANSWER_WAV_BASENAME.wav) same => n,Playback($ANSWER_WAV_BASENAME) same => n,System(/usr/bin/rm $ANSWER_WAV_BASENAME.wav) same => n,Wait(1) same => n,Playback(beep) same => n,Hangup ; 278 モーニングコール 取り消し exten => _278,1,NoOp(CANCEL-MORNINGCALL) same => n,Answer same => n,Playback(beep) same => n,Set(ANSWER_WAV_BASENAME=/var/tmp/morning-call-answer-$CALLERID(num)) same => n,System(/usr/bin/rm $ANSWER_WAV_BASENAME.wav) same => n,System(/usr/local/bin/asterisk-utils/morning-call.pl --action cancel --caller '$CALLERID(num)' --answer-wav $ANSWER_WAV_BASENAME.wav) same => n,Playback($ANSWER_WAV_BASENAME) same => n,System(/usr/bin/rm $ANSWER_WAV_BASENAME.wav) same => n,Wait(1) same => n,Playback(beep) same => n,Hangup ; 279 きみの朝 exten => 279,1,Answer same => n,Wait(1) same => n,Playback(/var/lib/asterisk/sounds/asadayo) same => n,HangupAsterisk を再起動後、SIP電話機から 279 へダイヤルし音源が再生されることを確認してください。場合によっては音源のボリュームを調整する必要もあるかもしれません。
使い方
セット
SIP電話機から「277」に続いて時刻を4桁でダイヤルします。例えば現在時刻が午後9時で明朝7時にモーニングコールをセットしたいときは、「2770700」とダイヤルします。「モーニングコールを 明日の 7時ちょうどにセットしました。」と音声が流れ、電話が切られます。この電話機に翌朝7時にモーニングコールが掛かってきます。
キャンセル
SIP電話機から「278」にダイヤルします。「モーニングコールをキャンセルしました。」と音声が流れ、電話が切られます。