From 139b64dd1b08636760cf55c9629d4090ecbc13d8 Mon Sep 17 00:00:00 2001 From: Mercurio <47455213+NotLugozzi@users.noreply.github.com> Date: Sun, 15 Dec 2024 15:42:33 +0100 Subject: [PATCH] Initial support for ApplicationLoopback audio recording (WASAPI only) --- 2dxAutoClip/iidxAutoClip.cs | 82 +++++++++++++++---- .../obj/2dxAutoClip.csproj.nuget.dgspec.json | 10 ++- .../obj/2dxAutoClip.csproj.nuget.g.props | 2 +- 2dxAutoClip/obj/project.assets.json | 10 ++- 2dxAutoClip/obj/project.nuget.cache | 2 +- 5 files changed, 82 insertions(+), 24 deletions(-) diff --git a/2dxAutoClip/iidxAutoClip.cs b/2dxAutoClip/iidxAutoClip.cs index d7dcf01..cc9bdb7 100644 --- a/2dxAutoClip/iidxAutoClip.cs +++ b/2dxAutoClip/iidxAutoClip.cs @@ -5,7 +5,6 @@ using System.Text.RegularExpressions; using NAudio.CoreAudioApi; using NAudio.Wave; using System.Net; -// ReSharper disable PossibleInvalidCastExceptionInForeachLoop namespace _2dxAutoClip; #pragma warning disable CA1416 @@ -15,6 +14,7 @@ class Program { private static readonly string WebsocketAddress = "localhost"; private static Process? _ffmpegProcess; + private static Process? _recorderprocess; private static string _ffmpegFolderPath = GetDefaultVideosFolderPath(); private static WasapiCapture? _waveSource; private static WaveFileWriter _writer = null!; @@ -69,6 +69,16 @@ class Program } } } + public static int GetFirstProcessIdByName(string executableName) + { + if (string.IsNullOrWhiteSpace(executableName)) + { + throw new ArgumentException("Executable name cannot be null or empty.", nameof(executableName)); + } + var processes = Process.GetProcessesByName(executableName); + Console.WriteLine($"Found spice PID: {processes.FirstOrDefault()?.Id}"); + return processes.FirstOrDefault()?.Id ?? -1; + } private static void LoadSettingsFromPropFile() { const string filePath = "prop.txt"; @@ -228,30 +238,46 @@ class Program private static void StartRecording(string songName) { - Task.Run(() => StartAudioRecording(songName)); + StartAudioProcessRecording(songName); StartFfmpegRecording(songName); } - private static void StartAudioRecording(string songName) + + + private static void StartAudioProcessRecording(string songName) { try { var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); _audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav"; - Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!); - _waveSource = new WasapiLoopbackCapture(); - _writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat); - _waveSource.DataAvailable += (sender, args) => + + var directory = Path.GetDirectoryName(_audioFilePath); + if (directory == null) { - _writer.Write(args.Buffer, 0, args.BytesRecorded); - }; - _waveSource.RecordingStopped += (sender, args) => + throw new InvalidOperationException("Invalid audio file path."); + } + Directory.CreateDirectory(directory); + + var procid = GetFirstProcessIdByName(_gameProcessName); + if (procid == -1) { - _writer.Dispose(); - _waveSource.Dispose(); + throw new InvalidOperationException("Target process is not running."); + } + + var args = $"{procid} includetree {_audioFilePath}"; + _recorderprocess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "applb", + Arguments = args, + UseShellExecute = false, + RedirectStandardError = true, + CreateNoWindow = true + } }; - _waveSource.StartRecording(); - Console.WriteLine("WASAPI Audio recording started."); + + _recorderprocess.Start(); } catch (Exception ex) { @@ -259,6 +285,7 @@ class Program } } + private static void StartFfmpegRecording(string songName) { var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"); @@ -380,15 +407,34 @@ class Program private static void StopRecording(string songName) { - StopAudioRecording(); + //StopAudioRecording(); + TerminateProcessByName(); StopFfmpegRecording(); CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName); } - private static void StopAudioRecording() + public static void TerminateProcessByName() { - _waveSource?.StopRecording(); - Console.WriteLine("WASAPI Audio recording stopped."); + var executableName = "applb"; + try + { + int processId = GetFirstProcessIdByName(executableName); + + if (processId == -1) + { + Console.WriteLine($"No running process found for '{executableName}'."); + return; + } + + var process = Process.GetProcessById(processId); + process.Kill(); + + Console.WriteLine($"Process '{executableName}' with ID {processId} terminated successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error terminating process '{executableName}': {ex.Message}"); + } } private static void StopFfmpegRecording() diff --git a/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.dgspec.json b/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.dgspec.json index 160793e..4c163c1 100644 --- a/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.dgspec.json +++ b/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.dgspec.json @@ -39,7 +39,13 @@ "warnAsError": [ "NU1605" ] - } + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "9.0.100" }, "frameworks": { "net8.0": { @@ -70,7 +76,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.101/PortableRuntimeIdentifierGraph.json" } } } diff --git a/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.g.props b/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.g.props index 4ceefb3..d00a9d2 100644 --- a/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.g.props +++ b/2dxAutoClip/obj/2dxAutoClip.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\Mercury\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.9.1 + 6.12.2 diff --git a/2dxAutoClip/obj/project.assets.json b/2dxAutoClip/obj/project.assets.json index d2cd0d2..7a55687 100644 --- a/2dxAutoClip/obj/project.assets.json +++ b/2dxAutoClip/obj/project.assets.json @@ -490,7 +490,13 @@ "warnAsError": [ "NU1605" ] - } + }, + "restoreAuditProperties": { + "enableAudit": "true", + "auditLevel": "low", + "auditMode": "direct" + }, + "SdkAnalysisLevel": "9.0.100" }, "frameworks": { "net8.0": { @@ -521,7 +527,7 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.101/PortableRuntimeIdentifierGraph.json" } } } diff --git a/2dxAutoClip/obj/project.nuget.cache b/2dxAutoClip/obj/project.nuget.cache index 8f7fa63..9ac972d 100644 --- a/2dxAutoClip/obj/project.nuget.cache +++ b/2dxAutoClip/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "xTfdkrpqvxmeY+pzZcnpBIJbSyoO94xa04eSLg8IlKmzbTfi/q7IfP/sLGg2XwIVxlpAVQKwcAeegUkFcOHA5A==", + "dgSpecHash": "Cu8IH0pDN8c=", "success": true, "projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "expectedPackageFiles": [