カセットテープにデータを記録

人生で初めて使い始めた Macintosh には、既に内蔵ハードディスク、CD ドライブ、FD ドライブが搭載されていたので、コンパクトカセット(音楽カセットテープ)を補助記憶媒体としてリアルタイムに使った事がありません。父が使っていた MZ-700 を譲り受けて持っていますが、肝心のデータレコーダのゴムが劣化していて、まともに使えないのが残念です。
中学生の頃までコンパクトカセットを使って、よくラジオで流れていた好きな音楽を録音していました。そんなコンパクトカセットにデジタルデータが記録されるのが、ひどくアナログな雰囲気だけど、ちゃんとデジタルなところが魅力的で、試してみたいなあ、と考えて、MP3 プレイヤーが 500 円で買えるご時世に、安いステレオラジカセを購入した事もありました。
古いコンピュータのエミュレータ環境のツールの一つとして、コンパクトカセットの情報をデジタルデータに変換するコンバータ・ソフトは、Windows ではよく見かけていました。
Linux で使えるものはないな、と思いリポジトリを漁ってみましたが、需要がなかった模様。
アマチュア無線関連のツールや本物(?)のテープレコーダ用のツール( tar )はあっても、民生用カセット向けのツールは乏しいみたいです。

そこで、Wikipedia でデータレコーダの項を調べてみると、

情報を FSK などの変調方式でオーディオ周波数帯の信号に変調して記録するもので、代表的な記録方式にKCS(カンサスシティスタンダード)があり1200Hz/2400HzのFSK方式で300bpsの記録ができた。NECのPC-8000シリーズなどではキャリア周波数はそのままでシンボル長のみ短縮した600bpsでの記録を標準としていた。シャープのMZシリーズではコンピュータ本体に直接内蔵され、ソフトウェア制御によるパルス幅変調方式で記録を行い、他の機種と比較し、エラーの少ないアクセスと共に、1200bpsの速度を実現していた。

という記事が見つかった。この KCS という記録方式を元に検索してみると、py-kcsというツールを見つけました。Python で書かれた KCS 方式のエンコーダ・デコーダらしい。入出力対象の音声ファイル形式は WAVE。使えそうです。

ダウンロード

http://linux.softpedia.com/get/Multimedia/Audio/py-kcs-59693.shtml

使い方

[bash]

Python 3.1.2 以降が必須。

テキストファイルを音声データにエンコード。

python3 kcs_encode.py input.txt output.wav

音声データをデコードし、内容を標準出力に印字。

python3 kcs_decode.py input.wav

RAW なバイナリデータとして印字する場合は -b オプションを用いる。

python3 kcs_decode.py -b input.wav
[/bash]

作者によるページでは、実際に Ohio Scientific Superboard II という 70 年代のボードに音声信号で BASIC プログラムを転送しています。楽しそうだなあ。

ちょっと使ってみる

Python 3.2 の環境を整えた後、py-kcs をダウンロードしてきます。解凍すると、kcs_decode.py と kcs_encode.py というスクリプトが得られます。

[bash]

試しに、README.txt をエンコードしてみる。

$ python3 kcs_encode.py README.txt hoge.wav

再生してみると、懐しいノイジーな音が出てくる。

$ mplayer hoge.wav

ここで、コンピュータの Line Out または Headphone 端子とラジカセの Line In

または Mic 端子をミニジャックで接続し、コンパクトカセットに録音後、すかさず

$ cat hoge.wav > /dev/dsp

する。再生が終わったら(手元の環境では1分58秒だった)、録音を停止。

[/bash]

これで多分、デジタルデータのアナログ音声化&カセットテープに保存が完了します。
次に、カセットテープを巻き戻し、

[bash]

冒頭の 5 秒くらいのところから再生をスタート。すかさず

$ cat /dev/dsp > fuga.wav

する。再生が終わったら、C-c してコンピュータ側の録音を停止。

得られた fuga.wav に対して、

$ python3 kcs_decode.py fuga.wav > README.new.txt

して、README.new.txt の内容が、正しく復号されたものか確認する。

$ cat README.new.txt
[/bash]

上記の要領で行けるだろうと思いました。

手元にラジカセがなかったので、試しに、変換した WAVE ファイルを一度、MP3 ファイルに変換し、再度 WAVE ファイルにデコードし直してから、kcs_decode に食わせてみました。
すると、一応デコードはしてくれましたが、文字化けをした意味の無いバイトコードが生成されてしまいました。

んー、データの維持に問題がありそうな周波数の変更はしていないし、そんなに劣化させてないハズなんだけどな。
やり方がマズかったのか。後日、実際にラジカセで試してみたいと思います。

Android SDK の導入

Ubuntu 11.10 に NEC LifeTouch NOTE をつないで、いじくる時に操作したメモです。基本的には、以下の手順で OK なはず。

  1. android-sdk_r*-linux.tgz のダウンロード、解凍。
  2. tools/android update sdk –no-ui の実行。

下記では、Java SDK をインストールしていなかったので、その導入からしています。

まず、都合がいい場所に作業ディレクトリを設け、バイナリを落としてきます。

[bash]
mkdir android
cd android
wget http://dl.google.com/android/android-sdk_r16-linux.tgz
tar xvf android-sdk_r16-linux.tgz
cd android-sdk-linux
[/bash]

解凍して出てきたSDK Readme.txtの中にupdate sdkせよ、との記述があるので、それに従います。

[bash]
tools/android update sdk –no-ui

Java がないよ、と怒られる

[/bash]

Java 環境をインストールしていなかったので、ここからJavaインストール・フェイズ。

[bash]
sudo aptitude update
sudo aptitude search java | less

OpenJDK や gcj だけで、Sun 製の Java がない。

Ubuntu11.10 用のリポジトリを追加してやる。

sudo add-apt-repository ppa:ferramroberto/java
sudo apt-get update
sudo apt-get install sun-java6-jdk

デフォルトの Java 環境設定

sudo update-alternatives –config java
[/bash]

気を取りなおして、再度SDKのインストーラを実行します。

[bash]
tools/android update sdk –no-ui
[/bash]

Android4.0 や 3.x 向け環境がわさわさ入ってしまう。うっわ要らねえ、うち 2.2 と 1.6 しか持ってないのに・・・と思ってマニュアルを読んでみると、ターゲットバージョンを指定するオプションがある模様です。
次に、Android 端末をホストに USB 接続します。

[bash]
dmesg # 反応あり。
adb devices # デバイスが表示された。
adb shell # つながったー。
[/bash]

拍子抜けするほど、あっさりつながりました。
adb を使いたいだけなのに、Java SDK をごっそり入れてしまいました。ちゃんと調べれば、必要なものだけ入れることができたのかもしれません。

エルミート曲線の描写

C を覚えたての2003年頃に書いたエルミート曲線の描写プログラムです。グラフィックライブラリの使い方も知らなかった為、モノクロビットマップ画像を直に書いています。( 実行には 256×256 の 256 色ビットマップ画像を head.dat という名前で同一ディレクトリに配置しておく必要があります。 )

[c]
////////////////////////////////////////////////
// エルミート曲線描写テスト
////////////////////////////////////////////////

#define BG_COLOR 0xFF //初期化カラー
#define GL_COLOR 0xD0 //グリッドカラー
#define GL_SCALE 20 //グリッド幅px
#define VC_COLOR 0x80 //ベクトルカラー
#define LN_ACRCY 0.001 //曲線描写精度
#define BITMAP_X 256 //画像横px
#define BITMAP_Y 256 //画像縦px
#define IMAGE_FILE "data.bmp" //出力画像ファイル名

#define HEADER_SIZE 0x435 //Bitmapファイルヘッダサイズ

#define X 0
#define Y 1

#include<stdio.h>
#include<stdlib.h>

void main(void)
{
FILE *fp,*fread;
int x,y,i,j,k;
int vram[256][256],vramt[256][256];

for(y=0;y<BITMAP_Y;y++) //イメージバッファを念のため初期化
for(x=0;x<BITMAP_X;x++)
vram[x][y]=BG_COLOR;

////////////////////////////////////////////////
//グリッド描写

for(i=0;i<256;i+=GL_SCALE) //縦線
{
for(j=0;j<256;j++)
vram[i][j]=GL_COLOR;
}
for(i=0;i<256;i+=GL_SCALE) //横線
{
for(j=0;j<256;j++)
vram[j][i]=GL_COLOR;
}

////////////////////////////////////////////////
//メイン描写

float t,fx,fy;
int v[2][2],p[2][2];

p[0][X]=3;
p[0][Y]=4;

v[0][X]=6;
v[0][Y]=-3;

p[1][X]=10;
p[1][Y]=10;

v[1][X]=-8;
v[1][Y]=1;

for(t=0;t<1.0;t+=LN_ACRCY) //エルミート曲線描写
{
fx=p[0][X]*((2*t+1)*((1-t)*(1-t)))+v[0][X]*(t*((1-t)*(1-t)))+v[1][X]*((-t)*(-t)*(1-t))+p[1][X]*(t*t*(3-2*t));
fy=p[0][Y]*((2*t+1)*((1-t)*(1-t)))+v[0][Y]*(t*((1-t)*(1-t)))+v[1][Y]*((-t)*(-t)*(1-t))+p[1][Y]*(t*t*(3-2*t));
fx=fx*GL_SCALE;
fy=fy*GL_SCALE;
x=fx;
y=fy;
vram[x][y]=0x00;
if(LN_ACRCY>=0.01) //描写精度が低ければ座標を表示。
printf("%5.5f\t%5.5f\t%d\t%d\n",fx,fy,x,y);
}

//ベクトル(ハンドル?)描写
for(k=0;k<2;k++)
{
p[k][X]=GL_SCALE*p[k][X];
p[k][Y]=GL_SCALE*p[k][Y];
v[k][X]=p[k][X]+GL_SCALE*v[k][X];
v[k][Y]=p[k][Y]+GL_SCALE*v[k][Y];
if(p[k][X]>v[k][X])
{
i=v[k][X];
j=p[k][X];
}
else
{
i=p[k][X];
j=v[k][X];
}
for(x=i;x<j;x++)
{
y=x*(p[k][Y]-v[k][Y])/(p[k][X]-v[k][X])+p[k][Y]-p[k][X]*(p[k][Y]-v[k][Y])/(p[k][X]-v[k][X]);
if(x>=0&&x<256&&y>=0&&y<256)
vram[x][y]=VC_COLOR;
}
vram[v[k][X]][v[k][Y]]=0xFF;
vram[v[k][X]][v[k][Y]+1]=0x00;
vram[v[k][X]][v[k][Y]-1]=0x00;
vram[v[k][X]+1][v[k][Y]]=0x00;
vram[v[k][X]+1][v[k][Y]+1]=0x00;
vram[v[k][X]+1][v[k][Y]-1]=0x00;
vram[v[k][X]-1][v[k][Y]]=0x00;
vram[v[k][X]-1][v[k][Y]+1]=0x00;
vram[v[k][X]-1][v[k][Y]-1]=0x00;
}

////////////////////////////////////////////////
//Y軸座標の問題解決(天地入れ替え)

for(i=0;i<256;i++)
{
for(j=0;j<256;j++)
{
vramt[i][j]=vram[i][255-j];
}
}

for(i=0;i<256;i++)
{
for(j=0;j<256;j++)
{
vram[i][j]=vramt[i][j];
}
}

////////////////////////////////////////////////

for(y=0;y<BITMAP_Y;y++) //カラーデータを念のため校正(0x00~0xFF)
{
for(x=0;x<BITMAP_X;x++)
{
if(vram[x][y]>0xFF)
vram[x][y]=0xFF;
if(vram[x][y]<0x00)
vram[x][y]=0x00;
}
}

////////////////////////////////////////////////

//書込ビットマップファイルをバイナリモードでオープン。
if( (fp=fopen(IMAGE_FILE,"wb"))==NULL){
printf("Image File Open Error.\n");
exit( EXIT_FAILURE );}

//ヘッダファイル読み込み
if((fread=fopen("head.dat","rb"))==NULL){
printf("Header File Open Error.\n");
exit( EXIT_FAILURE );}

//ヘッダーファイルからヘッダーを読み取り出力。
rewind(fread);
rewind(fp);
for(i=0;i<HEADER_SIZE;i++)
putc(getc(fread),fp);

//ドット描写
for(x=BITMAP_X;x>0;x–)
for(y=0;y<BITMAP_Y;y++)
fprintf(fp,"%c",vram[y][x]);

for(y=0;y<2;y++) //最後(必要?)
fprintf(fp,"%x",0);
fclose(fp);
printf("ビットマップ画像を出力しました。\n");
exit( EXIT_SUCCESS );
////////////////////////////////////////////////
}
[/c]

無駄が多いし、汚いプログラムですね…。
実行すると、以下のようなビットマップ画像が出力されます。

Net::POP3でメール受信

PerlのNet::POP2モジュールを利用したメール受信のサンプルです。このスクリプトをcronに登録し、定期的にメールボックスを確認させて、何かあったらコマンドを実行。なんて用途に使えます。パスワードも平文でのっけているので、セキュリティ的な注意は必要かと思います。

[perl]

!/usr/bin/env perl

use Net::POP3;

sub getmail()
{
my ($server, $user, $passwd, $delete) = @_;
my @result;
my $p = Net::POP3-&gt;new($server) or die &quot;Couldn’t connect to $server.&quot;;
$p-&gt;login($user, $passwd);
foreach $id (keys(%{$p-&gt;list()})) {
push(@result, @{$p-&gt;get($id)});
$p-&gt;delete($id) if ($delete);
}
$p-&gt;quit;
return @result;
}

print &amp;getmail(‘pop.example.com’, ‘example@example.com’, ‘mypassword’);
[/perl]

Tcl/Tkで簡易マップエディター

Tcl/Tkの勉強がてら、簡易マップエディターを書いてみました。マップデータは0から7までの色番号が並んだテキストファイル。16色カラー端末上で表示させる為のマップを想定しています。

実行結果

参考にしたサイト

  1. http://www.k5.dion.ne.jp/~minyu/script/tcltk.html
  2. http://homepage3.nifty.com/kaku-chan/tcl_tk/index.html
  3. 入門 tcl/tk 久野靖著・アスキー出版局