class Musicality::SuperCollider::Conductor
Constants
- BASE_FPATH_PLACEHOLDER
- COMPLETION_MSG
- POST_PID_MSG
- SC_FOOTER
- SC_HEADER
- SYNTHDEFS_PLACEHOLDER
Public Class Methods
new(score)
click to toggle source
# File lib/musicality/performance/supercollider/conductor.rb, line 5 def initialize score unless score.is_a?(Score::Timed) raise ArgumentError, "The given score is not a Score::Timed. \ Convert it first using ScoreConverter." end parts = score.collated? ? score.parts : ScoreCollator.new(score).collate_parts @performers = Hash[ parts.map do |name, part| [name, Performer.new(part)] end] end
Public Instance Methods
bundles(selected_parts = @performers.keys, lead_time)
click to toggle source
# File lib/musicality/performance/supercollider/conductor.rb, line 24 def bundles selected_parts = @performers.keys, lead_time Node.reset_id_counter default_group = Group.default(nil) aux_audio_bus = 16 volume_control_bus = 0 bundle_kwargs = { :parent_group => default_group, :aux_audio_bus => aux_audio_bus, :volume_control_bus => volume_control_bus, :lead_time => lead_time } all_bundles = [ default_group.bundle_queue(0.0) ] selected_parts.each do |name| bundles = @performers[name].bundles(**bundle_kwargs) bundle_kwargs[:aux_audio_bus] += 2 bundle_kwargs[:volume_control_bus] += 1 all_bundles.concat bundles end all_bundles = all_bundles.sort_by {|b| b.time } default_group.free_all all_bundles.push(default_group.bundle_queue(lead_time+all_bundles.last.time)) coalesced_bundles = [] all_bundles.each do |bundle| if coalesced_bundles.any? && coalesced_bundles.last.time == bundle.time coalesced_bundles.last.messages.concat bundle.messages else coalesced_bundles.push bundle end end return coalesced_bundles end
perform(base_fpath, selected_parts: @performers.keys, verbose: false, lead_time: 0.1, keep_code: false)
click to toggle source
# File lib/musicality/performance/supercollider/conductor.rb, line 17 def perform base_fpath, selected_parts: @performers.keys, verbose: false, lead_time: 0.1, keep_code: false bundles = bundles(selected_parts, lead_time) fpath = write_sc_code bundles, base_fpath exec_sc_code fpath, bundles.last.time, verbose File.delete(fpath) unless keep_code end
Private Instance Methods
exec_sc_code(fpath, last_time, verbose)
click to toggle source
# File lib/musicality/performance/supercollider/conductor.rb, line 105 def exec_sc_code fpath, last_time, verbose post_sclang_pid = Regexp.new(POST_PID_MSG + " ([0-9]+)") sclang_pid = nil IO.popen("sclang \"#{fpath}\"") do |io| pid = io.pid while response = io.gets puts response if verbose case response.chomp when post_sclang_pid sclang_pid = $1.to_i when COMPLETION_MSG if OS.windows? Process.kill 9, sclang_pid else Process.kill "INT", sclang_pid end end end end end
write_sc_code(bundles, base_fpath)
click to toggle source
# File lib/musicality/performance/supercollider/conductor.rb, line 93 def write_sc_code bundles, base_fpath fpath = "#{base_fpath}.scd" File.open(fpath, "w") do |f| synthdefs = [ SynthDefs::VOLUME_CONTROL, SynthDefs::VOLUME_CHANGE ] + @performers.values.map {|p| p.settings.synthdef } f.write SC_HEADER.gsub(SYNTHDEFS_PLACEHOLDER, synthdefs.map {|s| s.to_sclang }.join("\n\n")) f.write bundles.map {|b| " " + b.to_sclang }.join(",\n") + "\n" f.write SC_FOOTER.gsub(BASE_FPATH_PLACEHOLDER, base_fpath) end fpath end