音声ファイルの長さを調べるためのコマンドラインプログラムを探したけど,なかったので作った.
Beep Media Playerとかで一つ一つファイルを開けばわかるんだけど,ファイルがいっぱいだとGUIのツールは面倒だ.
画像ファイルの縦横サイズはfileコマンドで取れたりするので,音声ファイルの長さも取れないかなあと思ったが...
% file 'ACIDMAN - Loop - 01 - type A.mp3' ACIDMAN - Loop - 01 - type A.mp3: Audio file with ID3 version 2.3, MP3 encoding
うーん,取れん.
探し方が悪いのかもしれないけど,めぼしいコマンドはないようなので作ってみた.
ruby-audioinfoというライブラリでMP3ファイルやOgg,FLAC,...の情報が取れる. これは,各種ライブラリをラップして共通のインターフェースを用意してくれるライブラリなんだけど目的に十分かなっている.
インストールは以下でできる.
# gem install ruby-audioinfo
ということでruby-audioinfoを使って長さを取得してみた.
% cat ./getaudiolength #! /usr/bin/env ruby begin require "audioinfo" rescue LoadError require "rubygems" require "audioinfo" end ARGV.each do |filename| AudioInfo.open(filename) do |info| puts([filename, info.length].join(": ")) end end % ./getaudiolength 'ACIDMAN - Loop - 01 - type A.mp3' ACIDMAN - Loop - 01 - type A.mp3: 214
おお,取れる取れる. 実質5行で書けた. これは6行のrequireよりも短い.
...ってでも得られる結果は秒精度なのか!?
1/10秒精度ぐらいまでは出してくれてもいいんじゃないかと考えて調べてみる.
audioinfo.rbを読むと,以下のようにわざわざto_iメソッドで秒単位にしていることがわかった.
class AudioInfo ... VERSION = "0.1.4" ... attr_reader :artist, :album, :title, :length, :date ... def initialize(fn, encoding = 'utf-8') ... case @extension when 'mp3' @info = Mp3Info.new(fn, :encoding => @encoding) ... @length = @info.length.to_i
なお,この場合にバックエンドとして使われるruby-mp3infoではFloatで入っている. どんな音声ファイルでもAudioInfo#lengthはIntegerにしたかったとか,なにか作者の設計思想があるのかと思ってしまう.
しかしながら,FLACの場合はFloatが入るようだ.
when 'flac' @info = FlacInfo.new(fn) ... @length = @info.streaminfo["total_samples"] / @info.streaminfo["samplerate"].to_f
せっかくなんだからあえてto_iして精度を落とさなくてもいいんじゃないだろうか. 作者にはto_iしないでくださいと懇願しよう.
この場はAudioInfo#length_as_floatなるメソッドを追加して解決することにした.
修正後のものは以下のようになった. メソッド名の都合上to_fを呼んでいる.
% cat ./getaudiolength #! /usr/bin/ruby begin require "audioinfo" rescue LoadError require "rubygems" require "audioinfo" end class AudioInfo def length_as_float return (@info.respond_to?(:length) ? @info.length : @length).to_f end end ARGV.each do |filename| AudioInfo.open(filename) do |info| puts([filename, info.length_as_float].join(": ")) end end % ./getaudiolength 'ACIDMAN - Loop - 01 - type A.mp3' ACIDMAN - Loop - 01 - type A.mp3: 214.3346875 % ./getaudiolength 'ACIDMAN - green_chord - 13 - toward.mp3' ACIDMAN - green_chord - 13 - toward.mp3: 632.8946875
うん,多少長いファイルでもちゃんと取得できるようだ. 完成〜.よかった.よかった.
一時話題になったFizzBuzz問題,そういえば自分なりには解いてなかったなあと思い出し,今さらながらやってみた.
% ruby -e '100.times do |i| puts([[1, i], [3, "fizz"], [5, "buzz"]].collect { |mod, output| (i % mod).zero? ? output : "" }.join) end'
ワンライナではなくてワンステートメントでできた.
気に入ったところは,1で割った余りもまた0であるかどうかを判定するところだ. 条件分岐を一箇所で記述できたことがうれしい.