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
|