musa-dsl 0.21.4 → 0.22.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/musa-dsl.rb +1 -1
  3. data/lib/musa-dsl/core-ext.rb +1 -0
  4. data/lib/musa-dsl/core-ext/arrayfy.rb +9 -9
  5. data/lib/musa-dsl/core-ext/hashify.rb +42 -0
  6. data/lib/musa-dsl/core-ext/inspect-nice.rb +6 -1
  7. data/lib/musa-dsl/datasets/e.rb +22 -5
  8. data/lib/musa-dsl/datasets/gdv.rb +0 -1
  9. data/lib/musa-dsl/datasets/p.rb +28 -37
  10. data/lib/musa-dsl/datasets/pdv.rb +0 -1
  11. data/lib/musa-dsl/datasets/ps.rb +10 -78
  12. data/lib/musa-dsl/logger/logger.rb +4 -3
  13. data/lib/musa-dsl/matrix/matrix.rb +0 -57
  14. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +87 -0
  15. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +439 -0
  16. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +2 -2
  17. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +210 -0
  18. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +178 -0
  19. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +150 -597
  20. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +6 -8
  21. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +1 -5
  22. data/lib/musa-dsl/sequencer/{base-sequencer-public.rb → base-sequencer.rb} +59 -5
  23. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +11 -2
  24. data/lib/musa-dsl/sequencer/sequencer.rb +1 -1
  25. data/lib/musa-dsl/series/base-series.rb +43 -78
  26. data/lib/musa-dsl/series/flattener-timed-serie.rb +61 -0
  27. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +143 -0
  28. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  29. data/lib/musa-dsl/series/main-serie-constructors.rb +32 -92
  30. data/lib/musa-dsl/series/main-serie-operations.rb +60 -215
  31. data/lib/musa-dsl/series/proxy-serie.rb +1 -1
  32. data/lib/musa-dsl/series/quantizer-serie.rb +558 -0
  33. data/lib/musa-dsl/series/queue-serie.rb +1 -1
  34. data/lib/musa-dsl/series/series.rb +8 -2
  35. data/lib/musa-dsl/series/union-timed-series.rb +109 -0
  36. data/musa-dsl.gemspec +2 -2
  37. metadata +12 -5
  38. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +0 -216
  39. data/lib/musa-dsl/series/hash-serie-splitter.rb +0 -196
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dae00405142514f16df2be12de1eb9327893f3996ad3c86ec76fc315db521e7d
4
- data.tar.gz: f3ebf69b72452ab83ec33ab873221ee1f0d59954e2c67277c2386fc174278a7c
3
+ metadata.gz: 193117ba393c58eaf2387d7b0a5ba54bc7fbad0c6ac2e5a7171997b1629d5f69
4
+ data.tar.gz: 5ac3515f62f3c5d8ea34cd91552fb6d0f8d53e40f60d1383fa34a28c0b11289d
5
5
  SHA512:
6
- metadata.gz: 006f3143b93237b86600bde3b7229f14cae909a50603af4c0d1f33050835c582988325f8b2db63b74d20826d1682f09d51960e32d977f47ce86d69b4d25cd53b
7
- data.tar.gz: 7c07575e0bcf7a2b5f9e6361d8ae2b64193b8bba31bec3badfbf02f8c83f8a3595bc84df42aa6c8976c947528c82752f9869ed7c19240c9d0d536cb435b4da86
6
+ metadata.gz: 13ec912683ee5e0f0f0865ff9197e641671c430ed5a59d438b15acb5423f6d18f3f57624d5dd4c348daa69c93ac1d47428809cea84953893a4cb7f9b224613f9
7
+ data.tar.gz: b64ee9f844989e4db527787f763089c6bcd5aa050254da270c2c5c3461145aefef9b701da6944cda20b53286bfee2e42650c2a21de864ee8ea53f5d48aff0791
@@ -1,5 +1,5 @@
1
1
  module Musa
2
- VERSION = '0.21.1'
2
+ VERSION = '0.22.3'
3
3
  end
4
4
 
5
5
  require_relative 'musa-dsl/core-ext'
@@ -1,5 +1,6 @@
1
1
  require_relative 'core-ext/array-explode-ranges'
