Class: Wavify::Core::Duration

Inherits:
Object
  • Object
show all
Includes:
Comparable, Comparable[Duration]
Defined in:
lib/wavify/core/duration.rb,
sig/core.rbs

Overview

Immutable duration value object used across codecs and sequencer APIs.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(total_seconds) ⇒ Duration

Returns a new instance of Duration.

Parameters:

  • total_seconds (Numeric)

    non-negative duration in seconds



51
52
53
54
55
56
57
58
# File 'lib/wavify/core/duration.rb', line 51

def initialize(total_seconds)
  unless total_seconds.is_a?(Numeric) && total_seconds.respond_to?(:finite?) && total_seconds.finite? && total_seconds >= 0
    raise InvalidParameterError, "total_seconds must be a non-negative finite Numeric: #{total_seconds.inspect}"
  end

  @total_seconds = total_seconds.to_f
  freeze
end

Instance Attribute Details

#total_secondsFloat (readonly)

Returns the value of attribute total_seconds.

Returns:

  • (Float)


9
10
11
# File 'lib/wavify/core/duration.rb', line 9

def total_seconds
  @total_seconds
end

Class Method Details

.from_samples(sample_frames, sample_rate) ⇒ Duration

Builds a duration from a frame count and sample rate.

Parameters:

  • sample_frames (Integer)
  • sample_rate (Integer)

Returns:



16
17
18
19
20
21
22
23
24
25
# File 'lib/wavify/core/duration.rb', line 16

def self.from_samples(sample_frames, sample_rate)
  unless sample_frames.is_a?(Integer) && sample_frames >= 0
    raise InvalidParameterError, "sample_frames must be a non-negative Integer: #{sample_frames.inspect}"
  end
  unless sample_rate.is_a?(Integer) && sample_rate.positive?
    raise InvalidParameterError, "sample_rate must be a positive Integer: #{sample_rate.inspect}"
  end

  new(sample_frames.to_f / sample_rate)
end

.parse(value) ⇒ Duration

Parses SS.sss, MM:SS.sss, or HH:MM:SS.sss text.

Parameters:

  • value (String)

Returns:



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/wavify/core/duration.rb', line 31

def self.parse(value)
  raise InvalidParameterError, "duration must be a String" unless value.is_a?(String)

  parts = value.strip.split(":")
  component_count = parts.length
  raise InvalidParameterError, "invalid duration: #{value.inspect}" unless component_count.between?(1, 3)

  seconds = parse_seconds(parts.pop)
  minutes = parts.empty? ? 0.0 : parse_component(parts.pop, :minutes)
  hours = parts.empty? ? 0.0 : parse_component(parts.pop, :hours)
  raise InvalidParameterError, "invalid duration: #{value.inspect}" unless parts.empty?
  raise InvalidParameterError, "minutes must be less than 60" if component_count == 3 && minutes >= 60.0
  raise InvalidParameterError, "seconds must be less than 60" if component_count >= 2 && seconds >= 60.0

  new((hours * 3600.0) + (minutes * 60.0) + seconds)
rescue ArgumentError
  raise InvalidParameterError, "invalid duration: #{value.inspect}"
end

Instance Method Details

#+(other) ⇒ Duration

Parameters:

Returns:

Raises:



78
79
80
81
82
# File 'lib/wavify/core/duration.rb', line 78

def +(other)
  raise InvalidParameterError, "expected Duration: #{other.inspect}" unless other.is_a?(Duration)

  self.class.new(@total_seconds + other.total_seconds)
end

#-(other) ⇒ Duration

Parameters:

Returns:

Raises:



84
85
86
87
88
89
90
91
# File 'lib/wavify/core/duration.rb', line 84

def -(other)
  raise InvalidParameterError, "expected Duration: #{other.inspect}" unless other.is_a?(Duration)

  value = @total_seconds - other.total_seconds
  raise InvalidParameterError, "resulting duration cannot be negative: #{value}" if value.negative?

  self.class.new(value)
end

#<=>(other) ⇒ -1, ...

Compares two durations.

Parameters:

Returns:

  • (-1, 0, 1, nil)


64
65
66
67
68
# File 'lib/wavify/core/duration.rb', line 64

def <=>(other)
  return nil unless other.is_a?(Duration)

  @total_seconds <=> other.total_seconds
end

#eql?(other) ⇒ Boolean

Parameters:

  • other (Object)

Returns:

  • (Boolean)


70
71
72
# File 'lib/wavify/core/duration.rb', line 70

def eql?(other)
  other.is_a?(Duration) && @total_seconds.eql?(other.total_seconds)
end

#hashInteger

Returns:

  • (Integer)


74
75
76
# File 'lib/wavify/core/duration.rb', line 74

def hash
  @total_seconds.hash
end

#hoursInteger

Returns hours component for #to_s.

Returns:

  • (Integer)

    hours component for #to_s



94
95
96
# File 'lib/wavify/core/duration.rb', line 94

def hours
  (total_milliseconds / 3_600_000).floor
end

#millisecondsInteger

Returns milliseconds component for #to_s.

Returns:

  • (Integer)

    milliseconds component for #to_s



109
110
111
# File 'lib/wavify/core/duration.rb', line 109

def milliseconds
  (total_milliseconds % 1000).floor
end

#minutesInteger

Returns minutes component for #to_s.

Returns:

  • (Integer)

    minutes component for #to_s



99
100
101
# File 'lib/wavify/core/duration.rb', line 99

def minutes
  ((total_milliseconds % 3_600_000) / 60_000).floor
end

#secondsInteger

Returns seconds component for #to_s.

Returns:

  • (Integer)

    seconds component for #to_s



104
105
106
# File 'lib/wavify/core/duration.rb', line 104

def seconds
  ((total_milliseconds % 60_000) / 1000).floor
end

#to_sString

Returns a clock-style string (HH:MM:SS.mmm).

Returns:

  • (String)


116
117
118
119
120
121
122
# File 'lib/wavify/core/duration.rb', line 116

def to_s
  format("%<hours>02d:%<minutes>02d:%<seconds>02d.%<milliseconds>03d",
         hours: hours,
         minutes: minutes,
         seconds: seconds,
         milliseconds: milliseconds)
end