Mac::iPod::GNUpod の m4a ファイルサポート

Mac::iPod::GNUpod という PerliPod に曲を転送したり、取得したりする便利な module があるんだけど、こいつはどうも wav と mp3 にしか対応していないらしい。最近 AAC でばっかりエンコーディングしてるので m4a にも対応して欲しいところだ。

コードを見てみると対応予定なのかなんなのか、Mac::iPod::GNUpod::Utils::mp4_info というのはあるんだけど、何も書いてないし、呼び出されもしない。

m4a というか、MP4 を扱う Perl module は MP4::Info として CPAN に上がっているのでこいつを使って mp4_info を書いてみる。

--- Utils.pm.org        Wed Apr  5 00:08:02 2006
+++ Utils.pm    Wed Apr  5 00:12:34 2006
@@ -13,6 +13,7 @@
 use Unicode::String;
 use File::Spec;
 use MP3::Info qw(:all);
+use MP4::Info;
 use Audio::Wav;

 @ISA = qw/Exporter/;
@@ -24,6 +25,7 @@
 BEGIN {
     MP3::Info::use_winamp_genres();
     MP3::Info::use_mp3_utf8(0);
+    MP4::Info::use_mp4_utf8(0);
 }

 # Reformat shx numbers
@@ -111,11 +113,9 @@
     elsif ($file =~ m/\.wav$/) {
         $h = wav_info($file);
     }
-=rem Currently unimplemented
-    else if ($file =~ m/\.(mp4|m4a)$/) {
+    elsif ($file =~ m/\.(mp4|m4a)$/) {
         $h = mp4_info($file);
     }
-=cut

     # Unrecognized file types
     else {
@@ -218,6 +218,54 @@

 # This will be filled out someday
 sub mp4_info {
+    my $file = shift;
+
+    my $h = MP4::Info::get_mp4info($file);
+    return unless $h; #Not an mp3
+
+    #This is our default fallback:
+    #If we didn't find a title, we'll use the
+    #Filename.. why? because you are not able
+    #to play the file without a filename ;)
+    my $cf = (File::Spec->splitpath($file))[-1];
+
+    my %rh = ();
+
+    $rh{bitrate}  = $h->{BITRATE};
+    $rh{filesize} = $h->{SIZE};
+    $rh{srate}    = int($h->{FREQUENCY}*1000);
+    $rh{time}     = int($h->{SECS}*1000);
+    $rh{fdesc}    = $h->{TOO};
+
+    $h = MP4::Info::get_mp4tag($file,1);  #Get the IDv1 tag
+    my $hs = MP4::Info::get_mp4tag($file, 2, 2); #Get the IDv2 tag
+    # If any of these are array refs (multiple values), take last value
+    for (keys %$hs) {
+        if (ref($hs->{$_}) eq 'ARRAY') {
+            $hs->{$_} = $hs->{$_}->[-1];
+        }
+    }
+
+    #IDv2 is stronger than IDv1..
+    #Try to parse things like 01/01
+    no warnings 'uninitialized';
+    no warnings 'numeric';
+    my @songa = parseslashes($hs->{TRCK} || $h->{TRACKNUM});
+    my @cda   = parseslashes($hs->{TPOS});
+    $rh{songs}    = int($songa[1]);
+    $rh{songnum}  = int($songa[0]);
+    $rh{cdnum}    = int($cda[0]);
+    $rh{cds}      = int($cda[1]);
+    $rh{year}     = $hs->{TYER} || $h->{YEAR}   || 0;
+    $rh{title}    = $hs->{TIT2} || $h->{TITLE}  || $cf || "Untitled";
+    $rh{album}    = $hs->{TALB} || $h->{ALBUM}  || "Unknown Album";
+    $rh{artist}   = $hs->{TPE1} || $h->{ARTIST} || "Unknown Artist";
+    $rh{genre}    =                $h->{GENRE}  || "";
+    $rh{comment}  = $hs->{COMM} || $h->{COMMENT}|| "";
+    $rh{composer} = $hs->{TCOM} || "";
+    $rh{playcount}= int($hs->{PCNT}) || 0;
+
+    return \%rh;
 }

コメントアウトされている箇所に else if とか罠が書いてあったりもしたけど、まあ問題なく書けた。

MP4::Info は MP3::Info と同じ method を提供してるのと、method が返す値もまあ似ているので、mp4_info は mp3_info をほとんどコピペでオッケーなので簡単なんだけどね。

使ってみたけど問題なく m4a ファイルを転送できた。(日本語まわりが若干自信ないけど)ふむ。どうしようかな。

追記

なにげにこのパッチを author に送ったら apply してくれるとのこと。どうもこの module を作ったときには MP4::Info がなかったとか何とか。みんな幸せになれることはいいことだな。

2006-04-28 追記

Mac::iPod::GNUpod-1.22 パッチ追加された!!!。