fixing music nodes

This commit is contained in:
John Andrews
2022-06-23 09:06:30 +12:00
parent 20595137f5
commit 63625ee3d4
6 changed files with 165 additions and 131 deletions

View File

@@ -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)
{

View File

@@ -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<string> ffArgs = new List<string>();
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 = "<Pending>")]
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<string> ffArgs = new List<string>();
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<LoudNormStats>(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 = "<Pending>")]
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<LoudNormStats>(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; }
}
}

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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" },