Class: TEF::Sequencing::SheetSequence
- Inherits:
-
BaseSequence
- Object
- BaseSequence
- TEF::Sequencing::SheetSequence
- Defined in:
- lib/tef/Sequencing/SheetSequence.rb
Overview
Sheet Sequence class.
This is the main way for the user to specify an exact sequence of events to execute. It is construced with the help of a Sheet, which acts as specification for this Sheet Sequence.
Think of the Sheet as being the script for a play or movie, while the SheetSequence has the job of actually performing everything.
Instance Attribute Summary
Attributes inherited from BaseSequence
#end_time, #offset, #slope, #start_time, #state
Instance Method Summary collapse
-
#after(time, **options, &block) ⇒ Object
Similar to #at, but specifies time relative to the last event time.
-
#at(time, **options, &block) ⇒ Object
Insert an event or subsheet into the event list.
- #destroy! ⇒ Object
-
#initialize(offset, slope, **options) ⇒ SheetSequence
constructor
Initialize a SheetSequence.
-
#kill(pid) ⇒ Object
Shorthand to kill.
-
#play(music_piece, volume = 0.3) ⇒ Numeric
Play a music file, using ‘play`.
- #setup ⇒ Object
- #teardown ⇒ Object
Methods inherited from BaseSequence
#append_events, #parent_end_time, #parent_end_time=, #parent_start_time
Constructor Details
#initialize(offset, slope, **options) ⇒ SheetSequence
Initialize a SheetSequence.
This is mostly done via Player#[]= by passing a TEF::Sequencing::Sheet. However, a sequence can also be manually instantiated.
After initialization, the sheet’s TEF::Sequencing::Sheet#setup_block is called to fill this SheetSequence with actual content. The user may also call #at and #after manually to add additional events.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 25 def initialize(offset, slope, **) raise ArgumentError, 'Sheet must be supplied!' unless [:sheet] @sheet = [:sheet] if @sheet.tempo slope *= (@sheet.tempo / (60.to_f * ([:top_slope] || 1))) end super(offset, slope, **); @notes = [] @latest_note_time = nil; @subprograms = [] @active_music = [] @start_time = @sheet.start_time @end_time = @sheet.end_time if block = @sheet.fill_block instance_exec(@opts_hash, &block) @start_time ||= @notes[0]&.dig(:time) || 0; @end_time ||= @notes[-1]&.dig(:time) || 0; @state = :idle; end end |
Instance Method Details
#after(time, **options, &block) ⇒ Object
Similar to #at, but specifies time relative to the last event time.
147 148 149 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 147 def after(time, **, &block) at(time + (@latest_note_time || 0) , **, &block); end |
#at(time, **options, &block) ⇒ Object
Insert an event or subsheet into the event list.
This is the main way of adding events to this sequence. Each call to the function will insert a new event at the specified time, which will call the passed block. If, instead, a parameter called :sheet or :sequence is passed, instead the the passed sequence will be instantiated and added to the list of programs. Any further options are directly passed to the constructor of the class specified by the :sequence parameter
If a block was passed, it will be instance_exec’d at the specified time.
Note that any used or created resources shall be destroyed in the TEF::Sequencing::Sheet#teardown block. Sub-Sequences as well as notes played by #play are automatically torn down.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 105 def at(time, **, &block) time = time.to_f if repeat_time = @sheet.repeat_time time = time % repeat_time end time = time.round(3); @latest_note_time = time; [:sequence] = SheetSequence if [:sheet] if prog = [:sequence] [:slope] ||= 1 [:top_slope] = @slope prog = prog.new(time, [:slope], **) if e_time = [:end_time] prog.parent_end_time = e_time; end i = @subprograms.bsearch_index { |s| s.parent_start_time > prog.parent_start_time } @subprograms.insert((i || -1), prog); return end new_event = { time: time, code: block, instance: self, } i = @notes.bsearch_index { |e| e[:time] > time } @notes.insert((i || -1), new_event); end |
#destroy! ⇒ Object
81 82 83 84 85 86 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 81 def destroy!() super(); @notes = nil; @subprograms.each(&:destroy!) end |
#kill(pid) ⇒ Object
Shorthand to kill
173 174 175 176 177 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 173 def kill(pid) Process.kill('QUIT', pid); rescue Errno::ESRCH return false end |
#play(music_piece, volume = 0.3) ⇒ Numeric
Play a music file, using ‘play`
The PID of the music playing task is saved, and the process will be killed when this sheet is torn down. The user does not have to do anything else, but may choose to prematurely kill the playback by using #kill
160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 160 def play(music_piece, volume = 0.3) play_pid = spawn(*%W{play -q --volume #{volume} #{music_piece}}); Thread.new do @active_music << play_pid Process.wait(play_pid) @active_music.delete play_pid end play_pid end |
#setup ⇒ Object
55 56 57 58 59 60 61 62 63 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 55 def setup() return unless @state == :idle super(); if block = @sheet.setup_block instance_exec(@opts_hash, &block) end end |
#teardown ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/tef/Sequencing/SheetSequence.rb', line 65 def teardown() return unless @state == :running if block = @sheet.teardown_block instance_eval(&block) end @subprograms.each(&:teardown) @active_music.each do |pid| self.kill pid end super(); end |