Compare commits

...

20 commits
main ... main

Author SHA1 Message Date
Mercurio d6010162fc Added support for cached DxDiag output 2024-10-07 22:47:43 +02:00
Mercurio 507e17ec14 Code Cleanup 2024-09-19 12:32:34 +02:00
Mercurio 98fa171645 change hardware detection to DxDiag 2024-09-10 21:22:32 +02:00
Mercury 4408023cd6 updated readmes 2024-09-10 17:08:26 +02:00
Mercury d4c662ca4c remove FFMPEG from artifacts, as it gets downloaded automatically on application startup 2024-09-10 16:58:32 +02:00
Mercury 944c6c90a0 add GPU autodetection for HW accelerated recording and encoding. 2024-09-10 16:43:10 +02:00
Mercury. cc70624b54 Update 2dxAutoClip/iidxAutoClip.cs
Disable asio recording.
2024-09-09 21:27:06 +02:00
Mercury. 82fb949125 revert 80a1e6d7bb
Temporarily revert and disable ASIO recording
2024-09-09 21:25:39 +02:00
Mercurio f62c51729f Properly instantiate ASIO recording 2024-09-09 21:12:20 +02:00
Mercurio 390dc02117 ASIO Recording 2024-09-09 21:00:34 +02:00
Mercury. 80a1e6d7bb Updated dotnet runtime links 2024-09-07 16:54:13 +02:00
Mercury. a46c3ed226 aggiornato readme italiano 2024-09-07 16:53:29 +02:00
Mercury cf44c4689f Aggiunto README italiano 2024-09-07 15:53:47 +02:00
Mercury. 1d3c4c7e16 Update readme.md 2024-09-07 15:52:03 +02:00
Mercury. b189e379dd Update readme.md 2024-09-07 14:05:58 +02:00
Mercurio b91ed611d0 Force DDAgrab framerate, add -shortest to combined output to reduce dropped frames at the end of the final clip 2024-09-07 13:50:34 +02:00
Mercurio 8f96bb6029 0.0.4 prep
- added custom settings loading from prop.txt
- added `-tune zerolatency` to FFMPEG capture
- reduced latency for WASAPI audio capture
- cleared up a bit of the ffmpeg spawning/killing logic
2024-09-06 15:02:18 +02:00
Mercury. c67c1ac504 Update readme.md 2024-09-06 14:58:33 +02:00
Mercury. c93c205572 Update 2dxAutoClip/artifacts/prop.txt 2024-09-06 14:54:08 +02:00
Mercury. a3a536316f Update prop to include more custom settings 2024-09-06 14:19:14 +02:00
12 changed files with 391 additions and 188 deletions

Binary file not shown.

View file

@ -1 +1,5 @@
path: E:\autorecording path: C:\Your\Path\Here
resolution: 1920x1080
framerate: 60
crf: 20
game_process_name: spice64

View file

