xi-lang 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/xi/stream.rb CHANGED
@@ -3,7 +3,7 @@ require 'set'
3
3
 
4
4
  module Xi
5
5
  class Stream
6
- attr_reader :clock, :opts, :source, :state, :event_duration, :gate
6
+ attr_reader :clock, :opts, :source, :state, :delta, :gate
7
7
 
8
8
  DEFAULT_PARAMS = {
9
9
  degree: 0,
@@ -25,16 +25,18 @@ module Xi
25
25
  @state = {}
26
26
  @changed_params = [].to_set
27
27
  @playing_sound_objects = {}
28
- @prev_end = {}
28
+ @prev_ts = {}
29
+ @prev_delta = {}
29
30
 
30
31
  self.clock = clock
31
32
  end
32
33
 
33
- def set(event_duration: nil, gate: nil, **source)
34
+ def set(delta: nil, gate: nil, **source)
34
35
  @mutex.synchronize do
35
36
  @source = source
36
37
  @gate = gate if gate
37
- @event_duration = event_duration if event_duration
38
+ @delta = delta if delta
39
+ @reset = true unless @playing
38
40
  update_internal_structures
39
41
  end
40
42
  play
@@ -42,9 +44,9 @@ module Xi
42
44
  end
43
45
  alias_method :call, :set
44
46
 
45
- def event_duration=(new_value)
47
+ def delta=(new_value)
46
48
  @mutex.synchronize do
47
- @event_duration = new_value
49
+ @delta = new_value
48
50
  update_internal_structures
49
51
  end
50
52
  end
@@ -83,6 +85,8 @@ module Xi
83
85
  @mutex.synchronize do
84
86
  @playing = false
85
87
  @state.clear
88
+ @prev_ts.clear
89
+ @prev_delta.clear
86
90
  @clock.unsubscribe(self)
87
91
  end
88
92
  self
@@ -103,7 +107,8 @@ module Xi
103
107
  @mutex.synchronize do
104
108
  @changed_params.clear
105
109
 
106
- forward_enums(now, cps) if @must_forward
110
+ update_all_state if @reset
111
+
107
112
  gate_off = gate_off_old_sound_objects(now)
108
113
  gate_on = play_enums(now, cps)
109
114
 
@@ -131,36 +136,15 @@ module Xi
131
136
  @state.select { |k, _| @changed_params.include?(k) }
132
137
  end
133
138
 
134
- def forward_enums(now, cps)
135
- @enums.each do |p, (enum, total_dur)|
136
- next if total_dur == 0
137
-
138
- cur_pos = (now * cps) % total_dur
139
- start_ts = now - (cur_pos / cps)
140
-
141
- loop do
142
- next_ev = enum.peek
143
- distance = (cur_pos - next_ev.start) % total_dur
144
-
145
- @prev_end[p] = @clock.init_ts + start_ts + (next_ev.end / cps)
146
- enum.next
147
-
148
- break if distance <= next_ev.duration
149
- end
150
- end
151
-
152
- @must_forward = false
153
- end
154
-
155
139
  def gate_off_old_sound_objects(now)
156
140
  gate_off = []
157
141
 
158
142
  # Check if there are any currently playing sound objects that
159
143
  # must be gated off
160
- @playing_sound_objects.dup.each do |end_pos, h|
144
+ @playing_sound_objects.dup.each do |start_pos, h|
161
145
  if now + @clock.init_ts >= h[:at] - latency_sec
162
146
  gate_off << h
163
- @playing_sound_objects.delete(end_pos)
147
+ @playing_sound_objects.delete(start_pos)
164
148
  end
165
149
  end
166
150
 
@@ -170,43 +154,50 @@ module Xi
170
154
  def play_enums(now, cps)
171
155
  gate_on = []
172
156
 
173
- @enums.each do |p, (enum, total_dur)|
174
- next if total_dur == 0
157
+ @enums.each do |p, enum|
158
+ next unless enum.next?
159
+
160
+ n_value, n_start, n_dur = enum.peek
175
161
 
176
- cur_pos = (now * cps) % total_dur
177
- start_ts = now - (cur_pos / cps)
162
+ @prev_ts[p] ||= n_start / cps
163
+ @prev_delta[p] ||= n_dur
178
164
 
179
- next_ev = enum.peek
165
+ next_start = @prev_ts[p] + (@prev_delta[p] / cps)
180
166
 
181
- # Do we need to play next event now? If not, skip this parameter
182
- if (@prev_end[p].nil? || now + @clock.init_ts >= @prev_end[p]) &&
183
- cur_pos >= next_ev.start - latency_sec
167
+ # Do we need to play next event? If not, skip this parameter value
168
+ if now >= next_start - latency_sec
169
+ # If it is too late to play this event, skip it
170
+ if now < next_start
171
+ starts_at = @clock.init_ts + next_start
184
172
 
185
- # Update state based on pattern value
186
- # TODO: Pass as parameter exact time (start_ts + next_ev.start)
187
- update_state(p, next_ev.value)
188
- transform_state
173
+ # Update state based on pattern value
174
+ # TODO: Pass as parameter exact time: starts_at
175
+ update_state(p, n_value)
176
+ transform_state
189
177
 
190
- # If a gate parameter changed, create a new sound object
191
- if p == @gate
192
- new_so_ids = Array(next_ev.value)
193
- .size.times.map { new_sound_object_id }
178
+ # If a gate parameter changed, create a new sound object
179
+ if p == @gate
180
+ # If these sounds objects are new,
181
+ # consider them as new "gate on" events.
182
+ unless @playing_sound_objects.key?(n_start)
183
+ new_so_ids = Array(n_value)
184
+ .size.times.map { new_sound_object_id }
194
185
 
195
- gate_on << {
196
- so_ids: new_so_ids,
197
- at: @clock.init_ts + start_ts + (next_ev.start / cps),
198
- }
186
+ gate_on << {so_ids: new_so_ids, at: starts_at}
187
+ @playing_sound_objects[n_start] = {so_ids: new_so_ids}
188
+ end
199
189
 
200
- @playing_sound_objects[rand(100000)] = {
201
- so_ids: new_so_ids,
202
- duration: total_dur,
203
- at: @clock.init_ts + start_ts + (next_ev.end / cps),
204
- }
190
+ # Set (or update) ends_at timestamp
191
+ ends_at = @clock.init_ts + next_start + (n_dur / cps)
192
+ @playing_sound_objects[n_start][:at] = ends_at
193
+ end
205
194
  end
206
195
 
196
+ @prev_ts[p] = next_start
197
+ @prev_delta[p] = n_dur
198
+
207
199
  # Because we already processed event, advance enumerator
208
- next_ev = enum.next
209
- @prev_end[p] = @clock.init_ts + start_ts + (next_ev.end / cps)
200
+ enum.next
210
201
  end
211
202
  end
212
203
 
@@ -250,11 +241,8 @@ module Xi
250
241
  end
251
242
 
252
243
  def update_internal_structures
253
- @must_forward = true
254
- @enums = @source.map { |k, v|
255
- pat = v.p(@event_duration)
256
- [k, [enum_for(:loop_events, pat), pat.total_duration]]
257
- }.to_h
244
+ cycle = @clock.current_cycle
245
+ @enums = @source.map { |k, v| [k, v.p(@delta).each_event(cycle)] }.to_h
258
246
  end
259
247
 
260
248
  def do_gate_on_change(ss)
@@ -285,8 +273,12 @@ module Xi
285
273
  !@changed_params.empty?
286
274
  end
287
275
 
288
- def loop_events(pattern)
289
- loop { pattern.each_event { |e| yield e } }
276
+ def update_all_state
277
+ @enums.each do |p, enum|
278
+ n_value, _ = enum.peek
279
+ update_state(p, n_value)
280
+ end
281
+ @reset = false
290
282
  end
291
283
 
292
284
  def latency_sec
@@ -111,7 +111,8 @@ module Xi
111
111
  end
112
112
 
113
113
  def update_clock_from_server_data(h)
114
- @init_ts = h[:ts].to_f
114
+ # Do not set @init_ts for now
115
+ #@init_ts = h[:ts].to_f
115
116
  @cps = h[:cps]
116
117
  end
117
118
  end
data/lib/xi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Xi
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
data/lib/xi.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  require "xi/version"
2
2
  require 'xi/core_ext'
3
3
  require 'xi/pattern'
4
- require 'xi/event'
5
4
  require 'xi/stream'
6
5
  require 'xi/clock'
6
+ require 'xi/bjorklund'
7
+ require 'xi/step_sequencer'
7
8
 
8
9
  def inf
9
10
  Float::INFINITY
@@ -37,11 +38,19 @@ module Xi
37
38
  end
38
39
 
39
40
  def peek(pattern, *args)
41
+ pattern.peek_values(*args)
42
+ end
43
+
44
+ def peek_events(pattern, limit=10, *args)
40
45
  pattern.peek(*args)
41
46
  end
42
47
 
43
- def peek_events(pattern, *args)
44
- pattern.peek_events(*args)
48
+ def e(n, m, value=nil)
49
+ Bjorklund.new([n, m].min, [n, m].max, value)
50
+ end
51
+
52
+ def s(str, *values)
53
+ StepSequencer.new(str, *values)
45
54
  end
46
55
 
47
56
  def method_missing(method, backend=nil, **opts)
data/xi.gemspec CHANGED
@@ -30,5 +30,4 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "guard-minitest"
31
31
 
32
32
  spec.add_dependency 'pry'
33
- spec.add_dependency 'websocket'
34
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xi-lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damián Silvani
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-19 00:00:00.000000000 Z
11
+ date: 2017-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,20 +108,6 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: websocket
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
111
  description: |-
126
112
  A musical pattern language inspired in Tidal and SuperCollider
127
113
  for building higher-level musical constructs easily.
@@ -142,22 +128,25 @@ files:
142
128
  - Rakefile
143
129
  - bin/xi
144
130
  - lib/xi.rb
131
+ - lib/xi/bjorklund.rb
145
132
  - lib/xi/clock.rb
146
133
  - lib/xi/core_ext.rb
134
+ - lib/xi/core_ext/array.rb
147
135
  - lib/xi/core_ext/enumerable.rb
136
+ - lib/xi/core_ext/enumerator.rb
148
137
  - lib/xi/core_ext/fixnum.rb
149
138
  - lib/xi/core_ext/numeric.rb
150
139
  - lib/xi/core_ext/object.rb
151
- - lib/xi/core_ext/simple.rb
140
+ - lib/xi/core_ext/scalar.rb
152
141
  - lib/xi/core_ext/string.rb
153
142
  - lib/xi/error_log.rb
154
- - lib/xi/event.rb
155
143
  - lib/xi/logger.rb
156
144
  - lib/xi/pattern.rb
157
145
  - lib/xi/pattern/generators.rb
158
146
  - lib/xi/pattern/transforms.rb
159
147
  - lib/xi/repl.rb
160
148
  - lib/xi/scale.rb
149
+ - lib/xi/step_sequencer.rb
161
150
  - lib/xi/stream.rb
162
151
  - lib/xi/tidal_clock.rb
163
152
  - lib/xi/version.rb
@@ -1,15 +0,0 @@
1
- require 'xi/pattern'
2
-
3
- module Xi
4
- module Pattern::Simple
5
- def p(dur=nil, **metadata)
6
- [self].p(dur, metadata)
7
- end
8
- end
9
- end
10
-
11
- class Fixnum; include Xi::Pattern::Simple; end
12
- class Float; include Xi::Pattern::Simple; end
13
- class String; include Xi::Pattern::Simple; end
14
- class Symbol; include Xi::Pattern::Simple; end
15
- class Rational; include Xi::Pattern::Simple; end
data/lib/xi/event.rb DELETED
@@ -1,82 +0,0 @@
1
- module Xi
2
- # An Event is an object that represents a scalar +value+ of some type, and
3
- # has a +start+ position or onset, and +duration+ in time.
4
- #
5
- # Both +start+ and +duration+ are in terms of cycles.
6
- #
7
- # Usually you don't create events, they are created by a Pattern when
8
- # assigned to a Stream, or by some transformation methods on Pattern, so you
9
- # don't need to worry about them. Most of the time, you will manually build
10
- # Patterns from values and let the Pattern handle when values are applied in
11
- # time, based on its default event duration, for example.
12
- #
13
- # You can instantiate an Event using {.[]}, like this
14
- #
15
- # Event.new(42, 0, 2) #=> E[42,0,2]
16
- # Event[:a, 1, 1/2] #=> E[:a,1,1/2]
17
- #
18
- # E is an alias of Event, so you can build them using E instead. Note that
19
- # the string representation of the object can be used to build the same event
20
- # again (almost the same ignoring whitespace between constructor arguments).
21
- #
22
- # E[:a, 1, 1/4] #=> E[:a,1,1/4]
23
- #
24
- class Event
25
- attr_reader :value, :start, :duration
26
-
27
- # Creates a new Event with +value+, with both +start+ position and
28
- # +duration+ in cycles
29
- #
30
- # @param value [Object]
31
- # @param start [Numeric] default: 0
32
- # @param duration [Numeric] default: 1
33
- # @return [Event]
34
- #
35
- def initialize(value, start=0, duration=1)
36
- @value = value
37
- @start = start
38
- @duration = duration
39
- end
40
-
41
- # @see #initialize
42
- def self.[](*args)
43
- new(*args)
44
- end
45
-
46
- def ==(o)
47
- self.class == o.class &&
48
- value == o.value &&
49
- start == o.start &&
50
- duration == o.duration
51
- end
52
-
53
- # Return the end position in cycles
54
- #
55
- # @return [Numeric]
56
- #
57
- def end
58
- @start + @duration
59
- end
60
-
61
- # Creates a Pattern that only yields this event
62
- #
63
- # @param dur [Numeric, #each] event duration
64
- # @param metadata [Hash]
65
- # @return [Pattern]
66
- #
67
- def p(dur=nil, **metadata)
68
- [self].p(dur, metadata)
69
- end
70
-
71
- def inspect
72
- "E[#{@value.inspect},#{@start}" \
73
- "#{",#{@duration}" if @duration != 1}]"
74
- end
75
-
76
- def to_s
77
- inspect
78
- end
79
- end
80
- end
81
-
82
- E = Xi::Event