timecode 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt CHANGED
@@ -1,4 +1,13 @@
1
- === 1.0.0 / 2008-12-15
1
+ === 0.1.2 / 2008-12-25
2
+
3
+ * Fix to_uint
4
+ * Always use float frame rates and rely on a small delta for comparison
5
+
6
+ === 0.1.1 / 2008-12-15
7
+
8
+ * Allow passing framerate to from_uint
9
+
10
+ === 0.1.0 / 2008-12-15
2
11
 
3
12
  * 1 major enhancement
4
13
 
data/README.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  = timecode
2
2
 
3
- * http://projects.juli.nl/timecode
3
+ * http://wiretap.rubyforge.org/timecode
4
4
 
5
5
  == DESCRIPTION:
6
6
 
data/Rakefile CHANGED
@@ -1,9 +1,15 @@
1
1
  require 'rubygems'
2
- require 'hoe'
3
2
  require './lib/timecode.rb'
3
+ require 'hoe'
4
4
 
5
- Hoe.new('timecode', Timecode::VERSION) do |p|
5
+ Class.new(Hoe) do
6
+ def extra_deps
7
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
8
+ @extra_deps
9
+ end
10
+ end.new('timecode', Timecode::VERSION) do |p|
6
11
  p.developer('Julik', 'me@julik.nl')
7
12
  p.extra_deps.reject! {|e| e[0] == 'hoe' }
8
13
  p.rubyforge_name = 'wiretap'
14
+ p.remote_rdoc_dir = 'timecode'
9
15
  end
data/lib/timecode.rb CHANGED
@@ -1,6 +1,4 @@
1
- # Timecode is a convenience object for calculating SMPTE timecode natively. It is used in
2
- # various StoryTool models and templates, offers string output and is immutable.
3
- #
1
+ # Timecode is a convenience object for calculating SMPTE timecode natively.
4
2
  # The promise is that you only have to store two values to know the timecode - the amount
5
3
  # of frames and the framerate. An additional perk might be to save the dropframeness,
6
4
  # but we avoid that at this point.
@@ -14,10 +12,12 @@
14
12
  # :mapping => [%w(source_tc_frames total), %w(tape_fps fps)]
15
13
 
16
14
  class Timecode
17
- VERSION = '0.1.0'
15
+ VERSION = '0.1.2'
18
16
 
19
17
  include Comparable
20
- DEFAULT_FPS = 25
18
+
19
+ DEFAULT_FPS = 25.0
20
+ ALLOWED_FPS_DELTA = 0.001
21
21
  COMPLETE_TC_RE = /^(\d{1,2}):(\d{1,2}):(\d{1,2}):(\d{1,2})$/
22
22
 
23
23
  # All Timecode lib errors inherit from this
@@ -32,9 +32,6 @@ class Timecode
32
32
  # Self-explanatory
33
33
  class NonPositiveFps < RangeError; end
34
34
 
35
- # Gets raised when float frame count is passed
36
- class FrameIsWhole < RangeError; end
37
-
38
35
  # Gets raised when you divide by zero
39
36
  class TcIsZero < ZeroDivisionError; end
40
37
 
@@ -60,19 +57,17 @@ class Timecode
60
57
  end
61
58
 
62
59
  def initialize(total = 0, fps = DEFAULT_FPS) # :nodoc:
63
- if total.is_a?(Float)
64
- raise FrameIsWhole, "the number of frames cannot be partial (Integer value needed)"
65
- end
66
-
67
60
  raise RangeError, "Timecode cannot be negative" if total.to_f < 0
68
61
  raise WrongFramerate, "FPS cannot be zero" if fps.zero?
69
- @total, @fps = total, fps
62
+
63
+ # Always cast framerate to float, and num of rames to integer
64
+ @total, @fps = total.to_i, fps.to_f
70
65
  @value = validate!
71
66
  freeze
72
67
  end
73
68
 
74
69
  def inspect # :nodoc:
75
- super.gsub(/@fps/, self.to_s + ' @fps').gsub(/ @value=\[(.+)\],/, '')
70
+ "#<Timecode:%s (%dF@%.2f)>" % [to_s, total, fps]
76
71
  end
77
72
 
78
73
  TIME_FIELDS = 7 # :nodoc:
@@ -167,13 +162,14 @@ class Timecode
167
162
 
168
163
 
169
164
  # Some systems (like SGIs) and DPX format store timecode as unsigned integer, bit-packed
170
- def from_uint(uint)
165
+ def from_uint(uint, fps = DEFAULT_FPS)
171
166
  shift = 4 * TIME_FIELDS
172
167
  tc_elements = (0..TIME_FIELDS).map do
173
168
  part = ((uint >> shift) & 0x0F)
