Class: FPS::Timecode
- Inherits:
-
Object
- Object
- FPS::Timecode
- Defined in:
- lib/fps-timecode.rb
Overview
The Focal Point Systems timecode library has two main purposes.
-
1.) Given the timecode address of the first frame of a sequence of frames, and given n, what is the timecode address of the nth frame of a sequence?
-
2.) Given the timecode address of the first frame of a sequence, and given the timecode address for the nth frame of the sequence, what is n?
Example 1. A non-drop-frame 30 fps sequence starts at 00:01:00:00. What is the timecode address of the 100th frame of the sequence? FPS::Timecode.count_to_string(:fps_30_ndf, FPS::Timecode.string_to_count(:fps_30_ndf,“00:01:00:00”) + 100) => “00:01:03:10”
Example 2. A non-drop-frame 30 fps sequence starts at 00:01:00:00. What is n for the frame with the address 00:01:03:10? FPS::Timecode.string_to_count(:fps_30_ndf,“00:01:03:10”) - FPS::Timecode.string_to_count(:fps_30_ndf,“00:01:00:00”) => 100
An instance of the Timecode class represents one timecode address. A timecode mode is always required to be given. The mode determines the frame rate and the dropness (drop-frame or non-drop-frame) of the timecode address. The mode must be one of Timecode::Counts.keys. E.g. :fps_24, :fps_25, :fps_30_df, :fps_30_ndf
Create an instance of a Timecode using either a string in the format “xx:xx:xx:xx” or a frame count. The frame count represents n for the nth frame of a sequence that begins with timecode address “00:00:00:00” (the zeroeth frame of the sequence).
When creating a timecode instance, the default is to use the string argument ignoring the frame count argument and falling back to using the frame count argument if the string is invalid.
Since there are many class methods, often there is no need to create an instance of class Timecode.
Constant Summary collapse
- Counts =
{ fps_24: { fp24h: 2073600, fph: 86400, fptm: 14400, fpm: 1440, fps: 24 }, fps_25: { fp24h: 2160000, fph: 90000, fptm: 15000, fpm: 1500, fps: 25 }, fps_30_df: { fp24h: 2589408, fph: 107892, fptm: 17982, fpm: 1798, fps: 30 }, fps_30_ndf: { fp24h: 2592000, fph: 108000, fptm: 18000, fpm: 1800, fps: 30 }, fps_48: { fp24h: 4147200, fph: 172800, fptm: 28800, fpm: 2880, fps: 48 }, fps_50: { fp24h: 4320000, fph: 180000, fptm: 30000, fpm: 3000, fps: 50 }, fps_60_df: { fp24h: 5178816, fph: 215784, fptm: 35964, fpm: 3596, fps: 60 }, fps_60_ndf: { fp24h: 5184000, fph: 216000, fptm: 36000, fpm: 3600, fps: 60 }, }
Instance Attribute Summary collapse
-
#tc_count ⇒ Object
readonly
Returns the value of attribute tc_count.
-
#tc_mode ⇒ Object
readonly
Returns the value of attribute tc_mode.
-
#tc_string ⇒ Object
readonly
Returns the value of attribute tc_string.
Class Method Summary collapse
-
.count_to_string(tc_mode, tc_count, duration = false) ⇒ Object
count_to_string Class method to produce a timecode string from a frame count.
-
.normalize(tc_mode, tc_count) ⇒ Object
Class method to normalize a frame count >= 0 and < 24h.
-
.string_as_duration(tc_mode, tc_count) ⇒ Object
Class method to produce a timecode string as a duration from a frame count The difference of two timecodes might be used as a duration.
-
.string_to_count(tc_mode, tc_string) ⇒ Object
string_to_count Class method to compute a frame count from a timecode string.
Instance Method Summary collapse
-
#<=>(other) ⇒ Object
Compare two timecodes.
-
#==(other) ⇒ Object
Compare two timecodes for equality.
-
#initialize(tc_mode, tc_string, tc_count = nil) ⇒ Timecode
constructor
initialize.
-
#string_as_duration ⇒ Object
string_as_duration instance method.
-
#succ ⇒ Object
Return the next timecode address in the sequence.
Constructor Details
#initialize(tc_mode, tc_string, tc_count = nil) ⇒ Timecode
initialize
Construct a new instance of FPS::Timecode, given a mode and either a timecode string or a frame count.
Raises ArgumentError
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/fps-timecode.rb', line 150 def initialize(tc_mode, tc_string, tc_count = nil) # tc_string and tc_count cannot both be nil if(tc_string === nil && tc_count === nil) raise ArgumentError, "string and count both nil" end # tc_mode must be given and must be one of the known tc modes unless Counts.include?(tc_mode) raise ArgumentError, "invalid timecode mode" end @tc_mode = tc_mode # Try to use the string to construct the instance and fall back # to the count if an exception is raised begin @tc_count = Timecode.string_to_count(@tc_mode, tc_string) # always convert back to string because given string may not be drop-frame legal @tc_string = Timecode.count_to_string(@tc_mode, @tc_count) rescue @tc_count = Timecode.normalize(@tc_mode, tc_count) @tc_string = Timecode.count_to_string(@tc_mode, @tc_count) end end |
Instance Attribute Details
#tc_count ⇒ Object (readonly)
Returns the value of attribute tc_count.
42 43 44 |
# File 'lib/fps-timecode.rb', line 42 def tc_count @tc_count end |
#tc_mode ⇒ Object (readonly)
Returns the value of attribute tc_mode.
42 43 44 |
# File 'lib/fps-timecode.rb', line 42 def tc_mode @tc_mode end |
#tc_string ⇒ Object (readonly)
Returns the value of attribute tc_string.
42 43 44 |
# File 'lib/fps-timecode.rb', line 42 def tc_string @tc_string end |
Class Method Details
.count_to_string(tc_mode, tc_count, duration = false) ⇒ Object
count_to_string Class method to produce a timecode string from a frame count
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/fps-timecode.rb', line 56 def self.count_to_string(tc_mode, tc_count, duration = false) tc_count = Timecode.normalize(tc_mode, tc_count) counts = Counts[tc_mode] hours = tc_count / counts[:fph] rem = tc_count % counts[:fph] tens_mins = rem / counts[:fptm] rem = rem % counts[:fptm] units_mins = rem / counts[:fpm] rem = rem % counts[:fpm] if(duration == false) # not a duration, do drop-frame processing # handle 30 fps drop frame if(tc_mode == :fps_30_df && units_mins > 0 && rem <= 1) units_mins -= 1 rem += counts[:fpm] end # handle 60 fps drop frame if(tc_mode == :fps_60_df && units_mins > 0 && rem <= 3) units_mins -= 1 rem += counts[:fpm] end end secs = rem / counts[:fps] frms = rem % counts[:fps] "%02d:%d%d:%02d:%02d" % [hours, tens_mins, units_mins, secs, frms] end |
.normalize(tc_mode, tc_count) ⇒ Object
Class method to normalize a frame count >= 0 and < 24h. Corrects 24 hour overflow or underflow
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/fps-timecode.rb', line 112 def self.normalize(tc_mode, tc_count) # tc_mode must be given and must be one of the known tc modes unless Counts.include?(tc_mode) raise ArgumentError, "invalid timecode mode" end unless tc_count.is_a? Fixnum raise ArgumentError, "invalid frame count #{tc_count}" end # normalize to 24 hours _24h = Counts[tc_mode][:fp24h] while tc_count < 0 do tc_count += _24h end while tc_count >= _24h do tc_count -= _24h end tc_count end |
.string_as_duration(tc_mode, tc_count) ⇒ Object
Class method to produce a timecode string as a duration from a frame count The difference of two timecodes might be used as a duration. For non-drop frame, the duration string does not differ from the string as a timecode address. But for drop-frame, timecodes that don’t exist as an address can exist as a duration. One minute in drop frame is 1798 frames or 00:00:59:28. “00:01:00:00” as a drop-frame timecode address does not exist. But the difference between 00:01:59:28 and 00:00:59:28 should be displayed as “00:01:00:00” – one minute.
138 139 140 |
# File 'lib/fps-timecode.rb', line 138 def self.string_as_duration(tc_mode, tc_count) Timecode.count_to_string(tc_mode, tc_count, true) end |
.string_to_count(tc_mode, tc_string) ⇒ Object
string_to_count Class method to compute a frame count from a timecode string
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/fps-timecode.rb', line 88 def self.string_to_count(tc_mode, tc_string) unless Counts.include?(tc_mode) raise ArgumentError, "invalid timecode mode" end unless tc_string.is_a? String raise ArgumentError, "invalid timecode string" end unless tc_string =~ /\A(\d{2})[:;.](\d{2})[:;.](\d{2})[:;.](\d{2})\Z/ raise ArgumentError, "invalid timecode string" end if($1.to_i >= 24 || $2.to_i >= 60 || $3.to_i >= 60 || $4.to_i >= Counts[tc_mode][:fps]) raise ArgumentError, "invalid timecode string" end counts = Counts[tc_mode] tc_string =~ /\A(\d{2})[:;.](\d)(\d)[:;.](\d{2})[:;.](\d{2})\Z/ $1.to_i * counts[:fph] + $2.to_i * counts[:fptm] + $3.to_i * counts[:fpm] + $4.to_i * counts[:fps] + $5.to_i end |
Instance Method Details
#<=>(other) ⇒ Object
Compare two timecodes. Comparison (spaceship) operator does string compare
196 197 198 |
# File 'lib/fps-timecode.rb', line 196 def <=>(other) @tc_string <=> other.tc_string end |
#==(other) ⇒ Object
Compare two timecodes for equality. Equality operator does string comparison. Two timecodes may be considered equal even though their modes and counts may be different.
190 191 192 |
# File 'lib/fps-timecode.rb', line 190 def ==(other) @tc_string == other.tc_string end |
#string_as_duration ⇒ Object
string_as_duration instance method
177 178 179 |
# File 'lib/fps-timecode.rb', line 177 def string_as_duration Timecode.string_as_duration(@tc_mode, @tc_count) end |
#succ ⇒ Object
Return the next timecode address in the sequence
182 183 184 |
# File 'lib/fps-timecode.rb', line 182 def succ Timecode.new(@tc_mode, nil, @tc_count+1) end |