Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to play multiple audio tracks at once without the need of the launch option #10554

Closed
57o opened this issue Aug 19, 2022 · 19 comments
Closed

Comments

@57o
Copy link

57o commented Aug 19, 2022

Expected behavior of the wanted feature

Be able to select which tracks to play (multiple) without the use of the command line, a lua script would also work if anyone knows one that allows this that would also solve my issue. Essentially, a script or functionality that implements the "--lavfi-complex='[aid1] [aid2] amix [ao]'" launch option into the gui of mpv. I need to be able to select different tracks depending on the situation, so like track one playing simultaneously with track 4 for example.

Alternative behavior of the wanted feature

None

Log file

Not needed

@57o
Copy link
Author

57o commented Aug 19, 2022

I know, but this feature would be incredibly useful when there are multiple tracks, and afaik there are no other video players that can currently do this.

Another solution could be a lua which would hopefully allow me to select 1 or multiple tracks (maybe with keybinding numbers) to play concurrently. Unfortunately I don't know anything about lua so I wouldn't be able to make this script, but if anyone with lua knowledge reads this and decides to make one (or if one's already been made) I would be very thankful.

@hooke007
Copy link
Contributor

@57o
Copy link
Author

57o commented Aug 20, 2022

@hooke007 Trying to use the second one, little bit confused on what to do. Translated the necessary parts to English but pressing function keys does nothing. Anyways, back to the main topic, would something like this be possible to make in a lua script with a clickable gui? If so I might learn lua and see what I can do.

@hooke007
Copy link
Contributor

hooke007 commented Aug 20, 2022

After loading the file containing 2 or more audiotracks,
1.switch to the aid1 first and then activate keybind1
2.switch to the aid2 first and then activate keybind2,
3.now you could activate keybind3 to play the aid1&2 at the same time

would something like this be possible to make in a lua script with a clickable gui?

It should be possible. Ref an interactive filebrowser CogentRedTester/mpv-file-browser#22

@57o
Copy link
Author

57o commented Aug 20, 2022

Unfortunately I'm still unable to do that, after I press the buttons nothing happens. I think that's a little off topic though, is there another place I can message you? (like discord) or I can just use the issue tracker on your repository of that, whichever you'd prefer. Also, I think I'm gonna try to learn lua if I can. I'm gonna leave this thread open for all of tomorrow (20th) just in case anyone else has an idea on what to do. I'll close it after that.

@hooke007
Copy link
Contributor

hooke007 commented Aug 20, 2022

nothing happens

If you could not even receive any osd msg, maybe there was sth wrong in your modified script.

the issue tracker on your repository of that

Of course you can.

@CogentRedTester
Copy link
Contributor

I don't know if you're still looking for a solution to this, but I threw together a quick script that allows you to select multiple tracks for mixing in an interactive list using the arrow keys and ENTER. You'll need to download mpv-scroll-list as well.

It's extremely unpolished, I threw it together in 30 mins, but it's better than nothing. I don't plan to spend any more time on it.

--[[
    A horribly written script to change the audio tracks for `-lavfi-complex='[aid1] [aid2] amix [ao]'` at runtime
    requires mpv-scroll-list: https://github.com/CogentRedTester/mpv-scroll-list

    Open the browser with `N`
    Select tracks with `ENTER`
    Once two tracks are selected select again to undo the audio mix
]]

local mp = require "mp"

package.path = mp.command_native({"expand-path", "~~/script-modules/?.lua;"}) .. package.path
local list = require "scroll-list"

list.header = "Select multiple audio tracks:\\N------------------------------------------------"

-- updates the list with the current audio tracks
function update_tracks(tracks)
    list.list = {}
    for _, track in ipairs(tracks or mp.get_property_native("track-list")) do
        if track.type == "audio" then
            table.insert(list.list, {
                id = track.id,
                ass = ("{\\c&H%s&}aid%d: [%s] %s"):format(
                    track.selected and "33ff66" or "ffffff",
                    track.id, track.lang, track.title or "")
            })
        end
    end
end

mp.observe_property('track-list', 'native', function(_, tracks) update_tracks(tracks) end)
mp.observe_property("aid", "number", function()
    update_tracks()
    list:update()
end)

-- selects the tracks when ENTER is used on the list
function select_track()
    local track_1 = mp.get_property_number("aid")
    local track_2 = list.__current.id

    -- disables lavfi if it is already set
    if mp.get_property("lavfi-complex", "") ~= "" then
        mp.set_property("lavfi-complex", "")
        mp.set_property_number("aid", track_2)
    else
        if not track_1 or not track_2 then return end
        mp.set_property("lavfi-complex", ("[aid%d] [aid%d] amix [ao]"):format(track_1, track_2))
    end

    update_tracks()
    list:update()
end

table.insert(list.keybinds, {"ENTER", "select", select_track})

mp.add_key_binding("n", "multi-ao-browser", function() list:toggle() end)

@yuma-dev
Copy link

The Script provided by @CogentRedTester works almost perfectly but there's no way to enable more than 2
Is that because of technical limitations? If not is there a version out there that can enable more than 2?

@LifeJustDLC
Copy link

LifeJustDLC commented Jul 30, 2024

@yuma-dev The only fundamental part is --lavfi-complex="[aid1][aid2][aid3]amix=inputs=3[ao]" assuming you had --external-file=your-audio-file2.mp4 --external-file=your-audio-file3.mp4 specified. usage: https://www.ffmpeg.org/ffmpeg-filters.html#amix
Although, I experienced a little desync as #675 described, which was also nicely logged as soon as I noticed.

@guidocella
Copy link
Contributor

This selects a second audio track with g-A on git master:

local input = require "mp.input"

local function show_error(message)
    mp.msg.error(message)
    if mp.get_property_native("vo-configured") then
        mp.osd_message(message)
    end
end

local function format_flags(track)
    local flags = ""

    for _, flag in ipairs({
        "default", "forced", "dependent", "visual-impaired", "hearing-impaired",
        "image", "external"
    }) do
        if track[flag] then
            flags = flags .. flag .. " "
        end
    end

    if flags == "" then
        return ""
    end

    return " [" .. flags:sub(1, -2) .. "]"
end

local function format_track(track)
    return (track.selected and "" or "") ..
        (track.title and " " .. track.title or "") ..
        " (" .. (
            (track.lang and track.lang .. " " or "") ..
            (track.codec and track.codec .. " " or "") ..
            (track["demux-w"] and track["demux-w"] .. "x" .. track["demux-h"]
             .. " " or "") ..
            (track["demux-fps"] and not track.image
             and string.format("%.4f", track["demux-fps"]):gsub("%.?0*$", "") ..
             " fps " or "") ..
            (track["demux-channel-count"] and track["demux-channel-count"] ..
             "ch " or "") ..
            (track["codec-profile"] and track.type == "audio"
             and track["codec-profile"] .. " " or "") ..
            (track["demux-samplerate"] and track["demux-samplerate"] / 1000 ..
             " kHz " or "") ..
            (track["demux-bitrate"] and string.format("%.0f", track["demux-bitrate"] / 1000)
             .. " kbps " or "") ..
            (track["hls-bitrate"] and string.format("%.0f", track["hls-bitrate"] / 1000)
             .. " HLS kbps " or "")
        ):sub(1, -2) .. ")" .. format_flags(track)
end

mp.add_key_binding("g-A", "select-secondary-aid", function ()
    local items = {}
    local selected_tracks = {}

    for _, track in ipairs(mp.get_property_native("track-list")) do
        if track.type == "audio" then
            items[#items + 1] = format_track(track)
            if track.selected then
                selected_tracks[#selected_tracks + 1] = track.id
            end
        end
    end

    if #items == 0 then
        show_error("No available audio tracks.")
        return
    end

    input.select({
        prompt = "Select a secondary audio track:",
        items = items,
        submit = function (id)
            if #selected_tracks == 0 then
                mp.set_property("aid", id)
                return
            end

            if #selected_tracks == 1 and id == selected_tracks[1] then
                show_error("This track is already selected.")
                return
            end

            if id == selected_tracks[1] or id == selected_tracks[2] then
                mp.set_property("lavfi-complex", "")
                -- This doesn't always work.
                mp.set_property("aid", selected_tracks[id == selected_tracks[1] and 2 or 1])
                return
            end

            mp.set_property(
                "lavfi-complex",
                "[aid" .. selected_tracks[1] .. "] [aid" .. id .. "] amix [ao]"
            )
        end,
    })
end)

@profpyrus
Copy link

Is there a way to alter this to be able to toggle multiple audio tracks? In my case, I have a total of 6, of which I would like to listen to all or some of them, depending on what clip I am looking at.
Probably quite a niche usecase, though.

@guidocella
Copy link
Contributor

local input = require "mp.input"

local function show_error(message)
    mp.msg.error(message)
    if mp.get_property_native("vo-configured") then
        mp.osd_message(message)
    end
end

local function format_flags(track)
    local flags = ""

    for _, flag in ipairs({
        "default", "forced", "dependent", "visual-impaired", "hearing-impaired",
        "image", "external"
    }) do
        if track[flag] then
            flags = flags .. flag .. " "
        end
    end

    if flags == "" then
        return ""
    end

    return " [" .. flags:sub(1, -2) .. "]"
end

local function format_track(track)
    local bitrate = track["demux-bitrate"] or track["hls-bitrate"]

    return (track.selected and "" or "") ..
        (track.title and " " .. track.title or "") ..
        " (" .. (
            (track.lang and track.lang .. " " or "") ..
            (track.codec and track.codec .. " " or "") ..
            (track["demux-w"] and track["demux-w"] .. "x" .. track["demux-h"]
             .. " " or "") ..
            (track["demux-fps"] and not track.image
             and string.format("%.4f", track["demux-fps"]):gsub("%.?0*$", "") ..
             " fps " or "") ..
            (track["demux-channel-count"] and track["demux-channel-count"] ..
             "ch " or "") ..
            (track["codec-profile"] and track.type == "audio"
             and track["codec-profile"] .. " " or "") ..
            (track["demux-samplerate"] and track["demux-samplerate"] / 1000 ..
             " kHz " or "") ..
            (bitrate and string.format("%.0f", bitrate / 1000) ..
             " kbps " or "")
        ):sub(1, -2) .. ")" .. format_flags(track)
end

mp.add_key_binding("g-A", "select-other-aid", function ()
    local items = {}
    local selected_tracks = {}
    local selected_count = 0

    for _, track in ipairs(mp.get_property_native("track-list")) do
        if track.type == "audio" then
            items[#items + 1] = format_track(track)
            if track.selected then
                selected_tracks[track.id] = true
                selected_count = selected_count + 1
            end
        end
    end

    if #items == 0 then
        show_error("No available audio tracks.")
        return
    end

    input.select({
        prompt = "Select another audio track:",
        items = items,
        submit = function (id)
            if selected_count == 0 then
                mp.set_property("aid", id)
                return
            end

            if selected_tracks[id] then
                if selected_count == 1 then
                    show_error("This track is already selected.")
                    return
                end

                selected_tracks[id] = nil
                selected_count = selected_count - 1

                if selected_count == 1 then
                    mp.set_property("lavfi-complex", "")
                    -- This doesn't always work.
                    mp.set_property("aid", next(selected_tracks))
                    return
                end
            else
                selected_tracks[id] = true
                selected_count = selected_count + 1
            end

            local graph = ''
            for selected_id in pairs(selected_tracks) do
                graph = graph .. "[aid" .. selected_id .. "]"
            end

            mp.set_property("lavfi-complex",
                            graph .. "amix=inputs=" .. selected_count .. "[ao]")
        end,
    })
end)

@niwazukihon
Copy link

#10554 (comment) How to select multiple tracks with this script? I can only seem to select individual track with arrow keys and enter key.

@guidocella
Copy link
Contributor

By using it multiple times.

@niwazukihon
Copy link

#10554 (comment) That just switches to the newly selected track, not selecting them simultaneously.

@guidocella
Copy link
Contributor

Are you pressing shift+a?

@niwazukihon
Copy link

#10554 (comment) Typing g Shift+A works. Somehow it also advances the video by maybe one frame. I guess this is probably not intended?

@profpyrus
Copy link

I'm also here to confirm this works, and it works great! Just make sure to press g -> A (or g -> shift + a) and not g -> a, as this will change audio tracks instead of adding one. Thanks for this solution!
Just another quick bit, is there a way to make selecting all available tracks the default behaviour? :D

@guidocella
Copy link
Contributor

guidocella commented Sep 22, 2024

mp.add_hook("on_preloaded", 10, function ()
    local audio_tracks = {}

    for _, track in ipairs(mp.get_property_native("track-list")) do
        if track.type == "audio" then
            audio_tracks[#audio_tracks + 1] = track.id
        end
    end

    if #audio_tracks < 2 then
        return
    end

    local graph = ''
    for audio_id in pairs(audio_tracks) do
        graph = graph .. "[aid" .. audio_id .. "]"
    end

    mp.set_property("lavfi-complex", graph .. "amix=inputs=" .. #audio_tracks .. "[ao]")
end)


mp.register_event("end-file", function()
    mp.set_property("lavfi-complex", "")
end)

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

No branches or pull requests

9 participants