174
169
  shift -= 4
175
170
  part
176
171
  end.join.scan(/(\d{2})/).flatten.map{|e| e.to_i}
172
+ tc_elements << fps
177
173
  at(*tc_elements)
178
174
  end
179
175
  end
@@ -218,12 +214,12 @@ class Timecode
218
214
  1.0/@fps
219
215
  end
220
216
 
221
- # get the timecode as bit-packed unsigned int (suitable for DPX and SGI)
217
+ # get the timecode as bit-packed unsigned 32 bit int (suitable for DPX and SGI)
222
218
  def to_uint
223
219
  elements = (("%02d" * 4) % [hours,minutes,seconds,frames]).split(//).map{|e| e.to_i }
224
220
  uint = 0
225
- elements.each do | el |
226
- uint = ((uint >> el))
221
+ elements.reverse.each_with_index do | p, i |
222
+ uint |= p << 4 * i
227
223
  end
228
224
  uint
229
225
  end
@@ -255,7 +251,7 @@ class Timecode
255
251
 
256
252
  # add number of frames (or another timecode) to this one
257
253
  def +(arg)
258
- if (arg.is_a?(Timecode) && arg.fps == @fps)
254
+ if (arg.is_a?(Timecode) && framerate_in_delta(arg.fps, @fps))
259
255
  Timecode.new(@total+arg.total, @fps)
260
256
  elsif (arg.is_a?(Timecode))
261
257
  raise WrongFramerate, "You are calculating timecodes with different framerates"
@@ -266,7 +262,7 @@ class Timecode
266
262
 
267
263
  # Subtract a number of frames
268
264
  def -(arg)
269
- if (arg.is_a?(Timecode) && arg.fps == @fps)
265
+ if (arg.is_a?(Timecode) && framerate_in_delta(arg.fps, @fps))
270
266
  Timecode.new(@total-arg.total, @fps)
271
267
  elsif (arg.is_a?(Timecode))
272
268
  raise WrongFramerate, "You are calculating timecodes with different framerates"
@@ -283,7 +279,7 @@ class Timecode
283
279
 
284
280
  # Get the next frame
285
281
  def succ
286
- self.class.new(@total + 1)
282
+ self.class.new(@total + 1, @fps)
287
283
  end
288
284
 
289
285
  # Slice the timespan in pieces
@@ -293,13 +289,19 @@ class Timecode
293
289
 
294
290
  # Timecodes can be compared to each other
295
291
  def <=>(other_tc)
296
- if other_tc.is_a?(Timecode)
297
- self.total <=> other_tc.class.new(other_tc.total, self.fps).total
292
+ if other_tc.is_a?(Timecode) && framerate_in_delta(fps, other_tc.fps)
293
+ self.total <=> other_tc.total
298
294
  else
299
295
  self.total <=> other_tc
300
296
  end
301
297
  end
302
-
298
+
299
+
300
+ # Validate that framerates are within a small delta deviation considerable for floats
301
+ def framerate_in_delta(one, two)
302
+ (one.to_f - two.to_f).abs <= ALLOWED_FPS_DELTA
303
+ end
304
+
303
305
  private
304
306
 
305
307
  # Formats the actual timecode output from the number of frames
@@ -7,6 +7,37 @@ require 'active_support'
7
7
 
8
8
  class TimecodeTest < Test::Unit::TestCase
9
9
 
10
+ def test_timecode_with_nil_gives_zero
11
+ assert_equal Timecode.new(0), Timecode.new(nil)
12
+ end
13
+
14
+ def test_fps_always_coerced_to_float
15
+ t = Timecode.new(10, 25)
16
+ assert_kind_of Float, t.fps
17
+
18
+ t = Timecode.new(10, 25.0)
19
+ assert_kind_of Float, t.fps
20
+ end
21
+
22
+ def test_framerate_in_delta
23
+ tc = Timecode.new(1)
24
+ assert tc.framerate_in_delta(25.0000000000000001, 25.0000000000000003)
25
+ end
26
+
27
+ def test_equality_validated_based_on_deltas
28
+ t1, t2 = Timecode.new(10, 25.0000000000000000000000000001), Timecode.new(10, 25.0000000000000000000000000002)
29
+ assert t1 == t2
30
+ end
31
+
32
+ def test_inspect
33
+ tc = Timecode.new(10, 25)
34
+ assert_equal "#<Timecode:00:00:00:10 (10F@25.00)>", tc.inspect
35
+ end
36
+
37
+ def test_equality_on_succ
38
+ assert_equal Timecode.parse("05:43:02:01"), Timecode.parse("05:43:02:00").succ
39
+ end
40
+
10
41
  def test_basics
11
42
  five_seconds_of_pal = 5.seconds * 25
12
43
  tc = Timecode.new(five_seconds_of_pal, 25)
@@ -42,8 +73,26 @@ class TimecodeTest < Test::Unit::TestCase
42
73
  end
43
74
 
44
75
  end
45
-
46
- def test_parse
76
+
77
+ def test_succ
78
+ assert_equal Timecode.new(23), Timecode.new(22).succ
79
+ end
80
+
81
+ def test_zero
82
+ assert Timecode.new(0).zero?
83
+ assert !Timecode.new(1).zero?
84
+ assert !Timecode.new(1000).zero?
85
+ end
86
+
87
+ def test_plus
88
+ a, b = Timecode.new(24, 25.000000000000001), Timecode.new(22, 25.000000000000002)
89
+ assert_equal Timecode.new(24 + 22, 25.000000000000001), (a + b)
90
+ end
91
+ end
92
+
93
+ class TestParsing < Test::Unit::TestCase
94
+
95
+ def test_parse_simple
47
96
  simple_tc = "00:10:34:10"
48
97
 
49
98
  assert_nothing_raised do
@@ -63,16 +112,6 @@ class TimecodeTest < Test::Unit::TestCase
63
112
  end
64
113
  end
65
114
 
66
- def test_succ
67
- assert_equal Timecode.new(23), Timecode.new(22).succ
68
- end
69
-
70
- def test_zero
71
- assert Timecode.new(0).zero?
72
- assert !Timecode.new(1).zero?
73
- assert !Timecode.new(1000).zero?
74
- end
75
-
76
115
  def test_parse_from_numbers
77
116
  assert_equal Timecode.new(10), Timecode.parse("10")
78
117
  assert_equal Timecode.new(60), Timecode.parse("210")
@@ -84,8 +123,9 @@ class TimecodeTest < Test::Unit::TestCase
84
123
  end
85
124
 
86
125
  def test_parse_s
87
- assert_equal Timecode.new(50), Timecode.parse("2s")
88
- assert_equal Timecode.new(60), Timecode.parse("2s", 30)
126
+ assert_equal Timecode.new(50, 25), Timecode.parse("2s", 25)
127
+ assert_equal Timecode.new(60, 30), Timecode.parse("2s", 30)
128
+ assert_not_equal Timecode.new(60, 25), Timecode.parse("2s", 30)
89
129
  end
90
130
 
91
131
  def test_parse_m
@@ -107,10 +147,6 @@ class TimecodeTest < Test::Unit::TestCase
107
147
  assert_equal "00:00:02:00", tc.to_s
108
148
  end
109
149
 
110
- def test_timecode_with_nil_gives_zero
111
- assert_equal Timecode.new(0), Timecode.new(nil)
112
- end
113
-
114
150
  def test_parse_fractional_tc
115
151
  fraction = "00:00:07.1"
116
152
  tc = Timecode.parse_with_fractional_seconds(fraction, 10)
@@ -134,16 +170,6 @@ class TimecodeTest < Test::Unit::TestCase
134
170
  # assert_equal Timecode.new(17), tc
135
171
  # end
136
172
 
137
- def test_from_uint
138
- uint, tc = 87310853, Timecode.at(5,34,42,5)
139
- assert_equal tc, Timecode.from_uint(uint)
140
- end
141
-
142
- def test_to_uint
143
- uint, tc = 87310853, Timecode.at(5,34,42,5)
144
- assert_equal uint, tc.to_uint
145
- end
146
-
147
173
  def test_from_seconds
148
174
  fraction = 7.1
149
175
  tc = Timecode.from_seconds(fraction, 10)
@@ -158,3 +184,15 @@ class TimecodeTest < Test::Unit::TestCase
158
184
  assert_equal "00:00:07:02", tc.to_s
159
185
  end
160
186
  end
187
+
188
+ class TestUintConversion < Test::Unit::TestCase
189
+ def test_from_uint
190
+ uint, tc = 87310853, Timecode.at(5,34,42,5)
191
+ assert_equal tc, Timecode.from_uint(uint)
192
+ end
193
+
194
+ def test_to_uint
195
+ uint, tc = 87310853, Timecode.at(5,34,42,5)
196
+ assert_equal uint, tc.to_uint
197
+ end
198
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timecode
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-19 00:00:00 +01:00
12
+ date: 2008-12-25 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -41,7 +41,7 @@ files:
41
41
  - lib/timecode.rb
42
42
  - test/test_timecode.rb
43
43
  has_rdoc: true
44
- homepage: http://projects.juli.nl/timecode
44
+ homepage: http://wiretap.rubyforge.org/timecode
45
45
  post_install_message:
46
46
  rdoc_options:
47
47
  - --main