<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
   <title>プログラミングの小石・大石</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/" />
   <link rel="self" type="application/atom+xml" href="http://www.kanadas.com/program/atom.xml" />
   <id>tag:www.kanadas.com,2008:/program//22</id>
   <updated>2008-06-16T13:22:56Z</updated>
   <subtitle><![CDATA[ちょっとしたプログラムの断片からプログラミング技法，モジュールなど，いろいろとりあげます． 
公開したくないコメントは yasusi&nbsp;@&nbsp;kanadas.com におくってください．
もし内容にあやまりがあれば，あとからでも訂正しますので，連絡してください．]]></subtitle>
   <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.36</generator>

<entry>
   <title>単純な Perl 版メイル・プロキシ</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2008/03/_perl_1.html" />
   <id>tag:www.kanadas.com,2008:/program//22.2638</id>
   
   <published>2008-03-29T05:25:03Z</published>
   <updated>2008-06-16T13:22:56Z</updated>
   
   <summary> メイルの誤送信防止などの目的のため，メイル・プロキシ (SMTP プロキシ) が重要になっている．  Perl によって書かれたメイル・プロキシとしては Qsmtpd  があるが，自分で単純なプロキシをつくりたいとおもったときには，このプログラムは複雑すぎて，あまり参考にはならない．  そこで，できるだけ単純なプロキシのプログラムを書いてみた． ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
メイルの誤送信防止などの目的のため，メイル・プロキシ (SMTP プロキシ) が重要になっている． 
Perl によって書かれたメイル・プロキシとしては <a href="http://smtpd.develooper.com/" target="_blank">Qsmtpd</a> 
があるが，自分で単純なプロキシをつくりたいとおもったときには，このプログラムは複雑すぎて，あまり参考にはならない． 
そこで，できるだけ単純なプロキシのプログラムを書いてみた．
</p>
]]>
      <![CDATA[<p>
以下のプログラムはメイル・クライアントからメイルを SMTP によってうけとって SMTP サーバにリレーするが，"viagra" というキーワードがみつかると転送しない． 
動作させるためには 'smtp.server.address' のかわりに実際に使用する SMTP サーバのアドレス / ドメイン名を指定する． 
(ポートが既定の 25 でなければ，それもかきかえる必要がある.)
</p>
<pre>
############################################################################
#
#	Simple Mail Proxy
#
############################################################################

use Carp;
use Net::SMTP;
use Net::SMTP::Server;
use Mail::Message;
use Sys::Hostname;

use strict;

my $debug = 0;


## SMTP server address and port ##
#
my $SMTP_Server_Address = 'smtp.server.address';
my $SMTP_Server_Port = 25;


### Mail proxy (this server) address and port ##
#
my $Proxy_Port = 25;


#===========================================================================
#	Mail client connection service
#===========================================================================

### Constants ###

my $SUCCEEDED = 0;

my %commands = (DATA => \&cmd_data,
		EXPN => \&cmd_dummy,
		HELO => \&cmd_helo,
		HELP => \&cmd_help,
		MAIL => \&cmd_mail,
		NOOP => \&cmd_noop,
		QUIT => \&cmd_quit,
		RCPT => \&cmd_rcpt,
		RSET => \&cmd_rset,
		VRFY => \&cmd_dummy);

### Variables ###

my $client_socket;
my $from;
my @to;
my $message;


sub client_put ($) {
    my ($message) = @_;
    print "Sent:     $message\n" if ($debug);
    print $client_socket $message, "\r\n";
}

sub cmd_data () {
    if (!defined($from)) {
	client_put("503 5.5.1 Sender address not yet specified");
	return 1;
    };
    if (!@to) {
	client_put("503 5.5.1 Recepient address not yet specified");
	return 1;
    };
    client_put("354 Start mail input; end with <CRLF>.<CRLF>");

    my $done = 0;
    while (<$client_socket>) {
	# print "Received: $_" if ($debug);
	if (/^\.\r\n$/) {
	    $done = 1;
	    last;
	};
	s/^\.\./\./;
	$message .= $_;
    };
    if (!$done) {
	client_put("451 5.6.0 Message input failed");
	return 1;
    };
    return 0;
}

sub cmd_helo () {
    client_put("250-Action completed, okay");
    client_put("250 ENHANCEDSTATUSCODES");
}

sub cmd_help () {
    my $out = "214-Commands\r\n";
    my $total = keys %commands;
    my $i = 0;
    foreach my $cmd (keys %commands) {
	$out .= "\r\n214";
	if ($i++ % 5 != 0) {
	    $out .= $total - $i < 5 ? " " : "-";
	} else {
	    $out .= " ";
	};
    };
    client_put($out);
}

sub cmd_noop () {
    client_put("252 Unknown status, but attempting delivery");
}

sub cmd_quit () {
    client_put("221 Service closing");
    $client_socket->close();
    return 0;
}

sub cmd_mail ($) {
    my ($arg)  = @_;
    $arg =~ /FROM:\s*(\S+)$/i;
    $from = $1;
    client_put("250 Mail sender okay");
}

sub cmd_rcpt ($) {
    my ($arg) = @_;
    $arg =~ /TO:\s*(\S+)$/i;
    my $to = $1;
    push(@to, $to);
    client_put("250 Mail recepient okay");
}

sub cmd_rset () {
    $from = undef;
    @to = ();
    client_put("250 Reset action okay");
}

sub cmd_dummy () {
}



#===========================================================================
#	SMTP server connection service
#===========================================================================

### relay ($from, @to, $msg) ###
#   forward a mail to specified SMTP server
#
sub relay ($\@$) {
    my ($from, $to, $msg) = @_;
    
    $from =~ /<.*@(.*)>/;
    my $domain = $1;
    print "Domain: $domain\n" if ($debug);
    my $client = new Net::SMTP($SMTP_Server_Address, Port => $SMTP_Server_Port,
			       Hello => $domain, Timeout => 30, Debug => $debug) ||
	croak "Unable to connect to mail server: $!\n";
    if ($client) {
	$client->mail($from);
	foreach my $recipient (@$to) {
	    $client->to($recipient);
	};
	$client->data($msg);
	$client->quit() || croak "Relay failed: $!\n";
    };
}


#===========================================================================
#	Main
#===========================================================================

my $server = new Net::SMTP::Server(hostname(), $Proxy_Port) ||
    croak "Unable to create a new mail proxy: $!\n";

while ($client_socket = $server->accept()) {

    $from = undef;
    @to = ();
    $message = undef;
    my $accepted;

    client_put("220 Service ready");

    while (<$client_socket>) {
	print "Received: $_" if ($debug);
	chomp;
	my ($cmd, $arg);
	/^\s*(\S+)(\s+(.*\S))?\s*$/;
	$cmd = $1;
	$arg = $3;
	$cmd =~ tr/a-z/A-Z/;
	if (!defined($commands{$cmd})) {
	    client_put("500 5.5.2 Syntax error, command unrecognized");
	    next;
	};

	&{$commands{$cmd}}($arg);

	if ($cmd eq 'DATA') {
	    my $msg = Mail::Message->read($message);
	    my $body = $msg->body;
	    if ($body =~ /viagra/i) {
		client_put("554 5.6.0 Invalid keyword included: viagra");
		$accepted = 0;
	    } else {
		client_put("250 2.0.0 Message accepted for delivery");
		$accepted = 1;
	    };
	};
    };

    $client_socket->close();

    if ($accepted) {
	relay($from, @to, $message);
    };
}
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl におけるデータ構造の線形化 (serialization)</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_serialization_1.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2215</id>
   
   <published>2007-11-17T12:00:00Z</published>
   <updated>2008-03-29T05:41:34Z</updated>
   
   <summary> Perl には tie() というくみこみのサブルーティンがあって，ハッシュに設定した値を自動的に外部ファイルにかきこむことができる．  ただし，このハッシュにはどんな値でもかきこめるというわけではなく，構造データをいれることができない．  したがって，Perl の内部であつかう複雑なデータ構造をファイルにかきこむには，他のおおくの言語におけるのと同様に，データ構造を線形化 (serialize...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="データ構造" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
Perl には tie() というくみこみのサブルーティンがあって，ハッシュに設定した値を自動的に外部ファイルにかきこむことができる． 
ただし，このハッシュにはどんな値でもかきこめるというわけではなく，構造データをいれることができない． 
したがって，Perl の内部であつかう複雑なデータ構造をファイルにかきこむには，他のおおくの言語におけるのと同様に，データ構造を線形化 (serialize，シリアライズ) してやる必要がある． 
ここでは線形化 (serialization，シリアライゼーション) と逆線形化 (deserialization，デシリアライゼーション) の方法について書く．
</p>
]]>
      <![CDATA[<p>
私がこれまでよくつかってきた方法は，(k1 => v1, ..., k<i>n</i> => v<i>n</i>) というハッシュであれば 'k1=>v1|...|kn=>vn' というような文字列にする，(v1, ..., v<i>n</i>) という配列であれば 'v1|...|v<i>n</i>' というような文字列にするという方法である． 
階層的になっていれば (ハッシュや配列がネストしていれば)，階層ごとにことなるくぎり記号 (上記の例でいえば “|”) をつかえば split() でかんたんに要素をとりだすことができる． 
たとえば 2 階層の例をあげれば，(k1 => [v11, ..., v<i>m</i>], k2 => v2) というデータ構造を線形化するとき，くぎり記号として “|” と “,”をつかうことにすれば，'k1=>v11, ...,v<i>m</i>|k2=>v2' のように表現することができる． 
しかし，この方法では任意の階層をもつデータを線形化することができず，またデータの値としてつかえない文字が生じるため，汎用性がない． 
</p>
<p>
Wikipedia の <a href="http://en.wikipedia.org/wiki/Serialization" target="_blank">serialization という項目</a>をみると，もっと汎用的な方法がのべられている． 
その一部を引用すると，つぎのとおりである． 
<pre>
use Storable;

# Create a hash with some nested data structures
my %struct = ( text => 'Hello, world!', list => [1, 2, 3] );

# Serialize the hash into a file
store \%struct, 'serialized';

# Read the data back later
my $newstruct = retrieve 'serialized';
</pre>
<p>
この方法では線形化したデータをファイルにかいてしまうので，データベースに格納するのにはつかえない． 
<code>store</code>, <code>freeze</code> のかわりに <code>freeze</code>, <code>thaw</code> をつかうと線形化した結果を内部データとしてうけとることができるということである． 
(tie() のなかにこのしかけをくみこんでしまえば，構造化された値を直接データベースにかきこむことができて便利なのだが…)
</p>
]]>
   </content>
</entry>
<entry>
   <title>Java による簡易 SIP 風プロトコルとチャット・プログラム</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/java_sip.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2206</id>
   
   <published>2007-11-17T08:00:00Z</published>
   <updated>2007-11-17T10:50:16Z</updated>
   
   <summary> SSIP コミュニケータは GUI ベースでチャットができる，みじかい Java のプログラムである． プロトコルと通信プログラムの学習のためにつかうことを意図している． まずプログラムをうごかしてみてください． そして興味があれば，なかをのぞいてみてください． ここでは SSIP というプロトコルと SSIP コミュニケータの 使用法，構造などについて説明する． また，SSIP の問題点とその...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
SSIP コミュニケータは GUI ベースでチャットができる，みじかい Java のプログラムである． プロトコルと通信プログラムの学習のためにつかうことを意図している． まずプログラムをうごかしてみてください． そして興味があれば，なかをのぞいてみてください． ここでは SSIP というプロトコルと SSIP コミュニケータの 使用法，構造などについて説明する． また，SSIP の問題点とその可能な解決策などについてもふれる．
</p>
]]>
      <![CDATA[<p>
SSIP コミュニケータは IETF の標準プロトコルである UDP (User Datagram Protocol) または TCP (Transmission Control Protocol) のうえにのせた SSIP (Simple Session Initiation Protocol) という独自プロトコルをつかったチャットのプログラムである． 
Java でかいた GUI 上で動作する． 
</p>
<p>
SSIP コミュニケータに関するくわしい説明は 「<a href="/ssip/ssip-j.html" target="_blank">SSIP コミュニケータ</a>」 というページにある． 
ここからもたどることができるが，プログラムはかためて<a href="http://www.kanadas.com/ssip/ssip.zip">このファイル</a>にいれてある． 
解凍して，ためしてください． 
Java で書いてあるので Applet として Web ページにおくこともできるが，残念ながらすぐにアクセスできるようにはなっていない．
</p>
<img alt="[Example]" src="/ssip/example.gif"/>

]]>
   </content>
</entry>
<entry>
   <title>JavaScript による単純な電卓</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/javascript.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2245</id>
   
   <published>2007-11-17T07:47:19Z</published>
   <updated>2007-11-17T10:48:25Z</updated>
   
   <summary><![CDATA[ 私がもっともふるくから公開しているプログラムとして，JavaScript をつかって書いた単純な電卓がある．  プログラムをみるにはこのページをひらいて，Web ブラウザのメニューバーから 「表示 &gt; ページのソース」 を選択して，みてもらう必要がある．  ...]]></summary>
   <author>
      <name></name>
      
   </author>
         <category term="未整理" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
私がもっともふるくから公開しているプログラムとして，<a href="/javascript/calculator.html" target="_blank">JavaScript をつかって書いた単純な電卓</a>がある． 
プログラムをみるには<a href="/javascript/calculator.html" target="_blank">このページ</a>をひらいて，Web ブラウザのメニューバーから 「表示 &gt; ページのソース」 を選択して，みてもらう必要がある． 
</p>
]]>
      <![CDATA[<p>
この電卓は普通の電卓とはちがって，“+”, "*” などのキーをおしてもすぐには計算しない． 
“=” をおしたときにはじめて計算する． 
これは私が電卓をつかっているときにキーをおしまちがえたかどうかが確認できなくてイライラするので，そういうことがないようにしている． 
普通の電卓がつくりたければ，このプログラムをちょっとなおすとつくれるはずである．
</p>
<p>
邪道なつかいかただが，この電卓ではキーをつかわずにテキストボックスに直接，式を書いてから，“=” ボタンをおして計算することもできる． 
よかったら，ほかにもいろいろ，おためしあれ． 
アクセス・ログをみると，どうやらこのページは私の Web ページのなかでも比較的，人気がたかいようである． 
</p>
]]>
   </content>
</entry>
<entry>
   <title>Java によるランダム化された計算をつかった単純な問題解決の例</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/ccm.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2243</id>
   
   <published>2007-11-17T07:45:14Z</published>
   <updated>2008-03-30T13:13:30Z</updated>
   
   <summary> 1994 年前後に私は，局所的な情報だけをつかってランダムさをふくんだやりかた (計算順序などをきめるのに乱数をつかう方法) で記号処理などを計算する方法 CCM (Chemical Computation Method / Chemical Casting Model) の研究をしていた．  CCM による計算は Macintosh Common Lisp をつかって実行していたが，それでは ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="記号処理" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