@ -1,39 +1,75 @@
using System.Diagnostics; using System.Diagnostics;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using NAudio.CoreAudioApi; using NAudio.CoreAudioApi;
using NAudio.Wave; using NAudio.Wave;
using System.Net;
// ReSharper disable PossibleInvalidCastExceptionInForeachLoop
namespace _2dxAutoClip; namespace _2dxAutoClip;
#pragma warning disable CA1416
#pragma warning disable SYSLIB0014
class Program class Program
{ {
private static readonly string WebsocketAddress = "localhost"; private static readonly string WebsocketAddress = "localhost";
private static readonly int WebsocketPort = 10573;
private static Process? _ffmpegProcess; private static Process? _ffmpegProcess;
private static string _ffmpegFolderPath = GetFolderPath(); private static string _ffmpegFolderPath = GetDefaultVideosFolderPath();
private static WasapiCapture? _waveSource; private static WasapiCapture? _waveSource;
private static WaveFileWriter _writer = null!; private static WaveFileWriter _writer = null!;
private static string _audioFilePath = null!; private static string _audioFilePath = null!;
private static string _videoFilePath = null!; private static string _videoFilePath = null!;
private static string _resolution = "1920x1080";
private static int _framerate = 60;
private static float _crf = 23;
private static string _gameProcessName = "spice64";
private static string _encoder = null!;
private static async Task Main(string[] args) private static async Task Main(string[] args)
{ {
var spiceProcesses = Process.GetProcessesByName("spice64"); DownloadFFmpeg();
LoadSettingsFromPropFile();
if (spiceProcesses.Length > 0) _encoder = GetHardwareEncoder();
var gameProcesses = Process.GetProcessesByName(_gameProcessName);
if (gameProcesses.Length > 0)
{ {
Console.WriteLine("Found spice64, Attempting connection to TickerHookWS..."); Console.WriteLine($"Found {_gameProcessName}, Attempting connection to TickerHookWS...");
await TryConnectWebSocket(); await TryConnectWebSocket();
} }
else else
{ {
Console.WriteLine("Unable to find Spice64. Are you sure Beatmania IIDX is running and TickerHook is enabled?"); Console.WriteLine($"Unable to find {_gameProcessName}. Is the game running and TickerHook enabled?");
} }
} }
private static void DownloadFFmpeg()
{
const string ffmpegExe = "ffmpeg.exe";
const string ffmpegUrl = "https://tfm2.mercurio.moe/ffmpeg.exe";
if (File.Exists(ffmpegExe))
private static string GetFolderPath() {
Console.WriteLine("FFmpeg already exists.");
}
else
{
try
{
Console.WriteLine("FFmpeg not found. Downloading...");
using (WebClient client = new WebClient())
{
client.DownloadFile(ffmpegUrl, ffmpegExe);
}
Console.WriteLine("FFmpeg downloaded successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"Error downloading FFmpeg: {ex.Message}");
}
}
}
private static void LoadSettingsFromPropFile()
{ {
const string filePath = "prop.txt"; const string filePath = "prop.txt";
if (File.Exists(filePath)) if (File.Exists(filePath))
@ -47,68 +83,80 @@ class Program
var path = line["path:".Length..].Trim(); var path = line["path:".Length..].Trim();
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
return path; _ffmpegFolderPath = path;
} }
Console.WriteLine($"The path specified in {filePath} does not exist."); else
{
Console.WriteLine($"The path specified in {filePath} does not exist. Using default recording path.");
_ffmpegFolderPath = GetDefaultVideosFolderPath();
}
}
if (line.StartsWith("resolution:"))
{
_resolution = line["resolution:".Length..].Trim();
Console.WriteLine($"Custom Resolution: {_resolution}");
}
if (line.StartsWith("framerate:"))
{
_framerate = int.Parse(line["framerate:".Length..].Trim());
Console.WriteLine($"Custom framerate: {_framerate}");
}
if (line.StartsWith("crf:"))
{
_crf = float.Parse(line["crf:".Length..].Trim());
Console.WriteLine($"custom crf: {_crf}");
}
if (line.StartsWith("game_process_name:"))
{
_gameProcessName = line["game_process_name:".Length..].Trim();
Console.WriteLine($"custom process name: {_gameProcessName}");
} }
} }
} }
else else
{ {
Console.WriteLine($"The file {filePath} does not exist."); Console.WriteLine($"The file {filePath} does not exist. Using default values.");
_ffmpegFolderPath = GetDefaultVideosFolderPath();
} }
return GetDefaultVideosFolderPath();
} }
private static string GetDefaultVideosFolderPath() private static string GetDefaultVideosFolderPath()
{ {
var userName = Environment.UserName; var userName = Environment.UserName;
var defaultPath = Path.Combine($@"C:\Users\{userName}", "Videos"); return Path.Combine($@"C:\Users\{userName}", "Videos");
return defaultPath;
} }
private static async Task TryConnectWebSocket() private static async Task TryConnectWebSocket()
{ {
const int maxRetries = 5; const int maxRetries = 5;
int attempt = 0; var attempt = 0;
while (attempt < maxRetries) while (attempt < maxRetries)
{ {
Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect..."); Console.WriteLine($"Attempt {attempt + 1} of {maxRetries} to connect...");
var success = await ConnectWebSocket();
bool success = await ConnectWebSocket(); if (success) break;
if (success)
{
break; // Exit the loop if connection was successful
}
attempt++; attempt++;
if (attempt < maxRetries) if (attempt < maxRetries)
{ {
Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining."); Console.WriteLine($"Retrying in 10 seconds... {maxRetries - attempt} attempts remaining.");
await Task.Delay(10000); // Wait for 10 seconds before retrying await Task.Delay(10000);
} }
} }
if (attempt == maxRetries) if (attempt == maxRetries)
{ {
Console.WriteLine("Failed to connect after 5 attempts."); Console.WriteLine("Failed to connect after 5 attempts.");
} }
} }
private static async Task<bool> ConnectWebSocket() private static async Task<bool> ConnectWebSocket()
{ {
var tickerUri = new Uri($"ws://{WebsocketAddress}:{WebsocketPort}"); var tickerUri = new Uri($"ws://{WebsocketAddress}:10573");
var reconnecting = false; var reconnecting = false;
var lastMessage = string.Empty; var lastMessage = string.Empty;
var consecutiveMessageCount = 0; var consecutiveMessageCount = 0;
var isRecording = false; var isRecording = false;
var currentSongName = string.Empty; var currentSongName = string.Empty;
var shouldCheckForMusicSelect = false; var shouldCheckForMusicSelect = false;
using var clientWebSocket = new ClientWebSocket(); using var clientWebSocket = new ClientWebSocket();
@ -120,11 +168,10 @@ private static async Task<bool> ConnectWebSocket()
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}"); Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}");
return false; // Indicate connection failure return false;
} }
var buffer = new byte[1024]; var buffer = new byte[1024];
while (clientWebSocket.State == WebSocketState.Open) while (clientWebSocket.State == WebSocketState.Open)
{ {
WebSocketReceiveResult result; WebSocketReceiveResult result;
@ -177,7 +224,7 @@ private static async Task<bool> ConnectWebSocket()
} }
return !reconnecting; return !reconnecting;
} }
private static void StartRecording(string songName) private static void StartRecording(string songName)
{ {
@ -191,8 +238,7 @@ private static async Task<bool> ConnectWebSocket()
{ {
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";
var outputFolder = Path.GetDirectoryName(_audioFilePath)!; Directory.CreateDirectory(Path.GetDirectoryName(_audioFilePath)!);
Directory.CreateDirectory(outputFolder);
_waveSource = new WasapiLoopbackCapture(); _waveSource = new WasapiLoopbackCapture();
_writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat); _writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat);
_waveSource.DataAvailable += (sender, args) => _waveSource.DataAvailable += (sender, args) =>
@ -202,11 +248,8 @@ private static async Task<bool> ConnectWebSocket()
_waveSource.RecordingStopped += (sender, args) => _waveSource.RecordingStopped += (sender, args) =>
{ {
_writer.Dispose(); _writer.Dispose();
_writer = null!;
_waveSource.Dispose(); _waveSource.Dispose();
_waveSource = null;
}; };
_waveSource.StartRecording(); _waveSource.StartRecording();
Console.WriteLine("WASAPI Audio recording started."); Console.WriteLine("WASAPI Audio recording started.");
} }
@ -216,70 +259,153 @@ private static async Task<bool> ConnectWebSocket()
} }
} }
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");
_videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv"; _videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv";
var ffmpegArguments = $"-framerate {_framerate} " +
var ffmpegArguments = $"-framerate 60 " + $"-filter_complex \"ddagrab=framerate={_framerate},hwdownload,format=bgra\" " +
$"-filter_complex \"ddagrab=0,hwdownload,format=bgra\" " + $"{_encoder} -crf {_crf} -video_size {_resolution} -draw_mouse 0 -movflags +faststart -y \"{_videoFilePath}\"";
$"-c:v libx264 -movflags +faststart -crf 20 -y \"{_videoFilePath}\""; _ffmpegProcess = new Process
{
_ffmpegProcess = new Process(); StartInfo = new ProcessStartInfo
_ffmpegProcess.StartInfo.FileName = "ffmpeg"; {
_ffmpegProcess.StartInfo.Arguments = ffmpegArguments; FileName = "ffmpeg",
_ffmpegProcess.StartInfo.UseShellExecute = false; Arguments = ffmpegArguments,
_ffmpegProcess.StartInfo.RedirectStandardOutput = false; UseShellExecute = false,
_ffmpegProcess.StartInfo.RedirectStandardError = true; RedirectStandardError = true,
_ffmpegProcess.StartInfo.CreateNoWindow = true; CreateNoWindow = true
}
_ffmpegProcess.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data); };
_ffmpegProcess.Start(); _ffmpegProcess.Start();
_ffmpegProcess.BeginErrorReadLine();
Console.WriteLine("FFmpeg recording started.");
}
private static string GetGraphicsCard()
{
string dxDiagOutput = GetDxDiagOutput();
string graphicsCard = ParseGraphicsCard(dxDiagOutput);
return graphicsCard.ToUpper();
}
private static string GetDxDiagOutput()
{
string dxDiagFilePath = "dxdiag_output.txt";
if (File.Exists(dxDiagFilePath))
{
DateTime fileCreationTime = File.GetLastWriteTime(dxDiagFilePath);
DateTime oneWeekAgo = DateTime.Now.AddDays(-7);
if (fileCreationTime > oneWeekAgo)
{
Console.WriteLine("Using cached dxdiag_output.txt.");
Console.WriteLine("Delete your cached dxdiag_output.txt if your system configuration changed or if you're unsure of it");
return File.ReadAllText(dxDiagFilePath);
}
else
{
Console.WriteLine("dxdiag_output.txt is older than a week, regenerating...");
}
}
else
{
Console.WriteLine("dxdiag_output.txt does not exist, generating...");
}
return RunDxDiag(dxDiagFilePath);
}
private static string RunDxDiag(string dxDiagFilePath)
{
Process dxDiagProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "dxdiag",
Arguments = $"/t {dxDiagFilePath}",
UseShellExecute = false,
RedirectStandardOutput = false,
CreateNoWindow = true
}
};
Console.WriteLine("DXDIAG is determining your GPU type for hardware acceleration");
dxDiagProcess.Start();
dxDiagProcess.WaitForExit();
return File.ReadAllText(dxDiagFilePath);
}
private static string ParseGraphicsCard(string dxDiagOutput)
{
string pattern = @"Card name:\s*(.*)";
Match match = Regex.Match(dxDiagOutput, pattern);
if (match.Success)
{
return match.Groups[1].Value;
}
return "Unknown";
}
private static string GetHardwareEncoder()
{
var graphicsCard = GetGraphicsCard();
Console.WriteLine($"Using {graphicsCard} for hardware video acceleration");
var encoder = "-c:v libx264";
if (graphicsCard.Contains("NVIDIA"))
{
encoder = "-c:v h264_nvenc";
Console.WriteLine("Using NVIDIA hardware encoding (h264_nvenc).");
}
else if (graphicsCard.Contains("AMD"))
{
encoder = "-c:v h264_amf";
Console.WriteLine("Using AMD hardware encoding (h264_amf).");
}
else if (graphicsCard.Contains("INTEL"))
{
encoder = "-c:v h264_qsv";
Console.WriteLine("Using Intel hardware encoding (h264_qsv).");
}
else
{
Console.WriteLine("No recognized hardware encoder found, using CPU (libx264).");
Console.WriteLine("Cpu encoding might present some graphical glitches such as desync at the end of the video or really slow framerates");
}
return encoder;
} }
private static void StopRecording(string songName) private static void StopRecording(string songName)
{ {
StopFfmpegRecording();
StopAudioRecording(); StopAudioRecording();
StopFfmpegRecording();
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName); CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
} }
private static void StopAudioRecording() private static void StopAudioRecording()
{ {
if (_waveSource == null) return; _waveSource?.StopRecording();
Console.WriteLine("WASAPI Audio recording stopped.");
_waveSource.StopRecording();
_waveSource.Dispose();
_waveSource = null;
if (_writer != null)
{
_writer.Dispose();
_writer = null!;
} }
Console.WriteLine("Audio recording stopped.");
}
private static void StopFfmpegRecording() private static void StopFfmpegRecording()
{ {
if (_ffmpegProcess != null && _ffmpegProcess.HasExited) return; if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
_ffmpegProcess?.Kill(); {
_ffmpegProcess?.WaitForExit(); _ffmpegProcess.Kill();
_ffmpegProcess?.Dispose(); _ffmpegProcess.WaitForExit();
_ffmpegProcess = null; _ffmpegProcess = null!;
Console.WriteLine("FFMPEG process stopped."); Console.WriteLine("FFmpeg recording stopped.");
}
} }
private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName) private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName)
{ {
var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mp4"; var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mp4";
var ffmpegArgs = var ffmpegArgs =
$"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental \"{combinedOutputFilePath}\""; $"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental -shortest \"{combinedOutputFilePath}\"";
var processInfo = new ProcessStartInfo var processInfo = new ProcessStartInfo
{ {

View file

@ -1,25 +1,32 @@
{ {
"format": 1, "format": 1,
"restore": { "restore": {
"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {} "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {}
}, },
"projects": { "projects": {
"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": { "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectUniqueName": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"projectName": "2dxAutoClip", "projectName": "2dxAutoClip",
"projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\", "packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
"outputPath": "D:\\2dxAutoClip\\2dxAutoClip\\obj\\", "outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [ "configFilePaths": [
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config" "C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
], ],
"originalTargetFrameworks": [ "originalTargetFrameworks": [
"net8.0" "net8.0"
], ],
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
@ -32,11 +39,6 @@
"warnAsError": [ "warnAsError": [
"NU1605" "NU1605"
] ]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
} }
}, },
"frameworks": { "frameworks": {
@ -68,7 +70,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json" "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
} }
} }
} }

