<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
   <title>Small and Large Stones of Programming</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/" />
   <link rel="self" type="application/atom+xml" href="http://www.kanadas.com/program-e/atom.xml" />
   <id>tag:www.kanadas.com,2008:/program-e//23</id>
   <updated>2008-03-30T13:31:38Z</updated>
   <subtitle><![CDATA[Here, I will argue various topics from program fragments, programming techniques, and modules, and so on. 
If you do not want to publish your comment, send it to yasusi&nbsp;@&nbsp;kanadas.com . 
If you find an error, please let me know it.  I will fix it.


]]></subtitle>
   <generator uri="http://www.sixapart.com/movabletype/">Movable Type 3.36</generator>

<entry>
   <title>Simple mail proxy by Perl</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2008/03/simple_mail_proxy_by_perl.html" />
   <id>tag:www.kanadas.com,2008:/program-e//23.2648</id>
   
   <published>2008-03-30T13:19:36Z</published>
   <updated>2008-03-30T13:31:38Z</updated>
   
   <summary> Mail proxies (SMTP proxies) have become much more important to avoid errorneous mail delivery.  There is a mail proxy written by Perl: Qsmtpd.  However, when you want to build a simple proxy by yours...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Network and communication" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
Mail proxies (SMTP proxies) have become much more important to avoid errorneous mail delivery. 
There is a mail proxy written by Perl: <a href="http://smtpd.develooper.com/" target="_blank">Qsmtpd</a>. 
However, when you want to build a simple proxy by yourself, this program might not be helpful because the program is too much complicated. 
So I wrote a proxy, which is as simple as possible.
</p>
]]>
      <![CDATA[<p>
The following program receives mails by SMTP and relays it to an SMTP server, but if it found a keyword "viagra", the proxy does not relay the message. 
To use this program, 'smtp.server.address' in the program must be replaced by the address or domain name of the SMTP server. 
(If the port number is not the default value, 25, it must be rewritten.) 
</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>Receiving and playing linear voice (VoIP) using Perl</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/receiving_and_playing_linear_v.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2235</id>
   
   <published>2007-11-15T16:02:10Z</published>
   <updated>2008-03-30T13:43:48Z</updated>
   
   <summary> A program that receives a two-channel (i.e., stereo) voice by RTP (Real-time Transport Protocol) and that plays by audio.  The sound is 16-bit linear by default, but u-Law (G.711) or other codecs can...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Network and communication" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
A program that receives a two-channel (i.e., stereo) voice by RTP (Real-time Transport Protocol) and that plays by audio. 
The sound is 16-bit linear by default, but u-Law (G.711) or other codecs can be used.  
The port number used for receiving the voice is specified in the program (i.e., $IN_PORT_RTP)． 
A detailed description will be given in future, but it will be explained when it becomes necessary.
</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 $debug_switch = 0;
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).
    };
    if ($debug_switch) {
	print "Received: \$buf_rtp{$ssrc}[", $seq_no - $buf_displ, "] ",
	"Seq#=$seq_no Repeat_count=$repeat_count{$ssrc} Timestamp=$timestamp\n";
        # print "RTP payload_type=$payload_type ssrc=$ssrc seq_no=$seq_no ",
        # "timestamp=$timestamp received_time=$curr_time ",
        # "delay_estimate=$delay_estimate{$ssrc}\n";
        # print "Media data received from ", inet_ntoa($ip),
        # ":$port, length=", length($buf), "\n";
    };
}

### 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";
    if ($debug_switch) {
        my $report_count = $first_byte & 31;
        print "RTP packet_type=$packet_type rerport_count=$report_count ",
        "timestamp=($timestamp1 $timestamp2) ssrc=$ssrc\n";
        print "Control data received from ", inet_ntoa($ip),
        ":$port, length=", length($buf), "\n";
    };
}


#=============================================================================
# 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);
    };
}


#=============================================================================
# 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 mixing)
#=============================================================================

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 ($debug_switch) {
		print "SSRCs: $left_ssrc $right_ssrc, Packets: ",
		$#{$buf_rtp{$left_ssrc}} + 1, " ",
		$#{$buf_rtp{$right_ssrc}} + 1, ", Length: ",
		length($left_buf), ' ', length($right_buf), "\n";
	    };
            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 ($debug_switch) {
		    print "Seq# $left_seq $right_seq, ",
		    "Timestamp $left_ts $right_ts\n";
		};
                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}++;
		if ($debug_switch) {
		    print "L-ch repeated! ";
		};
            } 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}++;
		if ($debug_switch) {
		    print "R-ch repeated! ";
		};
            } 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]]);
            };
	    if ($debug_switch) {
		print "Output length = ", length($outbuf), "\n";
	    };

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


#=============================================================================
# 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>Recording and sending linear voice using Perl</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/record_and_play_linear_voice_u.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2234</id>
   
   <published>2007-11-15T15:40:07Z</published>
   <updated>2008-03-30T14:08:54Z</updated>
   
   <summary> A Perl program that sends a voice stream using RTP (Real-time Transport Protocol) is show below.  The default codec for input voice is 16-bit linear, but u-Law (G.711) and some other codecs can be us...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="MultiMedia" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
