タグ » perl «

2010/02/10 水曜日 | 投稿者: aqua

こんばんは、aquaです。

PSPgoへの転送ツールをMedia goに決定した。データフローはCDからNAC-HD1へ取り込み、それをATRAC形式でウォークマンへ転送。ウォークマンからSonicStage CPを使ってPCへ取り込む。PCに取り込まれたデータを前回作成したスクリプトで変換して、Media goに取り込むという流れ。Media goへの取り込みは、ライブラリ・フォルダへの追加設定で特に変更せずに決定すれば差分を取り込んでくれる。Media goに取り込まれたデータをしばらく放っておけば12音階分析を済ませてくれる。ここまで出来たら、PSPgoの本体メモリへ転送する事でSensMeにてAtracデータを再生できる。

しかし、SensMeは起動が遅い上、AVRCPに対応していないので、いろいろと使い勝手が悪い。音楽鑑賞→ちょこっとゲーム→音楽鑑賞と頻繁に切り替える使い方には不向き。再生方法も全曲シャッフルのみなので、何とかXMB上のミュージック・プレーヤで実現できないか調査してみる。Media goでプレイリストは作成可能なものの、ダイナミック・プレイリストは存在せず、SensMe用の12音階分析を元にしたプレイリストしか作成出来ない。アイデアに結び付きそうなヒントは既にあって、Media goに取り込む際に作成した『m3u』ファイルだ(ディレクトリ内の曲順を定義するファイル)。

Media goから普通にPSPgoへ転送すると、『\Music\アーティスト名\アルバム名』というディレクトリになるので、ディレクトリを越えられる『m3u』ファイルが作成できればよい。調べてみると『m3u8』ファイルがそのリクエストに応えてくれそう。ここにMusicディレクトリからのフルパスで、以下のように楽曲ファイルを記載していけば、ディレクトリを越えたプレイリストになるらしい。作成したファイルは『\PSP\PLAYLIST\Music\xxxx.m3u8』みたいな感じで配置する。早速、数件でサンプル作ってみたところ期待通りの動き。更にシャッフルすればプレイリストに対してランダム再生を実行してくれる。

E:\Music\アーティスト1\アルバム1\001_楽曲1.aa3
E:\Music\アーティスト1\アルバム1\001_楽曲2.aa3
E:\Music\アーティスト2\アルバム3\005_楽曲5.aa3
                :

というディレクトリ構成のときに以下のファイルを作成。
※文字コードはutf8で、改行コードはCR+LF。
E:\PSP\PLAYLIST\Music\sample.m3u8

\Music\アーティスト1\アルバム1\楽曲1.aa3
\Music\アーティスト1\アルバム1\楽曲3.aa3
\Music\アーティスト2\アルバム3\楽曲5.aa3
        :

あとは全曲プレイリストを作成すればよいのだが、1500曲についてまともにやるのは不可能。少し頭を捻って、コマンド・プロンプトでファイル一覧を取得してみる事に。PSPgoの本体メモリがマウントされているドライブに移動して、『dir *.aa3 /b/a-d/s』というコマンドを叩けば、全曲のフルパスがコマンド・プロンプトに表示される。これをテキスト・エディタにコピペして、行頭にあるドライブ名を削除すれば完成。しかし、PSPgoはプレイリストも999曲までしか認識しない。そこで、この1500曲プレイリストを元に10パターンのランダム999曲『m3u8』ファイル生成スクリプトを作成。手間ばかりかかってる気もするけど、やっと並のメディア・プレーヤと同様のレベルになった。以下、参考までに。

use strict;

our @input;
our $file;
our @output;
our $loop=10;
our $source=$ARGV[0];

open(IN,"<$source");
@input = ;
close(IN);

