xi-lang 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/lib/xi/clock.rb +2 -7
- data/lib/xi/core_ext/object.rb +5 -0
- data/lib/xi/core_ext.rb +1 -0
- data/lib/xi/logger.rb +25 -0
- data/lib/xi/music_parameters.rb +47 -0
- data/lib/xi/pattern/transforms.rb +34 -9
- data/lib/xi/repl.rb +6 -2
- data/lib/xi/scale.rb +97 -0
- data/lib/xi/stream.rb +47 -31
- data/lib/xi/version.rb +1 -1
- data/lib/xi.rb +58 -8
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65bceb35fd0dbad39331e9c89bea0a83254fe075
|
4
|
+
data.tar.gz: a20c3dd41afe001b698aab57d8cd3577e66d6dcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e92c7bf49fa2bba86bf0eb153d7c0d1fd363a21060d52faf3a472655ffce291c8e57563edc303ae1ef65c591dc6b6db746d2f64592e469e927ba56a35ce6cd31
|
7
|
+
data.tar.gz: 3ab57ef7a85d4cac9a9e260aba0b030972b80105e320036feefc3ef3a418b22aec361180685cdda28ef92ab72ee39639ca7786595c030000b96f76d4b04c5be5
|
data/lib/xi/clock.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require 'logger'
|
3
2
|
require 'set'
|
4
3
|
|
5
4
|
Thread.abort_on_exception = true
|
@@ -7,7 +6,7 @@ Thread.abort_on_exception = true
|
|
7
6
|
module Xi
|
8
7
|
class Clock
|
9
8
|
DEFAULT_CPS = 1.0
|
10
|
-
INTERVAL_SEC =
|
9
|
+
INTERVAL_SEC = 10 / 1000.0
|
11
10
|
|
12
11
|
def initialize(cps: DEFAULT_CPS)
|
13
12
|
@mutex = Mutex.new
|
@@ -83,11 +82,7 @@ module Xi
|
|
83
82
|
return unless playing?
|
84
83
|
@streams.each { |s| s.notify(cycles) }
|
85
84
|
rescue => err
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
def logger
|
90
|
-
@logger ||= Logger.new(STDOUT)
|
85
|
+
error(err)
|
91
86
|
end
|
92
87
|
end
|
93
88
|
end
|
data/lib/xi/core_ext.rb
CHANGED
data/lib/xi/logger.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Xi::Logger
|
5
|
+
LOG_FILE = File.join(Dir.tmpdir, 'xi.log')
|
6
|
+
|
7
|
+
def logger
|
8
|
+
@@logger ||= begin
|
9
|
+
logger = ::Logger.new(LOG_FILE)
|
10
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
11
|
+
"[#{datetime.strftime("%F %T %L")}] #{msg}\n"
|
12
|
+
end
|
13
|
+
logger
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def debug(*args)
|
18
|
+
logger.debug(args.map(&:to_s).join(' '.freeze))
|
19
|
+
end
|
20
|
+
|
21
|
+
def error(error)
|
22
|
+
logger.error("#{error}:\n#{error.backtrace.join("\n".freeze)}")
|
23
|
+
ErrorLog.instance << error.to_s
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'xi/scale'
|
2
|
+
|
3
|
+
class Xi::Stream
|
4
|
+
module MusicParameters
|
5
|
+
DEFAULT = {
|
6
|
+
degree: 0,
|
7
|
+
octave: 5,
|
8
|
+
root: 0,
|
9
|
+
scale: Xi::Scale.major,
|
10
|
+
steps_per_octave: 12,
|
11
|
+
}
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def transform_state
|
16
|
+
super
|
17
|
+
|
18
|
+
@state = DEFAULT.merge(@state)
|
19
|
+
|
20
|
+
if !changed_param?(:note) && changed_param?(:degree, :scale, :steps_per_octave)
|
21
|
+
@state[:note] = reduce_to_note
|
22
|
+
@changed_params << :note
|
23
|
+
end
|
24
|
+
|
25
|
+
if !changed_param?(:midinote) && changed_param?(:note)
|
26
|
+
@state[:midinote] = reduce_to_midinote
|
27
|
+
@changed_params << :midinote
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def reduce_to_midinote
|
32
|
+
Array(@state[:note]).compact.map { |n|
|
33
|
+
@state[:root].to_i + @state[:octave].to_i * @state[:steps_per_octave] + n
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def reduce_to_note
|
38
|
+
Array(@state[:degree]).compact.map do |d|
|
39
|
+
d.degree_to_key(Array(@state[:scale]), @state[:steps_per_octave])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def changed_param?(*params)
|
44
|
+
@changed_params.any? { |p| params.include?(p) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -210,18 +210,33 @@ module Xi
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
213
|
-
# Traverses the pattern in order and then in reverse order
|
213
|
+
# Traverses the pattern in order and then in reverse order, skipping
|
214
|
+
# first and last values if +skip_extremes+ is true.
|
214
215
|
#
|
215
216
|
# @example
|
216
|
-
# peek (0..3).p.bounce #=> [0, 1, 2, 3,
|
217
|
+
# peek (0..3).p.bounce #=> [0, 1, 2, 3, 2, 1]
|
218
|
+
# peek 10.p.bounce #=> [10]
|
217
219
|
#
|
220
|
+
# @example with skip_extremes=false
|
221
|
+
# peek (0..3).p.bounce(false) #=> [0, 1, 2, 3, 3, 2, 1, 0]
|
222
|
+
#
|
223
|
+
# @param skip_extremes [Boolean] Skip first and last values
|
224
|
+
# to avoid repeated values (default: true)
|
218
225
|
# @return [Pattern]
|
219
226
|
#
|
220
|
-
def bounce
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
227
|
+
def bounce(skip_extremes=true)
|
228
|
+
return self if size == 0 || size == 1
|
229
|
+
|
230
|
+
Pattern.new(self, size: size * 2 - 1) { |y|
|
231
|
+
last_id = 0
|
232
|
+
each.with_index { |v, i|
|
233
|
+
y << v
|
234
|
+
last_id = i
|
235
|
+
}
|
236
|
+
reverse_each.with_index { |v, i|
|
237
|
+
y << v unless skip_extremes && (i == 0 || i == last_id)
|
238
|
+
}
|
239
|
+
}
|
225
240
|
end
|
226
241
|
|
227
242
|
# Normalizes a pattern of values that range from +min+ to +max+ to 0..1
|
@@ -306,7 +321,12 @@ module Xi
|
|
306
321
|
#
|
307
322
|
def sometimes(probability=0.5)
|
308
323
|
prob_pat = probability.p
|
309
|
-
|
324
|
+
|
325
|
+
if times_pat.infinite?
|
326
|
+
fail ArgumentError, 'times must be a finite pattern'
|
327
|
+
end
|
328
|
+
|
329
|
+
Pattern.new(self, size: size * prob_pat.reduce(:+)) do |y|
|
310
330
|
prob_pat.each do |prob|
|
311
331
|
each { |v| y << (rand < prob ? v : nil) }
|
312
332
|
end
|
@@ -318,7 +338,12 @@ module Xi
|
|
318
338
|
#
|
319
339
|
def repeat_each(times)
|
320
340
|
times_pat = times.p
|
321
|
-
|
341
|
+
|
342
|
+
if times_pat.infinite?
|
343
|
+
fail ArgumentError, 'times must be a finite pattern'
|
344
|
+
end
|
345
|
+
|
346
|
+
Pattern.new(self, size: size * times_pat.reduce(:+)) do |y|
|
322
347
|
times_pat.each do |t|
|
323
348
|
each { |v| t.times { y << v } }
|
324
349
|
end
|
data/lib/xi/repl.rb
CHANGED
@@ -37,12 +37,16 @@ module Xi
|
|
37
37
|
Pry.hooks.add_hook(:after_eval, "check_for_errors") do |result, pry|
|
38
38
|
more_errors = ErrorLog.instance.more_errors?
|
39
39
|
ErrorLog.instance.each do |msg|
|
40
|
-
puts "
|
40
|
+
puts red("Error: #{msg}")
|
41
41
|
end
|
42
|
-
puts "
|
42
|
+
puts red("There were more errors...") if more_errors
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
+
def red(string)
|
47
|
+
"\e[31m\e[1m#{string}\e[22m\e[0m"
|
48
|
+
end
|
49
|
+
|
46
50
|
def load_init_script
|
47
51
|
require(init_script_path)
|
48
52
|
end
|
data/lib/xi/scale.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
class Xi::Scale
|
2
|
+
DEGREES = {
|
3
|
+
# TWELVE TONES PER OCTAVE
|
4
|
+
# 5 note scales
|
5
|
+
minorPentatonic: [0,3,5,7,10],
|
6
|
+
majorPentatonic: [0,2,4,7,9],
|
7
|
+
# another mode of major pentatonic
|
8
|
+
ritusen: [0,2,5,7,9],
|
9
|
+
# another mode of major pentatonic
|
10
|
+
egyptian: [0,2,5,7,10],
|
11
|
+
|
12
|
+
kumoi: [0,2,3,7,9],
|
13
|
+
hirajoshi: [0,2,3,7,8],
|
14
|
+
|
15
|
+
iwato: [0,1,5,6,10], # mode of hirajoshi
|
16
|
+
chinese: [0,4,6,7,11], # mode of hirajoshi
|
17
|
+
indian: [0,4,5,7,10],
|
18
|
+
pelog: [0,1,3,7,8],
|
19
|
+
|
20
|
+
prometheus: [0,2,4,6,11],
|
21
|
+
scriabin: [0,1,4,7,9],
|
22
|
+
|
23
|
+
# han chinese pentatonic scales
|
24
|
+
gong: [0,2,4,7,9],
|
25
|
+
shang: [0,2,5,7,10],
|
26
|
+
jiao: [0,3,5,8,10],
|
27
|
+
zhi: [0,2,5,7,9],
|
28
|
+
yu: [0,3,5,7,10],
|
29
|
+
|
30
|
+
# 6 note scales
|
31
|
+
whole: [0, 2, 4, 6, 8, 10],
|
32
|
+
augmented: [0,3,4,7,8,11],
|
33
|
+
augmented2: [0,1,4,5,8,9],
|
34
|
+
|
35
|
+
# hexatonic modes with no tritone
|
36
|
+
hexMajor7: [0,2,4,7,9,11],
|
37
|
+
hexDorian: [0,2,3,5,7,10],
|
38
|
+
hexPhrygian: [0,1,3,5,8,10],
|
39
|
+
hexSus: [0,2,5,7,9,10],
|
40
|
+
hexMajor6: [0,2,4,5,7,9],
|
41
|
+
hexAeolian: [0,3,5,7,8,10],
|
42
|
+
|
43
|
+
# 7 note scales
|
44
|
+
major: [0,2,4,5,7,9,11],
|
45
|
+
ionian: [0,2,4,5,7,9,11],
|
46
|
+
dorian: [0,2,3,5,7,9,10],
|
47
|
+
phrygian: [0,1,3,5,7,8,10],
|
48
|
+
lydian: [0,2,4,6,7,9,11],
|
49
|
+
mixolydian: [0,2,4,5,7,9,10],
|
50
|
+
aeolian: [0,2,3,5,7,8,10],
|
51
|
+
minor: [0,2,3,5,7,8,10],
|
52
|
+
locrian: [0,1,3,5,6,8,10],
|
53
|
+
|
54
|
+
harmonicMinor: [0,2,3,5,7,8,11],
|
55
|
+
harmonicMajor: [0,2,4,5,7,8,11],
|
56
|
+
|
57
|
+
melodicMinor: [0,2,3,5,7,9,11],
|
58
|
+
melodicMinorDesc: [0,2,3,5,7,8,10],
|
59
|
+
melodicMajor: [0,2,4,5,7,8,10],
|
60
|
+
|
61
|
+
bartok: [0,2,4,5,7,8,10],
|
62
|
+
hindu: [0,2,4,5,7,8,10],
|
63
|
+
|
64
|
+
# raga modes
|
65
|
+
todi: [0,1,3,6,7,8,11],
|
66
|
+
purvi: [0,1,4,6,7,8,11],
|
67
|
+
marva: [0,1,4,6,7,9,11],
|
68
|
+
bhairav: [0,1,4,5,7,8,11],
|
69
|
+
ahirbhairav: [0,1,4,5,7,9,10],
|
70
|
+
|
71
|
+
superLocrian: [0,1,3,4,6,8,10],
|
72
|
+
romanianMinor: [0,2,3,6,7,9,10],
|
73
|
+
hungarianMinor: [0,2,3,6,7,8,11],
|
74
|
+
neapolitanMinor: [0,1,3,5,7,8,11],
|
75
|
+
enigmatic: [0,1,4,6,8,10,11],
|
76
|
+
spanish: [0,1,4,5,7,8,10],
|
77
|
+
|
78
|
+
# modes of whole tones with added note ->
|
79
|
+
leadingWhole: [0,2,4,6,8,10,11],
|
80
|
+
lydianMinor: [0,2,4,6,7,8,10],
|
81
|
+
neapolitanMajor: [0,1,3,5,7,9,11],
|
82
|
+
locrianMajor: [0,2,4,5,6,8,10],
|
83
|
+
|
84
|
+
# 8 note scales
|
85
|
+
diminished: [0,1,3,4,6,7,9,10],
|
86
|
+
diminished2: [0,2,3,5,6,8,9,11],
|
87
|
+
|
88
|
+
# 12 note scales
|
89
|
+
chromatic: (0..11).to_a,
|
90
|
+
}
|
91
|
+
|
92
|
+
class << self
|
93
|
+
DEGREES.each do |name, list|
|
94
|
+
define_method(name) { list }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/xi/stream.rb
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'xi/music_parameters'
|
2
3
|
|
3
4
|
module Xi
|
4
5
|
class Stream
|
5
|
-
|
6
|
+
prepend MusicParameters
|
7
|
+
|
8
|
+
attr_reader :clock, :opts, :source, :state, :event_duration, :gate
|
9
|
+
|
10
|
+
def initialize(name, clock, **opts)
|
11
|
+
@name = name.to_sym
|
12
|
+
@opts = opts
|
6
13
|
|
7
|
-
def initialize(clock)
|
8
14
|
@mutex = Mutex.new
|
9
15
|
@playing = false
|
10
16
|
@last_sound_object_id = 0
|
@@ -18,6 +24,7 @@ module Xi
|
|
18
24
|
|
19
25
|
def set(event_duration: nil, gate: nil, **source)
|
20
26
|
@mutex.synchronize do
|
27
|
+
source[:s] ||= @name
|
21
28
|
@source = source
|
22
29
|
@gate = gate if gate
|
23
30
|
@event_duration = event_duration if event_duration
|
@@ -26,7 +33,7 @@ module Xi
|
|
26
33
|
play
|
27
34
|
self
|
28
35
|
end
|
29
|
-
alias_method
|
36
|
+
alias_method :call, :set
|
30
37
|
|
31
38
|
def event_duration=(new_value)
|
32
39
|
@mutex.synchronize do
|
@@ -76,10 +83,11 @@ module Xi
|
|
76
83
|
alias_method :pause, :play
|
77
84
|
|
78
85
|
def inspect
|
79
|
-
"#<#{self.class.name}:#{
|
80
|
-
"
|
86
|
+
"#<#{self.class.name} :#{@name} " \
|
87
|
+
"#{playing? ? :playing : :stopped} at #{@clock.cps}cps" \
|
88
|
+
"#{" #{@opts}" if @opts.any?}>"
|
81
89
|
rescue => err
|
82
|
-
|
90
|
+
error(err)
|
83
91
|
end
|
84
92
|
|
85
93
|
def notify(now)
|
@@ -89,9 +97,10 @@ module Xi
|
|
89
97
|
@changed_params.clear
|
90
98
|
|
91
99
|
forward_enums(now) if @must_forward
|
100
|
+
gate_off = gate_off_old_sound_objects(now)
|
101
|
+
gate_on = play_enums(now)
|
92
102
|
|
93
|
-
|
94
|
-
|
103
|
+
# Call hooks
|
95
104
|
do_gate_off_change(gate_off) unless gate_off.empty?
|
96
105
|
do_state_change if state_changed?
|
97
106
|
do_gate_on_change(gate_on) unless gate_on.empty?
|
@@ -106,6 +115,8 @@ module Xi
|
|
106
115
|
|
107
116
|
def forward_enums(now)
|
108
117
|
@enums.each do |p, (enum, total_dur)|
|
118
|
+
next if total_dur == 0
|
119
|
+
|
109
120
|
cur_pos = now % total_dur
|
110
121
|
start_pos = now - cur_pos
|
111
122
|
|
@@ -123,34 +134,40 @@ module Xi
|
|
123
134
|
@must_forward = false
|
124
135
|
end
|
125
136
|
|
126
|
-
def
|
137
|
+
def gate_off_old_sound_objects(now)
|
127
138
|
gate_off = []
|
139
|
+
|
140
|
+
# Check if there are any currently playing sound objects that
|
141
|
+
# must be gated off
|
142
|
+
@playing_sound_objects.dup.each do |end_pos, h|
|
143
|
+
if now >= h[:at] - latency_sec
|
144
|
+
gate_off << h
|
145
|
+
@playing_sound_objects.delete(end_pos)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
gate_off
|
150
|
+
end
|
151
|
+
|
152
|
+
def play_enums(now)
|
128
153
|
gate_on = []
|
129
154
|
|
130
155
|
@enums.each do |p, (enum, total_dur)|
|
156
|
+
next if total_dur == 0
|
157
|
+
|
131
158
|
cur_pos = now % total_dur
|
132
159
|
start_pos = now - cur_pos
|
133
160
|
|
134
|
-
# Check if there are any currently playing sound objects that
|
135
|
-
# must be gated off
|
136
|
-
@playing_sound_objects.dup.each do |end_pos, h|
|
137
|
-
if now >= h[:at] - latency_sec
|
138
|
-
gate_off << h
|
139
|
-
@playing_sound_objects.delete(end_pos)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
161
|
next_ev = enum.peek
|
144
162
|
|
145
163
|
# Do we need to play next event now? If not, skip this parameter
|
146
164
|
if (@prev_end[p].nil? || now >= @prev_end[p]) && cur_pos >= next_ev.start - latency_sec
|
147
|
-
#logger.info "cur_pos=#{cur_pos} >= next_ev.start=#{next_ev.start}"
|
148
165
|
# Update state based on pattern value
|
149
166
|
# TODO: Pass as parameter exact time (start_ts + next_ev.start)
|
150
167
|
update_state(p, next_ev.value)
|
168
|
+
transform_state
|
151
169
|
|
152
|
-
# If
|
153
|
-
# a new sound object
|
170
|
+
# If a gate parameter changed, create a new sound object
|
154
171
|
if p == @gate
|
155
172
|
new_so_ids = Array(next_ev.value)
|
156
173
|
.size.times.map { new_sound_object_id }
|
@@ -173,7 +190,11 @@ module Xi
|
|
173
190
|
end
|
174
191
|
end
|
175
192
|
|
176
|
-
|
193
|
+
gate_on
|
194
|
+
end
|
195
|
+
|
196
|
+
# @override
|
197
|
+
def transform_state
|
177
198
|
end
|
178
199
|
|
179
200
|
def new_sound_object_id
|
@@ -189,21 +210,21 @@ module Xi
|
|
189
210
|
end
|
190
211
|
|
191
212
|
def do_gate_on_change(ss)
|
192
|
-
|
213
|
+
debug "Gate on change: #{ss}"
|
193
214
|
end
|
194
215
|
|
195
216
|
def do_gate_off_change(ss)
|
196
|
-
|
217
|
+
debug "Gate off change: #{ss}"
|
197
218
|
end
|
198
219
|
|
199
220
|
def do_state_change
|
200
|
-
|
221
|
+
debug "State change: #{@state
|
201
222
|
.select { |k, v| @changed_params.include?(k) }.to_h}"
|
202
223
|
end
|
203
224
|
|
204
225
|
def update_state(p, v)
|
205
226
|
if v != @state[p]
|
206
|
-
|
227
|
+
debug "Update state of :#{p}: #{v}"
|
207
228
|
@changed_params << p
|
208
229
|
@state[p] = v
|
209
230
|
end
|
@@ -220,10 +241,5 @@ module Xi
|
|
220
241
|
def latency_sec
|
221
242
|
0.05
|
222
243
|
end
|
223
|
-
|
224
|
-
def logger
|
225
|
-
# FIXME this should be configurable
|
226
|
-
@logger ||= Logger.new("/tmp/xi.log")
|
227
|
-
end
|
228
244
|
end
|
229
245
|
end
|
data/lib/xi/version.rb
CHANGED
data/lib/xi.rb
CHANGED
@@ -9,18 +9,68 @@ def inf
|
|
9
9
|
Float::INFINITY
|
10
10
|
end
|
11
11
|
|
12
|
-
module Xi
|
13
|
-
def
|
14
|
-
@
|
12
|
+
module Xi
|
13
|
+
def self.default_backend
|
14
|
+
@default_backend
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
17
|
+
def self.default_backend=(new_name)
|
18
|
+
@default_backend = new_name && new_name.to_sym
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
pattern
|
21
|
+
module Init
|
22
|
+
def peek(pattern, *args)
|
23
|
+
pattern.peek(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def peek_events(pattern, *args)
|
27
|
+
pattern.peek_events(*args)
|
28
|
+
end
|
29
|
+
|
30
|
+
def clock
|
31
|
+
@default_clock ||= Clock.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop_all
|
35
|
+
@streams.each do |backend, ss|
|
36
|
+
ss.each do |name, stream|
|
37
|
+
stream.stop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
alias_method :hush, :stop_all
|
42
|
+
|
43
|
+
def method_missing(method, backend=nil, **opts)
|
44
|
+
backend ||= Xi.default_backend
|
45
|
+
super if backend.nil?
|
46
|
+
|
47
|
+
if !backend.is_a?(String) && !backend.is_a?(Symbol)
|
48
|
+
fail ArgumentError, "invalid backend '#{backend}'"
|
49
|
+
end
|
50
|
+
|
51
|
+
@streams ||= {}
|
52
|
+
@streams[backend] ||= {}
|
53
|
+
|
54
|
+
s = @streams[backend][method] ||= begin
|
55
|
+
require "xi/#{backend}"
|
56
|
+
cls = Class.const_get("#{backend.to_s.capitalize}::Stream")
|
57
|
+
cls.new(method, self.clock, **opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
b = Pry.binding_for(self)
|
61
|
+
b.local_variable_set(method, s)
|
62
|
+
|
63
|
+
s
|
64
|
+
end
|
23
65
|
end
|
24
66
|
end
|
25
67
|
|
26
|
-
|
68
|
+
singleton_class.include Xi::Init
|
69
|
+
|
70
|
+
# Try to load Supercollider backend and set it as default if installed
|
71
|
+
begin
|
72
|
+
require "xi/supercollider"
|
73
|
+
Xi.default_backend = :supercollider
|
74
|
+
rescue LoadError
|
75
|
+
Xi.default_backend = nil
|
76
|
+
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
|
+
version: 0.1.3
|
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-
|
11
|
+
date: 2017-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -133,13 +133,17 @@ files:
|
|
133
133
|
- lib/xi/core_ext/enumerable.rb
|
134
134
|
- lib/xi/core_ext/fixnum.rb
|
135
135
|
- lib/xi/core_ext/numeric.rb
|
136
|
+
- lib/xi/core_ext/object.rb
|
136
137
|
- lib/xi/core_ext/simple.rb
|
137
138
|
- lib/xi/error_log.rb
|
138
139
|
- lib/xi/event.rb
|
140
|
+
- lib/xi/logger.rb
|
141
|
+
- lib/xi/music_parameters.rb
|
139
142
|
- lib/xi/pattern.rb
|
140
143
|
- lib/xi/pattern/generators.rb
|
141
144
|
- lib/xi/pattern/transforms.rb
|
142
145
|
- lib/xi/repl.rb
|
146
|
+
- lib/xi/scale.rb
|
143
147
|
- lib/xi/stream.rb
|
144
148
|
- lib/xi/version.rb
|
145
149
|
- xi.gemspec
|