musa-dsl 0.21.1 → 0.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) 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/midi/midi-voices.rb +4 -0
  15. data/lib/musa-dsl/repl/repl.rb +30 -11
  16. data/lib/musa-dsl/sequencer/base-sequencer-implementation-every.rb +87 -0
  17. data/lib/musa-dsl/sequencer/base-sequencer-implementation-move.rb +439 -0
  18. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-helper.rb +3 -3
  19. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play-timed.rb +210 -0
  20. data/lib/musa-dsl/sequencer/base-sequencer-implementation-play.rb +178 -0
  21. data/lib/musa-dsl/sequencer/base-sequencer-implementation.rb +150 -595
  22. data/lib/musa-dsl/sequencer/base-sequencer-public.rb +58 -5
  23. data/lib/musa-dsl/sequencer/base-sequencer-tick-based.rb +5 -9
  24. data/lib/musa-dsl/sequencer/base-sequencer-tickless-based.rb +1 -5
  25. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +8 -0
  26. data/lib/musa-dsl/series/base-series.rb +43 -78
  27. data/lib/musa-dsl/series/flattener-timed-serie.rb +61 -0
  28. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +95 -0
  29. data/lib/musa-dsl/series/holder-serie.rb +1 -1
  30. data/lib/musa-dsl/series/main-serie-constructors.rb +29 -83
  31. data/lib/musa-dsl/series/main-serie-operations.rb +60 -215
  32. data/lib/musa-dsl/series/proxy-serie.rb +1 -1
  33. data/lib/musa-dsl/series/quantizer-serie.rb +546 -0
  34. data/lib/musa-dsl/series/queue-serie.rb +1 -1
  35. data/lib/musa-dsl/series/series.rb +7 -2
  36. data/lib/musa-dsl/transport/input-midi-clock.rb +19 -12
  37. data/lib/musa-dsl/transport/transport.rb +25 -12
  38. data/musa-dsl.gemspec +2 -2
  39. metadata +10 -4
  40. data/lib/musa-dsl/sequencer/base-sequencer-implementation-control.rb +0 -216
  41. 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: f6666b6d9729d138be960000a3dd86a517f9ed94031d74a6eab25b76052d45c5
4
- data.tar.gz: a6806869e3f06e54bc5596f71c35cfff8a9513504653a889e57287d227990419
3
+ metadata.gz: 4498538210551ab83a9f167c0e9d8656c9c5b70c9bc51f97e99e540109e5de7e
4
+ data.tar.gz: 9957a89a6db2ed2a4e0d75900f2eb3e1051c6dfc3fcc25c40ed5da1ba8fb6957
5
5
  SHA512:
6
- metadata.gz: 531fbb701e5774e11a1a1dc373308fada1f777c6b8c7981e8a27ffd1ddb3c242bc1e5723555553010d4ff10259c7c69cd2e2428b9d0eab65ef96ae8b13b14f7b
7
- data.tar.gz: c2db1fae44b49ee11fd8dd5d764dee4c7416c4b7fb6ce075213f84207d54cd7b5cc6301f59a74a723b467a103c74c779f68f9a946c0fcdd92fefbe77d26758c0
6
+ metadata.gz: 6c2bbea949c5c22dba0247a6f2ce050f515383ddf23aedf1c0b5755d8d94ddf355ebc2beedee3b6c03aea758da905d393db4fd559f26bb52a667bc9f82cf023e
7
+ data.tar.gz: da8bf0de7ba0cc96d5e96188293b901135bd4f052218ead671783620cf294c40eed37f89a46220d2eefd32f3cc1e03634c392783efac9164e725635e70b0e1c4
@@ -1,5 +1,5 @@
1
1
  module Musa
2
- VERSION = '0.21.1'
2
+ VERSION = '0.22.1'
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,13 +3,14 @@ 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
- level = "[#{severity}]" unless severity == 'DEBUG'
13
+ level = "[#{severity}] " unless severity == 'DEBUG'
13
14
 
14
15
  if msg
15
16
  position = if @sequencer
@@ -21,7 +22,7 @@ module Musa; module Logger
21
22
 
22
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