1994 年前後に私は，局所的な情報だけをつかってランダムさをふくんだやりかた (計算順序などをきめるのに乱数をつかう方法) で記号処理などを計算する方法 <a href="/research-themes-j/0000/01/ccm_chemical_casting_model.html" target="_blank">CCM</a> (Chemical Computation Method / Chemical Casting Model) の研究をしていた． 
CCM による計算は Macintosh Common Lisp をつかって実行していたが，それでは Web 上で計算の様子をみせることができないので，1996 年に Java で書いたプログラムを公開した． 
</p>
]]>
      <![CDATA[「<a href="/ccm/examples-j.html" target="_blank">CCM をつかった単純な問題解決の例</a>」 のページにも書いたように，これらのプログラムは，わけあって Java らしくないプログラムだが，動作させるには問題がないので，いまでもほぼもとのままのかたちで公開している．
</p>
<p>
例題は 3 つあって，
<ul>
<li><a href="/ccm/queens-sort/index-j.html" target="_blank"><i>N</i> クイーン問題とならべかえ (ソート)</a> -- 
ひとつのプログラムで実行させるたびにことなる <i>N</i> クイーン問題の解をもとめたり，ソートをしたりすることができる． 
</li>
<li><a href="/ccm/coloring/index-j.html" target="_blank">地図とグラフ頂点のぬりわけ</a> -- 
実行させるたびにことなる米国の 4 色ぬりわけができる 
(下図がぬりわけ結果の例)． 
</li>
<li><a href="/ccm/magic-square/index-j.html" target="_blank">魔方陣</a> -- 
実行させるたびにことなる魔方陣の解がもとめられる． 
</li>
</ul>
です． ためしてみてください． 
計算が停止したときには，こたえが表示されるのと同時に，何回，規則の条件部をテストしたか (#tests)，何回，規則を実行したか (#reactions)，どれだけ時間がかかったか (Time (秒)) が表示されるようになっている． 
プログラムを公開したときには “full speed” (計算中に表示を更新しない最高速モード) で計算しても 1 秒くらいの時間がかかったものが，その後 PC や Java の高速化によって，いまではミリ秒単位で計算できるようになった． 
</p>
<p>
これらの例題では，局所的な情報だけで計算する (せまい範囲だけをみてクイーンの位置や州の色や魔方陣の欄の値をきめる規則をつかう) と解をもとめるまでの時間がかかり，よりひろい範囲の情報をつかうとまちがったこたえでとまってしまう (局所最小値につかまる) が，さらにランダムさを導入する (“frustration” を ON にする) とただしいこたえにいきつきやすくなることがためせるようになっている． 
わかりにくいとはおもうが… 
もうすこしくわしい説明は 「<a href="/ccm/examples-j.html" target="_blank">CCM をつかった単純な問題解決の例</a>」 のページにある． 
</p>
<p align="center">
<img alt="CCM-coloring.png" src="http://www.kanadas.com/program/CCM-coloring.png" width="600" height="453" />
</p>
]]>
   </content>
</entry>
<entry>
   <title>Perl による音声 (VoIP) 受信と再生</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_voip_1.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2202</id>
   
   <published>2007-11-15T14:11:28Z</published>
   <updated>2007-11-17T14:39:15Z</updated>
   
   <summary> RTP (Real-time Transport Protocol) による 2 チャンネル (ステレオ) の音声 (VoIP, Voice over IP) を受信してオーディオ再生するプログラムをしめす．  音声は 16 bit 線形を基本とするが，u-Law (G.711) などもあつかえるようにしてある．  受信のためのポート番号はこのプログラムのなかで指定されている ($IN_PORT...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
         <category term="マルチメディア" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
RTP (Real-time Transport Protocol) による 2 チャンネル (ステレオ) の音声 (VoIP, Voice over IP) を受信してオーディオ再生するプログラムをしめす． 
音声は 16 bit 線形を基本とするが，u-Law (G.711) などもあつかえるようにしてある． 
受信のためのポート番号はこのプログラムのなかで指定されている ($IN_PORT_RTP)． 
とりあえず，こまかい説明ははぶくが，あとで必要に応じて説明をくわえることにしたい．
</p>
]]>
      <![CDATA[<pre>
#!/usr/bin/perl
##############################################################################
#
#	2-channel RTP Stream Player
#
##############################################################################

use strict;
use Socket;
use Time::HiRes qw(time);
require 'sys/ioctl.ph';
require 'sys/soundcard.ph';

my $PACKET_SIZE = 1500;	# Assumed max UDP packet size

my $IN_PORT_RTP = 8000;
my $IN_PORT_RTCP = 8001;	# local ports for input

my ($source_ip, $source_port_rtp, $source_port_rtcp);
my ($fd_in_rtp, $fd_in_rtcp);

my %buf_rtp;

my $buf_displ = -1;
my %repeat_count;

my $curr_time = 0;
my $delta_time;

my $inspection_switch = 0;


#=============================================================================
# Utility function
#=============================================================================

my $power32 = 4294967296;
my $power16 = 65536;

### current_time()
#   return current time in the timestamp format.
#
sub current_time() {
    return int(time * 8000);

    # return int(((time + 2208988800) * $power16) % $power32);
}


#=============================================================================
# Network Input functions
#=============================================================================

### open_socket($proto)
#   open a UDP port of the local host (both for input and output),
#   and return the file descriptor.
#
sub open_socket($) {
    my ($port) = @_;
    my $fd;
    socket($fd, AF_INET, SOCK_DGRAM, getprotobyname('udp')) ||
        die "socket($fd)$!\n";
    setsockopt($fd, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) ||
        die "setsockopt()$!\n";
    bind($fd, sockaddr_in($port, INADDR_ANY)) ||
        die "bind($fd)$!\n";
    return $fd;
}

my %delay_estimate;

### receive_rtp_packet($curr_time)
#   receive an RTP packet and put it into the ring buffer.
#
sub receive_rtp_packet($) {
    my ($curr_time) = @_;
    my $buf;
    my $source_addr = recv($fd_in_rtp, $buf, $PACKET_SIZE, 0);
    defined($source_addr) || die "recv()$!";
    my ($port, $ip) = sockaddr_in($source_addr);
    if ($source_ip ne '' && $ip ne $source_ip) {
        printf stderr "Packets from two IPs received: $source_ip and $ip\n";
    };
    $source_ip = $ip;
    if ($source_port_rtp ne '' && $port ne $source_port_rtp) {
        printf stderr
        "RTP Packets from two ports received: $source_port_rtp and $port\n";
    };
    $source_port_rtp = $port;
    my ($first_byte, $second_byte, $seq_no, $timestamp, $ssrc) =
        unpack('CCnNN', $buf);
    if ($inspection_switch) {
        my ($d1, $d2, $d3, $a1, $a2, $a3, $a4) = unpack('LNNCCCC', $buf);
        print inet_ntoa($ip),
        " in:  $a1 $a2 $a3 $a4 seq=$seq_no ssrc=$ssrc\n";
    };
    my $payload_type = $second_byte & 7;
    test_rtp_packet($first_byte, $second_byte, $payload_type, $ip, $buf);
    my $delta = length($buf) - 12;
    if ($delta_time == 0) {
        $delta_time = $delta;
    } elsif ($delta != $delta_time) {
        print stderr "Sample size not equal!\n";
    };
    my $time_diff = $curr_time - $timestamp;
    if ($delay_estimate{$ssrc} == 0) {
        $delay_estimate{$ssrc} = $time_diff;
    } else {
        $delay_estimate{$ssrc} =
            (31 * $delay_estimate{$ssrc} + $time_diff) / 32;
    };
    if ($buf_displ &lt; 0) {  # $buf_displ not yet defined
        $buf_displ = $seq_no;
    };
    if ($seq_no &gt;= $buf_displ) {
        ${$buf_rtp{$ssrc}}[$seq_no - $buf_displ] = $buf;
    } elsif ($seq_no == $buf_displ) {
        ${$buf_rtp{$ssrc}}[0] = $buf;
        $repeat_count{$ssrc} = 0;
    } else {
        # Discard the data if it is too old (i.e., nothing to do).
    };
}

### test_rtp_packet ($first_byte, $second_byte, $payload_type)
#   test validity of RTP contents.
#
sub test_rtp_packet ($$$) {
    my ($first_byte, $second_byte, $payload_type) = @_;
    my $version = $first_byte &gt;&gt; 6;		# must be 2
    my $extension = ($first_byte &gt;&gt; 4) & 1;	# must be 0
    my $n_csrc = $first_byte & 15;
    my $marker = $second_byte &gt;&gt; 7;
    ($version == 2 && $extension == 0 && $n_csrc == 0 && $marker == 0) ||
        die "Invalid RTP packet header: version=$version " .
        "extension=$extension #CSRC(CC)=$n_csrc marker=$marker\n";
    $payload_type == 0 ||
        die "Payload type ($payload_type) must be 0 (G.711 ulaw)\n";
};

### receive_rtcp_packet()
#   receive an RTCP packet.and put it into the ring buffer.
#
sub receive_rtcp_packet() {
    my $buf;
    my $source_addr = recv($fd_in_rtcp, $buf, $PACKET_SIZE, 0);
    defined($source_addr) || die "recv()$!";
    my ($port, $ip) = sockaddr_in($source_addr);
    if ($source_ip ne '' && $ip ne $source_ip) {
        printf stderr "Packets from two IPs received: $source_ip and $ip\n";
    };
    $source_ip = $ip;
    if ($source_port_rtcp ne '' && $port ne $source_port_rtcp) {
        printf stderr
        "RTCP Packets from two ports received: $source_port_rtcp and $port\n";
    };
    $source_port_rtcp = $port;
    my ($first_byte, $packet_type, $length, $ssrc, $timestamp1, $timestamp2) =
        unpack('CCnN', $buf);
    my $version = $first_byte &gt;&gt; 6;		# must be 2
    ($version == 2) || die "Invalid RTCP packet header: version=$version\n";
}

</pre>
<p>
ここから G.711 のエンコーダである． 
G.711 のあつかいについては 「<a href="/program/2007/11/perl_g711.html" target="_blank">Perl による G.711 の処理</a>」 において，よりくわしくあつかっている．
</p>
<pre>

#=============================================================================
# ulaw -&gt; linear conversion table generator
#=============================================================================

my $QUANT_MASK = 0xf;
my $BIAS = 0x84;
my $SEG_MASK = 0x70;
my $SEG_SHIFT = 4;
my $SIGN_BIT = 0x80;

my @u2l;

sub u2l($) {
    my ($uval) = @_;
    $uval = ~$uval;
    my $t = (($uval & $QUANT_MASK) &lt;&lt; 3) + $BIAS;
    $t &lt;&lt;= ($uval & $SEG_MASK) &gt;&gt; $SEG_SHIFT;
    return ($uval & $SIGN_BIT) ? ($BIAS - $t) : ($t - $BIAS);
}

### gen_u2l()
#   generate ulaw-to-linear conversion table (@u2l)
#
sub gen_u2l() {
    for (my $i = 0; $i &lt; 256; $i++) {
        $u2l[$i] = u2l($i);
    };
}

</pre>
<p>
ここから音声出力のための初期化をおこなう部分である．
</p>
<pre>

#=============================================================================
# Open sound output
#=============================================================================

my $BUF_SIZE = 2000;

sub open_sound_output() {
    my $status;
    my $parameter;

    open(SOUND_OUT, "&gt;/dev/audio") || die "Sound output open failed!\n";

    # 16 bit sampling for output
    my $bits = pack("L", 16);
    $status = ioctl(SOUND_OUT, SOUND_PCM_WRITE_BITS(), $bits);
    if ($status &lt; 0) {
        print stderr "8 bit sampling setting failed!\n";
    };

    # 2-channel (binaural)
    # $parameter = pack("L", 1);
    # $status = ioctl(SOUND_OUT, SNDCTL_DSP_STEREO(), $parameter);
    # if ($status &lt; 0) {
    #     print stderr "Stereo setting failed!\n";
    # };

    # Number of channels = 2
    $parameter = pack("L", 2);
    $status = ioctl(SOUND_OUT, SNDCTL_DSP_CHANNELS(), $parameter);
    # $status = ioctl(SOUND_OUT, SOUND_PCM_WRITE_CHANNELS(), $parameter);
    if ($status &lt; 0) {
	print stderr "#Channels setting failed!\n";
    };

    # 8000 Hz sampling rate for output
    my $sampling = pack("L", 8000);
    $status = ioctl(SOUND_OUT, SNDCTL_DSP_SPEED(), $sampling);
    # $status = ioctl(SOUND_OUT, SOUND_PCM_WRITE_RATE(), $sampling);
    if ($status &lt; 0) {
        print stderr "8000 Hz sampling rate setting failed!\n";
    };

    # Buffer underrun may be cause by network
    $parameter = APF_NETWORK();
    $status = ioctl(SOUND_OUT, SNDCTL_DSP_PROFILE(), $parameter);
    if ($status &lt; 0) {
        print stderr "ioctl(SNDCTL_DSP_PROFILE) failed!\n";
    };

    # Block size
    $parameter = pack("L", $BUF_SIZE);
    $status = ioctl(SOUND_OUT, SNDCTL_DSP_GETBLKSIZE(), $parameter);
    if ($status &lt; 0) {
        print stderr "ioctl(SNDCTL_DSP_GETBLKSIZE) failed!\n";
    };
}


#=============================================================================
# Playout (deciding timing for output)
#=============================================================================

my ($left_ssrc, $right_ssrc);

my %repeat_count;

### playout()
#   make the first element of each buffer the playout data.
#
sub playout() {
    if ($left_ssrc eq '') {
        my @ssrcs = keys %buf_rtp;
        if (@ssrcs &gt;= 2) {  # already received two streams (SSRCs)
            if ($ssrcs[0] &gt; $ssrcs[1]) {
                $left_ssrc = $ssrcs[1];
                $right_ssrc = $ssrcs[0];
            } else {
                $left_ssrc = $ssrcs[0];
                $right_ssrc = $ssrcs[1];
            };
        }
    } else {
        my $left_buf = ${$buf_rtp{$left_ssrc}}[0];
        my $right_buf = ${$buf_rtp{$right_ssrc}}[0];
        if ($repeat_count{$left_ssrc} == 0 && $repeat_count{$right_ssrc} == 0 ||
            @{$buf_rtp{$left_ssrc}} &gt; 2 && @{$buf_rtp{$right_ssrc}} &gt; 2) {
            if ($inspection_switch) {
                my ($left_seq, $right_seq, $left_ts, $right_ts);
                ($_, $left_seq, $left_ts) = unpack('nnNN', $left_buf);
                ($_, $right_seq, $right_ts) = unpack('nnNN', $right_buf);
                if ($left_seq != $right_seq) {
		    print stderr "Seq num mismatched! $left_seq $right_seq\n";
		};
                if ($left_ts != $right_ts) {
                    print stderr "Timestamp mismatch! $left_ts $right_ts\n";
                };
            };
            shift @{$buf_rtp{$left_ssrc}};	# discard an old packet
            shift @{$buf_rtp{$right_ssrc}};	# discard an old packet
            if (${$buf_rtp{$left_ssrc}}[0] eq '') {
					# no data in the L-ch buffer head
                ${$buf_rtp{$left_ssrc}}[0] = $left_buf;   # repeat the data
                $repeat_count{$left_ssrc}++;
            } else {
                $repeat_count{$left_ssrc} = 0;
            };
            if (${$buf_rtp{$right_ssrc}}[0] eq '') {
					# no data in the R-ch buffer head
                ${$buf_rtp{$right_ssrc}}[0] = $right_buf; # repeat the data
                $repeat_count{$right_ssrc}++;
            } else {
                $repeat_count{$right_ssrc} = 0;
            };
            $buf_displ++;

            my (@left_data, @right_data);
            ($_, $_, $_, @left_data) = unpack('LLLC*', $left_buf);
            ($_, $_, $_, @right_data) = unpack('LLLC*', $right_buf);
            if ($#left_data != $#right_data) {
                print stderr "Numbers of left/right data different!\n";
            };

            # decode @left_data and @right_data
	    my $outbuf;
            for (my $i = 0; $i &lt;= $#left_data; $i++) {
                $outbuf .=
		    pack("SS", $u2l[$left_data[$i]], $u2l[$right_data[$i]]);
            };

	    syswrite(SOUND_OUT, $outbuf);
        };
    };
}

</pre>
<p>
このプログラムの主要部分である．
</p>
<pre>

#=============================================================================
# main
#=============================================================================

open_sound_output();

$fd_in_rtp =  open_socket($IN_PORT_RTP);
$fd_in_rtcp = open_socket($IN_PORT_RTCP);

gen_u2l();

for (;;) {
    ## Test non-blocking I/O possibilities ##
    # $rin = file descriptor set [$fd_in_rtp, $fd_in_rtcp]
    my $rin = '';
    vec($rin, fileno($fd_in_rtcp), 1) = 1;
    vec($rin, fileno($fd_in_rtp), 1) = 1;

    my $rout;
    my $nfound = select($rout = $rin, undef, undef, 0);

    if ($nfound &gt; 0) {			# Non-blocking I/O possible
        ## Accept packets ##
        if (vec($rout, fileno($fd_in_rtcp), 1)) {	# RTCP data readable
        					# (Control data prioritized)
            receive_rtcp_packet();
        };
        if (vec($rout, fileno($fd_in_rtp), 1)) {	# RTP data readable
            receive_rtp_packet($curr_time);
        };
    };

    playout();
}
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl によるファイルからの音声 (VoIP) 送信</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_voip.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2201</id>
   
   <published>2007-11-15T13:39:48Z</published>
   <updated>2007-11-17T14:38:42Z</updated>
   
   <summary> ファイルにふくまれる音声 (VoIP, Voice over IP) を RTP (Real-time Transport Protocol) によって送信する Perl プログラムをしめす．  音声は線形 16 bit を基本とするが，u-Law (G.711) などもあつかえるようにしてある．  送信・受信のためのポート番号と受信者の IP アドレスはこのプログラムのなかで指定されている (...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
         <category term="マルチメディア" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
ファイルにふくまれる音声 (VoIP, Voice over IP) を RTP (Real-time Transport Protocol) によって送信する Perl プログラムをしめす． 
音声は線形 16 bit を基本とするが，u-Law (G.711) などもあつかえるようにしてある． 
送信・受信のためのポート番号と受信者の IP アドレスはこのプログラムのなかで指定されている ($REMOTE_PORT_RTP, $LOCAL_PORT_RTP, $ip など)．  
RTCP (Real-Time Control Protocol) もあつかっているが，RTCP によって特別の制御をしようとしているわけではない． 
</p>
]]>
      <![CDATA[<pre>
#!/usr/bin/perl
##############################################################################
#
#	Wave File Streamer
#
##############################################################################

use strict;
use Socket;
use Audio::Wav;
</pre>
<p>
<blockquote>
Audio::Wav は wave ファイルをあつかうためのパッケージである．
</blockquote>
</p>
<pre>
use Time::HiRes qw(time sleep);	# for exact time measurements and sleep
</pre>
<p>
<blockquote>
ここで Time::HiRes というパッケージは時刻を正確に計測するためのものである．
</blockquote>
</p>
<pre>
my $CODEC = 'linear16';		# 'ulaw', 'ulaw16', 'linear16' or 'linear32'

my $CHANNELS = $CODEC eq 'ulaw16' || $CODEC eq 'linear32' ? 2 : 1;
</pre>
<p>
<blockquote>
1 チャンネルまたは 2 チャンネル (ステレオ) の信号をあつかう． 
$CODEC が 'ulaw', 'linear16' のときは 1 チャンネル，'ulaw16', 'linear32' のときは 2 チャンネルである．
</blockquote>
</p>
<pre>
my $SAMPLING_RATE = 8000;
</pre>
<p>
<blockquote>
標本化レートは 8000 Hz である． 
他の値にかえても動作するはずである．
</blockquote>
</p>
<pre>
my $volume = 0.45;
</pre>
<p>
<blockquote>
この値をかえれば音量をかえることができる．
</blockquote>
</p>
<pre>
my $wav_file = "WaveFile.wav";
</pre>
<p>
<blockquote>
ファイル名はここであたえている．
</blockquote>
</p>
<pre>
my $FRAME_LENGTH = $SAMPLING_RATE / 50;	# number of samples in a packet

my $PACKET_SIZE = 1500;	# Assumed max UDP packet size

my $REMOTE_PORT_RTP = 10000;
my $REMOTE_PORT_RTCP = 10001;

my $LOCAL_PORT_RTP = 8000;
my $LOCAL_PORT_RTCP = 8001;	# local ports for output (UDP)
        # input and output local ports must be different.
        # (implementation restriction)

my $ip = '192.168.1.33';
</pre>
<p>
<blockquote>
IP アドレスとポート番号はここできめている．
</blockquote>
</p>
<pre>
my $rtp_addr = pack_sockaddr_in($REMOTE_PORT_RTP, inet_aton($ip));
my $rtcp_addr = pack_sockaddr_in($REMOTE_PORT_RTCP, inet_aton($ip));

my ($fd_out_rtp, $fd_out_rtcp, $fd_in_rtcp);

my (@out_ip_rtp, @out_buf_rtp);
my (@out_ip_rtcp, @out_buf_rtcp);

my $debug_switch = 0;
my $inspection_switch = 0;

$inspection_switch |= $debug_switch;


#=============================================================================
# Utility function
#=============================================================================

my $power32 = 4294967296;
my $power16 = 65536;

### current_npt_time()
#   return exact current time
#
sub current_npt_time() {
    my $time = time;
    my $lower = ($time - int($time)) * 4294967296.0;
    my $upper = int($time) + 2208988800;
    return ($upper, $lower);
}

### decode_sockaddr($sockaddr)
#   decode sockaddr_in structure to "$IP:$port" format.
#
sub decode_sockaddr($) {
    my ($sockaddr) = @_;
    if (length($sockaddr) != 16) {
        return '';
    };
    my ($port, $ip) = unpack_sockaddr_in($sockaddr);
    return inet_ntoa($ip) . ":$port";
}

### check_codec($codec)
#   check whether the specified CODEC is appropriate
#
sub check_codec($) {
    my ($codec) = @_;
    if ($codec ne 'ulaw' && $codec ne 'ulaw16' &&
        $codec ne 'linear16' && $codec ne 'linear32') {
	print STDERR "Unknown CODEC: $codec\n";
    };
}

### check_wav($details)
#   check whether the wave file format is appropriate
#
sub check_wav($) {
    my ($details) = @_;
    my $error = 0;
    # my $channels = $$details{channels};
    my $bits_sample = $$details{bits_sample};
    if ($bits_sample != 16) {
	$error = 1;
	print STDERR "#bits in sample ($bits_sample) must be 16\n";
    };
    # my $sample_rate = $$details{sample_rate};
    # if ($sample_rate != 8000) {
    #	$error = 1;
    #	print STDERR "Sample rate ($sample_rate) must be 8000\n";
    # };
    if ($error) {
	exit 1;
    };
}


#=============================================================================
# Network Output functions
#=============================================================================

### open_socket($proto)
#   open a UDP port of the local host (both for input and output),
#   and return the file descriptor.
#
sub open_socket($) {
    my ($port) = @_;
    my $fd;
    socket($fd, AF_INET, SOCK_DGRAM, getprotobyname('udp')) ||
        die "socket($fd)$!\n";
    setsockopt($fd, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) ||
        die "setsockopt()$!\n";
    bind($fd, pack_sockaddr_in($port, INADDR_ANY)) ||
        die "bind($fd)$!\n";
    return $fd;
}

my %packet_count;
my %octet_count;

### send_rtp($destination_addr, $buf)
#   send an RTP packet which is stored in the ring buffer.
#
sub send_rtp($$) {
    my ($destination_addr, $buf) = @_;
    send($fd_out_rtp, $buf, 0, $destination_addr) || die "send()$!";
    $packet_count{$destination_addr}++;
    $octet_count{$destination_addr} += length($buf);
    if ($inspection_switch) {
        my ($d1, $d2, $seq_no, $timestamp, $ssrc, $a1, $a2, $a3, $a4) =
            unpack('CCnNNCCCC', $buf);
        print decode_sockaddr($destination_addr),
	" out: $a1 $a2 $a3 $a4 seq=$seq_no ssrc=$ssrc\n";
	if ($debug_switch) {
	    print "RTP timestamp=$timestamp length=", length($buf), "\n";
	};
    };
}

### send_rtcp($destination_addr, $buf)
#   send an RTCP packet.
#
sub send_rtcp($$) {
    my ($destination_addr, $buf) = @_;
    send($fd_out_rtcp, $buf, 0, $destination_addr) || die "send()$!";
    if ($inspection_switch) {
	my ($d1, $d2, $seq_no, $timestamp, $ssrc, $a1, $a2, $a3, $a4) =
	    unpack('CCnNNCCCC', $buf);
        print decode_sockaddr($destination_addr),
	" out: $a1 $a2 $a3 $a4 seq=$seq_no ssrc=$ssrc\n";
	if ($debug_switch) {
	    print "RTCP timestamp=$timestamp length=", length($buf), "\n";
	};
    };
}

### receive_rtcp()
#   receive an RTCP packet and reply to the sender.
#
sub receive_rtcp() {
    my $buf;
    my $source_addr = recv($fd_in_rtcp, $buf, $PACKET_SIZE, 0);
    defined($source_addr) || die "recv()$!";
    my ($dummy, $ip) = unpack_sockaddr_in($source_addr);
    my ($first_byte, $payload_type, $length, $ssrc) = unpack('CCnN', $buf);
    my $version = $first_byte &gt;&gt; 6;		# must be 2
    unless ($version == 2) {
        print STDERR "Invalid RTCP packet header: version=$version\n";
    };

    # if ($inspection_switch) {
        my $report_count = $first_byte & 31;
        print "RTCP payload_type=$payload_type rerport_count=$report_count\n";
        print "Control data received from ", inet_ntoa($ip),
        ", length=", length($buf), "\n";
    # };

    ### to analyze the packet here ###

    if ($payload_type == 201) {	# if Receiver Report
        analyze_receiver_report($buf);
    };
}

### analyze_receiver_report()
#
sub analyze_receiver_report($) {
    my ($message) = @_;
    my $jitter = 0;
    my ($time_upper, $time_lower) = current_npt_time();
    my $curr_time = ($time_upper &lt;&lt; 16) + ($time_lower &gt;&gt; 16);
    my $delay_since_last_SR = 0;
    my ($ssrc, $frac_lost, $highest_seq_no, $jitter);
    my ($last_SR, $delay_since_last_SR);
    ($_, $_, $ssrc, $frac_lost, $highest_seq_no, $jitter,
     $last_SR, $delay_since_last_SR) =
        unpack('NNNNNNNN', $message);	# 2B rtp header + 6B report block 1
    my $fraction_lost = $frac_lost &gt;&gt; 24;
    my $cumulative_packets_lost = $frac_lost & 0xFFFFFF;
    printf "Round trip time: %5.6f S\n", ($curr_time - $last_SR) / 65536.0;
}

</pre>
<p>
ここから RTCP メッセージを生成するための部分である．
</p>
<pre>

#=============================================================================
# RTCP Message Generator
#=============================================================================

my $CNAME = 1;

my $RTCP_INTERVAL = 5; # sec

my %rtcp_next_time;

### generate_outgoing_rtcp($out_addr, $ssrc, $cname, $timestamp)
#   generate Source Description and Sender Report messages of RTCP
#
sub generate_outgoing_rtcp($$$$) {
    my ($out_addr, $ssrc, $cname, $timestamp) = @_;
    my $current_time = time;
    if ($current_time &gt;= $rtcp_next_time{$out_addr}) {
	if ($rtcp_next_time{$out_addr} == 0) {	# first time
	    $rtcp_next_time{$out_addr} = rand ($RTCP_INTERVAL / 2);
	    # Compute initial sending time
        # } elsif ($rtcp_next_time{$out_addr} == 0) {
	# 			# first time (SDES only)
        #     send_rtcp($out_addr, source_description($ssrc, $cname));
        #         # send a Source Description message of RTCP
        } else {		# not first time (SR and SDES)
            send_rtcp($out_addr,
                      sender_report($out_addr, $ssrc, $timestamp) .
                      source_description($ssrc, $cname));
                # send SR and SDES messages of RTCP
            print "Sending SR and SDES to ", decode_sockaddr($out_addr), "\n";

	    $rtcp_next_time{$out_addr} = $current_time +
		$RTCP_INTERVAL / 2 + rand ($RTCP_INTERVAL);
	};
    };
}

### source_description($ssrc, $my_cname)
#   Return a Source Description of RTCP
#
sub source_description($$) {
    my ($ssrc, $my_cname) = @_;
    my $cname_length = length($my_cname);
    return pack("nnNCCC*",
                0x81ca,	# V=2, PT=202 (SDES), SourceCount=1 (single chunk)
                1 + ($cname_length + 2 + 3) / 4, # length in words
		$ssrc, $CNAME, $cname_length) .
          $my_cname;
}

### sender_report($ssrc, $timestamp)
#   return a Sender Report of RTCP
#
sub sender_report($$$) {
    my ($out_addr, $ssrc, $timestamp) = @_;
    my ($curr_time_upper, $curr_time_lower) = current_npt_time();
    my $packet_count = $packet_count{$out_addr};
    my $octet_count = $octet_count{$out_addr};
    $packet_count{$out_addr} = 0;
    $octet_count{$out_addr} = 0;
    my $sender_info = pack("NNNNN", $curr_time_upper, $curr_time_lower,
                           $timestamp, $packet_count, $octet_count);
    my $header = pack("nnN", 
                      0x81c8, # V=2, PT=200(SR), ReportCount=1 (single chunk)
                      1 + length($sender_info) / 4,	# length in words
                      $ssrc);
    return $header . $sender_info;
}

</pre>
<p>
ここから G.711 のデコーダである． 
G.711 のあつかいについては 「<a href="/program/2007/11/perl_g711.html" target="_blank">Perl による G.711 の処理</a>」 において，よりくわしくあつかっている．
</p>
<pre>

#=============================================================================
# Linear to ulaw conversion table generator
#=============================================================================

my $QUANT_MASK = 0xf;
my $BIAS = 0x84;
my $SEG_MASK = 0x70;
my $SEG_SHIFT = 4;
my $SIGN_BIT = 0x80;

my (@u2l, @l2u);

sub u2l($) {
    my ($uval) = @_;
    $uval = ~$uval;
    my $t = (($uval & $QUANT_MASK) &lt;&lt; 3) + $BIAS;
    $t &lt;&lt;= ($uval & $SEG_MASK) &gt;&gt; $SEG_SHIFT;
    return ($uval & $SIGN_BIT) ? ($BIAS - $t) : ($t - $BIAS);
}

### gen_u2l()
#   generate ulaw-to-linear conversion table (@u2l)
#
sub gen_u2l() {
    for (my $i = 0; $i &lt; 256; $i++) {
        $u2l[$i] = u2l($i);
    };
}

### gen_l2u()
#   generate linear-to-ulaw conversion table (@l2u)
#   (This method might not generate an optimum converter.)
#
sub gen_l2u() {
    for (my $i = 0; $i &lt; 256; $i++) {
        my $j = $u2l[$i];
        if ($j &lt; 0) {
            $j += 65536;
        };
        $l2u[$j] = $i;
    };
    for (my $i = 1; $i &lt; 65536; $i++) {
        if ($l2u[$i] == 0) {
            $l2u[$i] = $l2u[$i-1];
        };
    };
}

### gen_ul_conv()
#   generate ulaw &lt;-&gt; linear conversion tables
#
sub gen_ul_conv() {
    gen_u2l();
    gen_l2u();
}

</pre>
<p>
ここから主要部分である．
</p>
<pre>

#=============================================================================
# main
#=============================================================================

if ($ARGV[0] ne '') {
    $wav_file = "$SAMPLING_RATE/$ARGV[0]";
    print "Playing file $wav_file\n";
};
if ($ARGV[1] ne '') {
    $volume = $ARGV[1];
    print "Volume=$volume\n";
};
my $loop = 1;
if ($ARGV[2] eq 'noloop') {
    $loop = 0;
}

$fd_out_rtp =  open_socket($LOCAL_PORT_RTP);
$fd_out_rtcp = open_socket($LOCAL_PORT_RTCP);
$fd_in_rtcp = $fd_out_rtcp;

gen_ul_conv();

check_codec($CODEC);

my $wav = new Audio::Wav;
my $wav_reader = $wav-&gt;read($wav_file);

check_wav($wav_reader-&gt;details());

my $ssrc = $ARGV[3] | int(rand 0x80000000);
my $seq_no = 0;

my $time_interval = 1000 * $FRAME_LENGTH / $SAMPLING_RATE;
my $timestamp = 0;
my $real_time = time;
my $playout_time = 1000 * $real_time;

for (;;) {
    my @buf;
    for (my $i = 0; $i &lt; $FRAME_LENGTH; $i++) {
        my @data = $wav_reader-&gt;read();
        if (!defined($data[0])) {
            if (!$loop) {
                exit 0;
            };
            $wav_reader = $wav-&gt;read($wav_file);	# read the file again
            @data = $wav_reader-&gt;read();
            print "Repeating...\n";
        };
        $data[0] = $volume * $data[0];
        if ($CODEC eq 'ulaw') {
            $buf[$i] = $l2u[$data[0]];	# only the first channel data is used
        } elsif ($CODEC eq 'linear16') {
            $buf[$i] = $data[0];	# only the first channel data is used
        } elsif ($CODEC eq 'linear32') {
            if (@data &gt;= 2) {		# use two-channel data
                $buf[2*$i] = $data[0];
                $data[1] = $volume * $data[1];
                $buf[2*$i+1] = $data[1];
            } else {			# duplicate single-channel data
                $buf[2*$i] = $data[0];
                $buf[2*$i+1] = $data[0];
            };
        } elsif ($CODEC eq 'ulaw16') {
            if (@data &gt;= 2) {		# use two-channel data
                $buf[2*$i] = $l2u[$data[0]];
                $data[1] = $volume * $data[1];
                $buf[2*$i+1] = $l2u[$data[1]];
            } else {			# duplicate single-channel data
        	my $data = $l2u[$data[0]];
                $buf[2*$i] = $data;
                $buf[2*$i+1] = $data;
            };
        };
    };
    my $buf;
    if ($CODEC eq 'ulaw') {
        $buf = pack('NNNC*', 0x80000000 + $seq_no, $timestamp, $ssrc, @buf);
    } elsif ($CODEC eq 'ulaw16') {
        $buf = pack('NNNC*', 0x80600000 + $seq_no, $timestamp, $ssrc, @buf);
    } else { # linear16 or linear32
        $buf = pack('NNNn*',
        	    ($CODEC eq 'linear16' ? 0x80610000 : 0x80620000) + $seq_no,
        	    $timestamp, $ssrc, @buf);
    };

    send_rtp($rtp_addr, $buf);
    my $cname = getlogin() . ":$LOCAL_PORT_RTP";
    generate_outgoing_rtcp($rtcp_addr, $ssrc, $cname, $timestamp);

    my $rin = '';
    vec($rin, fileno($fd_in_rtcp), 1) = 1;
    my $rout;
    my $nfound = select($rout = $rin, undef, undef, 0);
    if ($nfound &gt; 0) {			# Non-blocking I/O possible
        if (vec($rout, fileno($fd_in_rtcp), 1)) {	# RTCP data readable
            receive_rtcp();
        };
    };

    if ($debug_switch) {
        print "$real_time $playout_time ", decode_sockaddr($rtp_addr), "\n";
    };

    $seq_no++;
    if ($seq_no &gt;= 65536) {
        $seq_no = 0;
    };
    $timestamp += @buf / $CHANNELS;
    $real_time = time;
    $playout_time += $time_interval;
    my $sleep_time = $playout_time / 1000 - $real_time;
    if ($sleep_time &gt; 0) {
        sleep($sleep_time);
    };
    # printf "%f %f\n", $real_time, $sleep_time;
}
</pre>
]]>
   </content>
</entry>
<entry>
   <title>リアルタイム用途にもつかえた Perl による疑似リングバッファ</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_2.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2205</id>
   
   <published>2007-11-15T13:14:51Z</published>
   <updated>2007-12-16T15:09:56Z</updated>
   
   <summary> つぎつぎと入力されてくるデータを固定長の配列をつかってバッファリングするとき，たとえば VoIP (Voice over IP) の音声を入力するプログラムにおいて，リングバッファというデータ構造がよくつかわれる．  リングバッファをきちんとつくるのがめんどうだったので，Perl をつかって，常にあたらしい要素を配列の末尾に追加する “疑似リングバッファ” による VoIP のプログラムをつくっ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="データ構造" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
つぎつぎと入力されてくるデータを固定長の配列をつかってバッファリングするとき，たとえば VoIP (Voice over IP) の音声を入力するプログラムにおいて，リングバッファというデータ構造がよくつかわれる． 
リングバッファをきちんとつくるのがめんどうだったので，Perl をつかって，常にあたらしい要素を配列の末尾に追加する “疑似リングバッファ” による VoIP のプログラムをつくった． 
これでリアルタイムにちゃんと動作するのかどうか不安だったが，「<a href="/weblog/2007/10/voiscape_spatializer.html" target="_blank">Perl によるリアルタイム音声処理 ― voiscape 3D 化プログラムの奇跡</a>」 にも書いたようにうまく動作したので，ここではこの “疑似リングバッファ” について書いてみる．
</p>
]]>
      <![CDATA[<p>
リングバッファにおいては，配列の最後の要素がデータでうまると，つぎのデータは配列の最初の要素にいれる 
(そのときその要素が空になっていなければ，バッファあふれがおこる)． 
このような添字の管理はいささかめんどうなので，“リングバッファ” の最初の要素が空になったらその要素をなくしてしまうことによって，つねにあたらしい要素は配列の末尾に追加していくようにして VoIP のプログラムをつくった． 
つくったプログラムじたいは複雑なので，ここでは原理だけを書く． 
</p>
<p>
@buffer という配列があり，その末尾まで，すでにデータでうまっているとする． 
</p>
<ul>
<li>あたらしいデータ $new_data は push(@buffer, $new_data) によって配列の最後に付加すればよい． 
これによって配列のサイズがひとつ増加する． 
</li>
<li>逆に $buffer[0] が処理されて不要になったとき，shift @buffer によってその要素をなくす． 
これによって配列のサイズはひとつ減少する． 
</li>
</ul>
<p>
Perl プログラマにとってはまったくあたりまえの処理だが，こうやってつくった “疑似リングバッファ” が Linux (Fedora Core) 上でうまくリアルタイムに機能したということである． 
(他の OS ではためしていない.)
</p>
<p>
この方法をつかえば，配列添字のあつかいに注意をはらう必要がないだけでなく，本来のリングバッファとはちがって配列がのびちぢみするので，まちがえてまだ有効なデータをつぶしてしまう心配もない． 
ただし，リアルタイムに動作するといっても，データ構造のあつかいとしても効率がよいわけではないし，そもそも Perl をつかっているので，メディア処理のプログラムとしてはあまり実用的とはいえない． 
用途としては rapid prototyping に適している． 
</p>

]]>
   </content>
