mirror of
https://github.com/staxrip/staxrip.git
synced 2026-10-18 14:40:32 +08:00
851 lines
29 KiB
VB.net
851 lines
29 KiB
VB.net
|
|
Imports System.Text
|
|
Imports System.Globalization
|
|
Imports System.Text.RegularExpressions
|
|
Imports System.Runtime.Remoting
|
|
|
|
Public Class Audio
|
|
Shared Sub Process(ap As AudioProfile)
|
|
If Not File.Exists(ap.File) OrElse TypeOf ap Is NullAudioProfile Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Directory.Exists(p.TempDir) Then
|
|
p.TempDir = ap.File.Dir
|
|
End If
|
|
|
|
If ap.File <> p.SourceFile Then
|
|
Log.Write("Media Info Audio Source " & (ap.GetTrackIndex + 1), MediaInfo.GetSummary(ap.File))
|
|
End If
|
|
|
|
Dim normalized = False
|
|
Dim isGUIAP = TypeOf ap Is GUIAudioProfile
|
|
Dim gap = TryCast(ap, GUIAudioProfile)
|
|
|
|
If isGUIAP AndAlso gap.ContainsCommand("ffmpeg") Then
|
|
normalized = gap.NormalizeFF()
|
|
End If
|
|
|
|
Dim extractCore = isGUIAP AndAlso gap.ExtractCore
|
|
|
|
If ap.Decoder <> AudioDecoderMode.Automatic AndAlso Not extractCore Then
|
|
Convert(ap)
|
|
End If
|
|
|
|
If ap.HasStream Then
|
|
Dim cutting = p.Ranges.Count > 0
|
|
|
|
Dim directMux = TypeOf ap Is MuxAudioProfile AndAlso
|
|
p.VideoEncoder.Muxer.IsSupported(ap.Stream.Ext) AndAlso
|
|
p.VideoEncoder.Muxer.IsSupported(ap.File.Ext) AndAlso Not cutting
|
|
|
|
Dim trackIsSupportedButNotContainer = TypeOf ap Is MuxAudioProfile AndAlso
|
|
p.VideoEncoder.Muxer.IsSupported(ap.Stream.Ext) AndAlso
|
|
Not p.VideoEncoder.Muxer.IsSupported(ap.File.Ext)
|
|
|
|
If ((cutting OrElse Not ap.IsInputSupported) AndAlso Not directMux) OrElse
|
|
trackIsSupportedButNotContainer Then
|
|
|
|
Select Case ap.File.ExtFull
|
|
Case ".mkv", ".webm"
|
|
mkvDemuxer.Demux(ap.File, {ap.Stream}, Nothing, ap, p, False, False, True, "Demux MKV Audio " & (ap.GetTrackIndex + 1), True)
|
|
Case ".mp4"
|
|
MP4BoxDemuxer.DemuxAudio(ap.File, ap.Stream, ap, p, True)
|
|
Case Else
|
|
If p.Script.GetFilter("Source").Script.ToLowerInvariant.Contains("directshowsource") AndAlso TypeOf ap IsNot MuxAudioProfile Then
|
|
ConvertDirectShowSource(ap)
|
|
ElseIf Not ap.File.Ext = "m2ts" Then
|
|
ffmpegDemuxer.DemuxAudio(ap.File, ap.Stream, ap, p, True)
|
|
End If
|
|
End Select
|
|
End If
|
|
End If
|
|
|
|
Cut(ap)
|
|
|
|
If TypeOf ap IsNot MuxAudioProfile AndAlso ap.NeedConvert Then
|
|
Convert(ap, True)
|
|
End If
|
|
End Sub
|
|
|
|
Shared Function GetBaseNameForStream(filePath As String, stream As AudioStream) As String
|
|
Dim base = If(p.TempDir?.DirExists() AndAlso New DirectoryInfo(p.TempDir).Name.EndsWith("_temp") AndAlso filePath.Base.StartsWithEx(p.SourceFile.Base),
|
|
filePath.Base.Substring(p.SourceFile.Base.Length),
|
|
filePath.Base)
|
|
|
|
Dim ret = base + " ID" & (stream.Index + 1)
|
|
|
|
If stream.Language.TwoLetterCode <> "iv" Then
|
|
ret += $"_[{stream.Language.Name}] ({stream.Language.EnglishName})"
|
|
End If
|
|
|
|
If stream.Default Then
|
|
ret += " [Default]"
|
|
End If
|
|
|
|
If stream.Forced Then
|
|
ret += " [Forced]"
|
|
End If
|
|
|
|
If stream.SBR Then
|
|
ret += " [SBR]"
|
|
End If
|
|
|
|
If stream.Delay <> 0 Then
|
|
ret += " " & stream.Delay & "ms"
|
|
End If
|
|
|
|
If stream.Title <> "" Then
|
|
Dim pathLength = (p.TempDir + ret).Length
|
|
Dim leftLength = GlobalClass.MAX_PATH - 10 - pathLength
|
|
If leftLength > 3 Then
|
|
ret += " {" + stream.Title.Shorten(leftLength).EscapeIllegalFileSysChars + "}"
|
|
End If
|
|
End If
|
|
|
|
Return ret.Trim
|
|
End Function
|
|
|
|
Shared Sub Convert(ap As AudioProfile, Optional force As Boolean = False)
|
|
If Not force AndAlso ap.File.Ext = ap.ConvertExt Then Exit Sub
|
|
|
|
If ap.File.Ext = "avs" Then
|
|
Dim outPath = ap.File.DirAndBase + "." + ap.ConvertExt
|
|
Dim args = "-i " + ap.File.Escape + " -y -hide_banner " + outPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to " + outPath.Ext.ToUpperInvariant
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
ap.File = outPath
|
|
Exit Sub
|
|
End If
|
|
End If
|
|
|
|
Dim isGUIAP = TypeOf ap Is GUIAudioProfile
|
|
Dim gap = TryCast(ap, GUIAudioProfile)
|
|
Dim renormalize = isGUIAP AndAlso gap.Params.Normalize AndAlso gap.GetEncoder() <> GuiAudioEncoder.deezy AndAlso
|
|
(gap.Params.ffmpegNormalizeMode = ffmpegNormalizeMode.dynaudnorm OrElse
|
|
(gap.Params.ChannelsMode <> ChannelsMode.Original OrElse gap.GetEncoder() = GuiAudioEncoder.opusenc))
|
|
|
|
Select Case ap.Decoder
|
|
Case AudioDecoderMode.ffmpeg, AudioDecoderMode.Automatic
|
|
ConvertFF(ap)
|
|
Case AudioDecoderMode.FFAudioSource
|
|
ConvertFFAudioSource(ap)
|
|
Case AudioDecoderMode.eac3to
|
|
ConvertEac3to(ap)
|
|
Case AudioDecoderMode.NicAudio
|
|
ConvertNicAudio(ap)
|
|
Case AudioDecoderMode.DirectShow
|
|
ConvertDirectShowSource(ap)
|
|
End Select
|
|
|
|
If p.Script.GetFilter("Source").Script.ToLowerInvariant.Contains("directshowsource") Then
|
|
ConvertDirectShowSource(ap)
|
|
End If
|
|
|
|
gap.Params.Normalize = renormalize
|
|
gap.Params.ffmpegNormalizeMode = If(gap.Params.ffmpegNormalizeMode = ffmpegNormalizeMode.dynaudnorm, ffmpegNormalizeMode.volumedetect, gap.Params.ffmpegNormalizeMode)
|
|
|
|
ConvertFF(ap, renormalize)
|
|
ConvertEac3to(ap)
|
|
ConvertDirectShowSource(ap)
|
|
End Sub
|
|
|
|
Shared Sub CutNicAudio(ap As AudioProfile)
|
|
If ap.File.Contains("_cut_") Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not FileTypes.NicAudioInput.Contains(ap.File.Ext) Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
Dim wavPath = Path.Combine(p.TempDir, ap.File.Base + "_cut_na.wav")
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_cut_na.avs")
|
|
d.Filters.Insert(1, New VideoFilter(GetNicAudioCode(ap)))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + wavPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(wavPath) Then
|
|
ap.File = wavPath
|
|
Log.WriteLine(MediaInfo.GetSummary(wavPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub ConvertNicAudio(ap As AudioProfile)
|
|
If ap.File.Ext = ap.ConvertExt Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not FileTypes.NicAudioInput.Contains(ap.File.Ext) Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
d.RemoveFilter("Cutting")
|
|
Dim outPath = Path.Combine(p.TempDir, ap.File.Base + "_DecodeNicAudio." + ap.ConvertExt)
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_DecodeNicAudio.avs")
|
|
d.Filters.Insert(1, New VideoFilter(GetNicAudioCode(ap)))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + outPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
ap.File = outPath
|
|
Log.WriteLine(MediaInfo.GetSummary(outPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub Cut(ap As AudioProfile)
|
|
If p.Ranges.Count = 0 OrElse
|
|
Not File.Exists(ap.File) OrElse
|
|
TypeOf p.VideoEncoder Is NullEncoder OrElse
|
|
ap.File.Contains("_cut_.") Then
|
|
|
|
Exit Sub
|
|
End If
|
|
|
|
Select Case p.CuttingMode
|
|
Case CuttingMode.mkvmerge
|
|
CutMkvmerge(ap)
|
|
Case CuttingMode.NicAudio
|
|
If FileTypes.NicAudioInput.Contains(ap.File.Ext) AndAlso TypeOf ap IsNot MuxAudioProfile Then
|
|
CutNicAudio(ap)
|
|
Else
|
|
CutMkvmerge(ap)
|
|
End If
|
|
Case CuttingMode.DirectShow
|
|
If TypeOf ap IsNot MuxAudioProfile Then
|
|
CutDirectShowSource(ap)
|
|
Else
|
|
CutMkvmerge(ap)
|
|
End If
|
|
End Select
|
|
End Sub
|
|
|
|
Shared Function GetNicAudioCode(ap As AudioProfile) As String
|
|
Select Case ap.File.Ext
|
|
Case "ac3"
|
|
Return "AudioDub(last, NicAC3Source(""" + ap.File + """, Channels = " & ap.Channels & "))"
|
|
Case "mpa", "mp2", "mp3"
|
|
Return "AudioDub(last, NicMPASource(""" + ap.File + """))"
|
|
Case "wav"
|
|
Return "AudioDub(last, RaWavSource(""" + ap.File + """, Channels = " & ap.Channels & "))"
|
|
End Select
|
|
End Function
|
|
|
|
Shared Sub ConvertEac3to(ap As AudioProfile)
|
|
If ap.File.Ext = ap.ConvertExt Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not FileTypes.eac3toInput.Contains(ap.File.Ext) Then
|
|
Exit Sub
|
|
End If
|
|
|
|
Dim outPath = Path.Combine(p.TempDir, ap.File.Base + "." + ap.ConvertExt)
|
|
Dim args = ap.File.Escape + " " + outPath.Escape
|
|
|
|
If ap.Channels = 6 Then
|
|
args += " -down6"
|
|
ElseIf ap.Channels = 2 Then
|
|
args += " -down2"
|
|
End If
|
|
|
|
If TypeOf ap Is GUIAudioProfile Then
|
|
Dim gap = DirectCast(ap, GUIAudioProfile)
|
|
|
|
If gap.Params.Normalize Then
|
|
args += " -normalize"
|
|
gap.Params.Normalize = True
|
|
End If
|
|
End If
|
|
|
|
args += " -simple -progressnumbers"
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "Convert " + ap.File.Ext.ToUpperInvariant + " to " + outPath.Ext.ToUpperInvariant + " " & (ap.GetTrackIndex + 1)
|
|
proc.Package = Package.eac3to
|
|
proc.Arguments = args
|
|
proc.TrimChars = {"-"c, " "c}
|
|
proc.SkipStrings = {"process:", "analyze:"}
|
|
proc.AllowedExitCodes = {0, 1}
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
ap.File = outPath
|
|
Log.WriteLine(MediaInfo.GetSummary(outPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub ConvertFF(ap As AudioProfile, Optional force As Boolean = False)
|
|
If Not force AndAlso ap.File.Ext = ap.ConvertExt Then Exit Sub
|
|
|
|
Dim gap = TryCast(ap, GUIAudioProfile)
|
|
|
|
gap?.NormalizeFF()
|
|
|
|
Dim base = ap.File.Base
|
|
|
|
If Not base.Contains(ap.GetTrackID) Then
|
|
base += ap.GetTrackID
|
|
End If
|
|
|
|
Dim outPath = Path.Combine(p.TempDir, base + "." + ap.ConvertExt)
|
|
|
|
If ap.File = outPath Then
|
|
outPath += "." + ap.ConvertExt
|
|
End If
|
|
|
|
Dim cancel = True
|
|
Dim args = "-y -hide_banner -i " + ap.File.Escape
|
|
|
|
If ap.Stream IsNot Nothing Then
|
|
args += " -map 0:" & ap.Stream.StreamOrder
|
|
End If
|
|
|
|
If ap.Gain <> 0 Then
|
|
args += " -af volume=" + ap.Gain.ToInvariantString + "dB"
|
|
cancel = False
|
|
End If
|
|
|
|
If gap?.Params.Normalize Then
|
|
If gap.Params.ffmpegNormalizeMode = ffmpegNormalizeMode.dynaudnorm Then
|
|
args += " " + Audio.GetDynAudNormArgs(gap.Params)
|
|
cancel = False
|
|
ElseIf gap.Params.ffmpegNormalizeMode = ffmpegNormalizeMode.loudnorm Then
|
|
args += " " + Audio.GetLoudNormArgs(gap.Params)
|
|
cancel = False
|
|
End If
|
|
End If
|
|
|
|
If gap.Params.CenterOptimizedStereo AndAlso ((gap?.Params.Codec <> AudioCodec.Opus AndAlso gap?.Params.ChannelsMode = ChannelsMode._2) OrElse (gap?.Params.Codec = AudioCodec.Opus AndAlso gap?.Params.OpusencDownmix = OpusDownmix.Stereo)) Then
|
|
If gap.SourceChannels = 8 Then
|
|
args += " -af pan=stereo|c0=c2+0.30c0+0.30c4+0.30c6|c1=c2+0.30c1+0.30c5+0.30c7"
|
|
Else
|
|
args += " -af pan=stereo|c0=c2+0.30*c0+0.30*c4|c1=c2+0.30*c1+0.30*c5"
|
|
End If
|
|
gap.Params.ChannelsMode = ChannelsMode.Original
|
|
gap.Params.OpusencDownmix = OpusDownmix.Original
|
|
cancel = False
|
|
ElseIf gap.Params.ChannelsMode <> ChannelsMode.Original Then
|
|
args += " -ac " & CInt(gap.Params.ChannelsMode)
|
|
gap.Params.ChannelsMode = ChannelsMode.Original
|
|
gap.Params.OpusencDownmix = OpusDownmix.Original
|
|
ElseIf gap.Params.Codec = AudioCodec.Opus AndAlso gap.Params.OpusencDownmix <> OpusDownmix.Original Then
|
|
args += " -ac " & CInt(gap.Params.OpusencDownmix)
|
|
gap.Params.ChannelsMode = ChannelsMode.Original
|
|
gap.Params.OpusencDownmix = OpusDownmix.Original
|
|
Else
|
|
If Not force Then
|
|
args += " -ac " & ap.Channels
|
|
End If
|
|
gap.Params.ChannelsMode = ChannelsMode.Original
|
|
gap.Params.OpusencDownmix = OpusDownmix.Original
|
|
End If
|
|
|
|
If force AndAlso cancel Then Exit Sub
|
|
|
|
If ap.ConvertExt.EqualsAny("wav", "w64") Then
|
|
Select Case p.AudioIntermediateWaveBitDepth
|
|
Case IntermediateWaveBitDepth.s24
|
|
args += " -c:a pcm_s24le"
|
|
Case IntermediateWaveBitDepth.s32
|
|
args += " -c:a pcm_s32le"
|
|
Case IntermediateWaveBitDepth.f32
|
|
args += " -c:a pcm_f32le"
|
|
Case Else
|
|
args += " -c:a pcm_s32le"
|
|
End Select
|
|
End If
|
|
|
|
Dim matches = Regex.Matches(args, " -af ([^ ]+)")
|
|
If matches.Count > 1 Then
|
|
Dim concated = ""
|
|
For i = matches.Count - 1 To 0 Step -1
|
|
Dim match = matches(i)
|
|
concated = $"{match.Groups(1)},{concated}"
|
|
args = args.Remove(match.Index, match.Length)
|
|
Next
|
|
concated = " -af " + concated.Trim(","c)
|
|
args = args.Insert(matches(0).Index, concated)
|
|
End If
|
|
|
|
args = Regex.Replace(args, " -af ([^ ]+)", " -af ""$1""")
|
|
args += " " + outPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "Convert " + ap.File.Ext.ToUpperInvariant + " to " + outPath.Ext.ToUpperInvariant + " " & (ap.GetTrackIndex + 1)
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.AllowedExitCodes = {0, 1}
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
If outPath.StartsWith("\\?\") Then
|
|
outPath = outPath.Substring(4)
|
|
End If
|
|
|
|
ap.Gain = 0
|
|
gap.Params.CenterOptimizedStereo = False
|
|
ap.File = outPath
|
|
Log.WriteLine(MediaInfo.GetSummary(outPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Function GetLoudNormArgs(params As GUIAudioProfile.Parameters) As String
|
|
Return "-af loudnorm=" +
|
|
"I=" + params.ffmpegLoudnormIntegrated.ToInvariantString +
|
|
":TP=" + params.ffmpegLoudnormTruePeak.ToInvariantString +
|
|
":LRA=" + params.ffmpegLoudnormLRA.ToInvariantString +
|
|
":measured_I=" + params.ffmpegLoudnormIntegratedMeasured.ToInvariantString +
|
|
":measured_TP=" + params.ffmpegLoudnormTruePeakMeasured.ToInvariantString +
|
|
":measured_LRA=" + params.ffmpegLoudnormLraMeasured.ToInvariantString +
|
|
":measured_thresh=" + params.ffmpegLoudnormThresholdMeasured.ToInvariantString +
|
|
":print_format=summary"
|
|
End Function
|
|
|
|
Shared Function GetDynAudNormArgs(params As GUIAudioProfile.Parameters) As String
|
|
Dim ret = ""
|
|
|
|
If params.ffmpegDynaudnormF <> 500 Then ret += ":f=" + params.ffmpegDynaudnormF.ToInvariantString
|
|
If params.ffmpegDynaudnormG <> 31 Then ret += ":g=" + params.ffmpegDynaudnormG.ToInvariantString
|
|
If params.ffmpegDynaudnormP <> 0.95 Then ret += ":p=" + params.ffmpegDynaudnormP.ToInvariantString
|
|
If params.ffmpegDynaudnormM <> 10 Then ret += ":m=" + params.ffmpegDynaudnormM.ToInvariantString
|
|
If params.ffmpegDynaudnormR <> 0 Then ret += ":r=" + params.ffmpegDynaudnormR.ToInvariantString
|
|
If params.ffmpegDynaudnormS <> 0 Then ret += ":s=" + params.ffmpegDynaudnormS.ToInvariantString
|
|
If Not params.ffmpegDynaudnormN Then ret += ":n=false"
|
|
If params.ffmpegDynaudnormC Then ret += ":c=true"
|
|
If params.ffmpegDynaudnormB Then ret += ":b=true"
|
|
|
|
Return If(ret = "", "-af dynaudnorm", "-af dynaudnorm=" + ret.Trim(":"c))
|
|
End Function
|
|
|
|
Shared Sub ConvertDirectShowSource(ap As AudioProfile, Optional useFlac As Boolean = False)
|
|
If ap.File.Ext = ap.ConvertExt Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
d.RemoveFilter("Cutting")
|
|
Dim outPath = Path.Combine(p.TempDir, ap.File.Base + "_convDSS." + ap.ConvertExt)
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_DecDSS.avs")
|
|
d.Filters.Insert(1, New VideoFilter("AudioDub(last,DirectShowSource(""" + ap.File + """, video=false))"))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + outPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
ap.File = outPath
|
|
Log.WriteLine(MediaInfo.GetSummary(outPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub ConvertFFAudioSource(ap As AudioProfile)
|
|
If ap.File.Ext = ap.ConvertExt Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim cachefile = Path.Combine(p.TempDir, ap.File.Base + ".ffindex")
|
|
g.ffmsindex(ap.File, cachefile)
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
d.RemoveFilter("Cutting")
|
|
Dim outPath = Path.Combine(p.TempDir, ap.File.Base + "_convFFAudioSource." + ap.ConvertExt)
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_DecodeFFAudioSource.avs")
|
|
d.Filters.Insert(1, New VideoFilter("AudioDub(last,FFAudioSource(""" + ap.File + """, cachefile=""" + cachefile + """))"))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + outPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(outPath) Then
|
|
ap.File = outPath
|
|
Log.WriteLine(MediaInfo.GetSummary(outPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub CutDirectShowSource(ap As AudioProfile)
|
|
If ap.File.Contains("_cut_") Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
Dim wavPath = Path.Combine(p.TempDir, ap.File.Base + "_cut_ds.wav")
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_cut_ds.avs")
|
|
d.Filters.Insert(1, New VideoFilter("AudioDub(last,DirectShowSource(""" + ap.File + """, video=false))"))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + wavPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(wavPath) Then
|
|
ap.File = wavPath
|
|
Log.WriteLine(MediaInfo.GetSummary(wavPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub CutFFAudioSource(ap As AudioProfile)
|
|
If ap.File.Contains("_cut_") Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
ap.Delay = 0
|
|
Dim cachefile = Path.Combine(p.TempDir, ap.File.Base + ".ffindex")
|
|
g.ffmsindex(ap.File, cachefile)
|
|
Dim d As New VideoScript
|
|
d.Filters.AddRange(p.Script.Filters)
|
|
Dim wavPath = Path.Combine(p.TempDir, ap.File.Base + "_cut_ff.wav")
|
|
d.Path = Path.Combine(p.TempDir, ap.File.Base + "_cut_ff.avs")
|
|
d.Filters.Insert(1, New VideoFilter("AudioDub(last,FFAudioSource(""" + ap.File + """, cachefile=""" + cachefile + """))"))
|
|
|
|
If ap.Channels = 2 Then
|
|
d.Filters.Add(New VideoFilter(GetDown2Code))
|
|
End If
|
|
|
|
d.Synchronize()
|
|
|
|
Dim args = "-i " + d.Path.Escape + " -y -hide_banner " + wavPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "AVS to WAV"
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog(Macro.Expand(d.GetScript) + BR)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If g.FileExists(wavPath) Then
|
|
ap.File = wavPath
|
|
Log.WriteLine(MediaInfo.GetSummary(wavPath))
|
|
Else
|
|
Log.Write("Error", "no output found")
|
|
End If
|
|
End Sub
|
|
|
|
Shared Sub CutMkvmerge(ap As AudioProfile)
|
|
If ap.File.Contains("_cut_") Then
|
|
Exit Sub
|
|
End If
|
|
|
|
If Not Package.AviSynth.VerifyOK(True) Then
|
|
Throw New AbortException
|
|
End If
|
|
|
|
Dim base = ap.File.Base
|
|
|
|
If Not base.Contains(ap.GetTrackID) Then
|
|
base += ap.GetTrackID
|
|
End If
|
|
|
|
Dim aviPath = Path.Combine(p.TempDir, base + "_cut_.avi")
|
|
Dim d = (p.CutFrameCount / p.CutFrameRate).ToString("f9", CultureInfo.InvariantCulture)
|
|
Dim r = p.CutFrameRate.ToString("f9", CultureInfo.InvariantCulture)
|
|
Dim args = $"-f lavfi -i color=c=black:s=16x16:d={d}:r={r} -y -hide_banner -c:v ffv1 -g 1 " + aviPath.Escape
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "Create avi file for audio cutting " & (ap.GetTrackIndex + 1)
|
|
proc.SkipStrings = {"frame=", "size="}
|
|
proc.WriteLog("mkvmerge cannot cut audio without video so an avi file has to be created" + BR2)
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.ffmpeg
|
|
proc.Arguments = args
|
|
proc.Start()
|
|
End Using
|
|
|
|
If Not File.Exists(aviPath) Then
|
|
Throw New ErrorAbortException("Error", "Output file missing")
|
|
Else
|
|
Log.WriteLine(MediaInfo.GetSummary(aviPath))
|
|
End If
|
|
|
|
Dim mkvPath = Path.Combine(p.TempDir, base + "_cut_.mkv")
|
|
|
|
Dim args2 = "-o " + mkvPath.Escape + " " + aviPath.Escape + " " + ap.File.Escape
|
|
args2 += " --split parts-frames:" + p.Ranges.Select(Function(v) v.Start & "-" & (v.End + 1)).Join(",+")
|
|
args2 += " --engage append_and_split_flac --ui-language en"
|
|
|
|
Using proc As New Proc
|
|
proc.Header = "Cut Audio " & (ap.GetTrackIndex + 1)
|
|
proc.SkipString = "Progress: "
|
|
proc.Encoding = Encoding.UTF8
|
|
proc.Package = Package.mkvmerge
|
|
proc.Arguments = args2
|
|
proc.AllowedExitCodes = {0, 1}
|
|
proc.Start()
|
|
End Using
|
|
|
|
Dim fail As Boolean
|
|
|
|
If File.Exists(mkvPath) Then
|
|
Log.WriteLine(MediaInfo.GetSummary(mkvPath))
|
|
Dim streams = MediaInfo.GetAudioStreams(mkvPath)
|
|
|
|
If streams.Count > 0 Then
|
|
mkvDemuxer.Demux(mkvPath, {streams(0)}, Nothing, ap, p, False, False, True,
|
|
"Demux cutted MKV " & (ap.GetTrackIndex + 1), False)
|
|
Else
|
|
fail = True
|
|
End If
|
|
Else
|
|
fail = True
|
|
End If
|
|
|
|
If fail AndAlso TypeOf ap Is GUIAudioProfile AndAlso Not ap.File.Ext = "wav" Then
|
|
Log.Write("Error", "no output found")
|
|
Convert(ap)
|
|
|
|
If ap.File.Ext = "wav" Then
|
|
Cut(ap)
|
|
End If
|
|
End If
|
|
End Sub
|
|
|
|
Shared Function GetDown2Code() As String
|
|
Return "Audiochannels() >= 6 ? Down2(last) : last
|
|
|
|
function Down2(clip a)
|
|
{
|
|
a = ConvertAudioToFloat(a)
|
|
fl = GetChannel(a, 1)
|
|
fr = GetChannel(a, 2)
|
|
c = GetChannel(a, 3)
|
|
lfe = GetChannel(a, 4)
|
|
sl = GetChannel(a, 5)
|
|
sr = GetChannel(a, 6)
|
|
l_sl = MixAudio(fl, sl, 0.2929, 0.2929)
|
|
c_lfe = MixAudio(lfe, c, 0.2071, 0.2071)
|
|
r_sr = MixAudio(fr, sr, 0.2929, 0.2929)
|
|
l = MixAudio(l_sl, c_lfe, 1.0, 1.0)
|
|
r = MixAudio(r_sr, c_lfe, 1.0, 1.0)
|
|
return MergeChannels(l, r)
|
|
}"
|
|
End Function
|
|
|
|
Shared Function FileExist(fp As String) As Boolean
|
|
Return File.Exists(fp) AndAlso New FileInfo(fp).Length > 500
|
|
End Function
|
|
|
|
Shared Function IsEncoderUsed(encoder As GuiAudioEncoder) As Boolean
|
|
If Not p.AudioTracks.NothingOrEmpty() Then
|
|
For Each track In p.AudioTracks
|
|
If IsEncoderUsed(track.AudioProfile, encoder) Then
|
|
Return True
|
|
End If
|
|
Next
|
|
End If
|
|
|
|
If Not p.AudioFiles.NothingOrEmpty Then
|
|
For Each ap In p.AudioFiles
|
|
If IsEncoderUsed(ap, encoder) Then
|
|
Return True
|
|
End If
|
|
Next
|
|
End If
|
|
|
|
Return False
|
|
End Function
|
|
|
|
Shared Function IsEncoderUsed(ap As AudioProfile, encoder As GuiAudioEncoder) As Boolean
|
|
If TypeOf ap Is GUIAudioProfile AndAlso ap.File <> "" Then
|
|
Dim gap = DirectCast(ap, GUIAudioProfile)
|
|
|
|
If gap.GetEncoder = encoder Then
|
|
Return True
|
|
End If
|
|
End If
|
|
End Function
|
|
|
|
|
|
|
|
Shared Function CommandContains(find As String) As Boolean
|
|
If Not p.AudioTracks.NothingOrEmpty() Then
|
|
For Each track In p.AudioTracks
|
|
If track.AudioProfile.IsUsedAndContainsCommand(find) Then
|
|
Return True
|
|
End If
|
|
Next
|
|
End If
|
|
|
|
If Not p.AudioFiles.NothingOrEmpty Then
|
|
For Each ap In p.AudioFiles
|
|
If ap.IsUsedAndContainsCommand(find) Then
|
|
Return True
|
|
End If
|
|
Next
|
|
End If
|
|
|
|
Return False
|
|
End Function
|
|
End Class
|
|
|
|
Public Enum AudioDecoderMode
|
|
Automatic
|
|
ffmpeg
|
|
eac3to
|
|
NicAudio
|
|
DirectShow
|
|
FFAudioSource
|
|
End Enum
|
|
|
|
Public Enum AudioDecodingMode
|
|
FLAC
|
|
W64
|
|
WAVE
|
|
Pipe
|
|
End Enum
|
|
|
|
Public Enum CuttingMode
|
|
mkvmerge
|
|
DirectShow
|
|
NicAudio
|
|
End Enum
|
|
|
|
Public Enum ffNormalizeMode
|
|
volumedetect
|
|
loudnorm
|
|
dynaudnorm
|
|
End Enum
|
|
|
|
Public Enum ffmpegNormalizeMode
|
|
volumedetect
|
|
loudnorm
|
|
dynaudnorm
|
|
End Enum
|