From 3c21fd02b8245e8618507f6a6cb97186c48f78f8 Mon Sep 17 00:00:00 2001 From: Sam Aaron Date: Tue, 31 Oct 2023 22:09:09 +0000 Subject: [PATCH 1/4] Boot - set env vars for pipewire (matching pw-jack's behaviour) pw-jack sets the PIPEWIRE_QUANTUM and LD_LIBRARY_PATH env vars before execing a binary. To use this we'd need to run sonic pi from pw-jack (so the exec would do its work correctly). Instead of that we set the env vars within the call to popen2e when starting scsynth to match this behaviour. It is now also possible to use new change pipewire's buffsize and samplerate with two new audio-settings.toml config options: ``` linux_pipewire_buffsize = 1024 linux_pipewire_samplerate = 4800 ``` --- app/config/user-examples/audio-settings.toml | 11 +++++ app/server/ruby/bin/daemon.rb | 49 ++++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/app/config/user-examples/audio-settings.toml b/app/config/user-examples/audio-settings.toml index 1cb1e76663..1ad1e35dfe 100644 --- a/app/config/user-examples/audio-settings.toml +++ b/app/config/user-examples/audio-settings.toml @@ -121,6 +121,17 @@ # num_random_seeds = 64 +## =============================== +## Linux Audio - Pipewire settings +## =============================== +# +# Sonic Pi uses pipewire for Audio on Linux +# You can modify the pipewire parameters here: + +# linux_pipewire_buffsize = 1024 +# linux_pipewire_samplerate = 4800 + + ## ============ ## Escape hatch diff --git a/app/server/ruby/bin/daemon.rb b/app/server/ruby/bin/daemon.rb index dc6523086d..f74a0ed966 100755 --- a/app/server/ruby/bin/daemon.rb +++ b/app/server/ruby/bin/daemon.rb @@ -744,7 +744,8 @@ def idempotent_exit_cleanup class ProcessBooter attr_reader :pid, :args, :cmd, :log - def initialize(cmd, args, log_path, record_log=false) + def initialize(cmd, args, log_path, record_log=false, env=nil) + @env = env @pid = nil @log_file = nil @args = args.map {|el| el.to_s} @@ -786,7 +787,11 @@ def disable_internal_log_recording! def boot Util.log "Process Booter - booting #{@cmd} with args #{@args.inspect}" Util.log "#{@cmd} #{@args.join(' ')}" - @stdin, @stdout_and_err, @wait_thr = Open3.popen2e @cmd, *@args + if @env + @stdin, @stdout_and_err, @wait_thr = Open3.popen2e @env, @cmd, *@args + else + @stdin, @stdout_and_err, @wait_thr = Open3.popen2e @cmd, *@args + end @pid = @wait_thr.pid if @log_file @io_thr = Thread.new do @@ -1030,7 +1035,6 @@ def kill end end - class JackBooter < ProcessBooter def initialize cmd = "jackd" @@ -1134,6 +1138,10 @@ def initialize(ports, no_scsynth_inputs=false) toml_opts_hash = {} end + # freeze toml_opts_hash in case any nasty mutation happens below + # (oh for immutable data structures by default!) + toml_opts_hash.freeze + Util.log "Got Audio Settings toml hash: #{toml_opts_hash.inspect}" opts = unify_toml_opts_hash(toml_opts_hash) Util.log "Unified Audio Settings toml hash: #{opts.inspect}" @@ -1145,9 +1153,40 @@ def initialize(ports, no_scsynth_inputs=false) @num_outputs = opts["-o"].to_i args = opts.to_a.flatten cmd = Paths.scsynth_path + + case Util.os + when :linux, :raspberry + toml_pw_buffsize = toml_opts_hash["linux_pipewire_buffsize"].to_i + toml_pw_samplerate = toml_opts_hash["linux_pipewire_samplerate"].to_i + pw_buffsize = 1024 + pw_samplerate = 4800 + + if (toml_opts_hash.has_key?("linux_pipewire_buffsize") && (toml_pw_buffsize > 0)) + Util.log "Setting pipewire buffsize to: #{toml_pw_buffsize}" + pw_buffsize = toml_pw_buffsize + else + Util.log "Using default pipewire buffsize of: 1024" + end + + if (toml_opts_hash.has_key?("linux_pipewire_samplerate") && (toml_pw_samplerate > 0)) + Util.log "Setting pipewire samplerate to: #{toml_pw_samplerate}" + pw_samplerate = toml_pw_samplerate + else + Util.log "Using default pipewire buffsize of: 4800" + end + + ld_library_path = `pw-jack /bin/sh -c 'echo $LD_LIBRARY_PATH'`.strip + pw_quantum ="#{pw_buffsize}/#{pw_samplerate}" + + Util.log "Starting scsynth with LD_LIBRARY_PATH set to #{ld_library_path.inspect} so it uses pipewire's jack" + env = { "PIPEWIRE_QUANTUM" => pw_quantum , "LD_LIBRARY_PATH" => ld_library_path } + else + env = nil + end + @success = Promise.new run_pre_start_commands - super(cmd, args, Paths.scsynth_log_path, true) + super(cmd, args, Paths.scsynth_log_path, true, env) run_post_start_commands success = wait_for_boot disable_internal_log_recording! @@ -1234,7 +1273,7 @@ def run_pre_start_commands end end end - + def run_post_start_commands case Util.os when :raspberry, :linux From 5dd9db0e5dc48fde1f431b2b61f9db9d0d0a2d07 Mon Sep 17 00:00:00 2001 From: Sam Aaron Date: Tue, 31 Oct 2023 22:16:09 +0000 Subject: [PATCH 2/4] Boot - fix typos --- app/server/ruby/bin/daemon.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/server/ruby/bin/daemon.rb b/app/server/ruby/bin/daemon.rb index f74a0ed966..f48c77b97c 100755 --- a/app/server/ruby/bin/daemon.rb +++ b/app/server/ruby/bin/daemon.rb @@ -1159,7 +1159,7 @@ def initialize(ports, no_scsynth_inputs=false) toml_pw_buffsize = toml_opts_hash["linux_pipewire_buffsize"].to_i toml_pw_samplerate = toml_opts_hash["linux_pipewire_samplerate"].to_i pw_buffsize = 1024 - pw_samplerate = 4800 + pw_samplerate = 48000 if (toml_opts_hash.has_key?("linux_pipewire_buffsize") && (toml_pw_buffsize > 0)) Util.log "Setting pipewire buffsize to: #{toml_pw_buffsize}" @@ -1172,7 +1172,7 @@ def initialize(ports, no_scsynth_inputs=false) Util.log "Setting pipewire samplerate to: #{toml_pw_samplerate}" pw_samplerate = toml_pw_samplerate else - Util.log "Using default pipewire buffsize of: 4800" + Util.log "Using default pipewire samplerate of: 48000" end ld_library_path = `pw-jack /bin/sh -c 'echo $LD_LIBRARY_PATH'`.strip From d0ba8812306aa903bb5c0086dc1086243828e4e3 Mon Sep 17 00:00:00 2001 From: Sam Aaron Date: Tue, 31 Oct 2023 22:17:58 +0000 Subject: [PATCH 3/4] Boot - fix another typo --- app/config/user-examples/audio-settings.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config/user-examples/audio-settings.toml b/app/config/user-examples/audio-settings.toml index 1ad1e35dfe..f6077c8fcc 100644 --- a/app/config/user-examples/audio-settings.toml +++ b/app/config/user-examples/audio-settings.toml @@ -129,7 +129,7 @@ # You can modify the pipewire parameters here: # linux_pipewire_buffsize = 1024 -# linux_pipewire_samplerate = 4800 +# linux_pipewire_samplerate = 48000 From 2d6e9aec436a76131db325244348f1ef0aef7bf7 Mon Sep 17 00:00:00 2001 From: Sam Aaron Date: Tue, 31 Oct 2023 23:17:17 +0000 Subject: [PATCH 4/4] Boot - use symbols for linux_pipewire config keys --- app/server/ruby/bin/daemon.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/server/ruby/bin/daemon.rb b/app/server/ruby/bin/daemon.rb index f48c77b97c..ea46592ed8 100755 --- a/app/server/ruby/bin/daemon.rb +++ b/app/server/ruby/bin/daemon.rb @@ -1156,19 +1156,19 @@ def initialize(ports, no_scsynth_inputs=false) case Util.os when :linux, :raspberry - toml_pw_buffsize = toml_opts_hash["linux_pipewire_buffsize"].to_i - toml_pw_samplerate = toml_opts_hash["linux_pipewire_samplerate"].to_i + toml_pw_buffsize = toml_opts_hash[:linux_pipewire_buffsize].to_i + toml_pw_samplerate = toml_opts_hash[:linux_pipewire_samplerate].to_i pw_buffsize = 1024 pw_samplerate = 48000 - if (toml_opts_hash.has_key?("linux_pipewire_buffsize") && (toml_pw_buffsize > 0)) + if (toml_opts_hash.has_key?(:linux_pipewire_buffsize) && (toml_pw_buffsize > 0)) Util.log "Setting pipewire buffsize to: #{toml_pw_buffsize}" pw_buffsize = toml_pw_buffsize else Util.log "Using default pipewire buffsize of: 1024" end - if (toml_opts_hash.has_key?("linux_pipewire_samplerate") && (toml_pw_samplerate > 0)) + if (toml_opts_hash.has_key?(:linux_pipewire_samplerate) && (toml_pw_samplerate > 0)) Util.log "Setting pipewire samplerate to: #{toml_pw_samplerate}" pw_samplerate = toml_pw_samplerate else