</entry>
<entry>
   <title>ブログの目的</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/post_2.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2214</id>
   
   <published>2007-11-15T11:44:18Z</published>
   <updated>2007-11-15T11:54:43Z</updated>
   
   <summary> この “ブログ” の目的は，私がもっているプログラミングに関するちょっとした知識を紹介することです．  ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="未整理" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
この “ブログ” の目的は，私がもっているプログラミングに関するちょっとした知識を紹介することです． 
</p>
]]>
      <![CDATA[<p>
これまで，私がプログラムをかくうえで，Web 上のいろいろな情報でたすけられてきました． 
そういう Web ページの作者に感謝する意味をこめて，この “ブログ” を書くことにします． 
どれだけやくにたつか，わかりませんが，できるだけやくだつものにしたいとおもっています． 
</p>
]]>
   </content>
</entry>
<entry>
   <title>Linux のためのイーサネット・ハブもどきのプログラム</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/linux.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2213</id>
   
   <published>2007-11-15T11:31:41Z</published>
   <updated>2007-11-15T12:31:19Z</updated>
   
   <summary> Linux 上でイーサネットのハブのようなふるまいをするプログラムをつくろうとすると，ソケットを promiscuous mode (プロミスキャス・モード) で動作させる必要がある．  きちんとハブとして動作させようとするとやや複雑なプログラムを書く必要があるが，ここではもっとさぼって，ハブにちかいがもうすこしいいかげんに動作するプログラムをしめす． ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
Linux 上でイーサネットのハブのようなふるまいをするプログラムをつくろうとすると，ソケットを promiscuous mode (プロミスキャス・モード) で動作させる必要がある． 
きちんとハブとして動作させようとするとやや複雑なプログラムを書く必要があるが，ここではもっとさぼって，ハブにちかいがもうすこしいいかげんに動作するプログラムをしめす．
</p>
]]>
      <![CDATA[<p>
以下のプログラムは 2 つの NIC (ネットワーク・インタフェース・カード) をもつ Linux マシン上で動作する． 
ひとつのインタフェースにとどいたパケットをもうひとつのインタフェースに転送する． 
パケットがインタフェースにとどくといっても，そのインタフェースに指定された IP アドレスや MAC アドレスが指定されたパケットだけをうけとるのではなく，そのインタフェースがつながっているイーサネット・リンク上のすべてのパケットが対象になる． 
このように自分 (インタフェース) にあてられた以外のパケットも受信するモードが promiscuous mode である． 
以下のプログラムにはいくつかおまけの機能もくみこまれているが，いまはそれらについて解説しないまま，とりあえずプログラムをのせておく． 
あとでもうすこし整理しようとおもう． 
</p>
<pre>
/***
 * A Repeater with Partial XUDP Processing for Linux
 ***/

#include &lt;features.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdint.h&gt;
#include &lt;string.h&gt;
#include &lt;unistd.h&gt;
#include &lt;asm/types.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;linux/if_packet.h&gt;
#include &lt;linux/if_ether.h&gt;
#include &lt;linux/if_arcnet.h&gt;
#include &lt;linux/version.h&gt;
#include &lt;net/if.h&gt;
#include &lt;net/if_arp.h&gt;
#include &lt;sys/ioctl.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;sys/time.h&gt;
#include &lt;signal.h&gt;

#define MAX_PACKET_SIZE 2048
#define UDP 17

char *interface = NULL;
int fd = -1;


/**
 * Open a socket for the network interface
 */
int open_socket(char *interface, int *rifindex) {
  u_char buf[2048];

  int addrlen = ETH_ALEN; // Ethernet address length (= 6)

  int i;
  int ifindex;
  struct ifreq ifr;
  struct sockaddr_ll sll;

  fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
  // fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
  if (fd == -1) {
    perror("socket");
    _exit(1);
  };

  // get interface index
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, interface, IFNAMSIZ);
  if (ioctl(fd, SIOCGIFINDEX, &ifr) == -1) {
    perror("SIOCGIFINDEX");
    _exit(1);
  };
  ifindex = ifr.ifr_ifindex;
  *rifindex = ifindex;

  // set promiscuous mode
  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, interface, IFNAMSIZ);
  ioctl(fd, SIOCGIFFLAGS, &ifr);
  ifr.ifr_flags |= IFF_PROMISC;
  ioctl(fd, SIOCSIFFLAGS, &ifr);

  memset(&sll, 0xff, sizeof(sll));
  sll.sll_family = AF_PACKET;
  sll.sll_protocol = htons(ETH_P_IP);
  sll.sll_ifindex = ifindex;
  if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) == -1) {
    perror("bind");
    _exit(1);
  };

  /* flush all received packets.
   *
   * raw-socket receives packets from all interfaces
   * when the socket is not bound to an interface
   */
  do {
    fd_set fds;
    struct timeval t;
    FD_ZERO(&fds);	
    FD_SET(fd, &fds);
    memset(&t, 0, sizeof(t));
    i = select(FD_SETSIZE, &fds, NULL, NULL, &t);
    if (i &gt; 0) {
      recv(fd, buf, i, 0);
    };
    printf("flushed\n");
  } while (i);

  return fd;
}