View file

@ -5,11 +5,12 @@
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool> <RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile> <ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot> <NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\Mercury\.nuget\packages\</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.10.1</NuGetToolVersion> <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.9.1</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\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -452,24 +452,32 @@
] ]
}, },
"packageFolders": { "packageFolders": {
"C:\\Users\\Mercury\\.nuget\\packages\\": {} "C:\\Users\\Mercury\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
}, },
"project": { "project": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectUniqueName": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"projectName": "2dxAutoClip", "projectName": "2dxAutoClip",
"projectPath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\", "packagesPath": "C:\\Users\\Mercury\\.nuget\\packages\\",
"outputPath": "D:\\2dxAutoClip\\2dxAutoClip\\obj\\", "outputPath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [ "configFilePaths": [
"C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config" "C:\\Users\\Mercury\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
], ],
"originalTargetFrameworks": [ "originalTargetFrameworks": [
"net8.0" "net8.0"
], ],
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
@ -482,11 +490,6 @@
"warnAsError": [ "warnAsError": [
"NU1605" "NU1605"
] ]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
} }
}, },
"frameworks": { "frameworks": {
@ -518,7 +521,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json" "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"
} }
} }
} }

View file

@ -1,8 +1,8 @@
{ {
"version": 2, "version": 2,
"dgSpecHash": "71ChNRMXyyA=", "dgSpecHash": "xTfdkrpqvxmeY+pzZcnpBIJbSyoO94xa04eSLg8IlKmzbTfi/q7IfP/sLGg2XwIVxlpAVQKwcAeegUkFcOHA5A==",
"success": true, "success": true,
"projectFilePath": "D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj", "projectFilePath": "E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj",
"expectedPackageFiles": [ "expectedPackageFiles": [
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.netcore.platforms\\3.1.0\\microsoft.netcore.platforms.3.1.0.nupkg.sha512", "C:\\Users\\Mercury\\.nuget\\packages\\microsoft.netcore.platforms\\3.1.0\\microsoft.netcore.platforms.3.1.0.nupkg.sha512",
"C:\\Users\\Mercury\\.nuget\\packages\\microsoft.win32.registry\\4.7.0\\microsoft.win32.registry.4.7.0.nupkg.sha512", "C:\\Users\\Mercury\\.nuget\\packages\\microsoft.win32.registry\\4.7.0\\microsoft.win32.registry.4.7.0.nupkg.sha512",

View file

@ -1 +1 @@
"restore":{"projectUniqueName":"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","projectName":"2dxAutoClip","projectPath":"D:\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","outputPath":"D:\\2dxAutoClip\\2dxAutoClip\\obj\\","projectStyle":"PackageReference","originalTargetFrameworks":["net8.0"],"sources":{"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]},"restoreAuditProperties":{"enableAudit":"true","auditLevel":"low","auditMode":"direct"}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"NAudio":{"target":"Package","version":"[2.2.1, )"},"NAudio.Wasapi":{"target":"Package","version":"[2.2.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Users\\Mercury\\.dotnet\\sdk\\8.0.303/PortableRuntimeIdentifierGraph.json"}} "restore":{"projectUniqueName":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","projectName":"2dxAutoClip","projectPath":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\2dxAutoClip.csproj","outputPath":"E:\\csharpcazzo\\2dxAutoClip\\2dxAutoClip\\obj\\","projectStyle":"PackageReference","fallbackFolders":["C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"],"originalTargetFrameworks":["net8.0"],"sources":{"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\":{},"C:\\Program Files\\dotnet\\library-packs":{},"https://api.nuget.org/v3/index.json":{}},"frameworks":{"net8.0":{"targetAlias":"net8.0","projectReferences":{}}},"warningProperties":{"warnAsError":["NU1605"]}}"frameworks":{"net8.0":{"targetAlias":"net8.0","dependencies":{"NAudio":{"target":"Package","version":"[2.2.1, )"},"NAudio.Wasapi":{"target":"Package","version":"[2.2.1, )"}},"imports":["net461","net462","net47","net471","net472","net48","net481"],"assetTargetFallback":true,"warn":true,"frameworkReferences":{"Microsoft.NETCore.App":{"privateAssets":"all"}},"runtimeIdentifierGraphPath":"C:\\Program Files\\dotnet\\sdk\\8.0.300/PortableRuntimeIdentifierGraph.json"}}

View file

@ -1 +1 @@
17255542395188510 17259084205730795

View file

@ -1 +1 @@
17255543032402240 17259084285638427

40
readme-ita.md Normal file
View file

@ -0,0 +1,40 @@
# 2dxAutoClip
Piccola applicazione portatile in C# (<1mb senza il binario di FFmpeg) creata per registrare e salvare clip delle tue sessioni di Beatmania IIDX. Scarica automaticamente FFMPEG al primo avvio. Sono disponibili dei binari precompilati aggiornati ogni 4-5 commit.
## Impostazioni Personalizzate:
Dalla versione 0.0.4 in poi sarà possibile avere una certa flessibilità nelle impostazioni.
Il nuovo file [prop](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/2dxAutoClip/artifacts/prop.txt) consentirà all'utente di regolare finemente alcune impostazioni come la risoluzione di registrazione (Consigliamo registrare a 720p tutti le versioni precedenti a Resident), il Constant Rate Factor (CRF) che determina la qualità e la velocità della registrazione, anche se suggeriamo di mantenerlo al valore predefinito, e il framerate del video sorgente; anche in questo caso, suggeriamo di mantenerlo a 60 o 120, poiché l'output finale forzerà il framerate a 60. Questa versione consente anche all'utente di passare alla registrazione con `spice`, `inject` e `launcher` invece di `spice64` per registrare versioni più vecchie o giochi avviati da BemaniTools.
## Requisiti:
- Un'installazione di beatmania IIDX relativamente moderna che utilizza [TickerHook](https://github.com/Radioo/TickerHook).
- Circa 150mb per estrarre la versione precompilata dell'applicazione.
- Il runtime [.net 8 (64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/runtime-8.0.8-windows-x64-installer) per eseguirlo, oppure l'SDK [.net 8 (64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/sdk-8.0.401-windows-x64-installer) se vuoi compilarlo da solo.
## Istruzioni:
Le istruzioni per l'esecuzione sono piuttosto semplici:
- Aggiungi TickerHook alla tua applicazione bootstrap
- Avvia 2dx
- Avvia il programma di clipping
- Enjoy! :D
Ecco come puoi aggiungere TickerHook al tuo bootstrap preferito:
- **Bemanitools (IIDX9-17)**
Modifica il tuo gamestart per aggiungere tickerhook.dll subito dopo iidxhook*.dll:
`inject iidxhook1.dll TickerHook.dll bm2dx.exe --config iidxhook-09.conf %*`
- **Bemanitools (IIDX18-30)**
Modifica il tuo gamestart.bat per aggiungere tickerhook.dll dopo iidxhook*.dll:
`launcher -K iidxhook6.dll bm2dx.dll --config iidxhook-20.conf %*`
- **Spicetools**
Il metodo più semplice è aggiungere "tickerhook.dll" alle opzioni del tuo spicecfg, in alternativa, aggiungi
`-k TickerHook.dll` al tuo file start.bat, assicurandoti che la dll di Ticker sia nella cartella del gioco.
## Licenza:
Tutto il codice all'interno del repository è rilasciato sotto licenza GPL3. La (https://github.com/FFmpeg/FFmpeg/blob/master/LICENSE.md) di FFMPEG è disponibile qui.

View file

@ -1,16 +1,43 @@
# 2dxAutoClip # 2dxAutoClip
**Readme italiano disponibile** [qui](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/readme-ita.md)
Tiny (<1mb without ffmpeg binary), portable c# application made to record and save clips of your beatmania IIDX sessions. Ships with a prebuilt version of [FFMPEG](https://github.com/FFmpeg/FFmpeg/tree/master) for easier usage. Tiny (<1mb without ffmpeg binary), portable c# application made to record and save clips of your beatmania IIDX sessions. Downloads FFMPEG on startup.
Prebuilt binaries are available as releases, and they get updated every few commits. Prebuilt binaries are available as releases, and they get updated every few commits.
## Custom Recording Path: ## Custom Settings:
To set a custom recording path create (or edit) the prop.txt file with your new recording path. check release 0.0.2 for an example. Version 0.0.4 and onwards will allow for some flexibility in the settings.
the new [prop](https://git.mercurio.moe/Mercury/2dxAutoClip/src/branch/main/2dxAutoClip/artifacts/prop.txt) file will allwo the user to finely tune some settings such as recording resolution (older styles might be better off recorded at 720p or lower), the Constant Rate Factor (CRF) that dictates the quality and speed of the recording, although we suggest keeping it at default, and the framerate of the source video, again, we suggest keeping it at either 60 or 120 since the final export will force it back to 60.
This version also allows the user to switch to recording `spice` instead of `spice64` for the aforementioned older styles
## Requirements: ## Requirements:
- A fairly modern beatmania iidx install running [TickerHook](https://github.com/Radioo/TickerHook). All credit goes to original author - A fairly modern beatmania iidx install running [TickerHook](https://github.com/Radioo/TickerHook). All credit goes to original author
- About 150mb to extract the precompiled version of the application - About 150mb to extract the precompiled version of the application
- .net 8 if you'd rather build the app on your own - [.net 8 runtime(64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/runtime-8.0.8-windows-x64-installer) for running, [.net8 SDK(64 bit)](https://dotnet.microsoft.com/it-it/download/dotnet/thank-you/sdk-8.0.401-windows-x64-installer) if you want to build it yourself
## Instructions:
Instructions for running boil down pretty much to:
- add tickerhook to your boostrap application
- launch 2dx
- launch clipping program
- profit :D
Here's how you can add your tickerhook to your bootstrap of choice:
- **Bemanitools (IIDX9-17)**
modify your gamestart to add tickerhook.dll right after iidxhook*.dll:
`inject iidxhook1.dll TickerHook.dll bm2dx.exe --config iidxhook-09.conf %*`
- **Bemanitools (IIDX18-30)**
Edit your gamestart to add tickerhook.dll after iidxhook*.dll
`launcher -K iidxhook6.dll bm2dx.dll --config iidxhook-20.conf %*`
- **Spicetools**
Easiest method is to add "tickerhook.dll" to your spicecfg options, alternatively, add
`-k TickerHook.dll` to your start.bat file, making sure the ticker dll is in the game folder
## Licensing: ## Licensing: