xi-lang 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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