2
2
  require_relative 'core-ext/arrayfy'
3
+ require_relative 'core-ext/hashify'
3
4
  require_relative 'core-ext/deep-copy'
4
5
  require_relative 'core-ext/smart-proc-binder'
5
6
  require_relative 'core-ext/inspect-nice'
@@ -4,28 +4,28 @@ module Musa
4
4
  module Extension
5
5
  module Arrayfy
6
6
  refine Object do
7
- def arrayfy(size: nil)
7
+ def arrayfy(size: nil, default: nil)
8
8
  if size
9
- size.times.collect { self }
10
- else
11
- if nil?
12
- []
13
- else
14
- [self]
9
+ size.times.collect do
10
+ nil? ? default : self
15
11
  end
12
+ else
13
+ nil? ? [] : [self]
16
14
  end
17
15
  end
18
16
  end
19
17
 
18
+ # TODO add a refinement for Hash? Should receive a list parameter with the ordered keys
19
+
20
20
  refine Array do
21
- def arrayfy(size: nil)
21
+ def arrayfy(size: nil, default: nil)
22
22
  if size
23
23
  DeepCopy::DeepCopy.copy_singleton_class_modules(
24
24
  self,
25
25
  (self * (size / self.size + ((size % self.size).zero? ? 0 : 1) )).take(size))
26
26
  else
27
27
  self.clone
28
- end
28
+ end.map! { |value| value.nil? ? default : value }
29
29
  end
30
30
  end
31
31
  end
@@ -0,0 +1,42 @@
1
+ require_relative 'deep-copy'
2
+
3
+ module Musa
4
+ module Extension
5
+ module Hashify
6
+ refine Object do
7
+ def hashify(keys:, default: nil)
8
+ keys.collect do |key|
9
+ [ key,
10
+ if nil?
11
+ default
12
+ else
13
+ self
14
+ end ]
15
+ end.to_h
16
+ end
17
+ end
18
+
19
+ refine Array do
20
+ def hashify(keys:, default: nil)
21
+ values = clone
22
+ keys.collect do |key|
23
+ value = values.shift
24
+ [ key,
25
+ value.nil? ? default : value ]
26
+ end.to_hash
27
+ end
28
+ end
29
+
30
+ refine Hash do
31
+ def hashify(keys:, default: nil)
32
+ keys.collect do |key|
33
+ value = self[key]
34
+ [ key,
35
+ value.nil? ? default : value ]
36
+ end.to_h
37
+ .tap {|_| DeepCopy::DeepCopy.copy_singleton_class_modules(self, _) }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,6 +12,11 @@ module Musa
12
12
  alias to_s inspect
13
13
  end
14
14
 
15
+
16
+ refine Rational.singleton_class do
17
+ attr_accessor :to_s_as_inspect
18
+ end
19
+
15
20
  refine Rational do
16
21
  def inspect(simple: nil)
17
22
  value = self.abs
@@ -38,7 +43,7 @@ module Musa
38
43
  end
39
44
 
40
45
  def to_s
41
- inspect simple: true
46
+ inspect simple: true # !Rational.to_s_as_inspect
42
47
  end
43
48
  end
44
49
  end
@@ -5,11 +5,21 @@ module Musa::Datasets
5
5
  include Dataset
6
6
 
7
7
  NaturalKeys = [].freeze
8
+
9
+ # TODO implement valid? in all 'subclasses'. This implies recollecting from other places where validations are done and refactoring
10
+ # TODO should valid? and validate! be on Dataset instead of E? P dataset inherits from Dataset but probably it could be validated
11
+ #
12
+ def valid?
13
+ true
14
+ end
15
+
16
+ def validate!
17
+ raise RuntimeError, "Invalid dataset #{self}" unless valid?
18
+ end
8
19
  end
9
20
 
10
21
  module Abs
11
22
  include E
12
- def duration; 0; end
13
23
  end
14
24
 
15
25
  module Delta
@@ -20,6 +30,12 @@ module Musa::Datasets
20
30
  include Abs
21
31
  end
22
32
 
