[topic] タイムコードを表すクラス
Posted feedbacks - Flatten
Nested Hiddenそのまんまなのですが、こういうことなんでしょうか
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class TimeCode
attr_reader :h, :m, :s, :frame, :frame_rate
def initialize(h, m, s, frame, frame_rate)
@h, @m, @s, @frame, @frame_rate = h, m, s, frame, frame_rate
if frame > (frame_rate == "ntsc" ? 29.97 : frame_rate)
raise ArgumentError, "Invalid frame number."
end
end
def frame_rate=(v)
@frame_rate = v
end
def to_s
if @frame_rate == "ntsc" && @m != 10 && @s == 0 && (@frame == 0 || @frame == 1)
"%02d:%02d:%02d;%02d" % [@h, @m, @s, @frame]
else
"%02d:%02d:%02d:%02d" % [@h, @m, @s, @frame]
end
end
end
p TimeCode.new( 0, 0, 0, 0, "ntsc").to_s #=> "00:00:00;00"
p TimeCode.new( 0, 0, 0, 1, "ntsc").to_s #=> "00:00:00;01"
p TimeCode.new( 0, 0, 0, 2, "ntsc").to_s #=> "00:00:00:02"
p TimeCode.new( 0, 10, 0, 0, "ntsc").to_s #=> "00:10:00:00"
p TimeCode.new( 0, 10, 0, 1, "ntsc").to_s #=> "00:10:00:01"
p TimeCode.new( 0, 10, 0, 2, "ntsc").to_s #=> "00:10:00:02"
p TimeCode.new( 0, 0, 0, 0, 10).to_s #=> "00:00:00:00"
p TimeCode.new( 0, 0, 0, 11, 10).to_s #=> ArgumentError
|
あ、ドロップフレームの処理を読みまちがえていました。こうでしょうか。そもそもなんか違うのかもしれないですが
1 2 3 4 5 6 7 | def to_s
if @frame_rate == "ntsc" && (@m % 10 != 0) && @s == 0 && (@frame == 0 || @frame == 1)
"%02d:%02d:%02d;%02d" % [@h, @m, @s, @frame]
else
"%02d:%02d:%02d:%02d" % [@h, @m, @s, @frame]
end
end
|
お題について
すみません。狙いがうまく伝わっていませんでした。 ポイントは、総フレーム数とタイムコードの相互変換です。 以下の仕様を追加してみてください。
- 総フレーム数を指定するコンストラクタ持つこと
- 総フレーム数を取得するメソッドを持つこと
また、オブジェクト指向言語で無い場合には、 単純に任意のフレームレートにおいて、タイムコードと総フレーム数を相互変換する関数を書いてもOKとしたいと思います。
題意をいまいち汲み取れてない気がするのですが、とりあえず条件を
満たすように努力してみました。
フレームレートは可変とのことですが、NTSC以外ではどのように
フレームをドロップして良いのか分からなかったので、単純にfpsと
時間を掛けて取得しています。
;; 動作
; インスタンス作成
(setq tc (make-instance 'timecode :h 30 :m 31 :s 00 :f 0 :fps :ntsc))
(total-frames tc)
;=> 3292505
(timecode tc)
;=> "30:31:00;00"
(setf (fps tc) 10)
;フレームレートを変更
(total-frames tc)
;=> 1098600
(timecode tc2)
;=> "30:31:00:00"
満たすように努力してみました。
フレームレートは可変とのことですが、NTSC以外ではどのように
フレームをドロップして良いのか分からなかったので、単純にfpsと
時間を掛けて取得しています。
;; 動作
; インスタンス作成
(setq tc (make-instance 'timecode :h 30 :m 31 :s 00 :f 0 :fps :ntsc))
(total-frames tc)
;=> 3292505
(timecode tc)
;=> "30:31:00;00"
(setf (fps tc) 10)
;フレームレートを変更
(total-frames tc)
;=> 1098600
(timecode tc2)
;=> "30:31:00:00"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | (defpackage :doukaku-165 (:use :cl))
(in-package :doukaku-165)
(defclass timecode ()
((h :accessor h :initarg :h :initform 0)
(m :accessor m :initarg :m :initform 0)
(s :accessor s :initarg :s :initform 0)
(f :accessor f :initarg :f :initform 0)
(fps :accessor fps :initarg :fps :initform 0)))
(defmethod drop-frame-p ((tc timecode))
(and (/= 10 (m tc))
(zerop (s tc))
(or (<= 0 (f tc) 1))))
(defun tv-frame (f)
(cond ((numberp f) f)
((string-equal "NTSC" (string f)) 29.97)
('T 0)))
(defmethod ntsc-p ((tc timecode))
(= 29.97 (tv-frame (fps tc))))
(defmethod timecode ((tc timecode))
(format nil "~2,'0D:~2,'0D:~2,'0D~A~2,'0D"
(h tc) (m tc) (s tc)
(if (and (ntsc-p tc) (drop-frame-p tc)) ";" ":") (f tc)))
(defmethod total-frames ((tc timecode))
(flet ((frs (fps tc)
(+ (* (h tc) fps 60 60)
(* (m tc) fps 60)
(* (s tc) fps)
(f tc))))
(if (ntsc-p tc)
(let* ((fps (ceiling (tv-frame (fps tc)))) ;整数に切り上げ
(non-drop-total (frs fps tc)))
(- non-drop-total (truncate non-drop-total 1000)))
(frs (fps tc) tc))))
|
出題者です・・・ ううむ、お題が言葉たらずでした。すみません。 意図していたのは、以下のようなものです。 実行結果はこうなります。 DROP 00:00:59;28 00:00:59;29 00:01:00;02 00:01:00;03 00:01:00;04 NONDROP 00:00:59:28 00:00:59:29 00:01:00:00 00:01:00:01 00:01:00:02 DROP 10min 00:09:59;28 00:09:59;29 00:10:00;00 00:10:00;01 00:10:00;02 NONDROP 10min 00:09:59:28 00:09:59:29 00:10:00:00 00:10:00:01 00:10:00:02
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | class Timecode(val frames:Int, val rate:Any) {
def this(hh:Int, mm:Int, ss:Int, ff:Int, rate:Any) = this( rate match {
case "ntsc" =>
val minutes = hh * 60 + mm
17982 * (minutes/10) + 1798 * (minutes%10) + ss * 30 + ff
case r:Int =>
val mrate = r * 60
val hrate = mrate * 60
hh * hrate + mm * mrate + ss * r + ff
}, rate)
def changeRate(r:Any) = new Timecode(frames, r)
override def toString = rate match {
case "ntsc" =>
val hh = frames / 17982 / 6
val mm = frames / 17982 % 6 * 10 + (frames % 17982 - 2) / 1798
val ss = ((frames % 17982 - 2) % 1798 + 2) / 30
val ff = ((frames % 17982 - 2) % 1798 + 2) % 30
sprIntf("%02d:%02d:%02d;%02d", hh,mm,ss,ff)
case r:Int =>
val m = r * 60
val h = m * 60
val hh = frames / h
val mm = (frames % h) / m
val ss = ((frames % h) % m) / r
val ff = ((frames % h) % m) % r
sprIntf("%02d:%02d:%02d:%02d", hh,mm,ss,ff)
}
def +(add:Int) = new Timecode(frames + add, rate)
def -(sub:Int) = new Timecode(frames - sub, rate)
private def sprIntf(fmt:String, args:Any*) = String.format(fmt, args.map(e => e.asInstanceOf[AnyRef]).toArray)
}
object TimecodeTest extends Application {
println("DROP")
val t1 = new Timecode(0,0,59,27,"ntsc")
(1 to 5).foreach {i => println(t1 + i) }
println("NONDROP")
val t2 = t1.changeRate(30)
(1 to 5).foreach {i => println(t2 + i) }
println("DROP 10min")
val t4 = new Timecode(0,9,59,27, "ntsc")
(1 to 5).foreach {i => println(t4 + i) }
println("NONDROP 10min")
val t3 = new Timecode(0,9,59,27, 30)
(1 to 5).foreach {i => println(t3 + i) }
}
|
ドロップフレームの処理が地味に面白かったです. アニメ用のフレームレートも設定してみた.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class TimeCode
def initialize(h, m, s, f, frate)
@hour, @minute, @second = h, m, s
@frame, @frame_rate = f, frate
end
def treat
form = proc{|*ary| ary.map{|s| format("%02d", s)}.join(':')}
drop_frame
"#{form.call(@hour, @minute, @second)}; #{@frame}."
end
attr_writer :frame_rate
private
def inspect() treat end
def drop_frame
check = @second.zero? && (@minute.modulo(10).nonzero? || @minute.zero?)
@frame = [@frame%get_frate.ceil, check ? 0 : 2].max
end
def get_frate
case @frame_rate.to_s
when 'ntsc' then 29.97
when 'anime' then 24
else
raise RangeError if out_of_range?
@frame_rate.to_i
end
end
def out_of_range?() @frame_rate.to_i <= 0 end
end
p tc = TimeCode.new(22, 0, 0, 37, 'ntsc') #=> 22:00:00; 6.
tc.frame_rate = 5
p tc #=> 22:00:00; 1.
p TimeCode.new(22, 0, 0, 0, 'anime') #=> 22:00:00; 0.
p TimeCode.new(22, 10, 0, 0, 'anime') #=> 22:10:00; 2.
p TimeCode.new(22, 10, 0, 0, -1) #=> RangeError (RangeError)
|





ryugate
#5892()
Rating-10/12=-0.83
1. 最低限、「時、分、秒、フレーム、フレームレート」を引数にするコンストラクタが必要です
2.フレームレートには数値の他"ntsc"(ドロップフレーム 29.97pfs)も指定できること
3.ありえない引数に対するバリデーションはあっても無くてもかまいません。
4.生成してから、フレームレートの変更が出来ること(自身を変更しても、新しいインスタンスを生成してもかまいません。)
5.保持しているタイムコードを「時:分:秒:フレーム」という文字列に変換できること。(ただしドロップフレームの場合には「時:分:秒;フレーム」であること)
以上
ドロップフレームについては以下を参考にして下さい。
http://www.ite.or.jp/study/musen/tips/tip06.html
http://qtake.hp.infoseek.co.jp/1-4.html
[ reply ]