A Perl program that sends a voice stream using RTP (Real-time Transport Protocol) is show below. 
The default codec for input voice is 16-bit linear, but u-Law (G.711) and some other codecs can be used. 
The port numbers and the IP address of the receiver are specified in this program ($REMOTE_PORT_RTP, $LOCAL_PORT_RTP, $ip, etc.). 
RTCP (Real-Time Control Protocol) can also be handled, but no specific control is intended. 
A detailed description will be given in future, but it will be explained when it becomes necessary.
</p>

]]>
      <![CDATA[<pre>
#!/usr/bin/perl
##############################################################################
#
#	Wave File Streamer
#
##############################################################################

use strict;
use Socket;
use Audio::Wav;
</pre>
<p>
<blockquote>
Audio::Wav is a package that handles wave files.
</blockquote>
</p>
<pre>
use Time::HiRes qw(time sleep);	# for exact time measurements and sleep
</pre>
<p>
<blockquote>
Here, package Time::HiRes is to measure the time accurately.
</blockquote>
</p>
<pre>
my $CODEC = 'linear16';		# 'ulaw', 'ulaw16', 'linear16' or 'linear32'

my $CHANNELS = $CODEC eq 'ulaw16' || $CODEC eq 'linear32' ? 2 : 1;
</pre>
<p>
<blockquote>
This program handles 1- or 2-channel (i.e., stereo) signal. 
If the value of $CODEC is 'ulaw' or 'linear16', the voice is 1-channel, and it is 'ulaw16' or 'linear32', the voice is 2-channel. 
</blockquote>
</p>
<pre>
my $SAMPLING_RATE = 8000;
</pre>
<p>
<blockquote>
The sampling rate is 8000 Hz. 
This program must run when replacing the sampling rate by a different value. 
</blockquote>
</p>
<pre>
my $volume = 0.45;
</pre>
<p>
<blockquote>
The sound volume can be changed by this value.
</blockquote>
</p>
<pre>
my $wav_file = "WaveFile.wav";
</pre>
<p>
<blockquote>
The file name is given here.
</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 address and port number
IP アドレス s とポート番号 d はここできめている．
</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>Purpose of this blog</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/purpose_of_this_blog.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2231</id>
   
   <published>2007-11-15T15:34:18Z</published>
   <updated>2008-03-30T13:00:43Z</updated>
   
   <summary> The purpose of this &quot;blog&quot; is to introduce my programming tips. ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Miscellaneous" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
The purpose of this "blog" is to introduce my programming tips.
</p>
]]>
      <![CDATA[<p>
Various information on the web helped me much when I wrote programs. 
To thank the authors of such web pages, I write this "blog". 
I am not sure how useful the tips are, but I will try to make them useful.
</p>
]]>
   </content>
</entry>
<entry>
   <title>A program that &quot;kicks&quot; a UDP port using Perl</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/a_program_that_kicks_a_udp_por_1.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2227</id>
   
   <published>2007-11-15T15:25:37Z</published>
   <updated>2008-03-30T12:51:22Z</updated>
   
   <summary> When connecting multiple computers to a network and making the computers communicate each other through the network, it is convenient to start the communication by kicking a port of the application o...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Network and communication" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
