musa-dsl 0.40.0 → 0.42.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +0 -1
  4. data/README.md +15 -1
  5. data/docs/README.md +1 -0
  6. data/docs/subsystems/datasets.md +75 -0
  7. data/docs/subsystems/generative.md +92 -6
  8. data/docs/subsystems/music.md +349 -19
  9. data/docs/subsystems/transport.md +26 -0
  10. data/lib/musa-dsl/datasets/dataset.rb +2 -0
  11. data/lib/musa-dsl/datasets/gdv.rb +3 -3
  12. data/lib/musa-dsl/datasets/p.rb +1 -1
  13. data/lib/musa-dsl/datasets/score/to-mxml/process-time.rb +4 -2
  14. data/lib/musa-dsl/datasets/score.rb +3 -1
  15. data/lib/musa-dsl/generative/darwin.rb +36 -1
  16. data/lib/musa-dsl/generative/generative-grammar.rb +31 -1
  17. data/lib/musa-dsl/generative/markov.rb +3 -1
  18. data/lib/musa-dsl/generative/rules.rb +54 -0
  19. data/lib/musa-dsl/generative/variatio.rb +69 -0
  20. data/lib/musa-dsl/midi/midi-recorder.rb +4 -0
  21. data/lib/musa-dsl/midi/midi-voices.rb +13 -1
  22. data/lib/musa-dsl/music/chord-definition.rb +7 -5
  23. data/lib/musa-dsl/music/chord-definitions.rb +37 -0
  24. data/lib/musa-dsl/music/chords.rb +88 -21
  25. data/lib/musa-dsl/music/equally-tempered-12-tone-scale-system.rb +70 -521
  26. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_dominant_scale_kind.rb +110 -0
  27. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_major_scale_kind.rb +110 -0
  28. data/lib/musa-dsl/music/scale_kinds/bebop/bebop_minor_scale_kind.rb +110 -0
  29. data/lib/musa-dsl/music/scale_kinds/blues/blues_major_scale_kind.rb +100 -0
  30. data/lib/musa-dsl/music/scale_kinds/blues/blues_scale_kind.rb +99 -0
  31. data/lib/musa-dsl/music/scale_kinds/chromatic_scale_kind.rb +79 -0
  32. data/lib/musa-dsl/music/scale_kinds/ethnic/double_harmonic_scale_kind.rb +102 -0
  33. data/lib/musa-dsl/music/scale_kinds/ethnic/hungarian_minor_scale_kind.rb +102 -0
  34. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_major_scale_kind.rb +102 -0
  35. data/lib/musa-dsl/music/scale_kinds/ethnic/neapolitan_minor_scale_kind.rb +101 -0
  36. data/lib/musa-dsl/music/scale_kinds/ethnic/phrygian_dominant_scale_kind.rb +103 -0
  37. data/lib/musa-dsl/music/scale_kinds/harmonic_major/harmonic_major_scale_kind.rb +104 -0
  38. data/lib/musa-dsl/music/scale_kinds/major_scale_kind.rb +110 -0
  39. data/lib/musa-dsl/music/scale_kinds/melodic_minor/altered_scale_kind.rb +106 -0
  40. data/lib/musa-dsl/music/scale_kinds/melodic_minor/dorian_b2_scale_kind.rb +104 -0
  41. data/lib/musa-dsl/music/scale_kinds/melodic_minor/locrian_sharp2_scale_kind.rb +103 -0
  42. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_augmented_scale_kind.rb +103 -0
  43. data/lib/musa-dsl/music/scale_kinds/melodic_minor/lydian_dominant_scale_kind.rb +106 -0
  44. data/lib/musa-dsl/music/scale_kinds/melodic_minor/melodic_minor_scale_kind.rb +104 -0
  45. data/lib/musa-dsl/music/scale_kinds/melodic_minor/mixolydian_b6_scale_kind.rb +103 -0
  46. data/lib/musa-dsl/music/scale_kinds/minor_harmonic_scale_kind.rb +125 -0
  47. data/lib/musa-dsl/music/scale_kinds/minor_natural_scale_kind.rb +123 -0
  48. data/lib/musa-dsl/music/scale_kinds/modes/dorian_scale_kind.rb +111 -0
  49. data/lib/musa-dsl/music/scale_kinds/modes/locrian_scale_kind.rb +114 -0
  50. data/lib/musa-dsl/music/scale_kinds/modes/lydian_scale_kind.rb +111 -0
  51. data/lib/musa-dsl/music/scale_kinds/modes/mixolydian_scale_kind.rb +111 -0
  52. data/lib/musa-dsl/music/scale_kinds/modes/phrygian_scale_kind.rb +111 -0
  53. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_major_scale_kind.rb +93 -0
  54. data/lib/musa-dsl/music/scale_kinds/pentatonic/pentatonic_minor_scale_kind.rb +99 -0
  55. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_hw_scale_kind.rb +110 -0
  56. data/lib/musa-dsl/music/scale_kinds/symmetric/diminished_wh_scale_kind.rb +110 -0
  57. data/lib/musa-dsl/music/scale_kinds/symmetric/whole_tone_scale_kind.rb +99 -0
  58. data/lib/musa-dsl/music/scale_systems/equally_tempered_12_tone_scale_system.rb +80 -0
  59. data/lib/musa-dsl/music/scale_systems/twelve_semitones_scale_system.rb +60 -0
  60. data/lib/musa-dsl/music/scales.rb +606 -67
  61. data/lib/musa-dsl/musicxml/builder/note.rb +31 -92
  62. data/lib/musa-dsl/musicxml/builder/pitched-note.rb +33 -94
  63. data/lib/musa-dsl/musicxml/builder/rest.rb +30 -91
  64. data/lib/musa-dsl/musicxml/builder/unpitched-note.rb +31 -91
  65. data/lib/musa-dsl/neumas/array-to-neumas.rb +1 -1
  66. data/lib/musa-dsl/neumas/neuma-gdv-decoder.rb +2 -2
  67. data/lib/musa-dsl/sequencer/sequencer-dsl.rb +367 -3
  68. data/lib/musa-dsl/series/base-series.rb +250 -240
  69. data/lib/musa-dsl/series/buffer-serie.rb +16 -5
  70. data/lib/musa-dsl/series/hash-or-array-serie-splitter.rb +29 -3
  71. data/lib/musa-dsl/series/main-serie-constructors.rb +19 -15
  72. data/lib/musa-dsl/series/main-serie-operations.rb +74 -29
  73. data/lib/musa-dsl/series/proxy-serie.rb +5 -1
  74. data/lib/musa-dsl/series/quantizer-serie.rb +16 -2
  75. data/lib/musa-dsl/series/queue-serie.rb +15 -1
  76. data/lib/musa-dsl/series/series-composer.rb +5 -2
  77. data/lib/musa-dsl/series/timed-serie.rb +8 -4
  78. data/lib/musa-dsl/transport/timer-clock.rb +4 -2
  79. data/lib/musa-dsl/transport/timer.rb +27 -4
  80. data/lib/musa-dsl/version.rb +1 -1
  81. data/musa-dsl.gemspec +18 -15
  82. metadata +85 -22
@@ -47,13 +47,15 @@ module Musa
47
47
  #
48
48
  # ### Move - Relocate specific chord tones to different octaves:
49
49
  #
50
- # chord.move(root: -1, seventh: 1)
50
+ # chord.with_move(root: -1, seventh: 1)
51
51
  # # Root down one octave, seventh up one octave
52
+ # chord.move # => { root: -1, seventh: 1 } (current settings)
52
53
  #
53
54
  # ### Duplicate - Add copies of chord tones in other octaves:
54
55
  #
55
- # chord.duplicate(root: -2, third: [-1, 1])
56
+ # chord.with_duplicate(root: -2, third: [-1, 1])
56
57
  # # Add root 2 octaves down, third 1 octave down and 1 up
58
+ # chord.duplicate # => { root: -2, third: [-1, 1] } (current settings)
57
59
  #
58
60
  # ### Octave - Transpose entire chord:
59
61
  #
@@ -90,8 +92,8 @@ module Musa
90
92
  # @example Voicing with move and duplicate
91
93
  # scale = Scales::Scales.default_system.default_tuning.major[60]
92
94
  # chord = scale.dominant.chord(:seventh)
93
- # .move(root: -1, third: -1)
94
- # .duplicate(fifth: [0, 1])
95
+ # .with_move(root: -1, third: -1)
96
+ # .with_duplicate(fifth: [0, 1])
95
97
  #
96
98
  # @example Feature navigation
97
99
  # scale = Scales::Scales.default_system.default_tuning.major[60]
@@ -194,9 +196,9 @@ module Musa
194
196
  # Maps each chord position (root, third, fifth, etc.) to its corresponding
195
197
  # note in the scale or chromatic scale.
196
198
  #
197
- # @param root [NoteInScale] chord root note
199
+ # @param root [Scales::NoteInScale] chord root note
198
200
  # @param chord_definition [ChordDefinition] chord structure
199
- # @param scale [Scale] scale context
201
+ # @param scale [Scales::Scale] scale context
200
202
  # @return [Hash{Symbol => Array<NoteInScale>}] position to notes mapping
201
203
  #
202
204
  # @api private
@@ -214,7 +216,7 @@ module Musa
214
216
  #
215
217
  # @param root_pitch [Integer] MIDI pitch of chord root
216
218
  # @param features [Hash] desired chord features (quality:, size:, etc.)
217
- # @param scale [Scale] scale context for diatonic filtering
219
+ # @param scale [Scales::Scale] scale context for diatonic filtering
218
220
  # @param allow_chromatic [Boolean] allow non-diatonic chords
219
221
  # @return [ChordDefinition, nil] matching definition or nil
220
222
  #
@@ -242,7 +244,7 @@ module Musa
242
244
  # @!attribute grade
243
245
  # @return [Symbol] position name (:root, :third, :fifth, etc.)
244
246
  # @!attribute note
245
- # @return [NoteInScale] the note at this position
247
+ # @return [Scales::NoteInScale] the note at this position
246
248
  #
247
249
  # @api private
248
250
  ChordGradeNote = Struct.new(:grade, :note, keyword_init: true)
@@ -253,8 +255,8 @@ module Musa
253
255
  #
254
256
  # Use {with_root} or create chords from scale notes instead.
255
257
  #
256
- # @param root [NoteInScale] chord root note
257
- # @param scale [Scale, nil] scale context (nil if chromatic notes present)
258
+ # @param root [Scales::NoteInScale] chord root note
259
+ # @param scale [Scales::Scale, nil] scale context (nil if chromatic notes present)
258
260
  # @param chord_definition [ChordDefinition] chord structure
259
261
  # @param move [Hash, nil] octave moves for positions
260
262
  # @param duplicate [Hash, nil] octave duplications for positions
@@ -423,10 +425,10 @@ module Musa
423
425
  # chord.octave(2)
424
426
  def octave(octave)
425
427
  source_notes_map = @source_notes_map.transform_values do |notes|
426
- notes.collect { |note| note.octave(octave) }.freeze
428
+ notes.collect { |note| note.at_octave(octave) }.freeze
427
429
  end.freeze
428
430
 
429
- Chord.new(@root.octave(octave), @scale, chord_definition, @move, @duplicate, source_notes_map)
431
+ Chord.new(@root.at_octave(octave), @scale, chord_definition, @move, @duplicate, source_notes_map)
430
432
  end
431
433
 
432
434
  # Creates new chord with positions moved to different octaves.
@@ -439,11 +441,11 @@ module Musa
439
441
  # @return [Chord] new chord with moved positions
440
442
  #
441
443
  # @example Move root down, seventh up
442
- # chord.move(root: -1, seventh: 1)
444
+ # chord.with_move(root: -1, seventh: 1)
443
445
  #
444
446
  # @example Drop voicing (move third and seventh down)
445
- # chord.move(third: -1, seventh: -1)
446
- def move(**octaves)
447
+ # chord.with_move(third: -1, seventh: -1)
448
+ def with_move(**octaves)
447
449
  Chord.new(@root, @scale, @chord_definition, @move.merge(octaves), @duplicate, @source_notes_map)
448
450
  end
449
451
 
@@ -457,17 +459,82 @@ module Musa
457
459
  # @return [Chord] new chord with duplicated positions
458
460
  #
459
461
  # @example Duplicate root two octaves down
460
- # chord.duplicate(root: -2)
462
+ # chord.with_duplicate(root: -2)
461
463
  #
462
464
  # @example Duplicate third in multiple octaves
463
- # chord.duplicate(third: [-1, 1])
465
+ # chord.with_duplicate(third: [-1, 1])
464
466
  #
465
467
  # @example Duplicate multiple positions
466
- # chord.duplicate(root: -1, fifth: 1)
467
- def duplicate(**octaves)
468
+ # chord.with_duplicate(root: -1, fifth: 1)
469
+ def with_duplicate(**octaves)
468
470
  Chord.new(@root, @scale, @chord_definition, @move, @duplicate.merge(octaves), @source_notes_map)
469
471
  end
470
472
 
473
+ # Finds this chord in other scales.
474
+ #
475
+ # Searches through scale kinds matching the given metadata criteria to find
476
+ # all scales that contain this chord. Returns new chord instances, each with
477
+ # its containing scale as context.
478
+ #
479
+ # @param roots [Range, Array, nil] pitch offsets to search (default: full octave)
480
+ # @param metadata [Hash] metadata filters for scale kinds (family:, brightness:, etc.)
481
+ # @return [Array<Chord>] this chord in different scale contexts
482
+ #
483
+ # @example Find G major triad in diatonic scales
484
+ # g_triad = c_major.dominant.chord
485
+ # g_triad.in_scales(family: :diatonic)
486
+ #
487
+ # @example Find chord in scales with specific brightness
488
+ # g7.in_scales(brightness: -1..1)
489
+ #
490
+ # @example Iterate over results
491
+ # g7.in_scales(family: :greek_modes).each do |chord|
492
+ # scale = chord.scale
493
+ # degree = scale.degree_of_chord(chord)
494
+ # puts "#{scale.kind.class.id} on #{scale.root_pitch}: degree #{degree}"
495
+ # end
496
+ #
497
+ # @see Musa::Scales::Scale#chord_on
498
+ # @see Musa::Scales::ScaleSystemTuning#search_chord_in_scales
499
+ def search_in_scales(roots: nil, **metadata)
500
+ tuning = @scale&.kind&.tuning || @root.scale.kind.tuning
501
+ tuning.search_chord_in_scales(self, roots: roots, **metadata)
502
+ end
503
+
504
+ # Creates an equivalent chord with the given scale as its context.
505
+ #
506
+ # Returns a new Chord object representing the same chord but with
507
+ # the specified scale as its harmonic context. Returns nil if the
508
+ # chord is not contained in the scale.
509
+ #
510
+ # @param scale [Musa::Scales::Scale] the target scale
511
+ # @return [Chord, nil] new chord with target scale, or nil if not contained
512
+ #
513
+ # @example
514
+ # c_major = Scales.et12[440.0].major[60]
515
+ # g7 = c_major.dominant.chord :seventh
516
+ #
517
+ # g_mixolydian = Scales.et12[440.0].mixolydian[67]
518
+ # g7_in_mixolydian = g7.as_chord_in_scale(g_mixolydian)
519
+ # g7_in_mixolydian.scale # => G Mixolydian scale
520
+ #
521
+ # @see #search_in_scales
522
+ # @see Musa::Scales::Scale#contains_chord?
523
+ def as_chord_in_scale(scale)
524
+ return nil unless scale.contains_chord?(self)
525
+
526
+ root_note = scale.note_of_pitch(@root.pitch, allow_chromatic: false)
527
+ return nil unless root_note
528
+
529
+ Chord.with_root(
530
+ root_note,
531
+ scale: scale,
532
+ name: @chord_definition.name,
533
+ move: @move.empty? ? nil : @move,
534
+ duplicate: @duplicate.empty? ? nil : @duplicate
535
+ )
536
+ end
537
+
471
538
  # Checks chord equality.
472
539
  #
473
540
  # Chords are equal if they have the same notes and chord definition.
@@ -504,12 +571,12 @@ module Musa
504
571
  notes_map = notes_map.transform_values(&:dup)
505
572
 
506
573
  moved&.each do |position, octave|
507
- notes_map[position][0] = notes_map[position][0].octave(octave)
574
+ notes_map[position][0] = notes_map[position][0].at_octave(octave)
508
575
  end
509
576
 
510
577
  duplicated&.each do |position, octave|
511
578
  octave.arrayfy.each do |octave|
512
- notes_map[position] << notes_map[position][0].octave(octave)
579
+ notes_map[position] << notes_map[position][0].at_octave(octave)
513
580
  end
514
581
  end
515
582