Initial support for ApplicationLoopback audio recording (WASAPI only)
This commit is contained in:
parent
d6010162fc
commit
139b64dd1b
|
@ -5,7 +5,6 @@ using System.Text.RegularExpressions;
|
||||||
using NAudio.CoreAudioApi;
|
using NAudio.CoreAudioApi;
|
||||||
using NAudio.Wave;
|
using NAudio.Wave;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
|
|
||||||
|
|
||||||
namespace _2dxAutoClip;
|
namespace _2dxAutoClip;
|
||||||
#pragma warning disable CA1416
|
#pragma warning disable CA1416
|
||||||
|
@ -15,6 +14,7 @@ class Program
|
||||||
{
|
{
|
||||||
private static readonly string WebsocketAddress = "localhost";
|
private static readonly string WebsocketAddress = "localhost";
|
||||||
private static Process? _ffmpegProcess;
|
private static Process? _ffmpegProcess;
|
||||||
|
private static Process? _recorderprocess;
|
||||||
private static string _ffmpegFolderPath = GetDefaultVideosFolderPath();
|
private static string _ffmpegFolderPath = GetDefaultVideosFolderPath();
|
||||||
private static WasapiCapture? _waveSource;
|
private static WasapiCapture? _waveSource;
|
||||||
private static WaveFileWriter _writer = null!;
|
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()
|
private static void LoadSettingsFromPropFile()
|
||||||
{
|
{
|
||||||
const string filePath = "prop.txt";
|
const string filePath = "prop.txt";
|
||||||
|
@ -228,30 +238,46 @@ class Program
|
||||||
|
|
||||||
private static void StartRecording(string songName)
|
private static void StartRecording(string songName)
|
||||||
{
|
{
|
||||||
Task.Run(() => StartAudioRecording(songName));
|
StartAudioProcessRecording(songName);
|
||||||
StartFfmpegRecording(songName);
|
StartFfmpegRecording(songName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StartAudioRecording(string songName)
|
|
||||||
|
|
||||||
|
private static void StartAudioProcessRecording(string songName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
_audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav";
|
_audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav";
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!);
|
|
||||||
_waveSource = new WasapiLoopbackCapture();
|
var directory = Path.GetDirectoryName(_audioFilePath);
|
||||||
_writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat);
|
if (directory == null)
|
||||||
_waveSource.DataAvailable += (sender, args) =>
|
|
||||||
{
|
{
|
||||||
_writer.Write(args.Buffer, 0, args.BytesRecorded);
|
throw new InvalidOperationException("Invalid audio file path.");
|
||||||
};
|
}
|
||||||
_waveSource.RecordingStopped += (sender, args) =>
|
Directory.CreateDirectory(directory);
|
||||||
|
|
||||||
|
var procid = GetFirstProcessIdByName(_gameProcessName);
|
||||||
|
if (procid == -1)
|
||||||
{
|
{
|
||||||
_writer.Dispose();
|
throw new InvalidOperationException("Target process is not running.");
|
||||||
_waveSource.Dispose();
|
}
|
||||||
|
|
||||||
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -259,6 +285,7 @@ class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void StartFfmpegRecording(string songName)
|
private static void StartFfmpegRecording(string songName)
|
||||||
{
|
{
|
||||||
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
|
||||||
|
@ -380,15 +407,34 @@ class Program
|
||||||
|
|
||||||
private static void StopRecording(string songName)
|
private static void StopRecording(string songName)
|
||||||
{
|
{
|
||||||
StopAudioRecording();
|
//StopAudioRecording();
|
||||||
|
TerminateProcessByName();
|
||||||
StopFfmpegRecording();
|
StopFfmpegRecording();
|
||||||
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
|
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StopAudioRecording()
|
public static void TerminateProcessByName()
|
||||||
{
|
{
|
||||||
_waveSource?.StopRecording();
|
var executableName = "applb";
|
||||||
Console.WriteLine("WASAPI Audio recording stopped.");
|
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()
|
private static void StopFfmpegRecording()
|
||||||
|
|
|
@ -39,7 +39,13 @@
|
||||||
"warnAsError": [
|
"warnAsError": [
|
||||||
"NU1605"
|
"NU1605"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "9.0.100"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
|
@ -70,7 +76,7 @@
|
||||||
"privateAssets": "all"
|
"privateAssets": "all"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.101/PortableRuntimeIdentifierGraph.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.9.1</NuGetToolVersion>
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.12.2</NuGetToolVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
<SourceRoot Include="C:\Users\Mercury\.nuget\packages\" />
|
<SourceRoot Include="C:\Users\Mercury\.nuget\packages\" />
|
||||||
|
|
|
@ -490,7 +490,13 @@
|
||||||
"warnAsError": [
|
"warnAsError": [
|
||||||
"NU1605"
|
"NU1605"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "9.0.100"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
"net8.0": {
|
"net8.0": {
|
||||||
|
@ -521,7 +527,7 @@
|
||||||
"privateAssets": "all"
|
"privateAssets": "all"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.101/PortableRuntimeIdentifierGraph.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "xTfdkrpqvxmeY+pzZcnpBIJbSyoO94xa04eSLg8IlKmzbTfi/q7IfP/sLGg2XwIVxlpAVQKwcAeegUkFcOHA5A==",
|
"dgSpecHash": "Cu8IH0pDN8c=",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
"projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
|
||||||
"expectedPackageFiles": [
|
"expectedPackageFiles": [
|
||||||
|
|
Loading…
Reference in a new issue