Module: Musa::Series::Operations

Defined in:
lib/musa-dsl/series/base-series.rb,
lib/musa-dsl/series/proxy-serie.rb,
lib/musa-dsl/series/queue-serie.rb,
lib/musa-dsl/series/timed-serie.rb,
lib/musa-dsl/series/buffer-serie.rb,
lib/musa-dsl/series/quantizer-serie.rb,
lib/musa-dsl/series/series-composer.rb,
lib/musa-dsl/series/main-serie-operations.rb,
lib/musa-dsl/series/hash-or-array-serie-splitter.rb

Overview

Series transformation operations for composing and modifying series.

Provides methods for transforming, combining, and controlling series flow. All operations return new series (functional/immutable style).

Categories

Mapping & Transformation

  • map - Transform values via block
  • with - Combine multiple series for mapping
  • process_with - Generic processor with parameters
  • hashify - Convert array values to hash
  • shift - Shift values by offset

Filtering & Selection

  • select - Keep values matching condition
  • remove - Remove values matching condition
  • skip - Skip first N values
  • max_size - Limit to N values
  • cut - Cut into chunks

Flow Control

  • repeat - Repeat series N times or conditionally
  • autorestart - Auto-restart when exhausted
  • flatten - Flatten nested series
  • merge - Merge serie of series
  • after / + - Append series sequentially

Switching & Multiplexing

  • switch - Switch between series based on selector
  • multiplex - Multiplex series based on selector
  • switch_serie - Switch to different series entirely

Structural Operations

  • reverse - Reverse values
  • randomize - Shuffle values randomly
  • lock - Lock serie (prevent changes)
  • flatten - Flatten nested series

Timing Operations

  • anticipate - Evaluate block one step ahead
  • lazy - Delay evaluation to next step

Usage Patterns

Mapping

notes = S(60, 64, 67).map { |n| n + 12 }  # Transpose octave
notes.i.to_a  # => [72, 76, 79]

Filtering

evens = S(1, 2, 3, 4, 5, 6).select { |n| n.even? }
evens.i.to_a  # => [2, 4, 6]

Combining

pitches = S(60, 64, 67)
velocities = S(96, 80, 64)
notes = pitches.with(velocities) { |p, v| {pitch: p, velocity: v} }

Repeating

pattern = S(1, 2, 3).repeat(3)
pattern.i.to_a  # => [1, 2, 3, 1, 2, 3, 1, 2, 3]

Chaining Operations

result = S(1, 2, 3, 4, 5)
  .select { |n| n.even? }
  .map { |n| n * 10 }
  .repeat(2)
result.i.to_a  # => [20, 40, 20, 40]

Defined Under Namespace

Classes: ComposerAsOperationSerie, Processor

Instance Method Summary collapse

Instance Method Details

#+(other) ⇒ Sequence

Appends another serie (operator alias for after).

Examples:

Concatenate

s = S(1, 2) + S(3, 4)
s.i.to_a  # => [1, 2, 3, 4]

Parameters:

  • other (Serie)

    serie to append

Returns:

  • (Sequence)

    sequential combination



432
433
434
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 432

def +(other)
  Musa::Series::Constructors.MERGE self, other
end

#after(*series) ⇒ Sequence

Appends series sequentially.

Alias for MERGE - plays this serie, then others in sequence.

Examples:

Append

s = S(1, 2).after(S(3, 4), S(5, 6))
s.i.to_a  # => [1, 2, 3, 4, 5, 6]

Parameters:

Returns:

  • (Sequence)

    sequential combination



417
418
419
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 417

def after(*series)
  Musa::Series::Constructors.MERGE self, *series
end

#anticipate {|current, next_value| ... } ⇒ Anticipate

Evaluates block one step ahead (anticipate).

Block receives current value and NEXT value (peeked). Enables look-ahead transformations and transitions.

Examples:

Smooth transitions

s = S(1, 5, 3, 8).anticipate { |current, next_val|
  next_val ? (current + next_val) / 2.0 : current
}

Add interval information

notes = S(60, 64, 67, 72).anticipate { |pitch, next_pitch|
  interval = next_pitch ? next_pitch - pitch : nil
  {pitch: pitch, interval: interval}
}

Yields:

  • anticipation block

Yield Parameters:

  • current (Object)

    current value

  • next_value (Object, nil)

    next value (nil if last)

Yield Returns:

  • (Object)

    transformed value

Returns:

  • (Anticipate)

    anticipating serie



586
587
588
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 586

def anticipate(&block)
  Anticipate.new self, &block
end

#autorestartAutorestart

Auto-restarts serie when exhausted.

Creates an infinite serie from an original serie that automatically restarts from beginning when it reaches the end.

Examples:

Infinite loop

pattern = S(1, 2, 3).autorestart
pattern.infinite?  # => true
inst = pattern.i
inst.max_size(7).to_a  # => [1, 2, 3, 1, 2, 3, 1]

Returns:

  • (Autorestart)

    auto-restarting serie



111
112
113
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 111

def autorestart
  Autorestart.new self
end

#buffered(sync: false) ⇒ BufferSerie, SyncBufferSerie

Creates a buffered serie allowing multiple independent iterations over same source.

Provides buffering mechanism enabling multiple "readers" to independently iterate over the same serie source without interfering with each other.

Buffering Modes

  • Async (default): Buffers fill independently, each progresses at own pace
  • Sync: All buffers synchronized, restart affects all

Use Cases

  • Multiple voices reading same melodic sequence at different speeds
  • Polyphonic playback from single source
  • Canonic structures (rounds, fugues)
  • Independent transformations of same base material

Memory Management

History is automatically cleaned when all buffers have progressed past old values, preventing unbounded memory growth.

Examples:

Create buffered serie

buffered = S(1, 2, 3, 4).buffered
reader1 = buffered.buffer.i
reader2 = buffered.buffer.i

Multiple independent readers

source = S(1, 2, 3, 4).buffered
reader1 = source.buffer.i
reader2 = source.buffer.i

reader1.next_value  # => 1
reader2.next_value  # => 1 (independent)
reader1.next_value  # => 2
reader2.next_value  # => 2

Canon structure

melody = S(60, 64, 67, 72).buffered
voice1 = melody.buffer
voice2 = melody.buffer
# Play voice2 delayed by N beats

Parameters:

  • sync (Boolean) (defaults to: false)

    synchronized mode (default: false)

Returns:

  • (BufferSerie, SyncBufferSerie)

    buffered serie



51
52
53
54
55
56
57
# File 'lib/musa-dsl/series/buffer-serie.rb', line 51

def buffered(sync: false)
  if sync
    SyncBufferSerie.new(self)
  else
    BufferSerie.new(self)
  end
end

#compact_timedTimedCompacter

Removes timed events where all values are nil.

Filters out temporal "gaps" where no sources have active values. Useful after union operations that create nil placeholders, or for cleaning sparse sequences.

Removal logic:

  • Direct nil: { time: t, value: nil } → removed
  • All-nil Hash: { time: t, value: { a: nil, b: nil } } → removed
  • Partial Hash: { time: t, value: { a: 1, b: nil } } → kept (has non-nil)
  • All-nil Array: { time: t, value: [nil, nil] } → removed
  • Partial Array: { time: t, value: [1, nil] } → kept (has non-nil)

Examples:

Remove direct nil events

s = S({ time: 0r, value: 1 },
      { time: 1r, value: nil },
      { time: 2r, value: 3 })

s.compact_timed.i.to_a
# => [{ time: 0r, value: 1 },
#     { time: 2r, value: 3 }]

Remove all-nil hash events

s = S({ time: 0r, value: { a: 1, b: 2 } },
      { time: 1r, value: { a: nil, b: nil } },
      { time: 2r, value: { a: 3, b: nil } })

s.compact_timed.i.to_a
# => [{ time: 0r, value: { a: 1, b: 2 } },
#     { time: 2r, value: { a: 3, b: nil } }]  # Kept: has non-nil 'a'

Clean sparse union results

s1 = S({ time: 0r, value: 1 }, { time: 2r, value: 3 })
s2 = S({ time: 1r, value: 10 })

union = TIMED_UNION(melody: s1, bass: s2).i.to_a
# => [{ time: 0r, value: { melody: 1, bass: nil } },
#     { time: 1r, value: { melody: nil, bass: 10 } },
#     { time: 2r, value: { melody: 3, bass: nil } }]

# All events have at least one non-nil, so none removed

Returns:

  • (TimedCompacter)

    compacted serie

See Also:



578
579
580
# File 'lib/musa-dsl/series/timed-serie.rb', line 578

def compact_timed
  TimedCompacter.new(self)
end

#composer { ... } ⇒ ComposerAsOperationSerie

Creates a composer transformation pipeline for complex multi-stage transformations.

Composer provides declarative DSL for building transformation pipelines with multiple inputs, outputs, and intermediate processing stages.

Composer Concepts

  • Pipelines: Named transformation chains
  • Inputs: Named input series (proxied and buffered)
  • Outputs: Named output series
  • Operations: Transformation steps in pipeline
  • Auto-commit: Automatic finalization of pipelines

DSL Structure

composer do
  input_name >> operation1 >> operation2 >> :output_name
  :other_input >> transform >> :other_output
end

Musical Applications

  • Complex multi-voice processing
  • Effect chains and routing
  • Algorithmic composition pipelines
  • Multi-stage transformations
  • Modular synthesis-style routing

Examples:

Basic composer

s = S(1, 2, 3).composer do
  input.map { |x| x * 2 } >> :output
end
s.i.to_a  # => [2, 4, 6]

Multi-pipeline

composer = Composer.new(input: S(1, 2, 3)) do
  input.map { |x| x * 2 } >> :doubled
  input.map { |x| x * 3 } >> :tripled
end
composer.output(:doubled).i.to_a  # => [2, 4, 6]
composer.output(:tripled).i.to_a  # => [3, 6, 9]

Simple transformation

s.composer do
  input.map { |x| x + 1 } >> :output
end

Yields:

  • composer DSL block

Returns:



63
64
65
# File 'lib/musa-dsl/series/series-composer.rb', line 63

def composer(&block)
  ComposerAsOperationSerie.new(self, &block)
end

#cut(length) ⇒ Cutter

Cuts serie into chunks of specified length.

Returns serie of arrays, each containing length values.

Examples:

Cut into pairs

s = S(1, 2, 3, 4, 5, 6).cut(2)
s.i.to_a  # => [[1, 2], [3, 4], [5, 6]]

Parameters:

  • length (Integer)

    chunk size

Returns:

  • (Cutter)

    chunked serie



449
450
451
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 449

def cut(length)
  Cutter.new self, length
end

#flattenFlattener

Flattens nested series into single level.

Recursively consumes series elements that are themselves series.

Examples:

Flatten nested

s = S(S(1, 2), S(3, 4), 5).flatten
s.i.to_a  # => [1, 2, 3, 4, 5]

Returns:

  • (Flattener)

    flattened serie



196
197
198
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 196

def flatten
  Flattener.new self
end

#flatten_timedTimedFlattener

Splits compound timed values into individual timed events.

Converts events with Hash or Array values into separate timed events per element, preserving time and extra attributes. Direct values pass through unchanged.

Hash values → Hash of timed events (keyed by original keys):

{ time: 0, value: { a: 1, b: 2 }, velocity: { a: 80, b: 90 } }
# becomes:
{ a: { time: 0, value: 1, velocity: 80 },
  b: { time: 0, value: 2, velocity: 90 } }

Array values → Array of timed events (indexed):

{ time: 0, value: [1, 2], velocity: [80, 90] }
# becomes:
[{ time: 0, value: 1, velocity: 80 },
 { time: 0, value: 2, velocity: 90 }]

Direct values → Pass through unchanged (already flat)

