timecode 0.1.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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