When connecting multiple computers to a network and making the computers communicate each other through the network, it is convenient to start the communication by kicking a port of the application on one of the computers. 
For this purpose, I wrote a program that sends a UDP (User Datagram Protocol) message that has no content. 
(It is easy to replace UDP by TCP, but the receiver program will be a little more complicated.)
</p>
]]>
      <![CDATA[<p>
It is an easy program, but it sometimes helps us (at least it helps me). 
If it is possible to start the program wrongly when the UDP packet has no content, a specific value can be included in the payload and the application can check the value.
It may be convenient if the address and port can be given by command parameters, but they are assigned in the program here. 
(It is more convenient for me because I do not have to specify command parameters every time I use the program.)
</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>Simplified XML interface using Perl -- conversion of table-style data representation between hash and XML</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/simplified_xml_interface_using.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2226</id>
   
   <published>2007-11-15T15:23:13Z</published>
   <updated>2008-03-30T03:19:23Z</updated>
   
   <summary> A good method for building exact XML interface is use XML API.  However, it is easier to use Perl&apos;s pattern macth function to create a simpler XML (like) interface.  In an exact XML interface, a text...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="String" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
A good method for building exact XML interface is use XML API. 
However, it is easier to use Perl's pattern macth function to create a simpler XML (like) interface. 
In an exact XML interface, a text line may contain any number of tags, and a content enclosed by a beginning and ending tags.  
However, if we can add a restriction to this syntax, we can handle XML documents in an easier method. 
Such a simplified XML processing might not be encouraged, but it is 
convenient when building a simple prototype. 
</p>
]]>
      <![CDATA[<p>
There are many APIs to handle XML, such as Sax or Xerces.  
It is rather easy to use such API as Sax, but still it takes some time and effort to remember the usage of the API and to link the library to the program. 
You may want to reduce the time and effort when building a simple prototype.
</p>
<p>
In such cases, I often use simple (pseudo) XML interface by using the pattern match function of Perl. 
Examples of such a requester and responder program fragments are shown below. 
In this program, the requester sends a request by using an HTML-like protocol called XXP.  
(The essense of this program is to restrict the data format to a table.)  
</p>
<p>
A value of the table can be represented, for example, as follows.
</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>
The table has its type -- "table" is the type name. 
This means a table as follows.
</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>
However, the first line (col1, col2) represents the title (i.e., it is not part of the content).
</p>

<h3>Responder</h3>

<p>
make_xml() inputs an array that contains hashes and converts it to XML, and returns it ($result) as the function value. 
For example, you can input the following array to generate the XML text shown above. 
</p>
<p>
[{col1 => value11, col2 => value12}, {col1 => value21, col2 => value22}]
</p>
<p>
send_reply($result) returns a responce message that contains an XML text by XXP.
The request message part is omitted here, but it is a responce to an XXP-based request. 
The table type name is TAG here.
</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;
}
# -- Whole message is created on memory.

### 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>Receiver</h3>

<p>
The receiver first calls get_header($file_descriptor) to read the response header of XXP, and calls get_body($file_descriptor, $table_type, $body_length) to read the response body. 
It is assumed that the table type ($table_type) is decided by the request message in XXP. 
get_body() converts the XML text to hashes.
This is the reverse function of make_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>Warning: 
The above program is based on a tested program, but it was rewritten  and was not yet tested again.
</p>
]]>
   </content>
</entry>
<entry>
   <title>Perl database (tie) generation from text</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/perl_database_tie_generation_f.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2225</id>
   
   <published>2007-11-15T15:21:44Z</published>
   <updated>2008-03-30T02:48:57Z</updated>
   
   <summary> A simplified database can be handled easily using tie() of Perl, instead of using heavy-duty databases such as relational databases.  When using tie(), hashes can be tied to an external database and ...</summary>
   <author>
      <name></name>
      
   </author>
         <category term="Database" scheme="http://www.sixapart.com/ns/types#category" />
   
   
   <content type="html" xml:lang="ja" xml:base="http://www.kanadas.com/program-e/">
      <![CDATA[<p>
A simplified database can be handled easily using tie() of Perl, instead of using heavy-duty databases such as relational databases. 
When using tie(), hashes can be tied to an external database and the hashes can be permanent.  
However, in contrast to heavy-duty databases, it is not possible to write to the database in parallel and the performance is probably lower.  
So it is suited for prototypes but not suted for real use. 
</p>
<p>
Microsoft Excel is often used when creating a data group such as a database or data to be entered to a database.  
So, we often want to enter data created by Excel or another program, for example, by outputting it to a tab-separated text. 
The following program is an example for such task. 
</p>
]]>
      <![CDATA[<p>
The following program inputs a sequence of records that contains four fields separated by white-space characters such as tabs, and enters them into a simplified database. 
In this database, the records can be searched by using the contents of first field as keys. 
The keys must be unique (but the uniqueness is not checked here). 
</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>Circular layout of radio buttons to a Web page using Perl</title>
   <link rel="alternate" type="text/html" href="http://www.kanadas.com/program-e/2007/11/circular_layout_of_radio_butto_1.html" />
   <id>tag:www.kanadas.com,2007:/program-e//23.2224</id>
   
   <published>2007-11-15T15:20:29Z</published>
   <updated>2008-03-30T02:01:50Z</updated>
   
   <summary> Sometimes it is necessary to layout radio buttons freely on a Web page.  I once performed an experiment, in which I asked the human subjects to answer the sound direction and distance after hearing a...</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-e/">
      <![CDATA[<p>
Sometimes it is necessary to layout radio buttons freely on a Web page. 
I once performed an experiment, in which I asked the human subjects to answer the sound direction and distance after hearing a spatialized sound. 
For this purpose, I used a Web page that contained radio buttons.  
The subjects answered by clicking one of the buttons.  
This method enables free layout of buttons, e.g., circular or any other layout, but the layout must be decided carefully not to overlay them with other contents.
So, when it is possible to layout the buttons in an easier method, for example by a table, this method should not be used.
</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>
You can see the source code of this page, a radio button is basically represented by an HTML tag such as the one below.
</p>
<p>
&lt;div style="position:absolute; left:<i>x</i>coordinate px; top:<i>y</i>coordinate px;"&gt;button&lt;/div&gt;
</p>
<p>
Because the coordinates are specified by pixel, the location of a button does not move when changing other contents, such as the font size of the characters, so it may overlap with other contents. 
In the case of this page, I added extra white space not to overlap, but still they may overlap under some conditions.
</p>
<p>
An example of a Perl CGI program that generates a page with such a radio button. 
I had to write a more complicated program for the experiment, but a much more simpler program is shown here.
</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>
