Class: Wavify::Sequencer::Engine
- Inherits:
-
Object
- Object
- Wavify::Sequencer::Engine
- Defined in:
- lib/wavify/sequencer/engine.rb,
sig/sequencer.rbs
Overview
Schedules sequencer tracks and renders them into audio.
Constant Summary collapse
- DEFAULT_BEATS_PER_BAR =
Default beats-per-bar value used when omitted.
4- MAX_BARS =
Maximum bars accepted by a single render request.
100_000- MAX_ARRANGEMENT_SECTIONS =
Maximum structural sections accepted before repeat expansion.
10_000- MAX_EXPANDED_SECTIONS =
Maximum sections produced by lazy arrangement repeat expansion.
100_000- MAX_SECTION_REPEAT =
Maximum repeat count on one arrangement section.
10_000- MAX_EVENTS =
Maximum scheduled timeline events in one build.
1_000_000- MASTER_CEILING_DB =
Final sequencer ceiling leaves conversion margin below digital full scale.
-0.1 # The master limiter uses its full lookahead window to ramp gain before peaks.
- MASTER_LOOKAHEAD_SECONDS =
The master limiter uses its full lookahead window to ramp gain before peaks.
0.005
Instance Attribute Summary collapse
-
#beats_per_bar ⇒ Integer
readonly
Returns the value of attribute beats_per_bar.
-
#format ⇒ Core::Format
readonly
Returns the value of attribute format.
-
#swing ⇒ Float
readonly
Returns the value of attribute swing.
-
#tempo ⇒ Float
readonly
Returns the value of attribute tempo.
Instance Method Summary collapse
-
#bar_duration_seconds ⇒ Float
Duration of one bar in seconds.
-
#build_timeline(tracks:, arrangement: nil, default_bars: 1) ⇒ Array<Hash>
Builds a combined event timeline for tracks and optional arrangement.
- #expand_pattern_step(step, start_time:, duration:) ⇒ Array[Hash[Symbol, untyped]]
-
#initialize(tempo:, format: Wavify::Core::Format::CD_QUALITY, beats_per_bar: DEFAULT_BEATS_PER_BAR, swing: 0.5) ⇒ Engine
constructor
A new instance of Engine.
-
#render(tracks:, arrangement: nil, default_bars: 1) ⇒ Wavify::Audio
Renders tracks into a mixed audio object.
-
#seconds_per_beat ⇒ Float
Seconds per beat at the current tempo.
- #step_duration_at(index, resolution) ⇒ Float
- #step_duration_seconds(resolution) ⇒ Float
- #step_start_seconds(index, resolution) ⇒ Float
- #timeline_for_track(track, bars:, start_bar: 0, start_time: nil) ⇒ Array[Hash[Symbol, untyped]]
Constructor Details
#initialize(tempo:, format: Wavify::Core::Format::CD_QUALITY, beats_per_bar: DEFAULT_BEATS_PER_BAR, swing: 0.5) ⇒ Engine
Returns a new instance of Engine.
26 27 28 29 30 31 32 |
# File 'lib/wavify/sequencer/engine.rb', line 26 def initialize(tempo:, format: Wavify::Core::Format::CD_QUALITY, beats_per_bar: DEFAULT_BEATS_PER_BAR, swing: 0.5) @tempo = validate_tempo!(tempo) @format = validate_format!(format) @beats_per_bar = () @swing = validate_swing!(swing) @section_engine_cache = {} end |
Instance Attribute Details
#beats_per_bar ⇒ Integer (readonly)
Returns the value of attribute beats_per_bar.
24 25 26 |
# File 'lib/wavify/sequencer/engine.rb', line 24 def @beats_per_bar end |
#format ⇒ Core::Format (readonly)
Returns the value of attribute format.
24 25 26 |
# File 'lib/wavify/sequencer/engine.rb', line 24 def format @format end |
#swing ⇒ Float (readonly)
Returns the value of attribute swing.
24 25 26 |
# File 'lib/wavify/sequencer/engine.rb', line 24 def swing @swing end |
#tempo ⇒ Float (readonly)
Returns the value of attribute tempo.
24 25 26 |
# File 'lib/wavify/sequencer/engine.rb', line 24 def tempo @tempo end |
Instance Method Details
#bar_duration_seconds ⇒ Float
Returns duration of one bar in seconds.
40 41 42 |
# File 'lib/wavify/sequencer/engine.rb', line 40 def .to_f end |
#build_timeline(tracks:, arrangement: nil, default_bars: 1) ⇒ Array<Hash>
Builds a combined event timeline for tracks and optional arrangement.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/wavify/sequencer/engine.rb', line 99 def build_timeline(tracks:, arrangement: nil, default_bars: 1) track_map = normalize_tracks(tracks) if arrangement build_arranged_timeline(track_map, arrangement) else unless .is_a?(Integer) && .between?(1, MAX_BARS) raise SequencerError, "default_bars must be an Integer in 1..#{MAX_BARS}" end events = track_map.values.flat_map do |track| timeline_for_track(track, bars: ) end events.sort_by { |event| [event[:start_time], event[:track].to_s] } end end |
#expand_pattern_step(step, start_time:, duration:) ⇒ Array[Hash[Symbol, untyped]]
58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/wavify/sequencer/engine.rb', line 58 def (step, start_time:, duration:) ratchet = step.ratchet || 1 event_duration = duration / ratchet Array.new(ratchet) do |ratchet_index| { start_time: start_time + (event_duration * ratchet_index), duration: event_duration, velocity: step.velocity, probability: step.probability || 1.0, ratchet_index: ratchet_index, ratchet_count: ratchet } end end |
#render(tracks:, arrangement: nil, default_bars: 1) ⇒ Wavify::Audio
Renders tracks into a mixed audio object.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/wavify/sequencer/engine.rb', line 122 def render(tracks:, arrangement: nil, default_bars: 1) track_map = normalize_tracks(tracks) sections = arrangement ? normalize_arrangement(arrangement, track_map) : nil rendered_audios = if sections render_arranged_tracks(track_map, sections) else track_map.values.filter_map { |track| render_track_audio(track, bars: ) } end return Wavify::Audio.silence(0.0, format: @format) if rendered_audios.empty? master_audio(rendered_audios) end |
#seconds_per_beat ⇒ Float
Returns seconds per beat at the current tempo.
35 36 37 |
# File 'lib/wavify/sequencer/engine.rb', line 35 def seconds_per_beat seconds_per_beat_rational.to_f end |
#step_duration_at(index, resolution) ⇒ Float
54 55 56 |
# File 'lib/wavify/sequencer/engine.rb', line 54 def step_duration_at(index, resolution) step_duration_time_at(index, resolution).to_f end |
#step_duration_seconds(resolution) ⇒ Float
44 45 46 47 48 |
# File 'lib/wavify/sequencer/engine.rb', line 44 def step_duration_seconds(resolution) raise SequencerError, "resolution must be a positive Integer" unless resolution.is_a?(Integer) && resolution.positive? step_duration_time(resolution).to_f end |
#step_start_seconds(index, resolution) ⇒ Float
50 51 52 |
# File 'lib/wavify/sequencer/engine.rb', line 50 def step_start_seconds(index, resolution) step_start_time(index, resolution).to_f end |
#timeline_for_track(track, bars:, start_bar: 0, start_time: nil) ⇒ Array[Hash[Symbol, untyped]]
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/wavify/sequencer/engine.rb', line 73 def timeline_for_track(track, bars:, start_bar: 0, start_time: nil) raise SequencerError, "track must be a Sequencer::Track" unless track.is_a?(Track) unless .is_a?(Integer) && .between?(1, MAX_BARS) raise SequencerError, "bars must be an Integer in 1..#{MAX_BARS}" end raise SequencerError, "start_bar must be a non-negative Integer" unless .is_a?(Integer) && >= 0 if start_time && !(start_time.is_a?(Numeric) && start_time.respond_to?(:finite?) && start_time.finite? && start_time >= 0) raise SequencerError, "start_time must be a non-negative finite Numeric" end events = [] events.concat(schedule_pattern_events(track, bars: , start_bar: , start_time: start_time)) if track.pattern? events.concat(schedule_note_events(track, bars: , start_bar: , start_time: start_time)) if track.notes? events.concat(schedule_chord_events(track, bars: , start_bar: , start_time: start_time)) if track.chords? raise SequencerError, "timeline exceeds #{MAX_EVENTS} events" if events.length > MAX_EVENTS events.map { |event| decorate_event_timing(event) } .sort_by { |event| [event[:start_frame], event[:kind].to_s] } end |