33
+ module AbsTimed
34
+ include Abs
35
+
36
+ NaturalKeys = (NaturalKeys + [:time]).freeze
37
+ end
38
+
23
39
  module DeltaI
24
40
  include Delta
25
41
  end
@@ -27,10 +43,11 @@ module Musa::Datasets
27
43
  module AbsD
28
44
  include Abs
29
45
 
30
- NaturalKeys = [:duration, # duration of the process (note reproduction, dynamics evolution, etc)
31
- :note_duration, # duration of the note (a staccato note is effectvely shorter than elapsed duration until next note)
32
- :forward_duration # duration to wait until next event (if 0 means the next event should be executed at the same time than this one)
33
- ].freeze
46
+ NaturalKeys = (NaturalKeys +
47
+ [:duration, # duration of the process (note reproduction, dynamics evolution, etc)
48
+ :note_duration, # duration of the note (a staccato note is effectvely shorter than elapsed duration until next note)
49
+ :forward_duration # duration to wait until next event (if 0 means the next event should be executed at the same time than this one)
50
+ ]).freeze
34
51
 
35
52
  def forward_duration
36
53
  self[:forward_duration] || self[:duration]
@@ -9,7 +9,6 @@ using Musa::Extension::InspectNice
9
9
  module Musa::Datasets
10
10
  module GDV
11
11
  include AbsD
12
- include AbsI
13
12
 
14
13
  include Helper
15
14
 
@@ -1,18 +1,19 @@
1
1
  require_relative 'dataset'
2
2
  require_relative 'ps'
3
3
 
4
+ require_relative '../series'
4
5
  require_relative '../sequencer'
5
6
 
6
7
  module Musa::Datasets
7
8
  module P
8
9
  include Dataset
9
10
 
10
- def to_ps_serie(base_duration = nil)
11
- base_duration ||= 1/4r
11
+ def to_ps_serie(base_duration: nil)
12
+ base_duration ||= 1/4r # TODO review incoherence between neumalang 1/4r base duration for quarter notes and general 1r size of bar
12
13
 
13
- p = clone
14
+ # TODO if instead of using clone (needed because of p.shift) we use index counter the P elements would be evaluated on the last moment
14
15
 
15
- Musa::Series::E() do
16
+ Musa::Series::E(clone, base_duration) do |p, base_duration|
16
17
  (p.size >= 3) ?