/**
 * Print packet content in hex
 */
void hexdump(const u_char *p, int count) {
  int i, j;
  for (i = 0; i &lt; count; i += 16) {
    printf("%04x : ", i);
    for (j = 0; j &lt; 16 && i + j &lt; count; j++)
      printf("%2.2x ", p[i + j]);
    for (; j &lt; 16; j++) {
      printf("   ");
    };
    printf(": ");
    for (j = 0; j &lt; 16 && i + j &lt; count; j++) {
      char c = toascii(p[i + j]);
      printf("%c", isalnum(c) ? c : '.');
    };
    printf("\n");
  };
}


/**
 * Print an Ethernet address
 */
void print_ethaddr(const u_char *p) {
  int i;
  struct ethhdr *eh = (struct ethhdr *)p;

  for (i = 0; i &lt; 5; ++i) {
    printf("%02x:", (int)eh-&gt;h_source[i]);
  };
  printf("%02x -&gt; ", (int)eh-&gt;h_source[i]);

  for (i = 0; i &lt; 5; ++i) {
    printf("%02x:", (int)eh-&gt;h_dest[i]);
  };
  printf("%02x", (int)eh-&gt;h_dest[i]);
  printf("\n");
}


/**
 * Dump a packet
 */
void dump_message(const u_char *buf, int count) {
  if (count &gt; 0) {
    print_ethaddr(buf);
    hexdump(buf, count);
  };
}


/**
 * Signal handler
 */
void sigint(int signum) {
  struct ifreq ifr;

  if (fd == -1) {
    return;
  };

  memset(&ifr, 0, sizeof(ifr));
  strncpy(ifr.ifr_name, interface, IFNAMSIZ);
  ioctl(fd, SIOCGIFFLAGS, &ifr);
  ifr.ifr_flags &= ~IFF_PROMISC;
  ioctl(fd, SIOCSIFFLAGS, &ifr);

  close(fd);
  _exit(0);
}


/**
 * Receive a packet from the interface.
 * Return the packet size (exclding Ethernet header).
 */
int getPacket(int interface, u_char *buf, int maxSize) {
  fd = interface;
  int count = recv(fd, buf, maxSize, 0);
  printf("Count %d\n", count);
  if (count &lt; 0) {
    perror("recv");
    return -1;
  };
  dump_message(buf, count);
  return count;
}


/**
 * Send the message to the interface
 */
int putPacket(int interface, int interfaceIndex, u_char *buf, int count) {
  struct sockaddr_ll sll;
  memset(&sll, 0, sizeof(sll));
  sll.sll_family = AF_PACKET;
  sll.sll_protocol = htons(ETH_P_IP);
  sll.sll_ifindex = interfaceIndex;
  fd = interface;
  int count1 = sendto(fd, buf, count, 0, (struct sockaddr *)&sll, sizeof(sll));
  if (count1 == -1) {
    perror("sendto");
  };
  printf("send %d bytes\n", count1);
  dump_message(buf, count);
  return count1;
}


/**
 * Send an XUDP message and wait and send the original message
 * to the interface.
 */
int processXUDP(int interface, int interfaceIndex, u_char *buf, int count) {
  int oldType = buf[42];
  int pid = fork();
  if (pid &lt; 0) {	// error
    perror("fork");
    _exit(0);
  } else if (pid == 0) { // child
    sleep(1);
    putPacket(interface, interfaceIndex, buf, count);
    return;
  } else {		// parent
    buf[42] = 0x85; // !! Modify the message !!
    putPacket(interface, interfaceIndex, buf, count);
    buf[42] = oldType;
  };
}


int main(int argc, char **argv) {
  u_char buf[MAX_PACKET_SIZE];

  signal(SIGINT, sigint);

  if (argc &lt;= 2) {
    printf("Usage: repeat ethM ethN [XUDPport]\n");
    return;
  };

  printf("Copy packets from %s to %s\n", argv[1], argv[2]);
  int ethindex1, ethindex2;
  int eth1 = open_socket(argv[1], &ethindex1);
  printf("%s opened (%d)\n", argv[1], eth1);
  int eth2 = open_socket(argv[2], &ethindex2);
  printf("%s opened (%d)\n", argv[2], eth2);

  int port2check = 0;
  if (argc &gt; 3) {
    port2check = atoi(argv[3]);
  };

  for (;;) {
    int count = getPacket(eth1, buf, MAX_PACKET_SIZE);
    if (count &gt;= 40) {
      int protocol = buf[23];
      int sourcePort = (buf[34] &lt;&lt; 8) + buf[35];
      if (protocol == UDP && sourcePort == port2check) {
	processXUDP(eth2, ethindex2, buf, count);
      } else {
	putPacket(eth2, ethindex2, buf, count);
      };
    };
  };
}
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl による G.711 の処理</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_g711.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2196</id>
   
   <published>2007-11-14T14:31:06Z</published>
   <updated>2007-11-19T14:55:22Z</updated>
   
   <summary> 音声にしろ動画にしろ，おおくのコーデックの処理は複雑で，ライブラリのお世話にならなければならない．  しかし，電話などでつかわれる G.711 という ITU-T 標準のコーデックは非常にかんたんであり，ほとんどテーブル引きだけで実現することができる．  ここでは Perl による G.711 の変換・逆変換のプログラムをしめす．  もちろん，Perl の特殊機能はつかっていないので，他の言語に...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="マルチメディア" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
音声にしろ動画にしろ，おおくのコーデックの処理は複雑で，ライブラリのお世話にならなければならない． 
しかし，電話などでつかわれる G.711 という ITU-T 標準のコーデックは非常にかんたんであり，ほとんどテーブル引きだけで実現することができる． 
ここでは Perl による G.711 の変換・逆変換のプログラムをしめす． 
もちろん，Perl の特殊機能はつかっていないので，他の言語に容易にかきかえることができる．
</p>
]]>
      <![CDATA[<p>
G.711 は対数圧縮によって 16 bit の音声を 8 bit に圧縮するコーデックである． 
G.711 には u-Law (正確には μ-Law) と a-Law という 2 種類があるが，変換のてまはあまりかわらない． 
ここでは u-Law だけをあつかう． 
</p>
<p>
基本的には，あらかじめつぎのようなテーブルを用意しておけば，これらのテーブルをひくだけで uLaw から線形への変換ができる (ただし，添字の範囲が 0..255 からはずれないようにクリップする必要がある)． 
</p>
<pre>
# ULAW-to-linear conversion table for upper digit
my @ULAW_H =
   (130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 
    170, 174, 178, 182, 186, 190, 193, 195, 197, 199, 
    201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 
    221, 223, 224, 225, 226, 227, 228, 229, 230, 231, 
    232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 
    241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 
    246, 247, 247, 248, 248, 248, 249, 249, 249, 249, 
    250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 
    252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 
    253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 
    254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
    255, 255, 255, 255, 255, 255, 255, 0, 126, 121, 
    117, 113, 109, 105, 101, 97, 93, 89, 85, 81, 
    77, 73, 69, 65, 62, 60, 58, 56, 54, 52, 
    50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 
    31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 
    21, 20, 19, 18, 17, 16, 15, 14, 14, 13, 
    13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 
    8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 
    5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 
    3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0);

# ULAW-to-linear conversion table for lower digit
my @ULAW_L =
   (0, 4, 8, 12, 17, 21, 25, 29, 33, 38, 
    42, 46, 50, 55, 59, 63, 66, 68, 71, 73, 
    75, 77, 79, 81, 83, 85, 88, 90, 92, 94, 
    96, 98, 228, 229, 230, 231, 232, 233, 234, 235, 
    236, 237, 238, 239, 240, 241, 243, 244, 180, 53, 
    181, 54, 182, 55, 184, 56, 185, 57, 186, 58, 
    187, 59, 188, 60, 157, 221, 29, 94, 158, 222, 
    30, 95, 159, 223, 31, 96, 160, 224, 32, 97, 
    145, 177, 209, 241, 17, 50, 82, 114, 146, 178, 
    210, 242, 18, 51, 83, 115, 139, 155, 171, 187, 
    203, 219, 235, 251, 11, 28, 44, 60, 76, 92, 
    108, 124, 136, 144, 152, 160, 168, 176, 184, 192, 
    200, 208, 216, 224, 232, 240, 248, 0, 0, 252, 
    248, 244, 239, 235, 231, 227, 223, 218, 214, 210, 
    206, 201, 197, 193, 190, 188, 185, 183, 181, 179, 
    177, 175, 173, 171, 168, 166, 164, 162, 160, 158, 
    28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 
    18, 17, 16, 15, 13, 12, 76, 203, 75, 202, 
    74, 201, 72, 200, 71, 199, 70, 198, 69, 197, 
    68, 196, 99, 35, 227, 162, 98, 34, 226, 161, 
    97, 33, 225, 160, 96, 32, 224, 159, 111, 79, 
    47, 15, 239, 206, 174, 142, 110, 78, 46, 14, 
    238, 205, 173, 141, 117, 101, 85, 69, 53, 37, 
    21, 5, 245, 228, 212, 196, 180, 164, 148, 132, 
    120, 112, 104, 96, 88, 80, 72, 64, 56, 48, 
    40, 32, 24, 16, 8, 0);
</pre>
<p>
しかし，テーブルの数値をいれなくても，プログラムでそれを生成することができる． 
つぎにしめすのは初期設定時につぎの 2 つのサブルーティンをよびだすことによってテーブルを生成し，それをつかって変換をおこなうプログラムである． 
</p>
<ul>
<li>gen_u2l() (uLaw から線形への変換 (デコード) テーブルを生成)</li>
<li>gen_l2u() (線形から uLaw への変換 (エンコード) テーブルを生成)</li>
</ul>
<pre>

#=============================================================================
# ulaw &lt;-&gt; linear conversion table generator
#=============================================================================

my $QUANT_MASK = 0xf;
my $BIAS = 0x84;
my $SEG_MASK = 0x70;
my $SEG_SHIFT = 4;
my $SIGN_BIT = 0x80;

my (@l2u, @u2l);

### u2l($uval)
#   convert ulaw value to linear value.
#
sub u2l($) {
    my ($uval) = @_;
    $uval = ~$uval;
    my $t = (($uval & $QUANT_MASK) &lt;&lt; 3) + $BIAS;
    $t &lt;&lt;= ($uval & $SEG_MASK) &gt;&gt; $SEG_SHIFT;
    return ($uval & $SIGN_BIT) ? ($BIAS - $t) : ($t - $BIAS);
}

### gen_u2l()
#   generate ulaw-to-linear conversion table (@u2l)
#
sub gen_u2l() {
    for (my $i = 0; $i &lt; 256; $i++) {
        $u2l[$i] = u2l($i);
    };
}

### gen_l2u()
#   generate linear-to-ulaw conversion table (@l2u)
#   (This method might not generate an optimum converter.)
#
sub gen_l2u() {
    for (my $i = 0; $i &lt; 256; $i++) {
        my $j = $u2l[$i];
        if ($j &lt; 0) {
            $j += 65536;
        };
        $l2u[$j] = $i;
    };
    for (my $i = 1; $i &lt; 65536; $i++) {
        if ($l2u[$i] == 0) {
            $l2u[$i] = $l2u[$i-1];
        };
    };
}
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl によって UDP ポートを “たたく” プログラム</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_udp.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2209</id>
   
   <published>2007-11-14T12:44:34Z</published>
   <updated>2007-11-15T13:26:21Z</updated>
   
   <summary> 複数のコンピュータをネットワークを介してつなぎ，それらにのせられたアプリケーションのあいだで相互に通信させる実験をするとき，実験を起動するのに，最初に通信をおこなうアプリケーションのポートをたたくようにするのが便利である．  この目的で内容のない UDP (User Datagram Protocol) メッセージをおくるプログラムを書いてみた  (UDP を TCP にかえるのは容易であるが，...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
複数のコンピュータをネットワークを介してつなぎ，それらにのせられたアプリケーションのあいだで相互に通信させる実験をするとき，実験を起動するのに，最初に通信をおこなうアプリケーションのポートをたたくようにするのが便利である． 
この目的で内容のない UDP (User Datagram Protocol) メッセージをおくるプログラムを書いてみた 
(UDP を TCP にかえるのは容易であるが，受信プログラムはやや複雑になる)． 
</p>
]]>
      <![CDATA[<p>
なんということはないプログラムだが，用意しておくと (すくなくとも私のばあいは) やくにたつ． 
内容がからだと誤動作する可能性があるなら，特定の内容をいれて，それをアプリケーションでチェックするようにすればよい． 
たたくアドレスやポートをコマンド引数としてあたえられるようにすれば便利かもしれないが，ここではプログラムのなかであたえている． 
(このほうが，何度もつかうときに，いちいちコマンド引数をあたえる必要がないので私にとっては便利である.)
</p>
<pre>
#!/usr/bin/perl
##############################################################################
#
#	UDP port kicker
#
##############################################################################

use strict;
use Socket;

my $IP = '192.168.2.2';	# destination
my $REMOTE_PORT_RTP = 8000;
my $LOCAL_PORT_RTP = 8000;

socket(SOCK, AF_INET, SOCK_DGRAM, getprotobyname('udp')) ||
    die "socket(SOCK)$!\n";
setsockopt(SOCK, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) ||
    die "setsockopt()$!\n";
bind(SOCK, pack_sockaddr_in($LOCAL_PORT_RTP, INADDR_ANY)) ||
    die "bind(SOCK)$!\n";
open_socket($LOCAL_PORT_RTP);

send(SOCK, '', 0, pack_sockaddr_in($REMOTE_PORT_RTP, inet_aton($IP))) ||
    die "send()$!";

close(SOCK);

1;
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl による簡易 XML インタフェース ― テーブル型データ表現のハッシュと XML 間の変換</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_xml.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2200</id>
   
   <published>2007-11-14T12:15:26Z</published>
   <updated>2007-11-17T12:27:36Z</updated>
   
   <summary> ただしい XML インタフェースをつくるには XML の API をつかう必要があるが，かんたんに XML (もどき) のインタフェースをつくるには，Perl のパターンマッチ機能をつかうのが便利である．  本来の XML は 1 行にいくつタグがあってもよいし，タグでかこまれる内容が複数行にまたがってもかまわないが，それに制限をくわえればかんたんに処理することができる．  こんなインチキな X...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Web" scheme="http://www.sixapart.com/ns/types#category" />
         <category term="ネットワーク・通信" scheme="http://www.sixapart.com/ns/types#category" />
         <category term="文字列" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
ただしい XML インタフェースをつくるには XML の API をつかう必要があるが，かんたんに XML (もどき) のインタフェースをつくるには，Perl のパターンマッチ機能をつかうのが便利である． 
本来の XML は 1 行にいくつタグがあってもよいし，タグでかこまれる内容が複数行にまたがってもかまわないが，それに制限をくわえればかんたんに処理することができる． 
こんなインチキな XML 処理は奨励されないかもしれないが，ちょっとしたプロトタイプをつくるには便利である．
</p>
]]>
      <![CDATA[<p>
XML をあつかうための API 仕様として Sax があり，その実装としては Xerces をはじめ，いろいろある． 
Sax も比較的容易につかえるが，API のつかいかたをおぼえたり (おもいだしたり)，API とリンクしたりするために多少のてまがかかる． 
ちょっとしたプロトタイピングをするときには，そういうてまも，はぶきたくなる．
</p>
<p>
私はそういうとき，Perl のパターンマッチ機能をつかって，もっと安易な (疑似) XML インタフェースをつくっている． 
そういう簡易 XML インタフェースの応答者と要求者のプログラム断片の例を以下にしめす． 
このプログラムは要求者が XXP という HTML 風のプロトコルをつかって XML によってテーブルの値をかえすことを要求する 
(データ形式をテーブルに限定するところがミソである)． 
</p>
<p>
テーブルの値はたとえばつぎのように表現される．
</p>
<pre>
&lt;tables&gt;
&lt;table&gt;
&lt;col1&gt;value11&lt;/col1&gt;
&lt;col2&gt;value12&lt;/col2&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;col1&gt;value21&lt;/col1&gt;
&lt;col2&gt;value22&lt;/col2&gt;
&lt;/table&gt;
&lt;/tables&gt;
</pre>
<p>
テーブルには型があり，ここでは "table" が型名である． 
これはつぎのようなテーブルを意味する．
</p>
<table border="1">
<tr><td>col1</td><td>col2</td></tr>
<tr><td>value11</td><td>value12</td></tr>
<tr><td>value21</td><td>value22</td></tr>
</table>
<p>
ただし，最初の行 (col1, col2) はタイトルをあらわす (テーブルの内容ではない)．
</p>

<h3>応答者</h3>

<p>
make_xml() はハッシュを要素とする配列を入力してそれを XML に変換し，関数値 ($result) としてかえす． 
たとえば，上記の XML テキストを生成したければ，つぎの配列を入力する．
</p>
<p>
[{col1 => value11, col2 => value12}, {col1 => value21, col2 => value22}]
</p>
<p>
send_reply($result) はその XML テキストをふくむ HTML 風のプロトコル XXP による応答メッセージを受信者におくる． 
ここには質問メッセージに関する部分はのせないが，これは XXP による質問メッセージに対する応答である． 
ここではテーブル型名は TAG としている． 
</p>

<pre>
### make_xml(%hash)
#
sub make_xml(\%) {
    my ($group) = @_;
    my $result = "&lt;TAGs&gt;\r\n";
    foreach my $key (sort keys %group) {
	my $item = $group{$key};
	if (ref($item) eq 'HASH') {
	    $result .= "&lt;TAG&gt;\r\n";
	    foreach my $key (keys %$item) {
		$result .= "&lt;${key}&gt;$item-&gt;{$key}&lt;/${key}&gt;\r\n";
	    };
	    $result .= "&lt;/TAG&gt;\r\n";
	};
    };
    $result .= "&lt;/TAGs&gt;\r\n";
    return $result;
}
# -- すべてをメモリ上でつくる

### send_reply($result)
#
sub send_reply($) {
    my ($result) = @_;
    my $protocol = "XXP/1.0 ";
    if ($result &lt; 0) {
	put($Client, $protocol . (400-$result) . " $error_message{-$result}\r\n");
	put($Client, "Content-length: 0\r\n\r\n");
    } else {
	if ($result) {
	    $result = "&lt;?xml version=\"1.0\" encoding=\"shift_jis\"?&gt;\r\n" .
		"$result\r\n";
	};
	
	# Put XXP header
	put($Client, $protocol . "200 OK\r\n");
	put($Client, "Content-length: " . length($result) . "\r\n\r\n");

	# Put XXP body
	put($Client, $result);
    };
}
</pre>

<h3>受信者</h3>

<p>
受信者はまず get_header($file_descriptor) をよびだして XXP の応答ヘッダを送信者からうけとり，つづいて get_body($file_descriptor, $table_type, $body_length) をよびだして XXP のメッセージ本体をうけとる． 
$body_length は get_header() がかえす値である． 
XXP の質問メッセージによって，うけとるテーブルの型 ($table_type) がきまることを前提としている． 
get_body() は make_xml() とは逆によみこんだ XML のテキストをハッシュに変換する． 
</p>

<pre>
### get_header($file)
#   get XXP reply message header
#   returns $body_length;
#
sub get_header($) {
    my ($file) = @_;
    my $line = &lt;$file&gt;;
    if ($line =~ / 200 /) {	# OK
	my $body_length = 0;
	while (($line = &lt;$file&gt;) !~ /^\s*$/) {	# Until empty line read
	    if ($line =~ /Content-length:\s+(\d+)\s/i) {
		$body_length = $1;		# Use content-length header only
	    };
	};
	return $body_length;
    } else {	# Error
	return -1;
    };
}

### get_body($file, $table_type, $body_length)
#   get XXP reply message body
#
sub get_body($$$) {
    my ($file, $table_type, $body_length) = @_;
    my $length = 0;
    my $table = [];
    my $record = {};
    while ($length &lt; $body_length) {
	my $line = &lt;$file&gt;;
	$length += length($line);
	if ($line =~ /&lt;\s*(\S+)\s*&gt;(.*)&lt;\s*\/\s*(\S+)\s*&gt;/) {
	    my $stag = $1;
	    my $content = $2;
	    my $etag = $3;
	    if ($stag ne $etag) {
		system_error("Illegal tag syntax: &lt;$stag$gt;...$lt;/$etag$gt;");
	    };
	    $record-&gt;{$stag} = $content;
	} elsif ($line =~ /&lt;\s*${table_type}\s*&gt;/) {		# record start tag
	    $record = {};		# create a new record
	} elsif ($line =~ /&lt;\s*\/\s*${table_type}\s*&gt;/) {	# record end tag
	    push(@$table, $record);	# push the record into the table
	} elsif ($line =~ /&lt;\s*(\/\s*)?${table_type}s\s*&gt;/) {	# collection tag
	    # ignore
	} elsif ($line =~ /&lt;\s*\?\s*xml(.*)\?\s*&gt;/) {	# first line
	    # ignore
	} elsif ($line =~ /\s*/) {
	    # ignore
	} else {
	    print STDERR "Illegal line: $line ($length)\n";
	};
    };
    return $table;
}
</pre>

<p>注意: 
上記のプログラムは動作確認したプログラムをもとにしていますが，ここに掲載するにあたってかきかえて，それ以降はテストしていません． 
あしからず． 
</p>
]]>
   </content>
</entry>
<entry>
   <title>テキストからの Perl データベース (tie) 生成</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/_perl_tie.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2208</id>
   
   <published>2007-11-14T11:51:51Z</published>
   <updated>2007-11-15T12:09:14Z</updated>
   
   <summary> 関係データベースのような本格的なデータベースのかわりに Perl の tie() をつかうと，簡易データベースを容易にあつかうことができる．  つまり，tie() をつかうことによって Perl のハッシュを外部ファイルとむすびつけて，恒久的 (permanent) なものにすることができる．  ただし，本格的なデータベースとはちがって並列にかきこんだりすることはできないし，性能もひくいとかんが...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="データベース" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
関係データベースのような本格的なデータベースのかわりに Perl の tie() をつかうと，簡易データベースを容易にあつかうことができる． 
つまり，tie() をつかうことによって Perl のハッシュを外部ファイルとむすびつけて，恒久的 (permanent) なものにすることができる． 
ただし，本格的なデータベースとはちがって並列にかきこんだりすることはできないし，性能もひくいとかんがえられる． 
したがって，プロトタイプには適するが実用には適さない． 
</p>
<p>
一方，かんたんなデータベースのようなデータ群あるいはデータベースに登録するべきデータ群をつくるとき，Excel がよくつかわれる． 
そこで，Excel などでつくったデータを，たとえばタブくぎりのテキストとして出力して，それを Perl のデータベースにとりこみたくなる． 
そういうプログラムの例をしめす． 
</p>
]]>
      <![CDATA[<p>
以下のプログラムでは，タブなどの空白文字でくぎられた 4 つのフィールドからなるレコードのならびを入力して，それを簡易データベースに登録する． 
このデータベースにおいては 4 つのフィールドのうちの最初のものをキーとして，あとの 3 つのフィールドを検索することができる． 
キーは一意でなければならない 
(一意性はここでは検査していない)．
</p>
<pre>



############################################################################
#
#	Text to DBM converter
#
############################################################################

use strict 'subs';
use strict 'refs';

use SDBM_File;

use Fcntl;

my $FileName = "C:\DB_File";

sub error($) {
    my ($message) = @_;
    print STDERR "Error: $message\n";
}


my %db;

tie(%db, 'SDBM_File', $FileName, O_RDWR | O_CREAT, 0666) ||
    error("Can't open database file!");

%db = ();

$| = 1;
print "Input file name: ";
my $file = &lt;&gt;;
chomp $file;
open(INPUT, $file);
while (&lt;INPUT&gt;) {
    if (/^(\w+)\s+(\w+)\s+(\w+)\s+(\w+)/) {
	my $A = $1;
	my $B = $2;
	my $C = $3;
	my $D = $4;
	print "A=${A} B=${B} C=${C} D=${D}\n";
	$db{A} = "${B}\t${C}\t${D}";
    } else {
	error("Syntax error: $_");
    };
};

untie(%db);

1;
</pre>
]]>
   </content>
