Module: Musa::Extension::Matrix

Defined in:
lib/musa-dsl/matrix/matrix.rb

Overview

Note:

These refinements must be activated with using Musa::Extension::Matrix in the scope where you want to use them.

Refinements for Array and Matrix classes to support musical structure conversions.

These refinements add methods to convert between matrix representations and Musa's P (point sequence) format, which is used extensively in the DSL for representing musical gestures and trajectories.

Background

In Musa DSL, musical gestures are often represented as sequences of points in multidimensional space, where dimensions can represent time, pitch, velocity, or other musical parameters. The P format provides a compact representation suitable for sequencer playback and transformation.

Matrix to P Conversion

A matrix of points (rows = time steps, columns = parameters) can be converted to P format where:

  • One dimension represents time (usually the first column)
  • Other dimensions represent musical parameters
  • Each P is an array extended with P module
  • The P contains alternating values (arrays extended with V) and durations (numbers): [value1, duration1, value2, duration2, ..., valueN].extend(P)

Use Cases

  • Converting recorded MIDI data to playable sequences
  • Transforming algorithmic compositions from matrix form to time-based sequences
  • Merging fragmented musical gestures that share connection points
  • Decomposing complex trajectories into simpler monotonic segments

Methods Added

Array

Matrix

  • Matrix#to_p - Converts a matrix to P format (see examples in module documentation)
  • Matrix#_rows - Provides direct access to internal rows array (private API)

Examples:

Basic matrix conversion

using Musa::Extension::Matrix

# Matrix: [time, pitch]
matrix = Matrix[[0, 60], [1, 62], [2, 64]]
p_sequences = matrix.to_p(time_dimension: 0)
# => [[[60], 1, [62], 1, [64]]]

# An ARRAY of P, not a P: a matrix can decompose into more than one
# sequence, so the answer is always a list even when it holds one.
p_sequences.first.is_a?(Musa::Datasets::P)        # => true
p_sequences.first.first.is_a?(Musa::Datasets::V)  # => true

# The 1s between the points are the durations read off the time
# dimension, and each point is a V -- a vector of the rest.

Multi-dimensional musical parameters

using Musa::Extension::Matrix

# Matrix: [time, pitch, velocity]
matrix = Matrix[[0, 60, 100], [0.5, 62, 110], [1, 64, 120]]
p_sequences = matrix.to_p(time_dimension: 0, keep_time: false)
# => [[[60, 100], 0.5, [62, 110], 0.5, [64, 120]]]

# Two parameters per point now, and the time dimension is gone from
# them: it was read to compute the 0.5s and then dropped.

Condensing connected matrices

using Musa::Extension::Matrix

# Two phrases that connect at [1, 62]
phrase1 = Matrix[[0, 60], [1, 62]]
phrase2 = Matrix[[1, 62], [2, 64], [3, 65]]

[phrase1, phrase2].to_p(time_dimension: 0)
# => [[[[60], 1, [62], 1, [64], 1, [65]]]]

# One level deeper than a single matrix: an array of matrices answers
# with one entry per CONDENSED GROUP, and each group is itself a list of
# P. These two share the row [1, 62], so they condense into one and the
# 62 is not repeated. Phrases that do not touch stay apart:
[Matrix[[0, 60], [1, 62]], Matrix[[5, 64], [6, 65]]].to_p(time_dimension: 0).size
# => 2

See Also: