From 63625ee3d4921243df5cc73af0f457912ff07b57 Mon Sep 17 00:00:00 2001 From: John Andrews Date: Thu, 23 Jun 2022 09:06:30 +1200 Subject: [PATCH] fixing music nodes --- MusicNodes/InputNodes/MusicFile.cs | 14 +- MusicNodes/Nodes/AudioFileNormalization.cs | 171 +++++++++--------- MusicNodes/Nodes/ConvertNode.cs | 7 +- MusicNodes/Nodes/MusicNode.cs | 14 ++ .../Tests/AudioFileNormalizationTests.cs | 88 +++++---- .../FfmpegBuilderExecutor.cs | 2 +- 6 files changed, 165 insertions(+), 131 deletions(-) diff --git a/MusicNodes/InputNodes/MusicFile.cs b/MusicNodes/InputNodes/MusicFile.cs index f5c1c1b6..478b534e 100644 --- a/MusicNodes/InputNodes/MusicFile.cs +++ b/MusicNodes/InputNodes/MusicFile.cs @@ -43,17 +43,9 @@ namespace FileFlows.MusicNodes try { - - var musicInfo = new MusicInfoHelper(ffmpegExe, args.Logger).Read(args.WorkingFile); - if (musicInfo.Duration == 0) - { - args.Logger.ILog("Failed to load music information."); - return 0; - } - - SetMusicInfo(args, musicInfo, Variables); - - return 1; + if (ReadMusicFileInfo(args, ffmpegExe, args.WorkingFile)) + return 1; + return 0; } catch (Exception ex) { diff --git a/MusicNodes/Nodes/AudioFileNormalization.cs b/MusicNodes/Nodes/AudioFileNormalization.cs index 112aad87..e5cebb0f 100644 --- a/MusicNodes/Nodes/AudioFileNormalization.cs +++ b/MusicNodes/Nodes/AudioFileNormalization.cs @@ -2,97 +2,97 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; -namespace FileFlows.MusicNodes +namespace FileFlows.MusicNodes; + +public class AudioFileNormalization : MusicNode { - public class AudioFileNormalization : MusicNode + public override int Inputs => 1; + public override int Outputs => 1; + public override FlowElementType Type => FlowElementType.Process; + + public override string Icon => "fas fa-volume-up"; + + + const string LOUDNORM_TARGET = "I=-24:LRA=7:TP=-2.0"; + + public override int Execute(NodeParameters args) { - public override int Inputs => 1; - public override int Outputs => 1; - public override FlowElementType Type => FlowElementType.Process; - - public override string Icon => "fas fa-volume-up"; - - - const string LOUDNORM_TARGET = "I=-24:LRA=7:TP=-2.0"; - - public override int Execute(NodeParameters args) + try { - try - { - string ffmpegExe = GetFFMpegExe(args); - if (string.IsNullOrEmpty(ffmpegExe)) - return -1; - - MusicInfo musicInfo = GetMusicInfo(args); - if (musicInfo == null) - return -1; - - List ffArgs = new List(); - - - long sampleRate = musicInfo.Frequency > 0 ? musicInfo.Frequency : 48_000; - - string twoPass = DoTwoPass(args, ffmpegExe); - ffArgs.AddRange(new[] { "-i", args.WorkingFile, "-c:a", musicInfo.Codec, "-ar", sampleRate.ToString(), "-af", twoPass }); - - string extension = new FileInfo(args.WorkingFile).Extension; - if (extension.StartsWith(".")) - extension = extension.Substring(1); - - string outputFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + "." + extension); - ffArgs.Add(outputFile); - - var result = args.Execute(new ExecuteArgs - { - Command = ffmpegExe, - ArgumentList = ffArgs.ToArray() - }); - - return result.ExitCode == 0 ? 1 : -1; - } - catch (Exception ex) - { - args.Logger?.ELog("Failed processing AudioFile: " + ex.Message); + string ffmpegExe = GetFFMpegExe(args); + if (string.IsNullOrEmpty(ffmpegExe)) return -1; - } - } - [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] - public string DoTwoPass(NodeParameters args, string ffmpegExe) - { - //-af loudnorm=I=-24:LRA=7:TP=-2.0" + MusicInfo musicInfo = GetMusicInfo(args); + if (musicInfo == null) + return -1; + + List ffArgs = new List(); + + + long sampleRate = musicInfo.Frequency > 0 ? musicInfo.Frequency : 48_000; + + string twoPass = DoTwoPass(args, ffmpegExe); + ffArgs.AddRange(new[] { "-i", args.WorkingFile, "-c:a", musicInfo.Codec, "-ar", sampleRate.ToString(), "-af", twoPass }); + + string extension = new FileInfo(args.WorkingFile).Extension; + if (extension.StartsWith(".")) + extension = extension.Substring(1); + + string outputFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + "." + extension); + ffArgs.Add(outputFile); + var result = args.Execute(new ExecuteArgs { Command = ffmpegExe, - ArgumentList = new[] - { - "-hide_banner", - "-i", args.WorkingFile, - "-af", "loudnorm=" + LOUDNORM_TARGET + ":print_format=json", - "-f", "null", - "-" - } + ArgumentList = ffArgs.ToArray() }); - if(result.ExitCode != 0) - throw new Exception("Failed to prcoess audio track"); - string output = result.StandardOutput; - - int index = output.LastIndexOf("{"); - if (index == -1) - throw new Exception("Failed to detected json in output"); - string json = output.Substring(index); - json = json.Substring(0, json.IndexOf("}") + 1); - if (string.IsNullOrEmpty(json)) - throw new Exception("Failed to parse TwoPass json"); - LoudNormStats stats = JsonSerializer.Deserialize(json); - string ar = $"loudnorm=print_format=summary:linear=true:{LOUDNORM_TARGET}:measured_I={stats.input_i}:measured_LRA={stats.input_lra}:measured_tp={stats.input_tp}:measured_thresh={stats.input_thresh}:offset={stats.target_offset}"; - return ar; + return result.ExitCode == 0 ? 1 : -1; } - - private class LoudNormStats + catch (Exception ex) { - /* + args.Logger?.ELog("Failed processing AudioFile: " + ex.Message); + return -1; + } + } + + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] + public string DoTwoPass(NodeParameters args, string ffmpegExe) + { + //-af loudnorm=I=-24:LRA=7:TP=-2.0" + var result = args.Execute(new ExecuteArgs + { + Command = ffmpegExe, + ArgumentList = new[] + { + "-hide_banner", + "-i", args.WorkingFile, + "-af", "loudnorm=" + LOUDNORM_TARGET + ":print_format=json", + "-f", "null", + "-" + } + }); + if(result.ExitCode != 0) + throw new Exception("Failed to prcoess audio track"); + + string output = result.StandardOutput; + + int index = output.LastIndexOf("{"); + if (index == -1) + throw new Exception("Failed to detected json in output"); + string json = output.Substring(index); + json = json.Substring(0, json.IndexOf("}") + 1); + if (string.IsNullOrEmpty(json)) + throw new Exception("Failed to parse TwoPass json"); + LoudNormStats stats = JsonSerializer.Deserialize(json); + string ar = $"loudnorm=print_format=summary:linear=true:{LOUDNORM_TARGET}:measured_I={stats.input_i}:measured_LRA={stats.input_lra}:measured_tp={stats.input_tp}:measured_thresh={stats.input_thresh}:offset={stats.target_offset}"; + return ar; + } + + private class LoudNormStats + { + /* { "input_i" : "-7.47", "input_tp" : "12.33", @@ -105,12 +105,11 @@ namespace FileFlows.MusicNodes "normalization_type" : "dynamic", "target_offset" : "0.25" } - */ - public string input_i { get; set; } - public string input_tp { get; set; } - public string input_lra { get; set; } - public string input_thresh { get; set; } - public string target_offset { get; set; } - } + */ + public string input_i { get; set; } + public string input_tp { get; set; } + public string input_lra { get; set; } + public string input_thresh { get; set; } + public string target_offset { get; set; } } } diff --git a/MusicNodes/Nodes/ConvertNode.cs b/MusicNodes/Nodes/ConvertNode.cs index 38fa5050..b451ad41 100644 --- a/MusicNodes/Nodes/ConvertNode.cs +++ b/MusicNodes/Nodes/ConvertNode.cs @@ -258,7 +258,12 @@ namespace FileFlows.MusicNodes //CopyMetaData(outputFile, args.FileName); args.SetWorkingFile(outputFile); - return 1; + + // update the music file info + if (ReadMusicFileInfo(args, ffmpegExe, args.WorkingFile)) + return 1; + + return -1; } //private void CopyMetaData(string outputFile, string originalFile) diff --git a/MusicNodes/Nodes/MusicNode.cs b/MusicNodes/Nodes/MusicNode.cs index 87db3f9f..3ba211a5 100644 --- a/MusicNodes/Nodes/MusicNode.cs +++ b/MusicNodes/Nodes/MusicNode.cs @@ -91,5 +91,19 @@ namespace FileFlows.MusicNodes } return result; } + + protected bool ReadMusicFileInfo(NodeParameters args, string ffmpegExe, string filename) + { + + var musicInfo = new MusicInfoHelper(ffmpegExe, args.Logger).Read(filename); + if (musicInfo.Duration == 0) + { + args.Logger?.ILog("Failed to load music information."); + return false; + } + + SetMusicInfo(args, musicInfo, Variables); + return true; + } } } \ No newline at end of file diff --git a/MusicNodes/Tests/AudioFileNormalizationTests.cs b/MusicNodes/Tests/AudioFileNormalizationTests.cs index 25fda998..6e2f7602 100644 --- a/MusicNodes/Tests/AudioFileNormalizationTests.cs +++ b/MusicNodes/Tests/AudioFileNormalizationTests.cs @@ -1,26 +1,45 @@ #if(DEBUG) -namespace FileFlows.MusicNodes.Tests -{ - using FileFlows.MusicNodes; - using Microsoft.VisualStudio.TestTools.UnitTesting; - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; +namespace FileFlows.MusicNodes.Tests; - [TestClass] - public class AudioFileNormalizationTests +using FileFlows.MusicNodes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +[TestClass] +public class AudioFileNormalizationTests +{ + [TestMethod] + public void AudioFileNormalization_Mp3() { - [TestMethod] - public void AudioFileNormalization_Mp3() + + const string file = @"D:\music\unprocessed\01-billy_joel-movin_out.mp3"; + + AudioFileNormalization node = new (); + var logger = new TestLogger(); + var args = new FileFlows.Plugin.NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => @"C:\utils\ffmpeg\ffmpeg.exe"; + args.TempPath = @"D:\music\temp"; + new MusicFile().Execute(args); // need to read the music info and set it + int output = node.Execute(args); + + string log = logger.ToString(); + + Assert.AreEqual(1, output); + } + [TestMethod] + public void AudioFileNormalization_Bulk() + { + + foreach (var file in Directory.GetFiles(@"d:\music\unprocessed")) { - const string file = @"D:\music\unprocessed\01-billy_joel-movin_out.mp3"; - - AudioFileNormalization node = new (); + AudioFileNormalization node = new(); var logger = new TestLogger(); var args = new FileFlows.Plugin.NodeParameters(file, logger, false, string.Empty); args.GetToolPathActual = (string tool) => @"C:\utils\ffmpeg\ffmpeg.exe"; @@ -32,26 +51,31 @@ namespace FileFlows.MusicNodes.Tests Assert.AreEqual(1, output); } - [TestMethod] - public void AudioFileNormalization_Bulk() - { + } - foreach (var file in Directory.GetFiles(@"d:\music\unprocessed")) - { - AudioFileNormalization node = new(); - var logger = new TestLogger(); - var args = new FileFlows.Plugin.NodeParameters(file, logger, false, string.Empty); - args.GetToolPathActual = (string tool) => @"C:\utils\ffmpeg\ffmpeg.exe"; - args.TempPath = @"D:\music\temp"; - new MusicFile().Execute(args); // need to read the music info and set it - int output = node.Execute(args); + [TestMethod] + public void AudioFileNormalization_ConvertFlacToMp3() + { - string log = logger.ToString(); + const string file = @"D:\music\flacs\03-billy_joel-dont_ask_me_why.flac"; + var logger = new TestLogger(); + var args = new FileFlows.Plugin.NodeParameters(file, logger, false, string.Empty); + args.GetToolPathActual = (string tool) => @"C:\utils\ffmpeg\ffmpeg.exe"; + args.TempPath = @"D:\music\temp"; - Assert.AreEqual(1, output); - } - } + new MusicFile().Execute(args); // need to read the music info and set it + + ConvertToMP3 convertNode = new(); + int output = convertNode.Execute(args); + + + AudioFileNormalization normalNode = new(); + output = normalNode.Execute(args); + + string log = logger.ToString(); + + Assert.AreEqual(1, output); } } diff --git a/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs index 27123d5a..680951a8 100644 --- a/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs +++ b/VideoNodes/FfmpegBuilderNodes/FfmpegBuilderExecutor.cs @@ -161,7 +161,7 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes return new[] { new [] { "-hwaccel", "nvdec", "-hwaccel_output_format", "cuda" }, - new [] { "-hwaccel", "cuda" }, + new [] { "-hwaccel", "cuda" },rep new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" }, new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" }, new [] { "-hwaccel", "dxva2" },