Unified · Split
| @@ -0,0 +1,126 @@ | |||
| 1 | -- This script adds control to the dynamic range compression ffmpeg | ||
| 2 | -- filter including key bindings for adjusting parameters. | ||
| 3 | -- | ||
| 4 | -- See https://ffmpeg.org/ffmpeg-filters.html#acompressor for explanation | ||
| 5 | -- of the parameteres. | ||
| 6 | -- | ||
| 7 | -- Default key bindings: | ||
| 8 | -- n: Toggle dynamic range compression on or off | ||
| 9 | -- F1/Shift+F1: Increase/Decrease threshold parameter | ||
| 10 | -- F2/Shift+F2: Increase/Decrease ratio parameter | ||
| 11 | -- F3/Shift+F3: Increase/Decrease knee parameter | ||
| 12 | -- F4/Shift+F4: Increase/Decrease makeup gain parameter | ||
| 13 | -- F5/Shift+F5: Increase/Decrease attack parameter | ||
| 14 | -- F6/Shift+F6: Increase/Decrease release parameter | ||
| 15 | -- | ||
| 16 | -- To change key bindings in input.conf use: | ||
| 17 | -- BINDING script-message-to acompressor toggle-acompressor | ||
| 18 | -- BINDING script-message-to acompressor update-param PARAM INCREMENT | ||
| 19 | -- BINDING is the key binding to use, PARAM is either 'attack', 'release', | ||
| 20 | -- 'threshold', 'ratio', 'knee' or 'makeup' and INCREMENT is a signed floating | ||
| 21 | -- point value to add to the current parameter value. | ||
| 22 | -- | ||
| 23 | -- You may also just adjust default parameters to your liking in this table. | ||
| 24 | local params = { | ||
| 25 | -- 'hide' defines wether the parameter should be hidden from OSD display (it will still be included in ffmpeg filter graph). | ||
| 26 | -- 'input_format' is used to parse the value back from a obtained filter graph. | ||
| 27 | -- 'output_format' defines how the value shall be formatted for creating the filter graph. | ||
| 28 | { name = 'Attack', value= 20, min=0.01, max=2000, hide= 20, input_format='attack=(%d+[.%d+]*)', output_format='%g' }, | ||
| 29 | { name = 'Release', value=250, min=0.01, max=9000, hide=250, input_format='release=(%d+[.%d+]*)', output_format='%g' }, | ||
| 30 | { name = 'Threshold', value=-25, min= -30, max= 0, hide=nil, input_format='threshold=(-%d+[.%d+]*)dB', output_format='%gdB' }, | ||
| 31 | { name = 'Ratio', value= 3, min= 1, max= 20, hide=nil, input_format='ratio=(%d+[.%d+]*)', output_format='%g' }, | ||
| 32 | { name = 'Knee', value= 2, min= 1, max= 10, hide= 2, input_format='knee=(%d+[.%d+]*)dB', output_format='%gdB' }, | ||
| 33 | { name = 'Makeup', value= 8, min= 0, max= 24, hide=nil, input_format='makeup=(%d+[.%d+]*)dB', output_format='%gdB' } | ||
| 34 | } | ||
| 35 | |||
| 36 | -- Defines the mpv filter label to be used. This allows us to easily add/replace/remove it, so | ||
| 37 | -- it should be left so something meaningful and unique. | ||
| 38 | local filter_label = 'acompressor' | ||
| 39 | |||
| 40 | local function update_filter() | ||
| 41 | local graph = {} | ||
| 42 | local pretty = {} | ||
| 43 | |||
| 44 | for _,param in pairs(params) do | ||
| 45 | graph[#graph+1] = string.format('%s=' .. param.output_format, string.lower(param.name), param.value) | ||
| 46 | if param.hide ~= param.value then | ||
| 47 | pretty[#pretty+1] = string.format('%s: ' .. param.output_format, param.name, param.value) | ||
| 48 | end | ||
| 49 | end | ||
| 50 | |||
| 51 | if #pretty == 0 then | ||
| 52 | pretty = '' | ||
| 53 | else | ||
| 54 | pretty = '\n(' .. table.concat(pretty, ', ') .. ')' | ||
| 55 | end | ||
| 56 | |||
| 57 | mp.command(string.format('no-osd af add @%s:lavfi=[acompressor=%s]; show-text "Dynamic range compressor: enabled%s" 4000', filter_label, table.concat(graph, ':'), pretty)) | ||
| 58 | end | ||
| 59 | |||
| 60 | local function read_filter() | ||
| 61 | local graph = nil | ||
| 62 | local af = mp.get_property_native('af', {}) | ||
| 63 | |||
| 64 | for i = 1, #af do | ||
| 65 | if af[i]['name'] == 'lavfi' and af[i]['label'] == 'acompressor' then | ||
| 66 | graph = af[i]['params']['graph'] | ||
| 67 | break | ||
| 68 | end | ||
| 69 | end | ||
| 70 | |||
| 71 | if graph == nil then | ||
| 72 | return false | ||
| 73 | end | ||
| 74 | |||
| 75 | for _,param in pairs(params) do | ||
| 76 | local value = tonumber(string.match(graph, param.input_format)) | ||
| 77 | if value ~= nil and value >= param.min and value <= param.max then | ||
| 78 | param.value = value | ||
| 79 | end | ||
| 80 | end | ||
| 81 | return true | ||
| 82 | end | ||
| 83 | |||
| 84 | local function toggle_acompressor() | ||
| 85 | if read_filter() then | ||
| 86 | mp.command(string.format('no-osd af del @%s; show-text "Dynamic range compressor: disabled"', filter_label)) | ||
| 87 | else | ||
| 88 | update_filter() | ||
| 89 | end | ||
| 90 | end | ||
| 91 | |||
| 92 | local function update_param(name, increment) | ||
| 93 | -- The current filter values could come from a watch_later config. | ||
| 94 | -- read_filter() will parse them, so that we don't clobber those with our default values. | ||
| 95 | read_filter() | ||
| 96 | for _,param in pairs(params) do | ||
| 97 | if string.lower(param.name) == string.lower(name) then | ||
| 98 | param.value = math.max(param.min, math.min(param.value + increment, param.max)) | ||
| 99 | update_filter() | ||
| 100 | return | ||
| 101 | end | ||
| 102 | end | ||
| 103 | |||
| 104 | mp.msg.error('Unknown parameter "' .. name .. '"') | ||
| 105 | end | ||
| 106 | |||
| 107 | mp.add_key_binding("n", "toggle-acompressor", toggle_acompressor) | ||
| 108 | mp.register_script_message('update-param', update_param) | ||
| 109 | |||
| 110 | mp.add_key_binding("F1", 'acompressor-inc-threshold', function() update_param('threshold', -5); end, { repeatable = true }) | ||
| 111 | mp.add_key_binding("Shift+F1", 'acompressor-dec-threshold', function() update_param('Threshold', 5); end, { repeatable = true }) | ||
| 112 | |||
| 113 | mp.add_key_binding("F2", 'acompressor-inc-ratio', function() update_param('Ratio', 1); end, { repeatable = true }) | ||
| 114 | mp.add_key_binding("Shift+F2", 'acompressor-dec-ratio', function() update_param('Ratio', -1); end, { repeatable = true }) | ||
| 115 | |||
| 116 | mp.add_key_binding("F3", 'acompressor-inc-knee', function() update_param('Knee', 1); end, { repeatable = true }) | ||
| 117 | mp.add_key_binding("Shift+F3", 'acompressor-dec-knee', function() update_param('Knee', -1); end, { repeatable = true }) | ||
| 118 | |||
| 119 | mp.add_key_binding("F4", 'acompressor-inc-makeup', function() update_param('Makeup', 1); end, { repeatable = true }) | ||
| 120 | mp.add_key_binding("Shift+F4", 'acompressor-dec-makeup', function() update_param('Makeup', -1); end, { repeatable = true }) | ||
| 121 | |||
| 122 | mp.add_key_binding("F5", 'acompressor-inc-attack', function() update_param('Attack', 10); end, { repeatable = true }) | ||
| 123 | mp.add_key_binding("Shift+F5", 'acompressor-dec-attack', function() update_param('Attack', -10); end, { repeatable = true }) | ||
| 124 | |||
| 125 | mp.add_key_binding("F6", 'acompressor-inc-release', function() update_param('Release', 10); end, { repeatable = true }) | ||
| 126 | mp.add_key_binding("Shift+F6", 'acompressor-dec-release', function() update_param('Release', -10); end, { repeatable = true }) | ||