Class: Oscillo::Signal

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/oscillo/signal.rb

Overview

A signal that changes over time. This change can either be explicit through the use of #change or #<<, or it can be implicit when any of the signals that this signal follows changes.

Author:

Direct Known Subclasses

BoolSignal, NumSignal

Instance Method Summary collapse

Methods included from Enumerable

#all?, #any?, #collect, #count, #each, #find_all, #inject, #reject, #zip

Constructor Details

#initialize(*signals) {|*signals, self| ... } ⇒ Signal #initialize(start_value, *signals) {|*signals, self| ... } ⇒ Signal

Creates a new Signal with an optional initial value, signals to follow, and block. The block converts the values from any signals that this one follows to the value of this signal. If no block is given, the value will be an array of all the values of the signals that it follows (or if it is just following one signal, the value of that signal).

Overloads:

  • #initialize(*signals) {|*signals, self| ... } ⇒ Signal

    Follows some number of other signals, changing when they change.

    Parameters:

    • signals (Signal)

      the signals to follow

    Yields:

    • (*signals, self)

      gives the new values of the signals to the block

  • #initialize(start_value, *signals) {|*signals, self| ... } ⇒ Signal

    Starts with an initial value and follows other signals when they change.

    Parameters:

    • start_value

      the initial value of this signal

    • signals (Signal)

      the signals to follow

    Yields:

    • (*signals, self)

      gives the new values of the signals to the block



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/oscillo/signal.rb', line 28

def initialize(*values, &block)
  @on_change = []
  @follows = []
  @followed_by = []

  @block = block || ->(*x, s) { x.size <= 1 ? x.first : x }

  return if values.empty?

  # first may be initial value instead of signal
  first, *rest = *values
  if first.is_a?(self.class)
    follow(*values)
  else
    @value = first
    follow(*rest)
  end
end

Instance Method Details

#abortObject

Note:

This should only be called in the block passed to #initialize.

Aborts the change so that the value stays the same, no callbacks registered by #on_change are called, and no dependent signals are changed.



128
129
130
# File 'lib/oscillo/signal.rb', line 128

def abort
  @aborted = true
end

#change(new_value) ⇒ self Also known as: <<

Change the current value of the signal. This will update all signals that follow this on as well as call all #on_change callbacks registered.

Parameters:

  • new_value

    the new value of the signal

Returns:

  • (self)


100
101
102
103
104
105
106
107
108
109
110
# File 'lib/oscillo/signal.rb', line 100

def change(new_value)
  @value = new_value
  @on_change.each { |c| c.(new_value) }

  unless self === source
    @followed_by.each { |f| f.update(source || self) }
  end
  @source = nil

  self
end

#dont_follow(*signals) ⇒ self

Note:

Immediately updates value.

Stops following the given signals.

Parameters:

  • signals (Signal)

    the signals to stop following

Returns:

  • (self)


65
66
67
68
69
70
# File 'lib/oscillo/signal.rb', line 65

def dont_follow(*signals)
  @follows -= signals.each { |signal| signal.not_followed_by(self) } 
  update(self)

  self
end

#drop_repeatsSignal

The new signal ignores change events when the new value is the same as the old value.

Returns:



160
161
162
163
164
# File 'lib/oscillo/signal.rb', line 160

def drop_repeats
  self.class.new(self) do |v, s|
    s.abort if s.val == v; v
  end
end

#follow(*signals) ⇒ self

Note:

Immediately updates value.

Follows additional signals, changing when those signals change. This is necessary to define mutually recursive signals.

Parameters:

  • signals (Signal)

    the signals to follow

Returns:

  • (self)


53
54
55
56
57
58
# File 'lib/oscillo/signal.rb', line 53

def follow(*signals)
  @follows += signals.each { |signal| signal.followed_by(self) }
  update(self)

  self
end

#on_change {|value| ... } ⇒ self

Register a callback that will be called any time the signal value is changed.

Yields:

  • (value)

    gives the new value to the block

Returns:

  • (self)


118
119
120
121
122
# File 'lib/oscillo/signal.rb', line 118

def on_change(&block)
  @on_change << block

  self
end

#sample_on(sample) ⇒ Signal

Updates the new signal to the value of this signal any time the value of sample changes.

Parameters:

  • sample (Signal)

    the signal to update on

Returns:



171
172
173
174
175
# File 'lib/oscillo/signal.rb', line 171

def sample_on(sample)
  Signal.new(self.val, sample) do |v, s|
    self.val
  end
end

#sourceSignal

Note:

This is only defined within the block passed to #initialize.

The original source of the current run of signal changes. This can be useful to know which signal caused the change if you are following multiple signals.

Returns:

  • (Signal)

    the signal that caused the change



138
139
140
# File 'lib/oscillo/signal.rb', line 138

def source
  @source
end

#to_iFixnum

The value of the signal as an integer, for convenience.

Returns:

  • (Fixnum)

    the signal value



152
153
154
# File 'lib/oscillo/signal.rb', line 152

def to_i
  value.to_i
end

#to_sString

The value of the signal as a string, for convenience.

Returns:

  • (String)

    the signal value



145
146
147
# File 'lib/oscillo/signal.rb', line 145

def to_s
  value.to_s
end

#update(source = nil) ⇒ Object

Updates the value if this signal to represent the values of the signals that it follows. This is called automatically when dependent signals change so you don’t need to call this manually in most cases.

Parameters:

  • source (Signal) (defaults to: nil)

    The signal that initialized the update. If not passed, then this signal will be the source of the update.



78
79
80
81
82
83
84
85
# File 'lib/oscillo/signal.rb', line 78

def update(source=nil)
  return if @follows.empty? # nothing to update

  @source = source
  @aborted = false
  new_value = @block.(*@follows.map(&:value), self)
  self.change(new_value) unless @aborted
end

#valueObject Also known as: val

Returns the current value of the signal

Returns:

  • the current value



90
91
92
# File 'lib/oscillo/signal.rb', line 90

def value
  @value
end