</entry>
<entry>
   <title>Perl による Web ページへのラジオボタン円形配置</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program/2007/11/perl_web_2.html" />
   <id>tag:www.kanadas.com,2007:/program//22.2207</id>
   
   <published>2007-11-14T11:18:26Z</published>
   <updated>2007-11-17T09:03:09Z</updated>
   
   <summary> Web ページ上にラジオボタンを自由に配置したいことがある．  私のばあいは，被験者に立体音をきいてもらってその音源方向や距離をあててもらうときに，結果を Web ページ上にならべた，つぎのようなラジオボタンのうちのひとつをクリックして，こたえてもらった．  この方法をつかうとボタンを円形にかぎらず自由に配置できるかわりに，おなじページの他のコンテンツとかさならないように，配置を慎重にきめる必要...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Web" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program/">
      <![CDATA[<p>
Web ページ上にラジオボタンを自由に配置したいことがある． 
私のばあいは，被験者に立体音をきいてもらってその音源方向や距離をあててもらうときに，結果を Web ページ上にならべた，つぎのようなラジオボタンのうちのひとつをクリックして，こたえてもらった． 
この方法をつかうとボタンを円形にかぎらず自由に配置できるかわりに，おなじページの他のコンテンツとかさならないように，配置を慎重にきめる必要がある． 
したがって，他の方法たとえば表などをつかって容易に配置できる形状のときはつかわないほうがよいとかんがえられる．
</p>
]]>
      <![CDATA[<form id="form" action="" method="get">
<div style="position:absolute; left:103px; top:307px;">+</div>
<div style="position:absolute; left:138px; top:310px;"><input type="radio" name="A" value="1" /></div>
<div style="position:absolute; left:132px; top:330px;"><input type="radio" name="A" value="2" /></div>
<div style="position:absolute; left:117px; top:344px;"><input type="radio" name="A" value="3" /></div>
<div style="position:absolute; left:97px; top:349px;"><input type="radio" name="A" value="4" /></div>
<div style="position:absolute; left:77px; top:344px;"><input type="radio" name="A" value="5" /></div>
<div style="position:absolute; left:63px; top:329px;"><input type="radio" name="A" value="6" /></div>
<div style="position:absolute; left:58px; top:309px;"><input type="radio" name="A" value="7" /></div>
<div style="position:absolute; left:63px; top:289px;"><input type="radio" name="A" value="8" /></div>
<div style="position:absolute; left:78px; top:275px;"><input type="radio" name="A" value="9" /></div>
<div style="position:absolute; left:98px; top:270px;"><input type="radio" name="A" value="10" /></div>
<div style="position:absolute; left:118px; top:275px;"><input type="radio" name="A" value="11" /></div>
<div style="position:absolute; left:132px; top:290px;"><input type="radio" name="A" value="12" /></div>
</form>
<pre>










</pre>
<p>
このページのソースをみてみればわかるが，基本的にはつぎのような HTML タグによってひとつのラジオボタンが表現される．
</p>
<p>
&lt;div style="position:absolute; left:<i>x</i>座標px; top:<i>y</i>座標px;"&gt;ボタン&lt;/div&gt;
</p>
<p>
座標はピクセル単位で指定するため，フォントサイズなどをかえても位置はかわらず，他のコンテンツとかさなる危険がある． 
このページのばあい，余白を余計にとることによって，できるだけかさならないようにしているが，条件によってはやはりかさなる．
</p>
<p>
以下，このようなラジオボタンをふくむページを生成する Perl CGI プログラムの例をしめす． 
実験につかうためにはもうすこし，くふうが必要だったが，ここではプログラムをなるべくかんたんにしている．
</p>
<pre>


#!/usr/bin/perl	--	# -*-Perl-*-
############################################################################
#
#	Print radio button circle
#
############################################################################

use strict;


### print_button_circle($tableName, $y0, $value)
#   print a radio button array
#
sub print_button_circle($$$) {
    my ($tableName, $y0, $value) = @_;
    my $factor = 40;
    my $roomSize = $factor * 5;
    my $Pi = 3.1416;

    # Display center of the circle
    my $x = $factor * 2.25 + 13;
    my $y = $factor * 2.25 + $y0 - 3;
    print "&lt;div style=\"position:absolute; left:${x}px; top:${y}px;\"&gt;";
    print "+&lt;/div&gt;\n";

    # Display radio buttons
    my $distance = 1;
    for (my $angle = 1; $angle &lt;= 12; $angle++) {
	my $r = $factor * $distance;
	my $displacement = $factor * (2.25 - $distance);
	my $x = int($r * (1 + cos($Pi*($angle-1)/6))) + $displacement + 8;
	my $y = int($r * (1 + sin($Pi*($angle-1)/6))) + $displacement + $y0;
	my $checked = $value eq $angle ? 'checked="checked" ' : "";
	print "&lt;div style=\"position:absolute; left:${x}px; top:${y}px;\"&gt;";
	print "&lt;input type=\"radio\" name=\"${tableName}\" ";
	print "value=\"${angle}\" ${checked}/&gt;&lt;/div&gt;\n";
    };
}


#===========================================================================
#	Main program
#===========================================================================

print&lt;&lt;END;
Content-type: text/html

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE html PULIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" /&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"" xml:lang="ja" lang="ja"&gt;
&lt;head&gt;
&lt;title&gt;Title&lt;/title&gt;
&lt;meta HTTP-EQUIV=CONTENT-TYPE CONTENT="text/html; charset=UTF-8" /&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h1&gt;Header&lt;/h1&gt;

&lt;form id="form" action="" method="get"&gt;
END

print_button_circle("A", 220, 1);

print&lt;&lt;END;
&lt;/form&gt;
&lt;/body&gt;
&lt;/html&gt;
END

1;
</pre>
]]>
   </content>
</entry>

</feed>
