header={ "chef": "BeatRig", "dependencies": "ffprobe,ffmpeg", "title": "ATSC A/85 - General TV Broadcast", "description": "Validate or fix a file to match the ATSC A/85 loudness standard", "instructions": "Drop file(s) here", "recipe_version": "1.59", "tags": "audio, loudness, ATSC A/85, online streaming", "type": "demo", "os": "windows,macOS", "palette": "Clean Slate", "spice": "BQ==:G+Hy36kJcnQQYYavb/JV2Il6eCYr6h4hRJHCF9rRbhX5BcazL6cuTgUMdz0xA50VWA5PmmV0Q7aWkiUtNFtbk991l+t0e8IBXMReMIT1GNjnZZDv7n2YRAo0xcSQbY0ixjG6LLH4wGKnspKAoA/v69iLn6N2IH0XzIZwTGdHSo4=", "flavour": "0xij21t6/nsdE9OTh2ShIPJyrXEnNBa0IdkH58sH1vCfwK/3EqaXTs/EPKPZlrorRYlq8RwiIJhF3KQ9tS8SLF24fhgeEceSymARwwLnG1i/ADSfcUa7bjHWbTT3XhPCKUqjum+PQbfbiUkcXI22vhkYXrx7QQm2V9CxE6UuNT0=", "time": 1708112328, "core_version": "0.6.2", "magnetron_version": "1.0.282", "functions": "main,onConfig,onAbout", "uuid": "ab5e64ba85b64e26b91b1311c9418a27" }; // ============================================================================= // GLOBALS var settings = {}; // SETTINGS var settingsFile = ""; // LOCATION OF THE SETTINGS FILE var fileTypes = []; // ALLOWED FILE TYPES var has_ffmpeg = false; // CHECK IF FFMPEG & FFPROBE ARE AVAILABLE FOR TRUE PEAK LIMITING var has_sox = false; // CHECK IF FFMPEG HAS SOX RESAMPLER BUILT IN, WILL BE TESTET THE FIRST TIME NEEDED // ============================================================================= // SET DEFAULT VALUES function init(){ // LOCATION OF THE SETTINGS FILE settingsFile = folders.content+gvar.pss+'default.txt'; // SETTINGS settings = { "LUFS" : -24., // Target Loudness "tolerance" : true, // Tolerance (Permitted Deviation) "tolerance_val" : 2., "Dial" : false, // Use Dialog Intelligence // VALIDATE "TruePeak" : true, // Max True Peak Level "TruePeak_val" : -2., "tplimit" : true, // Enable True Peak Limiter "Max_S" : false, // Max Short Term "Max_S_val" : 0, "Max_M" : false, // Max Momentary "Max_M_val" : 0, "LRA" : false, // Loudness Range "LRA_val" : 0, // EXPORT "export" : 'ask', // ask = DEFAULT, never, always "overwrite" : false, // overwrite existing files "files" : 'fixed', // fixed (Default), Only files that need fixing will be processsed // all = Files that don't need fixing wil be copied (unprocessed) "customFolder" : false, // Move fixed files to a custom folder "customFolder_path" : folders.home, // By default this is the users home folder "nametag" : true, // Tag added to fixed file "nametag_val" : '-ATSC85', "subFolder" : false, // Move fixed files to a subfolder "subFolder_val" : '/ATSC85', "validate" : true, // Re-analyse after processing, // slow but more reliable }; // ALLOWED FILE TYPES fileTypes = [ 'wav', 'aif', 'aiff', ]; // CHECK IF FFMPEG & FFPROBE ARE AVAILABLE FOR TRUE PEAK LIMITING has_ffmpeg = !checkDependencies("ffprobe,ffmpeg"); // CHECK IF FFMPEG HAS SOX RESAMPLER BUILT IN // WILL BE TESTET THE FIRST TIME NEEDED has_sox = "NOTDEFINED"; } // ============================================================================= // MAIN // ============================================================================ function main() { // ----------------------------- // START THE MACHINE setProgress(0); // ----------------------------- // DEFAULT SETTINGS init(); // ----------------------------- // CUSTOM SETTINGS loadSettings(); // ----------------------------- // GET ALL FILES FROM THE APP var files = getFiles(); for (i=0;i (settings.TruePeak_val - 0.1) ? true : false); files[i]["fixme"] = 1; if(gvar.demo) msg.push('\n\n\n--------\nBuy Magnetron.APP to fix files'); needsfixing++; } // ------------------------------------ // TOOLTIP files[i]["file_tooltip"] = msg.join('\n'); // ------------------------------------ // UPDATE THE FILE LIST setFiles(files); } // ============================================================================= // IF SETTINGS IS AKS THEN ASK var doProcess = false; // ALWAYS EXPORT if( !gvar.demo && (settings.export == "yes" )){ doProcess = true; } // ASK TO EXPORT else if( !gvar.demo && settings.export == "ask" && needsfixing > 0){ if (askDialog().returns == 1) doProcess = true; } // ============================================================================= // PROCESS if( doProcess ) { // GET ALL FILES var files = getFiles(); // LOOP FILES THAT NEEDS FIXING for (i=0;i 0 ) { files[i]["file_icon"] = "ExclamationCircle"; files[i]["file_icon_color"] = "FFff0000"; files[i]["file_status"] = "Could not be fixed"; // REMOVE FILE IF ERRORS STILL EXIST if(fileExists(f.outFile)) deleteFile(f.outFile); } // FIXED else { files[i]["file_icon"] = "CheckSquare"; files[i]["file_icon_color"] = "FF0000FF"; files[i]["file_status"] = "Fixed"; } } // TOOLTIP files[i]["file_tooltip"] = files[i]["file_tooltip"]+msg.join('\n'); // ---- setFiles(files); // end loop } // end process } // ALL DONE setProgress(0); setMainMessage(""); setFiles(files); // end main } // ============================================================================= // GET THE LOUDNESS SPECIFICATIONS AND CHECK THEM, // IF NOT VALID RETURN AN ARRAY WITH ERRORS function validateLoudness(file, title ){ // --------------------------------------------------------------------- // RETURN VALUE var rtn = []; var err = 0; // --------------------------------------------------------------------- // ANALYSES AUDIO LEVELS var levels = levelFileAnalyse( file , { "AdjustTargetType": "LUFS", "AdjustTargetLevel": settings.LUFS, "DialogGate": settings.Dial, // "CalibrationLU": settings.LUFS, // "AdjustLevelLowered": true, // "LimitThresholddB" : settings.TruePeak_val, }); // --------------------------------------------------------------------- // PARSE RESULTS // ERROR if (levels == null) return false; if ( levels.LUFS == undefined ) { return false; } // FILE DID GET RESULTS, CHECK THEM if ( Math.abs(settings.LUFS - levels.LUFS) >= (settings.tolerance ? settings.tolerance_val : 0.1) ) { err++; rtn.push("x Loudness did not validate ("+toFixed(levels.LUFS, 1)+"LUFS)"); } else{ rtn.push("✓ Loudness ("+toFixed(levels.LUFS, 1)+"LUFS)"); } if(settings.TruePeak ){ if ( levels.TruePeak > settings.TruePeak_val ){ err++; rtn.push("x Peak Level exceededs "+toFixed(settings.TruePeak_val, 1)+"dBTP ("+toFixed(levels.TruePeak, 1)+"dBTP)"); } else{ rtn.push("✓ Peak Level ("+toFixed(levels.TruePeak, 1)+"LUFS)"); } } if(settings.Max_S ){ if ( levels.Max_S > settings.Max_S_val ) { err++; rtn.push("x MaxS exceeded "+toFixed(settings.Max_S_val, 1)+" ("+toFixed(levels.Max_S, 1)+"LUFS)"); } else{ rtn.push("✓ MaxS ("+toFixed(levels.Max_S, 1)+"LUFS)"); } } if(settings.Max_M){ if ( levels.Max_M > settings.Max_M_val ) { err++; rtn.push("x MaxM exceeded "+toFixed(settings.Max_M_val, 1)+" ("+toFixed(levels.Max_M, 1)+"LUFS)"); } else{ rtn.push("✓ MaxM ("+toFixed(levels.Max_M, 1)+"LUFS)"); } } if(settings.LRA){ if ( levels.LRA > settings.LRA_val ) { err++; rtn.push("x LRA exceeded "+toFixed(settings.LRA_val, 1)+" ("+toFixed(levels.LRA, 1)+"LUFS)"); } else{ rtn.push("✓ LRA ("+toFixed(levels.LRA, 1)+"LUFS)"); } } return { "status" : ( err > 0 ? 0 : 1 ), // if error = 0, passed = 1 "msg" : rtn, "levels" : levels }; // --------------------------------------------------------------------- } // ============================================================================= // LEVEL ANALYSE PROGRESS CALLBACK function LevelFileAnalyse_Progress(file, progress) { setProgress( progress * 100 ); } // ============================================================================= // LEVEL PROCESS PROGRESS CALLBACK function LevelFileProcess_Progress(file, progress) { setProgress( progress * 100 ); } // ============================================================================= // FFMPEG CALLBACK function cmd_callback() { setProgress(101); } // ============================================================================= // ON CONFIG, SHOW DIALOG AND SAVE SETTINGS function onConfig() { if(gvar.demo){ } else{ // ----------------------------- // DEFAULT SETTINGS init(); // ----------------------------- // LOAD CUSTOM SETTINGS loadSettings(); // ----------------------------- // IF THE CUSTOM PATH IS INVALID, SET IT TO HOME FOLDER if(!fileExists(settings.customFolder_path)){ settings.customFolder_path = folders.home; } // ----------------------------- // SETTINGS DIALOG var r = showSettings(); // ----------------------------- // IF RESET IS PUSHED if(r.returns === -1){ deleteFile(settingsFile); // DELETE THE SETTINGS FILE init(); // RESET TO DEFAULT SETTINGS onConfig(); // OPEN THIS CONFIG FUNCTION AGAIN } // ----------------------------- // IF SAVED WAS PUSHED else if(r.returns === 1){ // SUBFOLDER SHOULD START WITH A SLASH AND NO OTHER SLASHES ARE ALLOWED settings.subFolder_val = gvar.pss+replaceInString( settings.subFolder_val, gvar.pss, "" ); // CUSTOM FOLDER SHOULD NOT END WITH A SLASH settings.subFolder_val = ( settings.subFolder_val.charAt(myString.length - 1) == gvar.pss ? settings.subFolder_val.substring( 0 , myString.length-1) : settings.subFolder_val ); writeFile( settingsFile, objectToString(settings) ); } // ----------------------------- } } // ============================================================================= // ON ABOUT, SHOW POPUP WITH BUTTON TO OPEN WEBSITE function onAbout() { var args = { "info": { "type": "texteditor", "bounds" : { "y" : 70, "w" : 400, "h" : 150 }, "visible": true, "multiline" : true, "wordwrap" : true, "default" : header.description+'\n\nMore information on the recipes page on our website.', "enabled" : false }, "showpage" : { "type" : "button", "label" : "show page", "returns" : 1 }, "close" : { "type" : "button", "label" : "close", "returns" : 0 } }; var r = dialog(header.title, "" , "", args); if (r.showpage == 1) launchInBrowser('https://magnetron.app/recipes/uuid/'+header.uuid+'/'); } // ============================================================================= // LOAD DEFAULT VALUES function loadSettings(){ // ADD/UPDATE SETTINGS if ( fileExists(settingsFile) ){ // GET FILE var t = stringToObject( readFile(settingsFile) ); // REPLACE VALUES ONE-BY-ONE for (var j = 0; j < getObjectSize(t); j++) { settings[getObjectKey(t, j)] = getObjectValue(t, j); } } // RETURN THE VALUES return true; } // ============================================================================= function showSettings(arg) { var form = { // -------------------------------- "spacer0": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "heading1" : { "type" : "text", "label" : "// TARGET - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -", "just" : "l", "bounds" : { "x": 0, "w" : 600, "h" : 40 }, }, // -------------------------------- "LUFS_label" : { "type" : "text", "label" : "Loudnes", "bounds" : { "x": 0, "w" : 160, }, }, "LUFS_label2" : { "type" : "text", "label" : "0LU = ", "enabled" : false, "bounds" : { "y" : -1, "x": 0, "x" : 110, "w" : 80, }, }, "LUFS": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": -99, "max" : 0, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.LUFS }, "LUFS_d" : { "type" : "text", "label" : "LUFS", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "tolerance_label" : { "type" : "text", "label" : "Tolerance", "bounds" : { "x": 0, "w" : 160, }, }, "tolerance": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.tolerance }, "tolerance_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": 0, "max" : 10, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.tolerance_val }, "tolerance_d" : { "type" : "text", "label" : "+/- LU", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "dial_label" : { "type" : "text", "label" : "Dialogue Gate", "bounds" : { "x": 0, "w" : 160, }, }, "dial": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.dial }, // -------------------------------- "spacer0a": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "heading3" : { "type" : "text", "label" : "// VALIDATE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -", "just" : "l", "bounds" : { "x": 0, "w" : 600, "h" : 40 }, }, // -------------------------------- "TruePeak_label" : { "type" : "text", "label" : "TruePeak", "bounds" : { "x": 0, "w" : 160, }, }, "TruePeak": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.TruePeak }, "TruePeak_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": -9, "max" : 0, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.TruePeak_val }, "TruePeak_d" : { "type" : "text", "label" : "dBFS", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- // TP Limiter "tplimit_label" : { "type" : "text", "label" : "Limit", "just" : "l", "bounds" : { "y": -1, "x": 450, "w" : 100, }, }, "tplimit": { "type": "togglebox", "bounds" : { "y": -1, "x": 560, "w" : 25, }, "label" : "", "toggle" : settings.tplimit }, // -------------------------------- "Max_S_label" : { "type" : "text", "label" : "Max-S", "bounds" : { "x": 0, "w" : 160, }, }, "Max_S": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.Max_S }, "Max_S_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": 0, "max" : 12, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.Max_S_val }, "Max_S_d" : { "type" : "text", "label" : "LU", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "Max_M_label" : { "type" : "text", "label" : "Max-M", "bounds" : { "x": 0, "w" : 160, }, }, "Max_M": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.Max_M }, "Max_M_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": 0, "max" : 20, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.Max_M_val }, "Max_M_d" : { "type" : "text", "label" : "LU", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "LRA_label" : { "type" : "text", "label" : "LRA", "bounds" : { "x": 0, "w" : 160, }, }, "LRA": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.LRA }, "LRA_val": { "type": "slider", "style": "slider", "visible": true, "range" : { "min": 0, "max" : 20, "interval" : .1, "decimals" : 1 }, "bounds" : { "y" : -1, "x": 180, "w" : 100, }, "value": settings.LRA_val }, "LRA_d" : { "type" : "text", "label" : "LU", "bounds" : { "y" : -1,"x": 290, "w" : 160, }, }, // -------------------------------- "spacer1": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "heading2" : { "type" : "text", "label" : "// EXPORT - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -", "just" : "l", "bounds" : { "x": 0, "w" : 600, "h" : 40 }, }, // -------------------------------- // Fix "export_label" : { "type" : "text", "label" : "Export", "just" : "l", "bounds" : { "x": 0, "w" : 100, }, }, "export" : { "type" : "combobox", "default" : settings.export, "items" : "ask,yes,no", "bounds" : { "y" : -1, "x": 180, "w" : 120, }, "returns" : 0, }, // -------------------------------- // Overwrite "overwrite_label" : { "type" : "text", "label" : "Overwrite", "just" : "l", "bounds" : { "y": -1, "x": 450, "w" : 100, }, }, "overwrite": { "type": "togglebox", "bounds" : { "y": -1, "x": 560, "w" : 25, }, "label" : "", "toggle" : settings.overwrite }, // -------------------------------- "files_label" : { "type" : "text", "label" : "Files", "just" : "l", "bounds" : { "x": 0, "w" : 100, }, }, "files" : { "type" : "combobox", "default" : settings.files, "items" : "all,fixed", "bounds" : { "y" : -1, "x": 180, "w" : 120, }, "returns" : 0, }, // -------------------------------- "customFolder_label" : { "type" : "text", "label" : "Custom folder", "just" : "l", "bounds" : { "x": 0, "w" : 160, }, }, "customFolder": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.customFolder }, "customFolder_path" : { "type" : "fileselect", "path" : settings.customFolder_path, "editable" : false, "dir" : true, "saving" : true, "label" : "empty", "bounds" : { "y": -1, "x": 180, "w" : 400, }, "visible": false, }, // -------------------------------- "subFolder_label" : { "type" : "text", "label" : "Sub folder", "just" : "l", "bounds" : { "x": 0, "w" : 160, }, }, "subFolder": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.subFolder }, "subFolder_val": { "type": "texteditor", "bounds" : { "y": -1, "x" : 180, "w" : 400, }, "multiline" : false, "wordwrap" : false, "default" : settings.subFolder_val, "visible": true, }, // -------------------------------- "nametag_val_label" : { "type" : "text", "label" : "Name tag", "just" : "l", "bounds" : { "x": 0, "w" : 160, }, }, "nametag": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.nametag }, "nametag_val": { "type": "texteditor", "bounds" : { "y": -1, "x" : 180, "w" : 400, }, "multiline" : false, "wordwrap" : false, "default" : settings.nametag_val, "visible": false, }, // -------------------------------- "validate_label" : { "type" : "text", "label" : "Revalidate", "bounds" : { "x": 0, "w" : 160, }, }, "validate": { "type": "togglebox", "bounds" : { "y": -1, "x": 140, "w" : 25, }, "label" : "", "toggle" : settings.validate }, // -------------------------------- "spacer3" : { "type" : "text", "label" : " ", "just" : "r", "bounds" : { "x": 0, "w" : 200, "h" : 20 }, }, // -------------------------------- "reset" : { "type" : "button", "label" : "reset", "bounds" : { "x": 330, "w" : 80 }, "returns" : -1 }, "cancel" : { "type" : "button", "label" : "cancel", "bounds" : { "y": -1, "x": 415, "w" : 80 }, "returns" : 0 }, "save" : { "type" : "button", "label" : "save", "bounds" : { "y": -1, "x": 500, "w" : 80 }, "returns" : 1 }, // -------------------------------- }; // -------------------------------- return dialog(header.title, "", "w", form); } // ============================================================================= // ============================================================================ // DIALOG CALLBACK function dialog_callback(props) { // echo(objectToString(props)); // ---------------- // UPDATE SETTINGS if (props["name"] == "export") { settings['export'] = props["text"]; } if (props["name"] == "files") { settings['files'] = props["text"]; } if (props["name"] == "overwrite") { settings['overwrite'] = props["toggle"]; } if (props["name"] == "customFolder") { settings['customFolder'] = props["toggle"]; } if (props["name"] == "customFolder_path") { settings['customFolder_path'] = props["path"]; } if (props["name"] == "nametag") { settings['nametag'] = props["toggle"]; } if (props["name"] == "nametag_val") { settings['nametag_val'] = props["text"]; } if (props["name"] == "subFolder") { settings['subFolder'] = props["toggle"]; } if (props["name"] == "subFolder_val") { settings['subFolder_val'] = props["text"]; } if (props["name"] == "LUFS") { settings['LUFS'] = props["value"]; } if (props["name"] == "TruePeak") { settings['TruePeak'] = props["toggle"]; } if (props["name"] == "TruePeak_val") { settings['TruePeak_val'] = props["value"]; } if (props["name"] == "tplimit") { settings['tplimit'] = props["toggle"]; } if (props["name"] == "tolerance") { settings['tolerance'] = props["toggle"]; } if (props["name"] == "tolerance_val") { settings['tolerance_val'] = props["value"]; } if (props["name"] == "Max_S") { settings['Max_S'] = props["toggle"]; } if (props["name"] == "Max_S_val") { settings['Max_S_val'] = props["value"]; } if (props["name"] == "Max_M") { settings['Max_M'] = props["toggle"]; } if (props["name"] == "Max_M_val") { settings['Max_M_val'] = props["value"]; } if (props["name"] == "LRA") { settings['LRA'] = props["toggle"]; } if (props["name"] == "LRA_val") { settings['LRA_val'] = props["value"]; } if (props["name"] == "dial") { settings['dial'] = props["toggle"]; } if (props["name"] == "validate") { settings['validate'] = props["toggle"]; } // ---------------- // SHOW/HIDE WHEN EXPORT var exp = ( settings['export'] == 'yes' || settings['export'] == 'ask' ? true : false ); // ---------------- // UPDATE DIALOG var r = dialog({ "TruePeak_val" : { "visible" : settings['TruePeak'] }, "TruePeak_d" : { "visible" : settings['TruePeak'] }, "tplimit_label" : { "visible" : settings['TruePeak'] }, "tplimit" : { "visible" : settings['TruePeak'] }, "tolerance_val" : { "visible" : settings['tolerance'] }, "tolerance_d" : { "visible" : settings['tolerance'] }, "Max_S_val" : { "visible" : settings['Max_S'] }, "Max_S_d" : { "visible" : settings['Max_S'] }, "LRA_val" : { "visible" : settings['LRA'] }, "LRA_d" : { "visible" : settings['LRA'] }, "Max_M_val" : { "visible" : settings['Max_M'] }, "Max_M_d" : { "visible" : settings['Max_M'] }, "files_label" : { "enabled" : exp }, "files" : { "enabled" : exp, "visible" : exp, "default" : settings['files'] }, "overwrite_label" : { "enabled" : exp, "visible" : exp }, "overwrite" : { "enabled" : exp, "visible" : exp }, "validate_label" : { "enabled" : exp }, "validate" : { "enabled" : exp, "visible" : exp }, "customFolder_label" : { "enabled" : exp, }, "customFolder" : { "enabled" : exp, "visible" : exp }, "customFolder_path" : { "enabled" : exp, "visible" : exp && settings['customFolder'] }, "subFolder_label" : { "enabled" : exp, }, "subFolder" : { "enabled" : exp, "visible" : exp }, "subFolder_val" : { "enabled" : exp, "visible" : exp && settings['subFolder'] }, "nametag_val_label" : { "enabled" : exp, }, "nametag" : { "enabled" : exp, "visible" : exp }, "nametag_val" : { "enabled" : exp, "visible" : exp && settings['nametag'] }, }); // ---------------- } // ============================================================================= // DIALOG THE ASK THE USER TO FIX OR SKIP function askDialog() { var form = { // -------------------------------- "spacer0": { "type": "text", "label": "","just": "c", "bounds": { "x": 20, "w": 550, "h": 20 }, }, // -------------------------------- "text" : { "type" : "text", "label" : "Some files did not validate.\nDo you want to try to fix them?", "bounds" : { "x": 0, "w" : 550, "h" : 100 }, }, // -------------------------------- "text2" : { "type" : "text", "label" : "", "bounds" : { "x": 0, "w" : 550, "h" : 150 }, }, "skip" : { "type" : "button", "label" : "skip", "bounds" : { "x": 340, "w" : 100, }, "returns" : 0 }, "fix" : { "type" : "button", "label" : "fix", "bounds" : { "y": -1, "x": 450, "w" : 100 }, "returns" : 1 }, // -------------------------------- "text3" : { "type" : "text", "label" : "", "bounds" : { "x": 0, "w" : 550, "h" : 250 }, }, }; return dialog(header.title, '', "w", form); } // ============================================================================= function processFile(f) { var msg = []; var err = 0; // ---------------------------------------- // PROCESS USING FFMPEG if(f.process_limit && has_ffmpeg) { // WE NEED THE SAMPLE RATE BECAUSE IT WILL UPSAMPLE THE FILE TO 192 FOR THE TRUE PEAK // FILTER AND WE NEE TO BRING IT BACK TO THE ORIGINAL SAMPLE RATE var ffprobe = cmd("ffprobe", [ '-v','error', '-select_streams','a:0', '-show_entries','format=size :stream=sample_rate,bits_per_sample,channels', '-of','default=noprint_wrappers=1', '-i',f.path ]); var sample_rate = parseInt (searchRegEx(ffprobe, "(?:sample_rate=)(.*?)(?=[\n\r])", "o", 1)[0]); var channels = parseInt (searchRegEx(ffprobe, "(?:channels=)(.*?)(?=[\n\r])", "o", 1)[0]); // var bits_per_sample = parseInt (searchRegEx(ffprobe, "(?:bits_per_sample=)(.*?)(?=[\n\r])", "o", 1)[0]); // PREPARE THE FFMPEG CALL var specs = [ 'linear=true', 'I='+settings.LUFS, 'measured_I='+(Math.round(f["LUFS"]*1000)/1000), // 'measured_thresh=-39.20', // NEED TO HAVE A LOOK AT THIS // 'offset=0.58', // NEED TO HAVE A LOOK AT THIS 'print_format=summary', ]; // PEAKLIMITER LEVEL = MIN. -9 AND ALSO KEEP IT ON THE SAFE SIDE WITH -0.01 var tpVal = (Math.round((settings.TruePeak_val-0.01)*1000)/1000); tpVal = (tpVal < -9. ? -9. : tpVal); // ENABLE THE PEAK LIMITER if(settings.tplimit){ concatArray(specs,[ 'TP='+tpVal, 'measured_TP='+(Math.round(f["TruePeak"]*1000)/1000) ]); msg.push('- limiting at '+toFixed(tpVal,1)+'dBFS'); } specs.push('print_format=summary'); specs = specs.join(':'); // TEST IF FFMPEG HAS SOX BUILT IN if(has_ffmpeg && has_sox == "NOTDEFINED"){ var ffmpeg = cmd("ffmpeg", ['-version']); has_sox = containsString(ffmpeg, '--enable-libsoxr') ? true : false; } // IF SOX RESAMPLER IS PRESENT if(has_sox){ msg.push('- processed using FFMPEG (with SOX resampler)'); var ffmpeg = cmd("ffmpeg", [ '-i',f.path, '-filter:a','loudnorm='+specs+',aresample=resampler=soxr:out_sample_rate='+sample_rate+':precision=28', '-ac',channels, f.outFile, ]); } // NO SOX IS PRESENT, FALLBACK TO DEFAULT else{ msg.push('- processed using FMPEG'); var ffmpeg = cmd("ffmpeg", [ '-i',f.path, '-filter:a','loudnorm='+specs, '-ar',sample_rate, f.outFile, ]); } } // ---------------------------------------- // PROCESS USING BUILT IN PROCESSOR else { msg.push('- processed using built-in processor'); // adjust properties var args = { "AdjustLevel_NoLimit" : f.process_adjust, // "NeedLimiting" : f.process_limit, // "LimitThresholddB" : settings.TruePeak, }; // PROCESS var outfile = levelFileProcess( f.path, f.outFile, args ); // CHECK IF THE FILE WAS PROCESSED if (!outfile || typeof outfile.error !== 'undefined') { err++; msg.push(outfile.error); } } // ---------------------------------------- return { "msg" : msg, "err" : err }; }