my $count=0;
while($count<$loop){
	while(@output<999){
		my $i=int(rand($#input));
		$file=$input[$i];
		chomp $file;
		push(@output,$file);
		splice(@input,$i,1);
	}
	open(OUT,">shuffle$count.m3u8");
	for my $file(@output){print OUT "$file\n"};
	close(OUT);
	$count++;
	@input=@output;
	splice(@output,0);
}

カテゴリ: 製品  | タグ: , , ,  | コメント
2009/07/05 日曜日 | 投稿者: aqua

こんばんは、aquaです。

今回もperlのお話。初期化に100秒くらいかかる処理を最大で50回くらい繰り返す必要がある。そもそも100秒かかるのってどうなのよという議論もあるものの、差し当たっては現行仕様にて対応を検討する。幸い並行処理のコストは殆どないと考えてよいので、並列化する事で実行時間を短縮したい。並列処理と言えばスレッド、スレッドと言えばJAVAというのが自分の先入観だが、前回同様perlの手軽さに慣れつつある現状、この処理もperlで作成してみようと思う。まずはググったり、詳しい人にヒアリングしたりで情報収集。

ググった限りでは、perlのスレッドはバージョン5.8以降で大分よくなった模様。しかし、perl経験者に聞いてみるとやはりスレッドに対する不安感は払拭し切れていないようでマルチプロセスを薦められる。確かに5.8系のスレッドでも、生成に時間がかかるなどの不具合はあるらしい。今回はスレッド間でステータスの共有などをしたいので、(プロセスモデルでも出来るのかもしれないが)ともかくスレッドで作ってみて不具合が起きるようであれば再検討する事とする。という訳で、next stepとしてサンプル探しとコーティングに移る。

サンプルも思ったより幾つもあるし、スレッドを使用するに当たって特にモジュール・インストール等も必要ないようだ。以下のようなサンプルを作って実際の動きを試してみたところ、あっさりと期待通りの結果となり、またしてもperlの手軽さを実感する。

use threads;
use threads::shared;

for (1..5) {
  threads->new(\&main, $_);
}
$_->join for threads->list;

sub main {
  for (1..10) {
    sleep 1;
    print "thread => @_, count => $_ \n";
  }
}

見慣れない処理の説明だけ加えておくと、各スレッドの処理が完了するのを待つ為に『$_->join for threads->list;』という命令を入れている。この処理に今度は、スレッド間で共有するステータス管理用のハッシュを追加する。このような変数は『our %status : shared;』のように定義する。これを加えてみたサンプルが以下。

use threads;
use threads::shared;

our %status : shared;

for (1..5) {
  $status{$_} = "starting";
  threads->new(\&main, $_);
}
for my $name (sort keys %status) {
  print "thread$name status : $status{$name}\n";
}
$_->join for threads->list;
for my $name (sort keys %status) {
  print "thread$name status : $status{$name}\n";
}

sub main {
  for (1..10) {
    sleep 1;
    print "thread => @_, count => $_ \n";
  }
  $status{$_} = "done";
}

これも期待通りの動き。特にスレッドの生成に時間がかかるということもなく普通に動いてくれる。今回の処理には最低でも2回に分けて動かすなど更に幾つかの要件があるが、それらは特に言語でどうこうという話でもないので、このサンプルを元にperlのスレッドを使って実装していく事にする。

カテゴリ: 技術系  | タグ:  | コメント
2009/05/31 日曜日 | 投稿者: aqua

こんばんは、aquaです。

さて、開発に当たってフレームワーク選定に入ること早数ヶ月。未だに各候補の学習すら終わらない。何となくLightweight Languageに絞りつつあるし、その用途からもほぼこれにしよう、というのはあるんだけどね。こういう機会でもなければ一通り学ぶ事もないし、少し前までは割りとJAVA一辺倒だったから、きちんと向き合う事もなかった。まずは最も身近な言語であるPerl辺りからちょこちょこ使い始めている。まともなロジックは組んだ事ないけど、簡単な監視とかバッチとかでは普通に使っていた。

そういう訳で、今回は複数台のサーバにログインしてコマンドを発行する処理を作ってみる。用途から考えて当然、SSHによる自動処理を考える。パスワード認証を如何に通すが肝となる。普段なら、シェルでふにゃっと作って終わりにするところだが、発行するコマンドのパターンから、どうしてもハッシュやら連想配列やらを使いたい。もしかしたら、シェルでも可能なのかもしれないが、それほど頑張るところでもないのでPerlで組んでみる。こういう処理を組むときは、やっぱりPerlの手軽さが光る。

早速、ググってみるとNet::SSH::Perlというモジュールが存在するようだ。これを使えばパスワード認証に関しても、コード内で指定する形で自動化できる。インストールそのものはさくっと完了。更にググってて簡単なサンプルを探す。自分の環境に合わせてコードを準備して動作確認する。案の定、あれやこれやと不足しているモジュールを指摘される。言われるがままに必要なモジュールをインストールし続け、やっと動くところまでこぎつける。最終的にインストールしたモジュールは以下。

  1. Crypt-DES
  2. Crypt-DH
  3. Crypt-DSA
  4. Digest-HMAC
  5. Digest-SHA1
  6. Math-BigInt-GMP
  7. Math-GMP
  8. Math-Pari
  9. String-CRC32

各サーバへのログインはlogin()メソッドでユーザー名とパスワードを渡す形にしていたが、このままだとコード内にパスワード丸見えになってしまう。なんだかんだ言って、それはやっぱりイマイチなので公開鍵認証に切り替える。ローカル(プログラム起動側)に秘密鍵(id_dsa)を準備し、リモートサーバ(接続先)のauthorized_keysに公開鍵を登録する。コードの書き方に少し迷ったが、以下の方法でノーパス接続できた。

my %sshparams=(
  protocol=>1,
  interactive=>0,
  identity_files=>["$ENV{HOME}/.ssh/id_dsa"],
  UserKnownHostsFile=>["$ENV{HOME}/.ssh/known_hosts2",$ENV{HOME}/.ssh/known_hosts" ],
  User=>"root",
);
my $ssh = Net::SSH::Perl->new($host,%sshparams);
$ssh->login();
my( $stdout, $stderr, $exit) = $ssh->cmd("ls")

これはすごい便利。今まで微妙に自動化しづらいためにハンドで実行していたルーチンワークや、SSHを使いたいがためにシェルで汚く書かれたバッチなんかも全て置き換えられそう。DBとの連携部分もスマートに書けるしね。うーん、やっぱりPerlは便利だなあ。以下、参考までに。

カテゴリ: 技術系  | タグ:  | コメント