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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38043c0ad025b398a49d63e1c93681992c0b184a
4
- data.tar.gz: fbc0e32315abf8272f388bbe59917236633a0d09
3
+ metadata.gz: bd3c1d2086dab411b68d702c8ca72ba9ba0875dc
4
+ data.tar.gz: 4a3ef7494c570a1990ae49a97a3f6d29371398cd
5
5
  SHA512:
6
- metadata.gz: 10f089d5ecb56a89e70091b2845654fdbdf7beb9c7b3fc23366038ae078769065166b3b89fd08de19fca0ec99612bce60853f16554aacb2d269036e895df4a29
7
- data.tar.gz: e4fa8eef3158cbf1d14d8f9b8d4241dfe83de117a1ef6730c947992baf555a9c110f425e9d7828eaafdf9201cf71f52d9c2cd2737100e85c198397ce22048a68
6
+ metadata.gz: 102013a3fb01d7764e83f9d5fa461ab430ef27153502e4f4b4bee5a1528001ef15660c0600ab1681f435defd615fdd7cd307b14958a7df0d7d2752ba7e5d3774
7
+ data.tar.gz: d94f330514d532fe1aa4de6d627ad7c8accf689f9e173fbdd4ba638d433f07c2a83db7edc081653c798a5ddfa41144e550313be4084ccc676f4a650dd3c5b39e
data/README.md CHANGED
@@ -15,16 +15,25 @@ multiple known bugs, missing features, documentation and tests.
15
15
  ## Example
16
16
 
17
17
  ```ruby
18
- k = MIDI::Stream.new
19
-
20
- k.set degree: [0, 3, 5, 7],
21
- octave: [1, 2],
22
- scale: [Scale.egyptian],
23
- voice: (0..6).p.scale(0, 6, 0, 127),
24
- vel: [30, 25, 20].p + 20,
25
- cutoff: P.sin1(32, 2) * 128,
26
- delay_feedback: 1/2 * 128,
27
- delay_time: [0, 0x7f].p(1/16)
18
+ melody = [1,4,7,8,9]
19
+ scale = [Scale.iwato, Scale.jiao]
20
+
21
+ fm.set degree: melody.p(1/2,1/8,1/8,1/8,1/16,1/8,1/8).seq(2),
22
+ gate: :degree,
23
+ detune: [0.1, 0.4, 0.4],
24
+ sustain: [1,2,3,10],
25
+ accelerate: [0.5, 0, 0.1],
26
+ octave: [3,4,5,6].p(1/3),
27
+ scale: scale.p(1/2),
28
+ amp: P.new { |y| y << rand * 0.2 + 0.8 }.p(1/2)
29
+
30
+ kick.set freq: s("xi.x .ix. | xi.x xx.x", 70, 200), amp: 0.8, gate: :freq
31
+
32
+ clap.set n: s("..x. xyz. .x.. .xyx", 60, 61, 60).p.decelerate(2),
33
+ gate: :n,
34
+ amp: 0.35,
35
+ pan: P.sin(16, 2) * 0.6,
36
+ sustain: 0.25
28
37
  ```
29
38
 
30
39
  ## Installation
@@ -64,8 +73,9 @@ Then:
64
73
 
65
74
  $ cd xi
66
75
  $ bundle install
76
+ $ rake install
67
77
 
68
- You're done! Fire up the REPL from `bin/xi`.
78
+ You're done! Fire up the REPL using `xi`.
69
79
 
70
80
  ## Development
71
81
 
@@ -78,6 +88,9 @@ release a new version, update the version number in `version.rb`, and then run
78
88
  git commits and tags, and push the `.gem` file to
79
89
  [rubygems.org](https://rubygems.org).
80
90
 
91
+ You can also try things on the REPL without installing the gem on your machine
92
+ by doing `bundle exec bin/xi`.
93
+
81
94
  ## Contributing
82
95
 
83
96
  Bug reports and pull requests are welcome on GitHub at
data/bin/xi CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require 'bundler/setup'
3
2
  require "xi"
4
3
  require "xi/repl"
5
4
 
@@ -0,0 +1,60 @@
1
+ # Implementation adapted from Nebs' (MIT licensed)
2
+ # https://github.com/nebs/bjorklund-euclidean-rhythms
3
+ #
4
+ class Xi::Bjorklund
5
+ attr_reader :pulses, :slots, :value
6
+
7
+ def initialize(pulses, slots, value=nil)
8
+ @pulses = pulses.to_i
9
+ @slots = slots.to_i
10
+ @value = value || 1
11
+ end
12
+
13
+ def p(*args, **metadata)
14
+ ary = to_a
15
+ ary.map { |v| v ? @value : nil }.p(1 / ary.size, **metadata)
16
+ end
17
+
18
+ def inspect
19
+ "e(#{@pulses}, #{@slots}, #{@value.inspect})"
20
+ end
21
+
22
+ def to_s
23
+ to_a.map { |i| i ? 'x' : '.' }.join
24
+ end
25
+
26
+ def to_a
27
+ k = @pulses
28
+ n = @slots
29
+
30
+ return [] if n == 0 || k == 0
31
+
32
+ bins = []
33
+ remainders = []
34
+ k.times { |i| bins[i] = [true] }
35
+ (n-k).times { |i| remainders[i] = [false] }
36
+
37
+ return bins.flatten if n == k
38
+
39
+ loop do
40
+ new_remainders = []
41
+ bins.each_with_index do |bin, i|
42
+ if remainders.empty?
43
+ new_remainders.push bin
44
+ else
45
+ bin += remainders.shift
46
+ bins[i] = bin
47
+ end
48
+ end
49
+
50
+ if new_remainders.any?
51
+ bins.pop new_remainders.count
52
+ remainders = new_remainders
53
+ end
54
+
55
+ break unless remainders.size > 1
56
+ end
57
+
58
+ return (bins + remainders).flatten
59
+ end
60
+ end
data/lib/xi/clock.rb CHANGED
@@ -64,6 +64,14 @@ module Xi
64
64
  @mutex.synchronize { 1.0 / @cps }
65
65
  end
66
66
 
67
+ def current_time
68
+ Time.now.to_f - @init_ts + @latency
69
+ end
70
+
71
+ def current_cycle
72
+ current_time * cps
73
+ end
74
+
67
75
  def inspect
68
76
  "#<#{self.class.name}:#{"0x%014x" % object_id} " \
69
77
  "cps=#{cps.inspect} #{playing? ? :playing : :stopped}>"
@@ -80,7 +88,7 @@ module Xi
80
88
 
81
89
  def do_tick
82
90
  return unless playing?
83
- now = Time.now.to_f - @init_ts + @latency
91
+ now = self.current_time
84
92
  cps = self.cps
85
93
  @streams.each { |s| s.notify(now, cps) }
86
94
  rescue => err
@@ -0,0 +1,13 @@
1
+ require 'xi/pattern'
2
+
3
+ module Xi::CoreExt
4
+ module Array
5
+ def p(*delta, **metadata)
6
+ Xi::Pattern.new(self, delta: delta, **metadata)
7
+ end
8
+ end
9
+ end
10
+
11
+ class Array
12
+ include Xi::CoreExt::Array
13
+ end
@@ -1,25 +1,17 @@
1
1
  require 'xi/pattern'
2
2
 
3
- module Xi
4
- module Pattern::Enumerable
5
- def p(dur=nil, **metadata)
6
- Pattern.new(self, event_duration: dur, **metadata)
3
+ module Xi::CoreExt
4
+ module Enumerable
5
+ def p(*delta, **metadata)
6
+ Xi::Pattern.new(self.to_a, delta: delta, **metadata)
7
7
  end
8
8
  end
9
9
  end
10
10
 
11
11
  class Enumerator
12
- include Xi::Pattern::Enumerable
13
- end
14
-
15
- class Array
16
- include Xi::Pattern::Enumerable
17
- end
18
-
19
- class Hash
20
- include Xi::Pattern::Enumerable
12
+ include Xi::CoreExt::Enumerable
21
13
  end
22
14
 
23
15
  class Range
24
- include Xi::Pattern::Enumerable
16
+ include Xi::CoreExt::Enumerable
25
17
  end
@@ -0,0 +1,14 @@
1
+ module Xi::CoreExt
2
+ module Enumerator
3
+ def next?
4
+ peek
5
+ true
6
+ rescue StopIteration
7
+ false
8
+ end
9
+ end
10
+ end
11
+
12
+ class Enumerator
13
+ include Xi::CoreExt::Enumerator
14
+ end
@@ -1,5 +1,5 @@
1
- module Xi
2
- module CoerceToRational
1
+ module Xi::CoreExt
2
+ module Fixnum
3
3
  def /(o)
4
4
  super(o.to_r)
5
5
  end
@@ -7,5 +7,5 @@ module Xi
7
7
  end
8
8
 
9
9
  class Fixnum
10
- prepend Xi::CoerceToRational
10
+ prepend Xi::CoreExt::Fixnum
11
11
  end
@@ -1,4 +1,4 @@
1
- module Xi
1
+ module Xi::CoreExt
2
2
  module Numeric
3
3
  def midi_to_cps
4
4
  440 * (2 ** ((self - 69) / 12.0))
@@ -22,13 +22,13 @@ module Xi
22
22
  end
23
23
 
24
24
  class Fixnum
25
- include Xi::Numeric
25
+ include Xi::CoreExt::Numeric
26
26
  end
27
27
 
28
28
  class Float
29
- include Xi::Numeric
29
+ include Xi::CoreExt::Numeric
30
30
  end
31
31
 
32
32
  class Rational
33
- include Xi::Numeric
33
+ include Xi::CoreExt::Numeric
34
34
  end
@@ -0,0 +1,16 @@
1
+ require 'xi/pattern'
2
+
3
+ module Xi::CoreExt
4
+ module Scalar
5
+ def p(*delta, **metadata)
6
+ [self].p(*delta, **metadata)
7
+ end
8
+ end
9
+ end
10
+
11
+ class Fixnum; include Xi::CoreExt::Scalar; end
12
+ class Float; include Xi::CoreExt::Scalar; end
13
+ class String; include Xi::CoreExt::Scalar; end
14
+ class Symbol; include Xi::CoreExt::Scalar; end
15
+ class Rational; include Xi::CoreExt::Scalar; end
16
+ class Hash; include Xi::CoreExt::Scalar; end
@@ -1,50 +1,52 @@
1
- # String Inflectors, taken from ActiveSupport 5.0 source code
2
- module Xi::Inflectors
3
- # Converts strings to UpperCamelCase.
4
- # If the +uppercase_first_letter+ parameter is set to false, then produces
5
- # lowerCamelCase.
6
- #
7
- # Also converts '/' to '::' which is useful for converting
8
- # paths to namespaces.
9
- #
10
- # camelize('active_model') # => "ActiveModel"
11
- # camelize('active_model', false) # => "activeModel"
12
- # camelize('active_model/errors') # => "ActiveModel::Errors"
13
- # camelize('active_model/errors', false) # => "activeModel::Errors"
14
- #
15
- # As a rule of thumb you can think of +camelize+ as the inverse of
16
- # #underscore, though there are cases where that does not hold:
17
- #
18
- # camelize(underscore('SSLError')) # => "SslError"
19
- def camelize
20
- string = self.sub(/^[a-z\d]*/) { |match| match.capitalize }
21
- string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
22
- string.gsub!('/'.freeze, '::'.freeze)
23
- string
24
- end
1
+ module Xi::CoreExt
2
+ # String Inflectors, taken from ActiveSupport 5.0 source code
3
+ module String
4
+ # Converts strings to UpperCamelCase.
5
+ # If the +uppercase_first_letter+ parameter is set to false, then produces
6
+ # lowerCamelCase.
7
+ #
8
+ # Also converts '/' to '::' which is useful for converting
9
+ # paths to namespaces.
10
+ #
11
+ # camelize('active_model') # => "ActiveModel"
12
+ # camelize('active_model', false) # => "activeModel"
13
+ # camelize('active_model/errors') # => "ActiveModel::Errors"
14
+ # camelize('active_model/errors', false) # => "activeModel::Errors"
15
+ #
16
+ # As a rule of thumb you can think of +camelize+ as the inverse of
17
+ # #underscore, though there are cases where that does not hold:
18
+ #
19
+ # camelize(underscore('SSLError')) # => "SslError"
20
+ def camelize
21
+ string = self.sub(/^[a-z\d]*/) { |match| match.capitalize }
22
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
23
+ string.gsub!('/'.freeze, '::'.freeze)
24
+ string
25
+ end
25
26
 
26
- # Makes an underscored, lowercase form from the expression in the string.
27
- #
28
- # Changes '::' to '/' to convert namespaces to paths.
29
- #
30
- # underscore('ActiveModel') # => "active_model"
31
- # underscore('ActiveModel::Errors') # => "active_model/errors"
32
- #
33
- # As a rule of thumb you can think of +underscore+ as the inverse of
34
- # #camelize, though there are cases where that does not hold:
35
- #
36
- # camelize(underscore('SSLError')) # => "SslError"
37
- def underscore
38
- return self unless self =~ /[A-Z-]|::/
39
- word = self.to_s.gsub('::'.freeze, '/'.freeze)
40
- word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
41
- word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
42
- word.tr!("-".freeze, "_".freeze)
43
- word.downcase!
44
- word
27
+ # Makes an underscored, lowercase form from the expression in the string.
28
+ #
29
+ # Changes '::' to '/' to convert namespaces to paths.
30
+ #
31
+ # underscore('ActiveModel') # => "active_model"
32
+ # underscore('ActiveModel::Errors') # => "active_model/errors"
33
+ #
34
+ # As a rule of thumb you can think of +underscore+ as the inverse of
35
+ # #camelize, though there are cases where that does not hold:
36
+ #
37
+ # camelize(underscore('SSLError')) # => "SslError"
38
+ def underscore
39
+ return self unless self =~ /[A-Z-]|::/
40
+ word = self.to_s.gsub('::'.freeze, '/'.freeze)
41
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
42
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
43
+ word.tr!("-".freeze, "_".freeze)
44
+ word.downcase!
45
+ word
46
+ end
45
47
  end
46
48
  end
47
49
 
48
50
  class String
49
- include Xi::Inflectors
51
+ include Xi::CoreExt::String
50
52
  end
data/lib/xi/core_ext.rb CHANGED
@@ -1,6 +1,8 @@
1
+ require 'xi/core_ext/array'
1
2
  require 'xi/core_ext/enumerable'
3
+ require 'xi/core_ext/enumerator'
2
4
  require 'xi/core_ext/fixnum'
3
5
  require 'xi/core_ext/numeric'
4
6
  require 'xi/core_ext/object'
5
- require 'xi/core_ext/simple'
7
+ require 'xi/core_ext/scalar'
6
8
  require 'xi/core_ext/string'