Search code examples
macosaudioapplescriptquicktime

Quicktime Audio Channel Assign - Left, Right, Front Left, LFE, etc


I am trying to adapt the below Applescript code to change the audio channel assignment of a multichannel Quicktime file consisting of video and 8 audio channels from the default 'Mono' to 'Left, Right, Front Left, Front Right, LFE', etc, which can be done manually from 'Show Movie Properties/Audio Settings. The below script successfully changes the audio channels 'name' but I want it to change the 'channels' assignment, but I can't figure out how to get it working. I am on Mavericks with Quicktime Pro 7.

on run
    open (choose file with prompt "Choose source file(s)" with multiple selections allowed)
end run

on open ff
    repeat with f in ff
        remap_sound_tracks_name(f's POSIX path)
    end repeat
end open

on remap_sound_tracks_name(f)
    (*
        string f : POSIX path of source movie file
    *)
    script o
        on map(n)
            set mm to {¬
                {"Sound Track 1", "Left"}, ¬
                {"Sound Track 2", "Right"}, ¬
                {"Sound Track 3", "Center"}, ¬
                {"Sound Track 4", "LFE Screen"}, ¬
                {"Sound Track 5", "Left Surround"}, ¬
                {"Sound Track 6", "Right Surround"}, ¬
                {"Sound Track 7", "Left Total"}, ¬
                {"Sound Track 8", "Right Total"} ¬
                    }
            repeat with m in mm
                if m's item 1 = n then return m's item 2
            end repeat
            return n
        end map

        set f to f as POSIX file
        tell application id "com.apple.quicktimeplayer" -- QuickTime Player 7
            set dc to count documents
            open f
            repeat until (count documents) > dc
                delay 0.2
            end repeat
            tell document 1
                repeat with t in (get tracks whose audio channel count > 0)
                    set t to t's contents
                    set n to t's name
                    set n1 to my map(n)
                    if n1 ≠ n then set t's name to n1
                end repeat
                if modified then save
                close
            end tell
        end tell
    end script
    tell o to run
end remap_sound_tracks_name

Solution

  • This was answered by Hiroto on the apple forums. It runs and works, but is quite slow:

    (*
        remap audio channel layouts.applescript
        v0.1
    *)
    on run
        open (choose file with prompt ("Choose movie file(s)") ¬
            with multiple selections allowed)
    end run
    
    on open aa
        set channel_layouts_map1 to {¬
            {"Sound Track 1", "Sound Track 1", {"Left"}}, ¬
            {"Sound Track 2", "Sound Track 2", {"Right"}}, ¬
            {"Sound Track 3", "Sound Track 3", {"Center"}}, ¬
            {"Sound Track 4", "Sound Track 4", {"LFE Screen"}}, ¬
            {"Sound Track 5", "Sound Track 5", {"Left Surround"}}, ¬
            {"Sound Track 6", "Sound Track 6", {"Right Surround"}}, ¬
            {"Sound Track 7", "Sound Track 7", {"Left Total"}}, ¬
            {"Sound Track 8", "Sound Track 8", {"Right Total"}} ¬
                }
        set channel_layouts_map2 to {¬
            {"Sound Track 1", "Sound Track 1", {"Left"}}, ¬
            {"Sound Track 2", "Sound Track 2", {"Right"}}, ¬
            {"Sound Track 3", "Sound Track 3", {"Center"}}, ¬
            {"Sound Track 4", "Sound Track 4", {"LFE Screen"}}, ¬
            {"Sound Track 5", "Sound Track 5", {"Left Surround"}}, ¬
            {"Sound Track 6", "Sound Track 6", {"Right Surround"}}, ¬
            {"Sound Track 7", "Sound Track 7", {"Left Total", "Right Total"}} ¬
                }
        set channel_layouts_map3 to {¬
            {"Sound Track 1", "Sound Track 7", {"Left"}}, ¬
            {"Sound Track 2", "Sound Track 8", {"Right"}} ¬
                }
        set channel_layouts_map4 to {¬
            {"Sound Track", "Sound Track", {"Left", "Right"}} ¬
                }
    
        repeat with a in aa
            set f to a's POSIX path
            set k to count_sound_tracks(f, {_close:false})
            if k = 8 then
                remap_audio_channels(f, channel_layouts_map1)
            else if k = 7 then
                remap_audio_channels(f, channel_layouts_map2)
            else if k = 2 then
                remap_audio_channels(f, channel_layouts_map3)
            else if k = 1 then
                remap_audio_channels(f, channel_layouts_map4)
            else
                -- ignore it (just close it)
                close_document(f, {_save:false})
            end if
        end repeat
    end open
    
    on count_sound_tracks(f, {_close:_close})
        (*
            string f : POSIX path of QT movie
            boolean _close: true to close document, false othewise
        *)
        tell application id "com.apple.quicktimeplayer" -- QuickTime Player 7 Pro
            open (f as POSIX file)
            tell (document 1 whose path = f)
                repeat until exists
                    delay 0.2
                end repeat
                set k to count (tracks whose audio channel count > 0)
                if _close then close
            end tell
        end tell
        return k
    end count_sound_tracks
    
    on close_document(f, {_save:_save})
        (*
            string f : POSIX path of QT movie
            boolean _save: true to save document (if modified), false othewise
        *)
        tell application id "com.apple.quicktimeplayer" -- QuickTime Player 7 Pro
            tell (document 1 whose path = f)
                if exists then
                    if _save and modified then save
                    close
                end if
            end tell
        end tell
    end close_document
    
    on remap_audio_channels(f, channel_layouts_map)
        (*
            string f : POSIX path of source movie
            list channel_layouts_map : list of {trk, trk_new, layouts}
                trk = (string or integer) name or index of source sound track
                trk_new = (string or integer) new name for source track (integer i denotes original name of sound track i)
                layouts = list of audio channel layout for channel(s) in source sound track
                    Mono
                    Left
                    Right
                    Center
                    LFE Screen
                    Left Surround
                    Right Surround
                    Left Center
                    Right Center
                    Center Surround
                    Rear Surround Left
                    Rear Surround Right
                    Left Total
                    Right Total
                    Discrete-0
                    Discrete-1
                    Unused
    
                e.g. 1
                   {{"Sound Track 1", "Left", {"Left"}}, ¬
                    {"Sound Track 2", "Right", {"Right"}}, ¬
                    {"Sound Track 3", "Center", {"Center"}}, ¬
                    {"Sound Track 4", "LFE Screen", {"LFE Screen"}}, ¬
                    {"Sound Track 5", "Left Surround", {"Left Surround"}}, ¬
                    {"Sound Track 6", "Right Surround", {"Right Surround"}}, ¬
                    {"Sound Track 7", "Left Total", {"Left Total"}}, ¬
                    {"Sound Track 8", "Right Total", {"Right Total"}}}
    
                e.g. 2
                   {{1, 1, {"Left", "Right"}}, ¬
                    {2, 2, {"Center", "LFE, Screen"}}, ¬
                    {3, 3, {"Left Surround", "Right Surround"}}, ¬
                    {4, 4, {"Left Total", "Right Total"}}}
    
            * this handler behaves as follows:
                1) open f
                2) scan sound tracks of document 1 for each trk and remap the track's audio channel layouts as specified
                3) scan sound tracks of document 1 for each trk and rename the track as specified
                4) save and close document 1
    
                * if specified trk is not found, it is ignored and no remapping is performed on the track.
                * if specified layout is not found, it is ignored and no remapping is performed on the layout.
                * if specified layout count is greater than channel count of the target track, excessive layouts are ignored.
                * if specified layout count is smaller than channel count of target track, excessive channels are ignored.
                * if trk and trk_new denotes the same track, renaming is not performed on the track.
        *)
        script o
            property map : channel_layouts_map
            property pp : {}
            property qq : {}
    
            -- get name and id of sound tracks
            tell application id "com.apple.quicktimeplayer" -- QuickTime Player 7 Pro
                activate
                open (f as POSIX file)
                tell (document 1 whose path = f)
                    repeat until exists
                        delay 0.2
                    end repeat
                    tell (tracks whose audio channel count > 0)
                        set {pp, qq} to {name, id} -- name and id of sound tracks
                    end tell
                end tell
            end tell
    
            -- remap audio channel layouts as specified
            tell application "System Events"
                tell (process 1 whose bundle identifier = "com.apple.quicktimeplayer")
                    -- open movie properties window
                    keystroke "j" using {command down}
    
                    tell (window 1 whose subrole = "AXDialog") -- properties for movie
                        repeat until exists
                            delay 0.2
                        end repeat
                        repeat with m in my map
                            set {trk, undef, layouts} to m
                            -- [TRK:
                            repeat 1 times
                                if trk's class = integer then
                                    if trk < 1 or trk > (count my pp) then exit repeat -- TRK:
                                    set trk to my pp's item trk
                                end if
                                tell scroll area 1
                                    tell table 1
                                        tell (row 1 whose text field 1's value = trk) -- target sound track whose name = trk
                                            if not (exists) then exit repeat -- TRK:
                                            select
                                        end tell
                                    end tell
                                end tell
                                tell tab group 1
                                    click radio button 3 -- audio settings
                                    tell scroll area 1
                                        tell table 1 -- channel assignment table
                                            set ix to count layouts
                                            repeat with i from 1 to count rows
                                                if i > ix then exit repeat
                                                tell row i -- channel i
                                                    tell pop up button 1
                                                        click
                                                        tell menu 1 -- channel assignment menu
                                                            tell (menu item 1 whose title = layouts's item i)
                                                                if exists then click
                                                            end tell
                                                        end tell
                                                    end tell
                                                end tell
                                            end repeat
                                        end tell
                                    end tell
                                end tell
                            end repeat
                            -- /TRK:]
                        end repeat
    
                        -- close movie properties window
                        click (button 1 whose subrole = "AXCloseButton")
                    end tell
                end tell
            end tell
    
            -- rename sound tracks as specified
            tell application id "com.apple.quicktimeplayer"
                tell document 1
                    repeat with m in my map
                        -- [RENAME:
                        repeat 1 times
                            set {x, y} to m's items 1 thru 2 -- {old name or index, new name or index}
                            if x's class = integer then
                                if x < 1 or x > (count my pp) then exit repeat -- RENAME:
                            else
                                set x to my _index_of(pp, x)
                                if x = 0 then exit repeat -- RENAME:
                            end if
                            if y's class = integer then
                                if y < 1 or y > (count my pp) then exit repeat -- RENAME:
                                set y to my pp's item y
                            end if
                            set p to my pp's item x
                            set q to my qq's item x
                            if p ≠ y then set track id q's name to y
                        end repeat
                        -- /RENAME:]
                    end repeat
                    if modified then save
                    close
                end tell
            end tell
    
        end script
        tell o to run
    end remap_audio_channels
    
    on _index_of(xx, x) -- renamed _bsearch() v0.1
        (*
            list xx : source list
            anything x : item to be searched in xx
            return integer : the first index of x in xx if {x} is in xx, or 0 if not.
        *)
        script o
            property aa : xx
            local i, j, k
            if {x} is not in my aa then return 0
            set i to 1
            set j to count my aa
            repeat while j > i
                set k to (i + j) div 2
                if {x} is in my aa's items i thru k then
                    set j to k
                else
                    set i to k + 1
                end if
            end repeat
            return i
        end script
        tell o to run
    end _index_of