Use Cases

  • Separate polyphonic events into individual voices
  • Split multi-track sequences for independent processing
  • Prepare for voice-specific routing via split
  • Enable per-voice filtering with compact_timed

Examples:

Hash values to individual voices

s = S({ time: 0r, value: { a: 60, b: 64 }, velocity: { a: 80, b: 90 } })

flat = s.flatten_timed.i
flat.next_value
# => { a: { time: 0r, value: 60, velocity: 80 },
#      b: { time: 0r, value: 64, velocity: 90 } }

Array values to indexed events

s = S({ time: 0r, value: [60, 64], velocity: [80, 90] })

flat = s.flatten_timed.i
flat.next_value
# => [{ time: 0r, value: 60, velocity: 80 },
#     { time: 0r, value: 64, velocity: 90 }]

Direct values pass through

s = S({ time: 0r, value: 60, velocity: 80 })

flat = s.flatten_timed.i
flat.next_value  # => { time: 0r, value: 60, velocity: 80 }

Returns:

  • (TimedFlattener)

    flattened timed serie

See Also:



527
528
529
# File 'lib/musa-dsl/series/timed-serie.rb', line 527

def flatten_timed
  TimedFlattener.new(self)
end

#hashify(*keys) ⇒ HashFromSeriesArray

Converts array values to hash with specified keys.

Takes array-valued serie and converts to hash using provided keys.

Examples:

Array to hash

s = S([60, 96], [64, 80]).hashify(:pitch, :velocity)
s.i.next_value  # => {pitch: 60, velocity: 96}

Parameters:

  • keys (Array)

    hash keys for array elements

Returns:

  • (HashFromSeriesArray)

    hashified serie



229
230
231
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 229

def hashify(*keys)
  HashFromSeriesArray.new self, keys
end

#lazy {|previous| ... } ⇒ LazySerieEval

Delays evaluation to next step (lazy evaluation).

Block receives previous value and evaluates for current step. Enables state-dependent transformations.

Examples:

Cumulative sum

s = S(1, 2, 3, 4).lazy { |prev| (prev || 0) + value }

Yields:

  • lazy evaluation block

Yield Parameters:

  • previous (Object, nil)

    previous value

Yield Returns:

Returns:

  • (LazySerieEval)

    lazy-evaluated serie



605
606
607
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 605

def lazy(&block)
  LazySerieEval.new self, &block
end

#lockLocker

Locks serie preventing further modifications.

Returns locked copy that cannot be transformed further.

Returns:

  • (Locker)

    locked serie



274
275
276
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 274

def lock
  Locker.new self
end

#map(isolate_values: nil) {|value| ... } ⇒ ProcessWith

Maps values via transformation block.

Simplest and most common transformation. Applies block to each value. Shorthand for with without additional series.

Examples:

Transpose notes

notes = S(60, 64, 67).map { |n| n + 12 }
notes.i.to_a  # => [72, 76, 79]

Transform to hash

s = S(1, 2, 3).map { |n| {value: n, squared: n**2} }

Parameters:

  • isolate_values (Boolean, nil) (defaults to: nil)

    clone values to prevent mutation (default: false)

Yields:

  • transformation block

Yield Parameters:

  • value (Object)

    current value

Yield Returns:

  • (Object)

    transformed value

Returns:

  • (ProcessWith)

    mapped serie



556
557
558
559
560
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 556

def map(isolate_values: nil, &block)
  isolate_values ||= isolate_values.nil? ? false : isolate_values

  ProcessWith.new self, isolate_values: isolate_values, &block
end

#max_size(length) ⇒ LengthLimiter

Limits serie to maximum number of values.

Stops after N values regardless of source length.

Examples:

Limit to 5

s = FOR(from: 0, step: 1).max_size(5)
s.i.to_a  # => [0, 1, 2, 3, 4]

Parameters:

  • length (Integer)

    maximum number of values

Returns:

  • (LengthLimiter)

    length-limited serie



164
165
166
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 164

def max_size(length)
  LengthLimiter.new self, length
end

#mergeMergeSerieOfSeries

Merges serie of series into single serie.

Flattens one level: consumes serie where each element is itself a serie, merging them sequentially.

Examples:

Merge phrases

phrases = S(S(1, 2, 3), S(4, 5, 6))
merged = phrases.merge
merged.i.to_a  # => [1, 2, 3, 4, 5, 6]

Returns:

  • (MergeSerieOfSeries)

    merged serie



466
467
468
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 466

def merge
  MergeSerieOfSeries.new self
end

#multiplex(*indexed_series, **hash_series) ⇒ MultiplexSelector

Multiplexes values from multiple series based on selector.

Like switch but returns composite values instead of switching.

Parameters:

  • indexed_series (Array)

    series to multiplex

  • hash_series (Hash)

    series to multiplex by key

Returns:

  • (MultiplexSelector)

    multiplexed serie



386
387
388
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 386

def multiplex(*indexed_series, **hash_series)
  MultiplexSelector.new self, indexed_series, hash_series
end

#process_with(**parameters) {|value, parameters| ... } ⇒ Processor

Processes values with parameterized block.

Generic processor passing values and parameters to block.

Parameters:

  • parameters (Hash)

    parameters passed to processor block

Yields:

  • processor block

Yield Parameters:

  • value (Object)

    current value

  • parameters (Hash)

    processor parameters

Returns:



212
213
214
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 212

def process_with(**parameters, &processor)
  Processor.new self, parameters, &processor
end

#proxyObject

TODO add test case



112
113
114
# File 'lib/musa-dsl/series/proxy-serie.rb', line 112

def proxy
  Series::ProxySerie.new(self)
end

#quantize(reference: nil, step: nil, value_attribute: nil, stops: nil, predictive: nil, left_open: nil, right_open: nil) ⇒ RawQuantizer, PredictiveQuantizer

Quantizes time-value serie to discrete steps.

Quantization Modes

  • Raw: Rounds values to nearest step, interpolates between points
  • Predictive: Predicts crossings of quantization boundaries

Applications

  • Quantize MIDI controller data to discrete values
  • Convert continuous pitch bends to semitones
  • Snap timing to grid
  • Generate stepped automation curves
  • Convert analog input to digital steps

Examples:

Basic quantization

# Quantize to semitones (12 steps per octave)
pitch_bend = S({time: 0r, value: 60.3}, {time: 1r, value: 61.8})
quantized = pitch_bend.quantize(step: 1)
quantized.i.to_a  # => [{time: 0, value: 60, duration: 1}, ...]

Predictive quantization

continuous = S({time: 0r, value: 0}, {time: 4r, value: 10})
pred = continuous.quantize(step: 2, predictive: true)
# Generates crossing points at values 0, 2, 4, 6, 8, 10

Parameters:

  • reference (Numeric, nil) (defaults to: nil)

    quantization reference (default: 0)

  • step (Numeric, nil) (defaults to: nil)

    step size (default: 1)

  • value_attribute (Symbol, nil) (defaults to: nil)

    attribute to quantize (default: :value)

  • stops (Boolean, nil) (defaults to: nil)

    include stop points (default: false)

  • predictive (Boolean, nil) (defaults to: nil)

    use predictive mode (default: false)

  • left_open (Boolean, nil) (defaults to: nil)

    left boundary open (raw mode)

  • right_open (Boolean, nil) (defaults to: nil)

    right boundary open (raw mode)

Returns:

  • (RawQuantizer, PredictiveQuantizer)

    quantized serie



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/musa-dsl/series/quantizer-serie.rb', line 45

def quantize(reference: nil, step: nil,
  value_attribute: nil,
  stops: nil,
  predictive: nil,
  left_open: nil,
  right_open: nil)
  
  # TODO remove debugging puts, intermediate hash comments on :info and InspectNice
  
  Series::Constructors.QUANTIZE(self,
                  reference: reference,
                  step: step,
                  value_attribute: value_attribute,
                  stops: stops,
                  predictive: predictive,
                  left_open: left_open,
                  right_open: right_open)
