[topic] タイムコードを表すクラス

以下の条件でタイムコード「時:分:秒:フレーム」を表すクラスを書いてください。

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

Posted feedbacks - Nested

Flatten 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
題意をいまいち汲み取れてない気がするのですが、とりあえず条件を
満たすように努力してみました。
フレームレートは可変とのことですが、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))))

ドロップフレームの処理が地味に面白かったです. アニメ用のフレームレートも設定してみた.

 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)

Index

Feed

Other

Link

Pathtraq

loading...