diff --git a/.codex/config.toml##os.Linux b/.codex/config.toml##os.Linux index c61e680..08f0f7d 100644 --- a/.codex/config.toml##os.Linux +++ b/.codex/config.toml##os.Linux @@ -1,5 +1,5 @@ -model = "gpt-5.6-sol" -model_reasoning_effort = "high" +model = "gpt-6-astra" +model_reasoning_effort = "medium" personality = "pragmatic" tool_output_token_limit = 25000 # Leave room for native compaction near the 272–273k context window. @@ -18,6 +18,7 @@ notification_condition = "always" [tui.model_availability_nux] "gpt-5.5" = 4 "gpt-5.6-sol" = 4 +gpt-6-astra = 4 [sandbox_workspace_write] network_access = true @@ -254,6 +255,12 @@ trust_level = "trusted" [projects."/home/sudacode/projects/sudacode-blog"] trust_level = "trusted" +[projects."/home/sudacode/projects/japanese/Mangatan"] +trust_level = "trusted" + +[projects."/truenas/jellyfin/anime/I Made Friends with the Second Prettiest Girl in My Class/Season-1"] +trust_level = "trusted" + [notice.model_migrations] "gpt-5.3-codex" = "gpt-5.4" diff --git a/.config/hypr/hyprland.lua b/.config/hypr/hyprland.lua index e09048f..9391b5e 100644 --- a/.config/hypr/hyprland.lua +++ b/.config/hypr/hyprland.lua @@ -419,3 +419,4 @@ hl.on("hyprland.start", function() hl.exec_cmd("uwsm app -sb -t service -- tailscale systray") hl.exec_cmd("~/.local/bin/aria") end) +pcall(require, "/home/sudacode/.config/hypr/openwhispr-binds.lua") diff --git a/.config/hypr/keybindings.lua b/.config/hypr/keybindings.lua index bb8b756..b172f3c 100644 --- a/.config/hypr/keybindings.lua +++ b/.config/hypr/keybindings.lua @@ -11,7 +11,7 @@ local menu = "~/.config/rofi/launchers/type-6/launcher.sh 1" local mainMod = "ALT" -- Sets "Windows" key as main modifier -- Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more -hl.bind("SUPER + SUPER_L", hl.dsp.exec_cmd("~/.config/rofi/launchers/type-2/launcher.sh 10")) +-- hl.bind("SUPER + SUPER_L", hl.dsp.exec_cmd("~/.config/rofi/launchers/type-2/launcher.sh 10")) hl.bind(mainMod .. " + RETURN", hl.dsp.exec_cmd(terminal)) hl.bind(mainMod .. " + Q", hl.dsp.window.close()) hl.bind(mainMod .. " + SHIFT + M", hl.dsp.exec_cmd("uwsm stop")) diff --git a/.config/hypr/windowrules.lua b/.config/hypr/windowrules.lua index db03ce8..de2d2dd 100644 --- a/.config/hypr/windowrules.lua +++ b/.config/hypr/windowrules.lua @@ -299,7 +299,7 @@ hl.window_rule({ -- {{{ GSM Overlay and LunaTranslator tweaks hl.window_rule({ match = { - class = "gamesentenceminer", + class = "com.beangate.gamesentenceminer", }, float = true, border_size = 0, @@ -395,6 +395,19 @@ hl.window_rule({ pin = true, }) +hl.window_rule({ + match = { + class = "open-whispr", + title = "Voice Recorder", + }, + float = true, + xray = false, + no_shadow = true, + no_blur = true, + no_dim = true, + opaque = true, +}) + -- TODO: manual review — top-level key 'windowurle = no_vrr on, match:class mpv' has no enclosing section -- aibar popup (AI usage widget) diff --git a/.config/mimeapps.list b/.config/mimeapps.list index 149ced2..e1689ae 100644 --- a/.config/mimeapps.list +++ b/.config/mimeapps.list @@ -160,5 +160,5 @@ x-scheme-handler/subminer=subminer.desktop x-scheme-handler/claude-cli=claude-code-url-handler.desktop x-scheme-handler/mux=mux.desktop x-scheme-handler/claude=com.anthropic.claude-desktop.desktop -x-scheme-handler/t3code=t3code-url-handler.desktop +x-scheme-handler/t3code=com.t3tools.T3Code.desktop x-scheme-handler/codex=ChatGPT.desktop diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json index 79878b7..c5b8eba 100644 --- a/.config/nvim/lazy-lock.json +++ b/.config/nvim/lazy-lock.json @@ -20,6 +20,7 @@ "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" }, "gitsigns.nvim": { "branch": "main", "commit": "5be654f2232c10ddcad19c1607a67b6b4b78fc29" }, "goto-preview": { "branch": "main", "commit": "d2d6923c9b9e0e43f0b9b566f261a8b1ae016540" }, + "hererocks": { "branch": "master", "commit": "5d77b0bafc8b96f82355ca2ce5637c00d78a065c" }, "image.nvim": { "branch": "master", "commit": "5c6f29a5069e1f7bd5773ce5907454063b0f125d" }, "img-clip.nvim": { "branch": "main", "commit": "b6ddfb97b5600d99afe3452d707444afda658aca" }, "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" }, @@ -38,7 +39,7 @@ "nvim-lint": { "branch": "master", "commit": "3d55c8f67c6ae5c15e1042571e107c7a3d5c5f4e" }, "nvim-lspconfig": { "branch": "master", "commit": "16286347bdba1333c7d124d9de9fe6630731b2b2" }, "nvim-nio": { "branch": "master", "commit": "edcc181a875301dd21840189aa2f2f9ad69fc172" }, - "nvim-treesitter": { "branch": "master", "commit": "cf12346a3414fa1b06af75c79faebe7f76df080a" }, + "nvim-treesitter": { "branch": "main", "commit": "19071296d3d643b48615ee574a20e8a03ac40872" }, "nvim-treesitter-context": { "branch": "master", "commit": "f3061339b8eaf9fda873600bc425b8d2d8502533" }, "nvim-web-devicons": { "branch": "master", "commit": "5f032a85be210cd1c6ac98861eb3b187ff3bd5eb" }, "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" }, diff --git a/.config/pipewire/pipewire.conf.d/99-input-denoising.conf b/.config/pipewire/pipewire.conf.d/99-input-denoising.conf index 9c1e039..be9e767 100644 --- a/.config/pipewire/pipewire.conf.d/99-input-denoising.conf +++ b/.config/pipewire/pipewire.conf.d/99-input-denoising.conf @@ -22,11 +22,14 @@ context.modules = [ capture.props = { node.name = "capture.rnnoise_source" node.passive = true + target.object = "alsa_input.pci-0000_0d_00.4.analog-stereo" audio.rate = 48000 } playback.props = { node.name = "rnnoise_source" + node.description = "Noise-cancelled wired earbuds" media.class = "Audio/Source" + priority.session = 3000 audio.rate = 48000 } audio.channels = 1 diff --git a/.config/wireplumber/wireplumber.conf b/.config/wireplumber/wireplumber.conf deleted file mode 100644 index ee1aa1d..0000000 --- a/.config/wireplumber/wireplumber.conf +++ /dev/null @@ -1,1009 +0,0 @@ -## The WirePlumber configuration - -context.spa-libs = { - ## SPA factory name to library mappings - ## Used to find SPA factory names. It maps a SPA factory name regular - ## expression to a library name that should contain that factory. - ## - ## Syntax: - ## = - - api.alsa.* = alsa/libspa-alsa - api.bluez5.* = bluez5/libspa-bluez5 - api.v4l2.* = v4l2/libspa-v4l2 - api.libcamera.* = libcamera/libspa-libcamera - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - -context.modules = [ - ## PipeWire modules to load. - ## These modules are loaded before a connection to pipewire is attempted. - ## This section should be kept minimal and load only the modules that are - ## necessary for the protocol to work. - ## - ## If ifexists is given, the module is ignored when it is not found. - ## If nofail is given, module initialization failures are ignored. - ## - ## Syntax: - ## { - ## name = - ## [ args = { = ... } ] - ## [ flags = [ ifexists | nofail ] ] - ## } - - # Uses RTKit to boost the data thread priority. Also allows clamping - # of utilisation when using the Completely Fair Scheduler on Linux. - { - name = libpipewire-module-rt - args = { - nice.level = -11 - # rt.prio = 88 - # rt.time.soft = -1 - # rt.time.hard = -1 - # uclamp.min = 0 - # uclamp.max = 1024 - } - flags = [ ifexists, nofail ] - } - - ## The native communication protocol. - { name = libpipewire-module-protocol-native } - - ## Support for metadata objects - { name = libpipewire-module-metadata } -] - -wireplumber.profiles = { - ## Syntax: - ## = { - ## inherits = [ other, profile, names ] # optional - ## # optional is the default - ## = [ required | optional | disabled ] - ## ... - ## } - - # The default profile - main = { - inherits = [ base ] - - metadata.sm-settings = required - metadata.sm-objects = required - - policy.standard = required - - hardware.audio = required - hardware.bluetooth = required - hardware.video-capture = required - } - - # Profile for running on a systemwide level - main-systemwide = { - inherits = [ main, mixin.systemwide-session ] - } - - # Typical profile for embedded use cases, systemwide without maintaining state - main-embedded = { - inherits = [ main, mixin.systemwide-session, mixin.stateless ] - } - - # Profile for video-only use cases (camera & screen sharing) - video-only = { - inherits = [ main ] - hardware.audio = disabled - hardware.bluetooth = disabled - } - - ## - ## Profiles for multi-instance setup - ## - - policy = { - inherits = [ base ] - metadata.sm-settings = required - metadata.sm-objects = required - policy.standard = required - } - - audio = { - inherits = [ base ] - hardware.audio = required - } - - bluetooth = { - inherits = [ base ] - hardware.bluetooth = required - } - - video-capture = { - inherits = [ base ] - hardware.video-capture = required - } - - ## - ## The following are not complete profiles, but blocks that can be inherited - ## - - # The bare minimum that all instances should have by default - base = { - check.no-media-session = required - support.settings = required - support.log-settings = required - support.session-services = required - } - - # Disable features that are meant only for user sessions - mixin.systemwide-session = { - support.reserve-device = disabled - monitor.alsa.reserve-device = disabled - - support.portal-permissionstore = disabled - script.client.access-portal = disabled - - support.logind = disabled - monitor.bluez.seat-monitoring = disabled - } - - # Disable storing/restoring runtime state information regarding - # default nodes selection, profiles, routes and stream volume / properties - # This makes sense for embedded systems that should always reboot to a default - # state, not remembering any of the user's runtime changes - mixin.stateless = { - hooks.device.profile.state = disabled - hooks.device.routes.state = disabled - hooks.default-nodes.state = disabled - hooks.stream.state = disabled - } -} - -wireplumber.components = [ - ## WirePlumber components to load. - ## These components are loaded after a connection to pipewire is established. - ## type is mandatory; rest of the tags are optional - ## - ## Syntax: - ## { - ## name = - ## type = - ## arguments = { } - ## - ## # Feature that this component provides - ## provides = - ## - ## # List of features that must be provided before this component is loaded - ## requires = [ ] - ## - ## # List of features that would offer additional functionality if provided - ## # but are not strictly required - ## wants = [ ] - ## } - - ## Check to avoid loading together with media-session - { - name = ensure-no-media-session, type = built-in - provides = check.no-media-session - } - - ## Makes a secondary connection to PipeWire for exporting objects - { - name = export-core, type = built-in - provides = support.export-core - } - - ## Enables creating local nodes that are exported to pipewire - ## This is needed for LocalNode() / WpImplNode - ## This should be used with the export-core to avoid protocol deadlocks, - ## unless you know what you are doing - { - name = libpipewire-module-client-node, type = pw-module - provides = pw.client-node - wants = [ support.export-core ] - } - - ## Enables creating local devices that are exported to pipewire - ## This is needed for SpaDevice() / WpSpaDevice - ## This should be used with the export-core to avoid protocol deadlocks, - ## unless you know what you are doing - { - name = libpipewire-module-client-device, type = pw-module - provides = pw.client-device - wants = [ support.export-core ] - } - - # Provides a node factory to create SPA nodes - # You need this to use LocalNode("spa-node-factory", ...) - { - name = libpipewire-module-spa-node-factory, type = pw-module - provides = pw.node-factory.spa - requires = [ pw.client-node ] - } - - ## Provides a node factory to create SPA nodes wrapped in an adapter - ## You need this to use LocalNode("adapter", ...) - { - name = libpipewire-module-adapter, type = pw-module - provides = pw.node-factory.adapter - requires = [ pw.client-node ] - } - - ## Provides the "sm-settings" metadata object - { - name = libwireplumber-module-settings, type = module - arguments = { metadata.name = sm-settings } - provides = metadata.sm-settings - } - - ## Activates a global WpSettings instance, providing settings from - ## the sm-settings metadata object. Note that this blocks and waits for the - ## sm-settings metadata object to become available, so one instance must - ## provide that, while others should only load this to access settings - { - name = settings-instance, type = built-in - arguments = { metadata.name = sm-settings } - provides = support.settings - after = [ metadata.sm-settings ] - } - - ## Log level settings - { - name = libwireplumber-module-log-settings, type = module - provides = support.log-settings - } - - ## The lua scripting engine - { - name = libwireplumber-module-lua-scripting, type = module - provides = support.lua-scripting - } - - ## Module listening for pipewire objects to push events - { - name = libwireplumber-module-standard-event-source, type = module - provides = support.standard-event-source - } - - ## The shared D-Bus connection - { - name = libwireplumber-module-dbus-connection, type = module - arguments = { plugin.name = dbus-connection, bus.system = false } - provides = support.dbus - } - - ## The shared system D-Bus connection - { - name = libwireplumber-module-dbus-connection, type = module - arguments = { plugin.name = system-dbus-connection, bus.system = true } - provides = support.system-dbus - } - - ## Module managing the portal permissions - { - name = libwireplumber-module-portal-permissionstore, type = module - provides = support.portal-permissionstore - requires = [ support.dbus ] - } - - ## Needed for device reservation to work - { - name = libwireplumber-module-reserve-device, type = module - provides = support.reserve-device - requires = [ support.dbus ] - } - - ## logind integration to enable certain functionality only on the active seat - { - name = libwireplumber-module-logind, type = module - provides = support.logind - } - - ## ModemManager integration to control call audio during a call - { - name = libwireplumber-module-modem-manager, type = module - provides = support.modem-manager - requires = [ support.system-dbus ] - } - - ## Session item factories - { - name = libwireplumber-module-si-node, type = module - provides = si.node - } - { - name = libwireplumber-module-si-audio-adapter, type = module - provides = si.audio-adapter - } - { - name = libwireplumber-module-si-standard-link, type = module - provides = si.standard-link - } - - ## API to access default nodes from scripts - { - name = libwireplumber-module-default-nodes-api, type = module - provides = api.default-nodes - } - - ## API to access mixer controls - { - name = libwireplumber-module-mixer-api, type = module - provides = api.mixer - } - - ## API to get notified about file changes - { - name = libwireplumber-module-file-monitor-api, type = module - provides = api.file-monitor - } - - ## API to send D-Bus notifications - { - name = libwireplumber-module-notifications-api, type = module - provides = api.notifications - requires = [ support.dbus ] - } - - - ## Provide the "default" pw_metadata - { - name = metadata.lua, type = script/lua - arguments = { metadata.name = default } - provides = metadata.default - } - - ## Provide the "filters" pw_metadata - { - name = metadata.lua, type = script/lua - arguments = { metadata.name = filters } - provides = metadata.filters - } - - ## Provide the "sm-objects" pw_metadata, supporting dynamic loadable objects - { - name = sm-objects.lua, type = script/lua - provides = metadata.sm-objects - } - - ## Populates the "session.services" property on the WirePlumber client object - { - name = session-services.lua, type = script/lua - provides = support.session-services - } - - ## Provides support for MPRIS - { - name = libwireplumber-module-mpris, type = module - provides = support.mpris - requires = [ support.dbus ] - } - - ## Device monitors' optional features - { - type = virtual, provides = monitor.alsa.reserve-device, - requires = [ support.reserve-device ] - } - { - type = virtual, provides = monitor.alsa-midi.monitoring, - requires = [ api.file-monitor ] - } - { - type = virtual, provides = monitor.bluez.seat-monitoring, - requires = [ support.logind ] - } - - ## Device monitors - { - name = monitors/alsa.lua, type = script/lua - provides = monitor.alsa - requires = [ support.export-core, pw.client-device ] - wants = [ monitor.alsa.reserve-device ] - } - { - name = monitors/bluez.lua, type = script/lua - provides = monitor.bluez - requires = [ support.export-core, - pw.client-device, - pw.client-node, - pw.node-factory.adapter ] - wants = [ monitor.bluez.seat-monitoring ] - } - { - name = monitors/bluez-midi.lua, type = script/lua - provides = monitor.bluez-midi - requires = [ support.export-core, - pw.client-device, - pw.client-node, - pw.node-factory.spa ] - wants = [ monitor.bluez.seat-monitoring ] - } - { - name = monitors/alsa-midi.lua, type = script/lua - provides = monitor.alsa-midi - wants = [ monitor.alsa-midi.monitoring ] - } - ## v4l2 monitor - { - name = monitors/v4l2/name-device.lua, type = script/lua - provides = hooks.monitor.v4l2-name-device - } - { - name = monitors/v4l2/create-device.lua, type = script/lua - provides = hooks.monitor.v4l2-create-device - requires = [ support.export-core, - pw.client-device, - support.standard-event-source ] - } - { - name = monitors/v4l2/name-node.lua, type = script/lua - provides = hooks.monitor.v4l2-name-node - } - { - name = monitors/v4l2/create-node.lua, type = script/lua - provides = hooks.monitor.v4l2-create-node - } - { - name = monitors/v4l2/enumerate-device.lua, type = script/lua - provides = monitor.v4l2 - requires = [ support.export-core, - pw.client-device, - support.standard-event-source, - hooks.monitor.v4l2-create-device, - hooks.monitor.v4l2-create-node ] - wants = [ hooks.monitor.v4l2-name-device, - hooks.monitor.v4l2-name-node ] - } - ## libcamera monitor - { - name = monitors/libcamera/name-device.lua, type = script/lua - provides = hooks.monitor.libcamera-name-device - } - { - name = monitors/libcamera/create-device.lua, type = script/lua - provides = hooks.monitor.libcamera-create-device - requires = [ support.export-core, - pw.client-device, - support.standard-event-source ] - } - { - name = monitors/libcamera/name-node.lua, type = script/lua - provides = hooks.monitor.libcamera-name-node - } - { - name = monitors/libcamera/create-node.lua, type = script/lua - provides = hooks.monitor.libcamera-create-node - } - { - name = monitors/libcamera/enumerate-device.lua, type = script/lua - provides = monitor.libcamera - requires = [ support.export-core, - pw.client-device, - support.standard-event-source, - hooks.monitor.libcamera-create-device, - hooks.monitor.libcamera-create-node ] - wants = [ hooks.monitor.libcamera-name-device, - hooks.monitor.libcamera-name-node ] - } - - ## Client access configuration hooks - { - name = client/access-default.lua, type = script/lua - provides = script.client.access-default - } - { - name = client/access-portal.lua, type = script/lua - provides = script.client.access-portal - requires = [ support.portal-permissionstore ] - } - { - name = client/access-snap.lua, type = script/lua - provides = script.client.access-snap - } - { - type = virtual, provides = policy.client.access - wants = [ script.client.access-default, - script.client.access-portal, - script.client.access-snap ] - } - - ## Device profile selection hooks - { - name = device/select-profile.lua, type = script/lua - provides = hooks.device.profile.select - } - { - name = device/find-voice-call-profile.lua, type = script/lua - provides = hooks.device.profile.find-voice-call - requires = [ support.modem-manager ] - } - { - name = device/find-preferred-profile.lua, type = script/lua - provides = hooks.device.profile.find-preferred - } - { - name = device/find-best-profile.lua, type = script/lua - provides = hooks.device.profile.find-best - } - { - name = device/state-profile.lua, type = script/lua - provides = hooks.device.profile.state - } - { - name = device/apply-profile.lua, type = script/lua - provides = hooks.device.profile.apply - } - { - name = device/autoswitch-bluetooth-profile.lua, type = script/lua - provides = hooks.device.profile.autoswitch-bluetooth - } - { - type = virtual, provides = policy.device.profile - requires = [ hooks.device.profile.select, - hooks.device.profile.autoswitch-bluetooth, - hooks.device.profile.apply ] - wants = [ hooks.device.profile.find-voice-call, - hooks.device.profile.find-best, - hooks.device.profile.find-preferred, - hooks.device.profile.state ] - } - - # Device route selection hooks - { - name = device/select-routes.lua, type = script/lua - provides = hooks.device.routes.select - } - { - name = device/find-best-routes.lua, type = script/lua - provides = hooks.device.routes.find-best - } - { - name = device/state-routes.lua, type = script/lua - provides = hooks.device.routes.state - } - { - name = device/apply-routes.lua, type = script/lua - provides = hooks.device.routes.apply - } - { - name = device/automute-alsa-routes.lua, type = script/lua - provides = hooks.device.routes.automute-alsa - wants = [ api.notifications ] - } - { - type = virtual, provides = policy.device.routes - requires = [ hooks.device.routes.select, - hooks.device.routes.apply ] - wants = [ hooks.device.routes.find-best, - hooks.device.routes.state, - hooks.device.routes.automute-alsa ] - } - - ## Default nodes selection hooks - { - name = default-nodes/rescan.lua, type = script/lua - provides = hooks.default-nodes.rescan - } - { - name = default-nodes/find-selected-default-node.lua, type = script/lua - provides = hooks.default-nodes.find-selected - requires = [ metadata.default ] - } - { - name = default-nodes/find-best-default-node.lua, type = script/lua - provides = hooks.default-nodes.find-best - } - { - name = default-nodes/state-default-nodes.lua, type = script/lua - provides = hooks.default-nodes.state - requires = [ metadata.default ] - } - { - name = default-nodes/apply-default-node.lua, type = script/lua, - provides = hooks.default-nodes.apply - requires = [ metadata.default ] - } - { - type = virtual, provides = policy.default-nodes - requires = [ hooks.default-nodes.rescan, - hooks.default-nodes.apply ] - wants = [ hooks.default-nodes.find-selected, - hooks.default-nodes.find-best, - hooks.default-nodes.state ] - } - - ## Node configuration hooks - { - name = node/create-item.lua, type = script/lua - provides = hooks.node.create-session-item - requires = [ si.audio-adapter, si.node ] - } - { - name = node/suspend-node.lua, type = script/lua - provides = hooks.node.suspend - } - { - name = node/state-stream.lua, type = script/lua - provides = hooks.stream.state - } - { - name = node/filter-forward-format.lua, type = script/lua - provides = hooks.filter.forward-format - } - { - name = node/filter-graph.lua, type = script/lua - provides = hooks.filter.graph - } - { - type = virtual, provides = policy.node - requires = [ hooks.node.create-session-item ] - wants = [ hooks.node.suspend - hooks.stream.state - hooks.filter.forward-format - hooks.filter.graph ] - } - { - name = node/software-dsp.lua, type = script/lua - provides = node.software-dsp - } - { - name = node/audio-group.lua, type = script/lua - provides = node.audio-group - } - - ## Linking hooks - { - name = linking/rescan.lua, type = script/lua - provides = hooks.linking.rescan - } - { - name = linking/find-media-role-target.lua, type = script/lua - provides = hooks.linking.target.find-media-role - } - { - name = linking/find-defined-target.lua, type = script/lua - provides = hooks.linking.target.find-defined - } - { - name = linking/find-audio-group-target.lua, type = script/lua - provides = hooks.linking.target.find-audio-group - requires = [ node.audio-group ] - } - { - name = linking/find-filter-target.lua, type = script/lua - provides = hooks.linking.target.find-filter - requires = [ metadata.filters ] - } - { - name = linking/find-default-target.lua, type = script/lua - provides = hooks.linking.target.find-default - requires = [ api.default-nodes ] - } - { - name = linking/find-best-target.lua, type = script/lua - provides = hooks.linking.target.find-best - requires = [ metadata.filters ] - } - { - name = linking/get-filter-from-target.lua, type = script/lua - provides = hooks.linking.target.get-filter-from - requires = [ metadata.filters ] - } - { - name = linking/prepare-link.lua, type = script/lua - provides = hooks.linking.target.prepare-link - requires = [ api.default-nodes ] - } - { - name = linking/link-target.lua, type = script/lua - provides = hooks.linking.target.link - requires = [ si.standard-link ] - } - { - name = linking/mpris-pause.lua, type = script/lua - provides = hooks.linking.pause-playback - requires = [ support.mpris ] - } - { - type = virtual, provides = policy.linking.standard - requires = [ hooks.linking.rescan, - hooks.linking.target.prepare-link, - hooks.linking.target.link ] - wants = [ hooks.linking.target.find-media-role, - hooks.linking.target.find-defined, - hooks.linking.target.find-audio-group, - hooks.linking.target.find-filter, - hooks.linking.target.find-default, - hooks.linking.target.find-best, - hooks.linking.target.get-filter-from, - hooks.linking.pause-playback ] - } - - ## Linking: Role-based priority system - { - name = linking/rescan-media-role-links.lua, type = script/lua - provides = hooks.linking.role-based.rescan - requires = [ api.mixer ] - } - { - name = node/find-media-role-default-volume.lua, type = script/lua - provides = hooks.node.role-based.default-volume - requires = [ hooks.linking.role-based.rescan ] - } - { - name = linking/find-media-role-sink-target.lua, type = script/lua - provides = hooks.linking.target.find-media-role-sink - } - { - type = virtual, provides = policy.linking.role-based - requires = [ policy.linking.standard, - hooks.linking.role-based.rescan, - hooks.node.role-based.default-volume, - hooks.linking.target.find-media-role-sink ] - } - - ## Standard policy definition - { - type = virtual, provides = policy.standard - requires = [ policy.client.access - policy.device.profile - policy.device.routes - policy.default-nodes - policy.linking.standard - policy.linking.role-based - policy.node - support.standard-event-source ] - } - - ## Load targets - { - type = virtual, provides = hardware.audio - wants = [ monitor.alsa, monitor.alsa-midi ] - } - { - type = virtual, provides = hardware.bluetooth - wants = [ monitor.bluez, monitor.bluez-midi ] - } - { - type = virtual, provides = hardware.video-capture - wants = [ monitor.v4l2, monitor.libcamera ] - } -] - -wireplumber.components.rules = [ - ## Rules to apply on top of wireplumber.components - ## Syntax: - ## { - ## matches = [ - ## { - ## [ = ... ] - ## } - ## ... - ## ] - ## actions = { - ## = { - ## [ = ... ] - ## } - ## ... - ## } - ## } - - { - matches = [ - { - type = "script/lua" - } - ] - actions = { - merge = { - requires = [ support.lua-scripting ] - } - } - } - { - matches = [ - { - provides = "~hooks.*" - name = "!~monitors/.*" - } - ] - actions = { - merge = { - before = [ support.standard-event-source ] - } - } - } - { - matches = [ - { provides = "~monitor.*" } - ] - actions = { - merge = { - after = [ support.standard-event-source ] - } - } - } - # session-services.lua must execute at the very end - { - matches = [ - { name = "!session-services.lua" } - ] - actions = { - merge = { - before = [ support.session-services ] - } - } - } -] - -wireplumber.settings.schema = { - ## Bluetooth - bluetooth.use-persistent-storage = { - name = "Persistent storage" - description = "Remember and restore Bluetooth headset mode status" - type = "bool" - default = true - } - bluetooth.autoswitch-to-headset-profile = { - name = "Auto-switch to headset profile" - description = "Always show microphone for Bluetooth headsets, and switch to headset mode when recording" - type = "bool" - default = true - } - - ## Device - device.restore-profile = { - name = "Restore profile" - description = "Remember and restore device profiles" - type = "bool" - default = true - } - device.restore-routes = { - name = "Restore routes" - description = "Remember and restore device routes" - type = "bool" - default = true - } - device.routes.default-sink-volume = { - name = "Default sink volume" - description = "The default volume for audio sinks" - type = "float" - default = 0.064 - min = 0.0 - max = 1.0 - } - device.routes.default-source-volume = { - name = "Default source volume" - description = "The default volume for audio sources" - type = "float" - default = 1.0 - min = 0.0 - max = 1.0 - } - device.routes.mute-on-alsa-playback-removed = { - name = "Auto-mute on wired audio disconnect" - description = "Automatically mute all audio devices when active wired headphones/speakers are disconnected to prevent unintended sound output" - type = "bool" - default = false - } - device.routes.mute-on-bluetooth-playback-removed = { - name = "Auto-mute on Bluetooth audio disconnect" - description = "Automatically mute all audio devices when active Bluetooth headphones/speakers are disconnected to prevent unintended sound output" - type = "bool" - default = false - } - - - ## Linking - linking.role-based.duck-level = { - name = "Ducking level" - description = "The volume level to apply when ducking (= reducing volume for a higher priority stream to be audible) in the role-based linking policy" - type = "float" - default = 0.3 - min = 0.0 - max = 1.0 - } - linking.allow-moving-streams = { - name = "Allow moving streams" - description = "Streams may be moved by adding PipeWire metadata at runtime" - type = "bool" - default = true - } - linking.follow-default-target = { - name = "Follow default target" - description = "Streams connected to the default device follow when default changes" - type = "bool" - default = true - } - linking.pause-playback = { - name = "Pause playback if output removed" - description = "Pause media players if their target sink is removed" - type = "bool" - default = true - } - - ## Monitor - monitor.camera-discovery-timeout = { - name = "Discovery timeout" - description = "The camera discovery timeout in milliseconds" - type = "int" - default = 1000 - min = 0 - max = 60000 - } - monitor.alsa.autodetect-hdmi-channels = { - name = "Automatically detect HDMI channels (experimental)" - description = "Automatically detect channel count and positions for HDMI devices (experimental)" - type = "bool" - default = false - } - - ## Node - node.features.audio.no-dsp = { - name = "No DSP" - description = "Do not convert audio to F32 format" - type = "bool" - default = false - } - node.features.audio.monitor-ports = { - name = "Monitor ports" - description = "Enable monitor ports on audio nodes" - type = "bool" - default = true - } - node.features.audio.control-port = { - name = "Control ports" - description = "Enable control ports on audio nodes" - type = "bool" - default = false - } - node.features.audio.mono = { - name = "Mono" - description = "Configure all audio device sink nodes in MONO" - type = "bool" - default = false - } - node.stream.restore-props = { - name = "Restore properties" - description = "Remember and restore properties of streams" - type = "bool" - default = true - } - node.stream.restore-target = { - name = "Restore target" - description = "Remember and restore stream targets" - type = "bool" - default = true - } - node.stream.default-playback-volume = { - name = "Default playback volume" - description = "The default volume for playback nodes" - type = "float" - default = 1.0 - min = 0.0 - max = 1.0 - } - node.stream.default-capture-volume = { - name = "Default capture volume" - description = "The default volume for capture nodes" - type = "float" - default = 1.0 - min = 0.0 - max = 1.0 - } - node.stream.default-media-role = { - name = "Default media role" - description = "Default media.role to assign on streams that do not specify it" - type = "string" - default = null - } - node.filter.forward-format = { - name = "Forward format" - description = "Forward format on filter nodes or not" - type = "bool" - default = false - } - node.restore-default-targets = { - name = "Restore default target" - description = "Remember and restore default audio/video input/output devices" - type = "bool" - default = true - } -} diff --git a/.config/wireplumber/wireplumber.conf.d/90-airpods.conf b/.config/wireplumber/wireplumber.conf.d/90-airpods.conf index 7d262d8..134aec5 100644 --- a/.config/wireplumber/wireplumber.conf.d/90-airpods.conf +++ b/.config/wireplumber/wireplumber.conf.d/90-airpods.conf @@ -1,16 +1,17 @@ # AirPods-specific Bluetooth policy override. -# Keep the device on A2DP/AAC and avoid restoring stale device state. +# Keep the device on A2DP/AAC and restore its last volume. wireplumber.settings = { + bluetooth.autoswitch-to-headset-profile = false device.restore-profile = false - device.restore-routes = false + device.restore-routes = true } monitor.bluez.rules = [ { matches = [ { - device.name = "bluez_card.18_3F_70_4E_02_CC" + device.description = "~AirPods.*" } ] actions = {