end

#queuedObject



151
152
153
# File 'lib/musa-dsl/series/queue-serie.rb', line 151

def queued
  Series::Constructors.QUEUE(self)
end

#randomize(random: nil) ⇒ Randomizer

Randomizes order of values.

Shuffles values randomly. Requires finite serie.

Examples:

Shuffle

s = S(1, 2, 3, 4, 5).randomize
s.i.to_a  # => random permutation

Parameters:

  • random (Random, nil) (defaults to: nil)

    Random instance (default: new Random)

Returns:

  • (Randomizer)

    randomized serie



307
308
309
310
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 307

def randomize(random: nil)
  random ||= Random.new
  Randomizer.new self, random
end

#remove(block = nil) {|value| ... } ⇒ Remover

Removes values matching condition.

Filters out values where block returns true.

Examples:

Remove odds

s = S(1, 2, 3, 4, 5).remove { |n| n.odd? }
s.i.to_a  # => [2, 4]

Parameters:

  • block (Proc, nil) (defaults to: nil)

    filter block

Yields:

  • filter condition

Yield Parameters:

  • value (Object)

    current value

Yield Returns:

  • (Boolean)

    true to remove value

Returns:

  • (Remover)

    filtered serie



328
329
330
331
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 328

def remove(block = nil, &yield_block)
  block ||= yield_block
  Remover.new self, &block
end

#repeat(times = nil, condition: nil) { ... } ⇒ Repeater, InfiniteRepeater

Repeats serie multiple times or conditionally.

Three modes:

  • times: Repeat exact number of times
  • condition: Repeat while condition true
  • neither: Infinite repetition

Examples:

Fixed repetitions

s = S(1, 2, 3).repeat(3)
s.i.to_a  # => [1, 2, 3, 1, 2, 3, 1, 2, 3]

Conditional repeat

count = 0
s = S(1, 2, 3).repeat { count += 1; count < 3 }

Infinite repeat

s = S(1, 2, 3).repeat
s.infinite?  # => true

Parameters:

  • times (Integer, nil) (defaults to: nil)

    number of repetitions

  • condition (Proc, nil) (defaults to: nil)

    condition block

Yields:

  • optional condition block

Returns:

  • (Repeater, InfiniteRepeater)

    repeating serie



141
142
143
144
145
146
147
148
149
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 141

def repeat(times = nil, condition: nil, &condition_block)
  condition ||= condition_block

  if times || condition
    Repeater.new self, times, &condition
  else
    InfiniteRepeater.new self
  end
end

#reverseReverser

Reverses order of values.

Consumes entire serie and returns values in reverse order. Requires finite serie.

Examples:

Retrograde

s = S(1, 2, 3, 4).reverse
s.i.to_a  # => [4, 3, 2, 1]

Returns:

  • (Reverser)

    reversed serie



290
291
292
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 290

def reverse
  Reverser.new self
end

#select(block = nil) {|value| ... } ⇒ Selector

Selects values matching condition.

Keeps only values where block returns true.

Examples:

Select evens

s = S(1, 2, 3, 4, 5).select { |n| n.even? }
s.i.to_a  # => [2, 4]

Parameters:

  • block (Proc, nil) (defaults to: nil)

    filter block

Yields:

  • filter condition

Yield Parameters:

  • value (Object)

    current value

Yield Returns:

  • (Boolean)

    true to keep value

Returns:

  • (Selector)

    filtered serie



349
350
351
352
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 349

def select(block = nil, &yield_block)
  block ||= yield_block
  Selector.new self, &block
end

#shift(shift) ⇒ Shifter

Rotates serie elements circularly.

Performs circular rotation of elements:

  • Negative values rotate left (first elements move to end)
  • Positive values rotate right (last elements move to beginning)
  • Zero performs no rotation

Note: Right rotation (positive values) requires finite series as the entire serie must be loaded into memory for rotation.

Examples:

Rotate left (negative shift)

s = S(60, 64, 67).shift(-1)  # First element moves to end
s.i.to_a  # => [64, 67, 60]

Rotate right (positive shift)

s = S(1, 2, 3, 4, 5).shift(2)  # Last 2 elements move to beginning
s.i.to_a  # => [4, 5, 1, 2, 3]

No rotation

s = S(1, 2, 3).shift(0)
s.i.to_a  # => [1, 2, 3]

Parameters:

  • shift (Integer)

    rotation amount

    • Negative: rotate left by N positions
    • Positive: rotate right by N positions
    • Zero: no rotation

Returns:

  • (Shifter)

    rotated serie



263
264
265
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 263

def shift(shift)
  Shifter.new self, shift
end

#skip(length) ⇒ Skipper

Skips first N values.

Discards first length values, returns rest.

Examples:

Skip first 2

s = S(1, 2, 3, 4, 5).skip(2)
s.i.to_a  # => [3, 4, 5]

Parameters:

  • length (Integer)

    number of values to skip

Returns:

  • (Skipper)

    serie with skipped values



181
182
183
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 181

def skip(length)
  Skipper.new self, length
end

#splitObject

Serie splitter for decomposing hash/array values into component series.

Splits series of hash or array values into individual component series, enabling independent access to each component.

Splitting Modes

  • Hash mode: Split {pitch: 60, velocity: 96} into separate series for :pitch and :velocity
  • Array mode: Split [60, 96] into separate series for indices 0, 1

Component Access

  • Hash: splitter[:pitch], splitter[:velocity]
  • Array: splitter[0], splitter[1]
  • Enumerable: splitter.each { |component| ... }

Use Cases

  • Separate polyphonic voices from single source
  • Independent processing of musical parameters
  • Extract specific components (pitch, duration, velocity, etc.)
  • Multi-track decomposition

Examples:

Split hash values

notes = S({pitch: 60, vel: 96}, {pitch: 64, vel: 80})
splitter = notes.split.i

pitches = splitter[:pitch]
velocities = splitter[:vel]

pitches.next_value  # => 60
velocities.next_value  # => 96

Split array values

pairs = S([1, 10], [2, 20], [3, 30]).split.i
first = pairs[0]
second = pairs[1]


44
45
46
# File 'lib/musa-dsl/series/hash-or-array-serie-splitter.rb', line 44

def split
  Splitter.new(self)
end

#switch(*indexed_series, **hash_series) ⇒ Switcher

Switches between multiple series based on selector values.

Uses selector serie to choose which source serie to read from. Selector values can be indices (Integer) or keys (Symbol).

Examples:

Index switching

s1 = S(1, 2, 3)
s2 = S(10, 20, 30)
selector = S(0, 1, 0, 1)
result = selector.switch(s1, s2)
result.i.to_a  # => [1, 10, 2, 20]

Parameters:

  • indexed_series (Array)

    series indexed by integer

  • hash_series (Hash)

    series indexed by symbol key

Returns:

  • (Switcher)

    switching serie



372
373
374
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 372

def switch(*indexed_series, **hash_series)
  Switcher.new self, indexed_series, hash_series
end

#switch_serie(*indexed_series, **hash_series) ⇒ SwitchFullSerie

Switches to entirely different series based on selector.

Changes which serie is being consumed entirely.

Parameters:

  • indexed_series (Array)

    series to switch between

  • hash_series (Hash)

    series to switch between by key

Returns:

  • (SwitchFullSerie)

    serie switcher



400
401
402
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 400

def switch_serie(*indexed_series, **hash_series)
  SwitchFullSerie.new self, indexed_series, hash_series
end

#union_timed(*other_timed_series, key: nil, **other_key_timed_series) ⇒ TimedUnionOfArrayOfTimedSeries, TimedUnionOfHashOfTimedSeries

Combines this timed serie with others via TIMED_UNION.

Convenience method for unioning series, supporting both array and hash modes. Calls Constructors#TIMED_UNION constructor with appropriate parameters.

