Class: FPS::Timecode

Inherits:
Object
  • Object
show all
Defined in:
lib/fps-timecode.rb

Overview

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(tc_mode, tc_string, tc_count = nil) ⇒ Timecode

initialize Construct a new instance of Timecode, given a mode and either a timecode string or a frame count. Raises ArgumentError



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/fps-timecode.rb', line 140

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_countObject (readonly)

Returns the value of attribute tc_count.



43
44
45
# File 'lib/fps-timecode.rb', line 43

def tc_count
  @tc_count
end

#tc_modeObject (readonly)

Returns the value of attribute tc_mode.



43
44
45
# File 'lib/fps-timecode.rb', line 43

def tc_mode
  @tc_mode
end

#tc_stringObject (readonly)

Returns the value of attribute tc_string.



43
44
45
# File 'lib/fps-timecode.rb', line 43

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 compute a string from a frame count



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
85
# File 'lib/fps-timecode.rb', line 57

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 Correct 24 hour overflow or underflow



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/fps-timecode.rb', line 113

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 compute a string as a duration from a frame count_to_string



130
131
132
# File 'lib/fps-timecode.rb', line 130

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 count from a string



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/fps-timecode.rb', line 89

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



195
196
197
# File 'lib/fps-timecode.rb', line 195

def <=>(other)
  @tc_string <=> other.tc_string
end

#==(other) ⇒ Object

compare two timecodes for equality

equality operator does string compare two timecodes may be considered equal even though their modes and counts may be different.



189
190
191
# File 'lib/fps-timecode.rb', line 189

def ==(other)
  @tc_string == other.tc_string
end

#string_as_durationObject

string_as_duration 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.



175
176
177
# File 'lib/fps-timecode.rb', line 175

def string_as_duration
  Timecode.string_as_duration(@tc_mode, @tc_count)
end

#succObject

succ return the next timecode address in the sequence



181
182
183
# File 'lib/fps-timecode.rb', line 181

def succ
  Timecode.new(@tc_mode, nil, @tc_count+1)
end