#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=Icons\peakmeter.ico #AutoIt3Wrapper_Res_Comment=Measure loudness with ffmpeg according to R128. #AutoIt3Wrapper_Res_Description=Measure loudness with ffmpeg according to R128. #AutoIt3Wrapper_Res_Fileversion=1.1.0.16 #AutoIt3Wrapper_Res_Fileversion_AutoIncrement=p #AutoIt3Wrapper_Res_CompanyName=Norddeutscher Rundfunk #AutoIt3Wrapper_Res_LegalCopyright=Conrad Zelck #AutoIt3Wrapper_Res_SaveSource=y #AutoIt3Wrapper_Res_Language=1031 #AutoIt3Wrapper_Res_Field=Copyright|Conrad Zelck #AutoIt3Wrapper_Res_Field=Compile Date|%date% %time% #AutoIt3Wrapper_AU3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7 #AutoIt3Wrapper_Run_Au3Stripper=y #Au3Stripper_Parameters=/mo #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** #include <AutoItConstants.au3> #include <ButtonConstants.au3> #include <Date.au3> #include <FileConstants.au3> #include <GUIConstantsEx.au3> #include <MsgBoxConstants.au3> #include <StaticConstants.au3> #include <StringConstants.au3> #include <TrayCox.au3> ; source: https://github.com/SimpelMe/TrayCox - not needed for functionality FileInstall('K:\ffmpeg\bin\ffmpeg.exe', @TempDir & "\ffmpeg.exe", $FC_OVERWRITE) Local $sPathFFmpeg = @TempDir & "\" FileInstall('K:\ffmpeg\bin\ffprobe.exe', @TempDir & "\ffprobe.exe", $FC_OVERWRITE) Local $sPathFFprobe = @TempDir & "\" Global $g_sStdErrAll $cmdlineraw = StringReplace($cmdlineraw, '"', '') ; if there are spaces in filename $cmdlineraw is adding leading and trailing " Local $sFile If FileExists($cmdlineraw) Then $sFile = $cmdlineraw Else $sFile = FileOpenDialog("Choose a file", "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}", "Alle (*.*)", $FD_FILEMUSTEXIST) If @error Then MsgBox($MB_TOPMOST, "Error", "File selection failed." & @CRLF & @CRLF & "Application exits.") Exit EndIf EndIf Local $sOutputFileWithoutExtension = _StripFileExtension(_FileName($sFile)) SplashTextOn("Be patient", "R128 is analysing file ...", 300, 50) ; how many video streams to increase stream counter Local $sCommand = '-i "' & $sFile & '" -v 0 -show_entries stream=codec_type -of default=nw=1:nk=1' Local $sCodecs = _runFFprobe('ffprobe ' & $sCommand, $sPathFFprobe) ConsoleWrite("Codecs: " & $sCodecs & @CRLF) StringReplace($sCodecs, "video", "video") ; just to get the count Local $iCounterVideo = Number(@extended) ConsoleWrite("Counter video: " & $iCounterVideo & @CRLF) ; is audio inside? StringReplace($sCodecs, "audio", "audio") ; just to get the count If @extended = 0 Then SplashOff() MsgBox($MB_TOPMOST, "Error", "No audio channels found.") Exit EndIf ; channel layout for all audio streams $sCommand = '-i "' & $sFile & '" -v 0 -select_streams a -show_entries stream=channels -of default=nw=1:nk=1' Local $sChannels = _runFFprobe('ffprobe ' & $sCommand, $sPathFFprobe) SplashOff() Local $aChannels = StringSplit($sChannels, @CRLF, $STR_ENTIRESPLIT) If Not IsArray($aChannels) Then MsgBox($MB_TOPMOST, "Error", "No audio channels found.") Exit EndIf For $i = 0 To $aChannels[0] $aChannels[$i] = Number($aChannels[$i]) Next ConsoleWrite("Streams: " & $aChannels[0] & @CRLF) For $i = 1 To $aChannels[0] ConsoleWrite("Channels: " & $aChannels[$i] & @CRLF) Next ; are all streams mono or stereo Local Enum $eMONO = 1, $eSTEREO Local $iMonoCount = 0, $iStereoCount = 0 Local $iLayout = 0 ; only one number of channels returned If $aChannels[0] = 1 Then ConsoleWrite("Only one number of channels returned: " & $aChannels[0] & @CRLF) $iLayout = $eMONO If Mod($iMonoCount, 2) <> 0 Then $iLayout = 0 ; uneven counter $iMonoCount = $aChannels[1] ConsoleWrite("Monofiles: " & $iMonoCount & @CRLF) Else ; hope all returned channels are the same StringReplace($sChannels, "1", "1") ; just to get the count If @extended > 0 Then $iMonoCount = @extended If $iMonoCount = $aChannels[0] Then $iLayout = $eMONO If Mod($iMonoCount, 2) <> 0 Then $iLayout = 0 ; uneven counter EndIf ConsoleWrite("Monofiles: " & $iMonoCount & @CRLF) EndIf StringReplace($sChannels, "2", "2") ; just to get the count If @extended > 0 Then $iStereoCount = @extended If $iStereoCount = $aChannels[0] Then $iLayout = $eSTEREO EndIf ConsoleWrite("Stereofiles: " & $iStereoCount & @CRLF) EndIf EndIf Local $iMeasuringPairs = 0 Switch $iLayout Case $eMONO $iMeasuringPairs = $iMonoCount / 2 ConsoleWrite("All MONO" & @CRLF) Case $eSTEREO $iMeasuringPairs = $iStereoCount ConsoleWrite("All STEREO" & @CRLF) Case Else ConsoleWrite("UNDEFINED Layout" & @CRLF) MsgBox($MB_TOPMOST, "Error", "Track layout is undefined." & @CRLF & @CRLF & "Application exits.") Exit EndSwitch ConsoleWrite("Pairs for measuring: " & $iMeasuringPairs & @CRLF) Local $bShowTrackSelection = True If $iMeasuringPairs = 1 Then $bShowTrackSelection = False ; then there is no choice of tracks Local $iTrackL = 1 Local $iTrackR = 2 If $bShowTrackSelection Then GUICreate("Tracks", 300, 50) Local $idTracks12 = GUICtrlCreateRadio("1+2", 10, 10, 50, 30) GUICtrlSetState(-1, $GUI_CHECKED) Local $idTracks34 = GUICtrlCreateRadio("3+4", 60, 10, 50, 30) Local $idTracks56 = GUICtrlCreateRadio("5+6", 110, 10, 50, 30) Local $idTracks78 = GUICtrlCreateRadio("7+8", 160, 10, 50, 30) If $iMeasuringPairs < 3 Then GUICtrlSetState($idTracks56, $GUI_HIDE) GUICtrlSetState($idTracks78, $GUI_HIDE) ElseIf $iMeasuringPairs < 4 Then GUICtrlSetState($idTracks78, $GUI_HIDE) EndIf Local $idButtonOK = GUICtrlCreateButton("OK", 210, 10, 80, 30, $BS_DEFPUSHBUTTON) GUISetState(@SW_SHOW) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE Exit Case $idButtonOK ExitLoop Case $idTracks12 $iTrackL = 1 $iTrackR = 2 Case $idTracks34 $iTrackL = 3 $iTrackR = 4 Case $idTracks56 $iTrackL = 5 $iTrackR = 6 Case $idTracks78 $iTrackL = 7 $iTrackR = 8 EndSwitch WEnd GUIDelete() EndIf ConsoleWrite("L: " & $iTrackL & @CRLF) ConsoleWrite("R: " & $iTrackR & @CRLF) GUICreate("R128", 600, 270) GUICtrlCreateLabel(_FileName($sFile), 10, 10, 580, 30) GUICtrlSetFont(-1, 14, 400, 0, "Courier New") GUICtrlCreateLabel("Extract Audio:", 10, 50, 580, 30) GUICtrlSetFont(-1, 12, 400, 0, "Courier New") Global $Progress1 = GUICtrlCreateProgress(10, 80, 580, 20) GUICtrlCreateLabel("Loudness Audio:", 10, 120, 580, 30) GUICtrlSetFont(-1, 12, 400, 0, "Courier New") Global $Progress2 = GUICtrlCreateProgress(10, 150, 580, 20) Global $Edit = GUICtrlCreateLabel("", 10, 200, 260, 60) GUICtrlSetFont(-1, 14, 400, 0, "Courier New") Local $g_hLabelRunningTime = GUICtrlCreateLabel("", 440, 200, 150, 30, $SS_CENTER) GUICtrlSetFont(-1, 14, 400, 0, "Courier New") Local $idButtonExport = GUICtrlCreateButton("Export Data", 280, 230, 150, 30) GUICtrlSetFont(-1, 12, 400, 0, "Courier New") GUICtrlSetState(-1, $GUI_DISABLE) Local $idButtonCopy = GUICtrlCreateButton("Copy Data", 440, 230, 150, 30) GUICtrlSetFont(-1, 12, 400, 0, "Courier New") GUICtrlSetState(-1, $GUI_DISABLE) GUISetState(@SW_SHOW) Global $hTimerStart = TimerInit() Switch $iLayout Case $eMONO $sCommand = '-i "' & $sFile & '" -filter_complex "[0:' & $iTrackL - 1 + $iCounterVideo & '][0:' & $iTrackR - 1 + $iCounterVideo & '] amerge" -c:a pcm_s24le -ar 48000 -y ' & @TempDir & '\' & $sOutputFileWithoutExtension & '.wav' Case $eSTEREO $sCommand = '-i "' & $sFile & '" -map 0:' & $iTrackR / 2 - 1 + $iCounterVideo & ' -c:a pcm_s24le -ar 48000 -y ' & @TempDir & '\' & $sOutputFileWithoutExtension & '.wav' EndSwitch _runFFmpeg('ffmpeg ' & $sCommand, $sPathFFmpeg, 1) GUICtrlSetData($Progress1, 100) ; if ffmpeg is done than set progress to 100 - sometimes last StderrRead with 100 is missed If Not FileExists(@TempDir & '\' & $sOutputFileWithoutExtension & '.wav') Then ; error GUICtrlSetData($Edit, "Error: Could not extract audio.") Else $sCommand = '-i "' & @TempDir & '\' & $sOutputFileWithoutExtension & '.wav" -filter_complex ebur128=framelog=verbose:peak=true -f null -' _runFFmpeg('ffmpeg ' & $sCommand, $sPathFFmpeg, 2) GUICtrlSetData($Progress2, 100) ; if ffmpeg is done than set progress to 100 - sometimes last StderrRead with 100 is missed GUICtrlSetData($Edit, _GetR128($g_sStdErrAll)) GUICtrlSetState($idButtonExport, $GUI_ENABLE) GUICtrlSetState($idButtonCopy, $GUI_ENABLE) EndIf WinActivate("R128","") While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop Case $idButtonExport FileWrite(@TempDir & '\' & $sOutputFileWithoutExtension & '.txt', $sFile & @CRLF & @CRLF & GUICtrlRead($Edit) & @CRLF & @CRLF & "measurement time: " & StringRegExpReplace(GUICtrlRead($g_hLabelRunningTime), "\W", "")) Case $idButtonCopy ClipPut($sFile & @CRLF & @CRLF & GUICtrlRead($Edit) & @CRLF & @CRLF & "measurement time: " & StringRegExpReplace(GUICtrlRead($g_hLabelRunningTime), "\W", "")) EndSwitch WEnd Exit Func _runFFmpeg($command, $wd, $iProgress) Local $hPid = Run('"' & @ComSpec & '" /c ' & $command, $wd, @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) Local $sStdErr, $sTimer Local $iTicksDuration = 0, $iTicksTime = 0, $iTimer While 1 Sleep(500) $sStdErr = StderrRead($hPid) If @error Then ExitLoop $g_sStdErrAll &= $sStdErr If StringLen($sStdErr) > 0 Then If Not $iTicksDuration Then $iTicksDuration = _GetDuration($sStdErr) $iTicksTime = _GetTime($sStdErr) If Not @error Then $sStdErr = "" Switch $iProgress Case 1 GUICtrlSetData($Progress1, $iTicksTime * 100 / $iTicksDuration) Case 2 GUICtrlSetData($Progress2, $iTicksTime * 100 / $iTicksDuration) EndSwitch EndIf $iTimer = TimerDiff($hTimerStart) $sTimer = _Zeit($iTimer) If GUICtrlRead($g_hLabelRunningTime) <> $sTimer Then GUICtrlSetData($g_hLabelRunningTime, $sTimer) EndIf WEnd EndFunc Func _runFFprobe($command, $wd) Local $sStdErrAll Local $sStdErr Local $hPid = Run('"' & @ComSpec & '" /c ' & $command, $wd, @SW_HIDE, $STDOUT_CHILD + $STDERR_CHILD) While 1 Sleep(50) $sStdErr = StdoutRead($hPid) If @error Then ExitLoop $sStdErrAll &= $sStdErr WEnd $sStdErrAll = StringRegExpReplace($sStdErrAll, "\R$", "") ; delete last new line sequence Return $sStdErrAll EndFunc Func _GetDuration($sStdErr) If Not StringInStr($sStdErr, "Duration:") Then Return SetError(1, 0, 0) Local $aRegExp = StringRegExp($sStdErr, "(?i)Duration.+?([0-9:]+)", 3) If @error Or Not IsArray($aRegExp) Then Return SetError(2, 0, 0) Local $sTime = $aRegExp[UBound($aRegExp) - 1] Local $aTime = StringSplit($sTime, ":", 2) If @error Or Not IsArray($aTime) Then Return SetError(3, 0, 0) Return _TimeToTicks($aTime[0], $aTime[1], $aTime[2]) EndFunc ;==>_GetDuration Func _GetTime($sStdErr) If Not StringInStr($sStdErr, "time=") Then Return SetError(1, 0, 0) Local $aRegExp = StringRegExp($sStdErr, "(?i)time.+?([0-9:]+)", 3) If @error Or Not IsArray($aRegExp) Then Return SetError(2, 0, 0) Local $sTime = $aRegExp[UBound($aRegExp) - 1] Local $aTime = StringSplit($sTime, ":", 2) If @error Or Not IsArray($aTime) Then Return SetError(3, 0, 0) Return _TimeToTicks($aTime[0], $aTime[1], $aTime[2]) EndFunc ;==>_GetTime Func _GetR128($sStdErr) If Not StringInStr($sStdErr, "Integrated loudness:") Then Return SetError(1, 0, "Fehler") Local $aRegExp = StringRegExp($sStdErr, "(?isU)Integrated loudness:.*(I:.*LUFS).*(LRA:.*LU).*\h\h(Peak:.*dBFS)", 3) If @error Or Not IsArray($aRegExp) Then Return SetError(2, 0, "Fehler") Local $iUbound = UBound($aRegExp) Return $aRegExp[$iUbound -3] & @CRLF & $aRegExp[$iUbound - 2] & @CRLF & $aRegExp[$iUbound - 1] EndFunc ;==>_GetR128 Func _FileName($sFullPath) Local $iDelimiter = StringInStr($sFullPath, "\", 0, -1) Return StringTrimLeft($sFullPath, $iDelimiter) EndFunc Func _StripFileExtension($sFile) Local $iDelimiter = StringInStr($sFile, ".", 0, -1) Return StringLeft($sFile, $iDelimiter - 1) EndFunc Func _Zeit($iMs, $bComfortView = True) ; from ms to a format: "12h 36m 56s 13f" (with special space between - ChrW(8239)) Local $sReturn $iMs = Int($iMs) Local $iFrames, $iMSec, $iSec, $iMin, $iHour, $sSign If $iMs < 0 Then $iMs = Abs($iMs) $sSign = '-' EndIf $iMSec = StringRight($iMs, 3) $iFrames = $iMSec / 40 $iSec = $iMs / 1000 $iMin = $iSec / 60 $iHour = $iMin / 60 $iMin -= Int($iHour) * 60 $iSec -= Int($iMin) * 60 If $bComfortView Then ; no hours if not present and no frames If Not Int($iHour) = 0 Then $sReturn &= StringRight('0' & Int($iHour), 2) & 'h' & ChrW(8239) $sReturn &= StringRight('0' & Int($iMin), 2) & 'm' & ChrW(8239) If Int($iHour) = 0 Then $sReturn &= StringRight('0' & Int($iSec), 2) & 's' ; zum DEBUGGING auskommentieren Else $sReturn = $sSign & StringRight('0' & Int($iHour), 2) & 'h' & ChrW(8239) & StringRight('0' & Int($iMin), 2) & 'm' & ChrW(8239) & StringRight('0' & Int($iSec), 2) & 's' & ChrW(8239) & StringRight('0' & Int($iFrames), 2) & 'f' EndIf Return $sReturn EndFunc ;==>_Zeit