mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2025-12-30 21:30:05 -06:00
fixing music nodes
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user