17
18
  { from: p.shift,
18
19
  duration: p.shift * base_duration,
@@ -21,47 +22,37 @@ module Musa::Datasets
21
22
  end
22
23
  end
23
24
 
24
- def to_score(score: nil,
25
- mapper: nil,
26
- position: nil,
27
- sequencer: nil,
28
- beats_per_bar: nil, ticks_per_beat: nil,
29
- right_open: nil,
30
- do_log: nil,
31
- &block)
25
+ def to_timed_serie(time_start = nil, base_duration: nil)
26
+ time_start ||= 0r
27
+ base_duration ||= 1/4r # TODO review incoherence between neumalang 1/4r base duration for quarter notes and general 1r size of bar
32
28
 
33
- raise ArgumentError,
34
- "'beats_per_bar' and 'ticks_per_beat' parameters should be both nil or both have values" \
35
- unless beats_per_bar && ticks_per_beat || beats_per_bar.nil? && ticks_per_beat.nil?
29
+ # TODO if instead of using clone (needed because of p.shift) we use index counter the P elements would be evaluated on the last moment
36
30
 
37
- raise ArgumentError,
38
- "'sequencer' parameter should not be used when 'beats_per_bar' and 'ticks_per_beat' parameters are used" \
39
- if sequencer && beats_per_bar
31
+ Musa::Series::E(clone, base_duration, context: { time: time_start }) do |p, base_duration, context: |
32
+ value = p.shift
40
33
 
41
- run_sequencer = sequencer.nil?
34
+ if value
35
+ r = { time: context[:time], value: value } if !value.nil?
42
36
 
43
- score ||= Musa::Datasets::Score.new
37
+ delta_time = p.shift
38
+ context[:time] += delta_time * base_duration if delta_time
44
39
 
45
- sequencer ||= Sequencer.new(beats_per_bar, ticks_per_beat, do_log: do_log)
46
-
47
- sequencer.at(position || 1r) do |_|
48
-
49
- _.play to_ps_serie do |_, line|
50
-
51
- line.to_score(sequencer: _,
52
- score: score,
53
- mapper: mapper,
54
- position: _.position,
55
- right_open: right_open,
56
- &block)
40
+ r&.extend(AbsTimed)
57
41
  end
58
42
  end
43
+ end
59
44
 
60
- if run_sequencer
61
- sequencer.run
62
- score
63
- else
64
- nil
45
+ def map(&block)
46
+ i = 0
47
+ clone.map! do |element|
48
+ # Process with block only the values (values are the alternating elements because P
49
+ # structure is <value> <duration> <value> <duration> <value>)
50
+ #
51
+ if (i += 1) % 2 == 1
52
+ block.call(element)
53
+ else
54
+ element
55
+ end
65
56
  end
66
57
  end
67
58
  end
@@ -6,7 +6,6 @@ require_relative 'helper'
6
6
  module Musa::Datasets
7
7
  module PDV
8
8
  include AbsD
9
- include AbsI
10
9
 
11
10
  include Helper
12
11
 
@@ -29,85 +29,17 @@ module Musa::Datasets
29
29
  # TODO ?????
30
30
  end
31
31
 
32
- def to_score(score: nil,
33
- mapper: nil,
34
- position: nil,
35
- sequencer: nil,
36
- beats_per_bar: nil, ticks_per_beat: nil,
37
- right_open: nil,
38
- do_log: nil,
39
- &block)
40
-
41
- raise ArgumentError,
42
- "'beats_per_bar' and 'ticks_per_beat' parameters should be both nil or both have values" \
43
- unless beats_per_bar && ticks_per_beat || beats_per_bar.nil? && ticks_per_beat.nil?
44
-
45
- raise ArgumentError,
46
- "'sequencer' parameter should not be used when 'beats_per_bar' and 'ticks_per_beat' parameters are used" \
47
- if sequencer && beats_per_bar
48
-
49
- run_sequencer = sequencer.nil?
50
-
51
- binder = Musa::Extension::SmartProcBinder::SmartProcBinder.new(block)
52
-
53
- score ||= Musa::Datasets::Score.new
54
-
55
- sequencer ||= Musa::Sequencer::Sequencer.new(beats_per_bar, ticks_per_beat, do_log: do_log)
56
-
57
- ticks_per_bar = sequencer.ticks_per_bar
58
-
59
- from = mapper && self[:from].is_a?(V) ? self[:from].to_packed_V(mapper) : self[:from]
60
- to = mapper && self[:to].is_a?(V) ? self[:to].to_packed_V(mapper) : self[:to]
61
-
62
- if right_open.is_a?(Array)
63
- right_open = right_open.collect do |v|
64
- v.is_a?(Symbol) ? v : mapper[v]
65
- end
66
- end
67
-
68
- # TODO sequencer step should be 1????
69
- #
70
- sequencer.at(position || 1r) do |_|
71
- _.move from: from,
72
- to: to,
73
- right_open: right_open,
74
- duration: _quantize(self[:duration], ticks_per_bar),
75
- step: 1 do
76
- | _,
77
- value, next_value,
78
- duration:,
79
- quantized_duration:,
80
- position_jitter:, duration_jitter:,
81
- started_ago:|
82
-
83
- binder.call value, next_value,
84
- position: _.position,
85
- duration: duration,
86
- quantized_duration: quantized_duration,
87
- position_jitter: position_jitter,
88
- duration_jitter: duration_jitter,
89
- started_ago: started_ago,
90
- score: score,
91
- logger: _.logger
92
- end
93
- end
94
-
95
- if run_sequencer
96
- sequencer.run
97
- score
98
- else
99
- nil
100
- end
101
- end
102
-
103
- private
104
-
105
- def _quantize(duration, ticks_per_bar)
106
- if ticks_per_bar &.< Float::INFINITY
107
- ((duration.rationalize * ticks_per_bar).round / ticks_per_bar).to_r
32
+ def valid?
33
+ case self[:from]
34
+ when Array
35
+ self[:to].is_a?(Array) &&
36
+ self[:from].size == self[:to].size
37
+ when Hash
38
+ self[:to].is_a?(Hash) &&
39
+ self[:from].keys == self[:to].keys
108
40
  else
109
- duration.rationalize
110
- end
41
+ false
42
+ end && self[:duration].is_a?(Numeric) && self[:duration] > 0
111
43
  end
112
44
  end
113
45
  end
@@ -3,11 +3,12 @@ require 'logger'
3
3
  module Musa; module Logger
4
4
  class Logger < ::Logger
5
5
  def initialize(sequencer: nil, position_format: nil)
6
- super STDERR
6
+ super STDERR, level: WARN
7
7
 
8
8
  @sequencer = sequencer
9
9
  @position_format = position_format || 3.3
10
10
 
11
+
11
12
  self.formatter = proc do |severity, time, progname, msg|
12
13
  level = "[#{severity}] " unless severity == 'DEBUG'
13
14
 
@@ -19,9 +20,9 @@ module Musa; module Logger
19
20
  "%#{integer_digits + decimal_digits + 1}s: " % ("%.#{decimal_digits}f" % sequencer.position.to_f)
20
21
  end
21
22
 
22
- progname = "[#{progname}] " if progname
23
+ progname = "[#{progname}]" if progname
23
24
 
24
- "#{position}#{level}#{progname}#{msg}\n"
25
+ "#{position}#{level}#{progname} #{msg}\n"
25
26
  else
26
27
  "\n"
27
28
  end
@@ -70,7 +70,6 @@ module Musa
70
70
  include Musa::Datasets
71
71
 
72
72
  def to_p(time_dimension, keep_time: nil)
73
-
74
73
  decompose(self.to_a, time_dimension).collect do |points|
75
74
  line = []
76
75
 
@@ -94,62 +93,6 @@ module Musa
94
93
  end
95
94
  end
96
95
 
97
- def to_score(time_dimension,
98
- mapper: nil,
99
- score: nil,
100
- position: nil,
101
- sequencer: nil,
102
- beats_per_bar: nil, ticks_per_beat: nil,
103
- right_open: nil,
104
- do_log: nil,
105
- &block)
106
-
107
- raise ArgumentError,
108
- "'beats_per_bar' and 'ticks_per_beat' parameters should be both nil or both have values" \
109
- unless beats_per_bar && ticks_per_beat ||
110
- beats_per_bar.nil? && ticks_per_beat.nil?
111
-
112
- raise ArgumentError,
113
- "'sequencer' parameter should not be used when 'beats_per_bar' and 'ticks_per_beat' parameters are used" \
114
- if sequencer && beats_per_bar
115
-
116
- raise ArgumentError,
117
- "'time_dimension' parameter should be an index number if no 'mapping' is provided" \
118
- unless time_dimension.is_a?(Symbol) && mapper&.include?(time_dimension) ||
119
- time_dimension.is_a?(Integer) && (0..self.column_size-1).include(time_dimension)
120
-
121
- time_dimension = mapper&.find_index(time_dimension) if time_dimension.is_a?(Symbol)
122
-
123
- mapper = mapper.clone.tap { |_| _.delete_at(time_dimension) } if mapper
124
-
125
- run_sequencer = sequencer.nil?
126
-
127
- score ||= Musa::Datasets::Score.new
128
-
129
- sequencer ||= Sequencer::Sequencer.new(beats_per_bar, ticks_per_beat, do_log: do_log)
130
-
131
- right_open = right_open.clone.tap { |_| _.delete_at(time_dimension) } if right_open&.is_a?(Array)
132
-
133
- sequencer.at(position || 1r) do |_|
134
- to_p(time_dimension).each do |p|
135
- p.to_score(score: score,
136
- mapper: mapper,
137
- sequencer: _,
138
- position: _.position,
139
- right_open: right_open,
140
- do_log: do_log,
141
- &block)
142
- end
143
- end
144
-
145
- if run_sequencer
146
- sequencer.run
147
- score
148
- else
149
- nil
150
- end
151
- end
152
-
153
96
  def _rows
154
97
  @rows
155
98
  end