Skip to content

Instantly share code, notes, and snippets.

@bitingsock
Last active January 29, 2024 03:10
Show Gist options
  • Star 21 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bitingsock/ad58ee5da560ecb922fa4a867ac0ecfd to your computer and use it in GitHub Desktop.
Save bitingsock/ad58ee5da560ecb922fa4a867ac0ecfd to your computer and use it in GitHub Desktop.
Cycle through available audio devices with key binds(shift+a,ctrl+a). Change "wasapi" on line 1 to your relevant audio api.
local api = "wasapi"
local deviceList = mp.get_property_native("audio-device-list")
local aid = 1
local function cycle_adevice(s, e, d)
mp.enable_messages("error")
while s ~= e + d do -- until the loop would cycle back to the number we started on
if string.find(mp.get_property("audio-device"), deviceList[s].name, 1, true) then
while true do
if s + d == 0 then --the device list starts at 1; 0 means we iterated to far
s = #deviceList + 1 --so lets restart at the last device
elseif s + d == #deviceList + 1 then --we iterated past the last device
s = 0 --then start from the beginning
end
s = s + d --next device
if string.find(deviceList[s].name, api, 1, true) then
mp.set_property("audio-device", deviceList[s].name)
deviceList[s].description = "•" .. string.match(deviceList[s].description, "[^%(]+")
local list = "AUDIO DEVICE:\n"
for i = 1, #deviceList do
if string.find(deviceList[i].name, api, 1, true) then
if deviceList[i].name ~= deviceList[s].name then list = list .. "◦" end
list = list .. string.match(deviceList[i].description, "[^%(]+") .. "\n"
end
end
if mp.get_property("vid") == "no" then
print("audio=" .. deviceList[s].description)
else
mp.osd_message(list, 3)
end
mp.set_property("aid", aid)
mp.command("seek 0 exact")
return
end
end
end
s = s + d
end
end
mp.observe_property("aid", function(id)
if id ~= "no" then aid = id end
end)
mp.register_event("log-message", function(event)
if event.text:find("Try unsetting it") then
mp.set_property("audio-device", "auto")
mp.set_property("aid", aid)
end
end)
mp.add_key_binding("A", "cycle_adevice", function()
deviceList = mp.get_property_native("audio-device-list")
cycle_adevice(1, #deviceList, 1) --'s'tart at device 1, 'e'nd at last device, iterate forward 'd'elta=1
end)
mp.add_key_binding("Ctrl+a", "cycleBack_adevice", function()
deviceList = mp.get_property_native("audio-device-list")
cycle_adevice(#deviceList, 1, -1) --'s'tart at last device, 'e'nd at device 1, iterate backward 'd'elta=-1
end)
@slrslr
Copy link

slrslr commented Jun 12, 2021

Following two commands installed it for me:

  1. wget https://gist.githubusercontent.com/bitingsock/ad58ee5da560ecb922fa4a867ac0ecfd/raw/e022cc7b0e9ef361c75207c96b61aaa5bf866d47/cycle-adevice.lua -O ~/.config/mpv/scripts/cycle-adevice.lua;
  2. api=$(pactl list|grep api| cut -d " -f2 | cut -d " -f1);sed -i "s|wasapi|"$api"|g" ~/.config/mpv/scripts/cycle-adevice.lua;

and after mpv restart i can Ctrl+A to switch, but while i am having more than 10 virtual devices (even i have only one built in MB sound card) i am unable to switch properly thanks to this high number, it somehow stuck at some entry and no longer switch.

@Andreadj
Copy link

One question: Do you think there's a way to load simultaneosly tracks instead of cycle them?
I mean playing 1 language on a device and the second one on another device and maybe more?

@bitingsock
Copy link
Author

bitingsock commented Jan 20, 2023

One question: Do you think there's a way to load simultaneosly tracks instead of cycle them? I mean playing 1 language on a device and the second one on another device and maybe more?

I have a separate script for that:
audio-dupe.lua

If using something other than Windows, it does require you to know your environment's literal pipe structure; changing line 1

@Andreadj
Copy link

Andreadj commented Jan 20, 2023

Many thanks, I tried it under windows but it doesn't work.... Maybe am I doing something wrong? I copied that lua and cycle_adevice.lua under "scripts" folder in mpv folder. "cycle_adevice" works, with ctrl+a it changes device output but with Ctrl+alt+a nothing happen...

EDIT: Solved thank you so much!!!
Last question: I have 2 output devices and I'm able to stream 2 different language output, do you think it can be done with more? For example 3 different audio languages at same time? I saw every time I push ctrl+A a new process is started.

@bitingsock
Copy link
Author

Many thanks, I tried it under windows but it doesn't work.... Maybe am I doing something wrong? I copied that lua and cycle_adevice.lua under "scripts" folder in mpv folder. "cycle_adevice" works, with ctrl+a it changes device output but with Ctrl+alt+a nothing happen...

Notice the capital "A"
This makes the key binding ctrl+shift+a

@bitingsock
Copy link
Author

bitingsock commented Jan 20, 2023

For example 3 different audio languages at same time?

maybe, I'll take a look some time soon

@Andreadj
Copy link

I tried with 3 output devices. It actually creates 3 processes but only 2 work at the same time.

@bitingsock
Copy link
Author

I tried with 3 output devices. It actually creates 3 processes but only 2 work at the same time.

that's because the pipe is a static name

@bitingsock
Copy link
Author

@Andreadj
I have updated audio-dupe.lua
please check it out

@bitingsock
Copy link
Author

bitingsock commented Jan 22, 2023

@slrslr

and after mpv restart i can Ctrl+A to switch, but while i am having more than 10 virtual devices (even i have only one built in MB sound card) i am unable to switch properly thanks to this high number, it somehow stuck at some entry and no longer switch.

If your device name is very long, my first guess is either that mp.get_property_native("audio-device-list") no longer contains your API string, making the condition on line#13 never be true and thus the loop on line#6 would never return;
Or, maybe either mp.get_property("audio-device") or deviceList[s].name on line#5 is getting truncated.
If you have a way to verify this, I would be interested.

@Andreadj
Copy link

@Andreadj I have updated audio-dupe.lua please check it out

Checked, it works! Amazing work!!!
Thanks

@HairyOtter
Copy link

HairyOtter commented Aug 22, 2023

but while i am having more than 10 virtual devices (even i have only one built in MB sound card) i am unable to switch properly thanks to this high number, it somehow stuck at some entry and no longer switch.

I can verify this behavior on ubuntu 22.04.03 LTS.
The only thing I changed in the script was replacing "wasapi" with "alsa".
The audio devices that are listed are:

Built-in Audio Analog Stereo
GK104 HDMI Audio Controller Device
Default (alsa)
Rate Converter Plugin Using Samplerate Library
Rate Converter Plugin Using Speex Resampler
JACK Audio Connection Kit
Open Sound System
Plugin for channel upmix (4,6,8)
Plugin for channel downmix (stereo) with a simple spacialization
HDA ATI SB ALC887-VD Analog/Hardware device with all software conversions

Might be more, as the last line is cut short at the bottom of my screen.

Only

Built-in Audio Analog Stereo
GK104 HDMI Audio Controller Device

and
Digital Output (s/PDIF) - Built-in Audio
are listed as output devices within the ubuntu GUI ("Built-in Audio Analog Stereo" and "Digital Output (s/PDIF) - Built-in Audio" are the audio jacks on the back of the mainboard and "GK104 HDMI Audio Controller Device" is the HDMI Audio of the GPU)

When I start up a video with HDMI Audio set as default in ubuntu, I can switch to "Built-in Audio Analog Stereo" by pressing Shift+a.
If I press Ctrl+a tho, none of the displayed audio devices are selected and the audio track is deselected "-/2".
By pressing Ctrl+a and Shift+a a few times I can get back to

GK104 HDMI Audio Controller Device
Default (alsa)
Rate Converter Plugin Using Samplerate Library

after which I can select an audio track again.

However then I can no longer switch to the analog audio "Built-in Audio Analog Stereo" and the audio devices do not cycle. I can only go up or down within these 3 devices via Ctrl+a and Shift+a.

If I set "Built-in Audio Analog Stereo" as default in the ubuntu GUI before opening a video in mpv, the same as above applies, only difference is that I then can no longer select the HDMI output, I can only switch between:

Built-in Audio Analog Stereo
Default (alsa)
Rate Converter Plugin Using Samplerate Library

I wonder if it's possible to filter/limit the audio devices to only display (and cycle between) those that show up in the ubuntu GUI.

@bitingsock
Copy link
Author

It works on Windows with 10+ devices. I have to assume it is something wrong between mpv and Ubuntu when requesting outputs.
If you can post a mpv log with at least one -v I'll take a look at it. I'd also be interested in what mpv show's you when you run it with --audio-device=help

@HairyOtter
Copy link

HairyOtter commented Aug 25, 2023

I'd also be interested in what mpv show's you when you run it with --audio-device=help

That solved my issues. I had to use "pulse" instead of "alsa" as the value of "local api". Now mpv only shows the active devices and I can cycle between them without issues. Thanks!

Here's the output of: mpv --audio-device=help anyways in case you're curious:

List of detected audio devices:
  'auto' (Autoselect device)
  'pulse/alsa_output.pci-0000_01_00.1.hdmi-stereo' (Oland/Hainan/Cape Verde/Pitcairn HDMI Audio [Radeon HD 7000 Series] Digital Stereo (HDMI))
  'pulse/alsa_output.pci-0000_05_00.0.analog-stereo' (EMU20k1 [Sound Blaster X-Fi Series] (X-Fi Platinum) Analog Stereo)
  'alsa' (Default (alsa))
  'alsa/samplerate' (Rate Converter Plugin Using Samplerate Library)
  'alsa/speexrate' (Rate Converter Plugin Using Speex Resampler)
  'alsa/jack' (JACK Audio Connection Kit)
  'alsa/oss' (Open Sound System)
  'alsa/upmix' (Plugin for channel upmix (4,6,8))
  'alsa/vdownmix' (Plugin for channel downmix (stereo) with a simple spacialization)
  'alsa/plughw:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/Hardware device with all software conversions)
  'alsa/plughw:CARD=XFi,DEV=1' (Creative X-Fi, Surround/Hardware device with all software conversions)
  'alsa/plughw:CARD=XFi,DEV=2' (Creative X-Fi, Center/LFE/Hardware device with all software conversions)
  'alsa/plughw:CARD=XFi,DEV=3' (Creative X-Fi, Side/Hardware device with all software conversions)
  'alsa/plughw:CARD=XFi,DEV=4' (Creative X-Fi, IEC958 Non-audio/Hardware device with all software conversions)
  'alsa/sysdefault:CARD=XFi' (Creative X-Fi, Front/WaveIn/Default Audio Device)
  'alsa/front:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/Front output / input)
  'alsa/surround21:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/2.1 Surround output to Front and Subwoofer speakers)
  'alsa/surround40:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/4.0 Surround output to Front and Rear speakers)
  'alsa/surround41:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/4.1 Surround output to Front, Rear and Subwoofer speakers)
  'alsa/surround50:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/5.0 Surround output to Front, Center and Rear speakers)
  'alsa/surround51:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/5.1 Surround output to Front, Center, Rear and Subwoofer speakers)
  'alsa/surround71:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/7.1 Surround output to Front, Center, Side, Rear and Woofer speakers)
  'alsa/iec958:CARD=XFi,DEV=0' (Creative X-Fi, IEC958 Non-audio/IEC958 (S/PDIF) Digital Audio Output)
  'alsa/dmix:CARD=XFi,DEV=0' (Creative X-Fi, Front/WaveIn/Direct sample mixing device)
  'alsa/dmix:CARD=XFi,DEV=1' (Creative X-Fi, Surround/Direct sample mixing device)
  'alsa/dmix:CARD=XFi,DEV=2' (Creative X-Fi, Center/LFE/Direct sample mixing device)
  'alsa/dmix:CARD=XFi,DEV=3' (Creative X-Fi, Side/Direct sample mixing device)
  'alsa/dmix:CARD=XFi,DEV=4' (Creative X-Fi, IEC958 Non-audio/Direct sample mixing device)
  'alsa/usbstream:CARD=XFi' (Creative X-Fi/USB Stream Output)
  'alsa/plughw:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/Hardware device with all software conversions)
  'alsa/sysdefault:CARD=PCH' (HDA Intel PCH, ALC887-VD Analog/Default Audio Device)
  'alsa/front:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/Front output / input)
  'alsa/surround21:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/2.1 Surround output to Front and Subwoofer speakers)
  'alsa/surround40:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/4.0 Surround output to Front and Rear speakers)
  'alsa/surround41:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/4.1 Surround output to Front, Rear and Subwoofer speakers)
  'alsa/surround50:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/5.0 Surround output to Front, Center and Rear speakers)
  'alsa/surround51:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/5.1 Surround output to Front, Center, Rear and Subwoofer speakers)
  'alsa/surround71:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/7.1 Surround output to Front, Center, Side, Rear and Woofer speakers)
  'alsa/dmix:CARD=PCH,DEV=0' (HDA Intel PCH, ALC887-VD Analog/Direct sample mixing device)
  'alsa/usbstream:CARD=PCH' (HDA Intel PCH/USB Stream Output)
  'alsa/plughw:CARD=HDMI,DEV=3' (HDA ATI HDMI, HDMI 0/Hardware device with all software conversions)
  'alsa/plughw:CARD=HDMI,DEV=7' (HDA ATI HDMI, HDMI 1/Hardware device with all software conversions)
  'alsa/plughw:CARD=HDMI,DEV=8' (HDA ATI HDMI, HDMI 2/Hardware device with all software conversions)
  'alsa/plughw:CARD=HDMI,DEV=9' (HDA ATI HDMI, HDMI 3/Hardware device with all software conversions)
  'alsa/plughw:CARD=HDMI,DEV=10' (HDA ATI HDMI, HDMI 4/Hardware device with all software conversions)
  'alsa/plughw:CARD=HDMI,DEV=11' (HDA ATI HDMI, HDMI 5/Hardware device with all software conversions)
  'alsa/hdmi:CARD=HDMI,DEV=0' (HDA ATI HDMI, HDMI 0/HDMI Audio Output)
  'alsa/hdmi:CARD=HDMI,DEV=1' (HDA ATI HDMI, HDMI 1/HDMI Audio Output)
  'alsa/hdmi:CARD=HDMI,DEV=2' (HDA ATI HDMI, HDMI 2/HDMI Audio Output)
  'alsa/hdmi:CARD=HDMI,DEV=3' (HDA ATI HDMI, HDMI 3/HDMI Audio Output)
  'alsa/hdmi:CARD=HDMI,DEV=4' (HDA ATI HDMI, HDMI 4/HDMI Audio Output)
  'alsa/hdmi:CARD=HDMI,DEV=5' (HDA ATI HDMI, HDMI 5/HDMI Audio Output)
  'alsa/dmix:CARD=HDMI,DEV=3' (HDA ATI HDMI, HDMI 0/Direct sample mixing device)
  'alsa/dmix:CARD=HDMI,DEV=7' (HDA ATI HDMI, HDMI 1/Direct sample mixing device)
  'alsa/dmix:CARD=HDMI,DEV=8' (HDA ATI HDMI, HDMI 2/Direct sample mixing device)
  'alsa/dmix:CARD=HDMI,DEV=9' (HDA ATI HDMI, HDMI 3/Direct sample mixing device)
  'alsa/dmix:CARD=HDMI,DEV=10' (HDA ATI HDMI, HDMI 4/Direct sample mixing device)
  'alsa/dmix:CARD=HDMI,DEV=11' (HDA ATI HDMI, HDMI 5/Direct sample mixing device)
  'alsa/usbstream:CARD=HDMI' (HDA ATI HDMI/USB Stream Output)
  'jack' (Default (jack))
  'openal' (Default (openal))

@bitingsock
Copy link
Author

while it doesn't really 'solve' the issue, I'll leave it for now until someone else brings it up.

@HairyOtter
Copy link

HairyOtter commented Aug 25, 2023

while it doesn't really 'solve' the issue, I'll leave it for now until someone else brings it up.

I did some further testing today and it does not resolve the issue.

My use case needs audio passthrough via HDMI for one of the audio devices. For that to work pulse audio needs to be disabled.
With pulse audio disabled, the switching for alsa doesn't work (it only switches between 2 virtual devices).

Since I have a static use case, I've got it to work by hardcoding one of 2 audio devices (the HDMI connection to the AVR) in mpv.conf, where I've also added:

audio-device=<devicename>
audio-spdif=ac3,eac3,dts,dts-hd,truehd
audio-channels=7.1,5.1,stereo

for audio-passthrough and multichannel to work over HDMI.
mpv --audio-device=help showed me which devices to add.

I then added the second one to audio-dupe.lua in the start_child function, replacing "--audio-device=" .. mp.get_property("audio-device"), with "--audio-device=<devicename>",

That way the default is the HDMI passthrough to the TV in case I watch something alone and only need one audio device.
And the subprocess starts on the headphones, in case 2 different tracks are needed at the same time.

While that works well for me so far, it eliminates a part of the flexibility of your scripts.

I just thought I'd add my workaround if someone else desires a "static" version until/if you deem it important enough to look into the alsa problem further.

I'm very grateful for your scripts as they allow me to watch TV with my father again without one of us being frustrated.

He gets the German dub that he needs (poor English comprehension and he can't read quickly enough for subs any more either) via headphones in a volume of his choosing and I get the original audio over speakers. I've looked for a solution like this for literally years.

Thanks again!

@bitingsock
Copy link
Author

dude, that's awesome

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment