Class: TEF::Sequencing::SheetSequence

Inherits:
BaseSequence show all
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

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.

Raises:

  • (ArgumentError)


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, **options)
	raise ArgumentError, 'Sheet must be supplied!' unless options[:sheet]

	@sheet = options[:sheet]

	if @sheet.tempo
		slope *= (@sheet.tempo / (60.to_f * (options[:top_slope] || 1)))
	end

	super(offset, slope, **options);

	@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.

See Also:



147
148
149
# File 'lib/tef/Sequencing/SheetSequence.rb', line 147

def after(time, **options, &block)
	at(time + (@latest_note_time || 0) , **options, &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, **options, &block)
	time = time.to_f

	if repeat_time = @sheet.repeat_time
		time = time % repeat_time
	end

	time = time.round(3);

	@latest_note_time = time;

	options[:sequence] = SheetSequence if options[:sheet]

	if prog = options[:sequence]
		options[:slope] ||= 1
		options[:top_slope] = @slope

		prog = prog.new(time, options[:slope], **options)

		if e_time = options[: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

Parameters:

  • music_piece (String)

    Path of the file to play.

Returns:

  • (Numeric)

    The PID of the spawned process.



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

#setupObject



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

#teardownObject



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