File: smpte_timecode.rb

package info (click to toggle)
ruby-wavefile 1.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,708 kB
  • sloc: ruby: 4,171; makefile: 2
file content (61 lines) | stat: -rw-r--r-- 2,622 bytes parent folder | download
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
module WaveFile
  # Public: Error that is raised when constructing a SMPTETimecode instance that is not valid.
  # Valid means that each field is in the range that can be encoded in a *.wav file, but not
  # not necessarily semantically correct. For example, a SMPTETimecode field can be constructed
  # with a hours value of 100, even though this isn't a valid value in real life.
  class InvalidSMPTETimecodeError < StandardError; end

  # Public: Represents an SMPTE timecode: https://en.wikipedia.org/wiki/SMPTE_timecode
  #         If a *.wav file has a "smpl" chunk, then Reader#sampler_info#smpte_offset
  #         will return an instance of this class.
  class SMPTETimecode
    VALID_8_BIT_UNSIGNED_INTEGER_RANGE = 0..255    # :nodoc:
    private_constant :VALID_8_BIT_UNSIGNED_INTEGER_RANGE
    VALID_8_BIT_SIGNED_INTEGER_RANGE = -128..127    # :nodoc:
    private_constant :VALID_8_BIT_SIGNED_INTEGER_RANGE


    # Public: Constructs a new SMPTETimecode instance.
    #
    # Raises InvalidSMPTETimecodeError if the given arguments can't be written to a *.wav file.
    def initialize(hours: required("hours"),
                   minutes: required("minutes"),
                   seconds: required("seconds"),
                   frames: required("frames"))
      validate_8_bit_signed_integer_field(hours, "hours")
      validate_8_bit_unsigned_integer_field(minutes, "minutes")
      validate_8_bit_unsigned_integer_field(seconds, "seconds")
      validate_8_bit_unsigned_integer_field(frames, "frames")

      @hours = hours
      @minutes = minutes
      @seconds = seconds
      @frames = frames
    end

    attr_reader :hours
    attr_reader :minutes
    attr_reader :seconds
    attr_reader :frames

    private

    def required(keyword)
      raise ArgumentError.new("missing keyword: #{keyword}")
    end

    def validate_8_bit_unsigned_integer_field(candidate, field_name)
      unless candidate.is_a?(Integer) && VALID_8_BIT_UNSIGNED_INTEGER_RANGE === candidate
        raise InvalidSMPTETimecodeError,
              "Invalid `#{field_name}` value: `#{candidate}`. Must be an Integer between #{VALID_8_BIT_UNSIGNED_INTEGER_RANGE.min} and #{VALID_8_BIT_UNSIGNED_INTEGER_RANGE.max}"
      end
    end

    def validate_8_bit_signed_integer_field(candidate, field_name)
      unless candidate.is_a?(Integer) && VALID_8_BIT_SIGNED_INTEGER_RANGE === candidate
        raise InvalidSMPTETimecodeError,
              "Invalid `#{field_name}` value: `#{candidate}`. Must be an Integer between #{VALID_8_BIT_SIGNED_INTEGER_RANGE.min} and #{VALID_8_BIT_SIGNED_INTEGER_RANGE.max}"
      end
    end
  end
end