Array mode: s1.union_timed(s2, s3) Hash mode: s1.union_timed(key: :melody, bass: s2, drums: s3)

Examples:

Array mode

melody = S({ time: 0r, value: 60 })
bass = S({ time: 0r, value: 36 })

melody.union_timed(bass).i.next_value
# => { time: 0r, value: [60, 36] }

Hash mode

melody = S({ time: 0r, value: 60 })
bass = S({ time: 0r, value: 36 })
drums = S({ time: 0r, value: 38 })

melody.union_timed(key: :melody, bass: bass, drums: drums).i.next_value
# => { time: 0r, value: { melody: 60, bass: 36, drums: 38 } }

Parameters:

  • other_timed_series (Array<Serie>)

    additional series (array mode)

  • key (Symbol, nil) (defaults to: nil)

    key name for this serie (hash mode)

  • other_key_timed_series (Hash{Symbol => Serie})

    named series (hash mode)

Returns:

  • (TimedUnionOfArrayOfTimedSeries, TimedUnionOfHashOfTimedSeries)

    union

Raises:

  • (ArgumentError)

    if mixing array and hash modes

See Also:



616
617
618
619
620
621
622
623
624
625
626
# File 'lib/musa-dsl/series/timed-serie.rb', line 616

def union_timed(*other_timed_series, key: nil, **other_key_timed_series)
  if key && other_key_timed_series.any?
    Series::Constructors.TIMED_UNION(key => self, **other_key_timed_series)

  elsif other_timed_series.any? && other_key_timed_series.empty?
    Series::Constructors.TIMED_UNION(self, *other_timed_series)

  else
    raise ArgumentError, 'Can\'t union an array of series with a hash of series'
  end
end

#with(*with_series, on_restart: nil, isolate_values: nil, **with_key_series) {|main_value, with_values, with_key_values| ... } ⇒ With Also known as: eval

Combines multiple series for mapping.

Synchronously iterates multiple series, passing all values to block. Enables multi-voice transformations and combinations.

Parameters

  • with_series: Positional series (passed as array to block)
  • with_key_series: Named series (passed as keywords to block)
  • on_restart: Block called on restart
  • isolate_values: Clone values to prevent mutation

Block Parameters

Block receives:

  • Main serie value (first argument)
  • Positional with_series values (array)
  • Keyword with_key_series values (keywords)

Examples:

Combine pitches and velocities

pitches = S(60, 64, 67)
velocities = S(96, 80, 64)
notes = pitches.with(velocities) { |p, v| {pitch: p, velocity: v} }
notes.i.to_a  # => [{pitch: 60, velocity: 96}, ...]

Named series

melody = S(60, 64, 67)
rhythm = S(1r, 0.5r, 0.5r)
combined = melody.with(duration: rhythm) { |pitch, duration:|
  {pitch: pitch, duration: duration}
}

Parameters:

  • with_series (Array<Serie>)

    positional series to combine

  • on_restart (Proc, nil) (defaults to: nil)

    restart callback

  • isolate_values (Boolean, nil) (defaults to: nil)

    clone values to prevent mutation

  • with_key_series (Hash)

    keyword series to combine

Yields:

  • combination block

Yield Parameters:

  • main_value (Object)

    value from main serie

  • with_values (Array)

    values from positional series

  • with_key_values (Hash)

    values from keyword series

Yield Returns:

Returns:

  • (With)

    combined serie



515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/musa-dsl/series/main-serie-operations.rb', line 515

def with(*with_series, on_restart: nil, isolate_values: nil, **with_key_series, &block)
  if with_series.any? && with_key_series.any?
    raise ArgumentError, 'Can\'t use extra parameters series and key named parameters series'
  end

  extra_series = if with_series.any?
                   with_series
                 elsif with_key_series.any?
                   with_key_series
                 end

  isolate_values ||= isolate_values.nil? ? true : isolate_values

  ProcessWith.new self, extra_series, on_restart, isolate_values: isolate_values, &block
end