2dxAutoClip/2dxAutoClip/Program.cs
2024-09-05 12:55:58 +02:00

262 lines
8.2 KiB
C#

using System.Diagnostics;
using System.Net.WebSockets;
using System.Text;
using NAudio.CoreAudioApi;
using NAudio.Wave;
namespace _2dxAutoClip;
class Program
{
private static readonly string WebsocketAddress = "localhost";
private static readonly int WebsocketPort = 10573;
private static Process? _ffmpegProcess;
private static string _ffmpegFolderPath = "E:\\autorecording"; // Output folder path
private static WasapiCapture? _waveSource;
private static WaveFileWriter _writer;
private static string _audioFilePath;
private static string _videoFilePath;
private static async Task Main(string[] args)
{
var spiceProcesses = Process.GetProcessesByName("spice64");
if (spiceProcesses.Length > 0)
{
Console.WriteLine("Found spice64, Attempting connection to TickerHookWS...");
await ConnectWebSocket();
}
else
{
Console.WriteLine("Unable to find Spice64. Are you sure Beatmania IIDX is running and TickerHook is enabled?");
}
}
private static async Task ConnectWebSocket()
{
var tickerUri = new Uri($"ws://{WebsocketAddress}:{WebsocketPort}");
var reconnecting = false;
var lastMessage = string.Empty;
var consecutiveMessageCount = 0;
var isRecording = false;
var currentSongName = string.Empty;
// Flag to track if we need to check for "MUSIC SELECT!!"
var shouldCheckForMusicSelect = false;
using var clientWebSocket = new ClientWebSocket();
try
{
await clientWebSocket.ConnectAsync(tickerUri, CancellationToken.None);
Console.WriteLine("Connected to TickerHook WebSocket.");
}
catch (Exception ex)
{
Console.WriteLine($"Error connecting to TickerHook WebSocket: {ex.Message}");
return;
}
var buffer = new byte[1024];
while (clientWebSocket.State == WebSocketState.Open)
{
WebSocketReceiveResult result;
try
{
result = await clientWebSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
catch (Exception ex)
{
Console.WriteLine($"Error receiving message: {ex.Message}");
reconnecting = true;
break;
}
var message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim().ToUpper();
Console.WriteLine($"Received message: {message}");
if (message == lastMessage && !message.Contains("SELECT FROM"))
{
consecutiveMessageCount++;
}
else
{
consecutiveMessageCount = 1;
lastMessage = message;
}
if (consecutiveMessageCount >= 2 && !message.Contains("SELECT FROM") && !isRecording)
{
currentSongName = message;
Console.WriteLine("Starting recording...");
StartRecording(currentSongName);
isRecording = true;
}
if (isRecording)
{
if (shouldCheckForMusicSelect && message.Contains("MUSIC SELECT!!"))
{
Console.WriteLine("Stopping recording...");
StopRecording(currentSongName);
isRecording = false;
shouldCheckForMusicSelect = false;
}
else if (message.EndsWith("CLEAR!") || message.EndsWith("FAILED.."))
{
// Set the flag to check the next message for "MUSIC SELECT!!"
shouldCheckForMusicSelect = true;
}
}
}
if (reconnecting)
{
await Task.Delay(10000);
await ConnectWebSocket();
}
}
private static void StartRecording(string songName)
{
Task.Run(() => StartAudioRecording(songName));
StartFfmpegRecording(songName);
}
private static void StartAudioRecording(string songName)
{
try
{
string date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
_audioFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.wav";
// Ensure the output folder exists
string outputFolder = Path.GetDirectoryName(_audioFilePath);
Directory.CreateDirectory(outputFolder);
// Set up the WasapiLoopbackCapture
_waveSource = new WasapiLoopbackCapture();
_writer = new WaveFileWriter(_audioFilePath, _waveSource.WaveFormat);
// Handle the DataAvailable event
_waveSource.DataAvailable += (sender, args) =>
{
_writer.Write(args.Buffer, 0, args.BytesRecorded);
};
// Handle the RecordingStopped event
_waveSource.RecordingStopped += (sender, args) =>
{
_writer.Dispose();
_writer = null;
_waveSource.Dispose();
_waveSource = null;
};
// Start recording
_waveSource.StartRecording();
Console.WriteLine("WASAPI Audio recording started.");
}
catch (Exception ex)
{
Console.WriteLine($"Error starting audio recording: {ex.Message}");
}
}
private static void StartFfmpegRecording(string songName)
{
var date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
_videoFilePath = $"{_ffmpegFolderPath}\\{songName}_{date}.mkv";
var ffmpegArguments = $"-framerate 60 " +
$"-filter_complex \"ddagrab=0,hwdownload,format=bgra\" " +
$"-c:v libx264 -movflags +faststart -crf 20 -y \"{_videoFilePath}\"";
_ffmpegProcess = new Process();
_ffmpegProcess.StartInfo.FileName = "ffmpeg";
_ffmpegProcess.StartInfo.Arguments = ffmpegArguments;
_ffmpegProcess.StartInfo.UseShellExecute = false;
_ffmpegProcess.StartInfo.RedirectStandardOutput = false;
_ffmpegProcess.StartInfo.RedirectStandardError = true;
_ffmpegProcess.StartInfo.CreateNoWindow = true;
_ffmpegProcess.ErrorDataReceived += (_, args) => Console.WriteLine(args.Data);
_ffmpegProcess.Start();
_ffmpegProcess.BeginErrorReadLine();
}
private static void StopRecording(string songName)
{
StopFfmpegRecording();
StopAudioRecording();
CombineAudioAndVideo(_videoFilePath, _audioFilePath, songName);
}
private static void StopAudioRecording()
{
if (_waveSource == null) return;
_waveSource.StopRecording();
_waveSource.Dispose();
_waveSource = null;
if (_writer != null)
{
_writer.Dispose();
_writer = null;
}
Console.WriteLine("Audio recording stopped.");
}
private static void StopFfmpegRecording()
{
if (_ffmpegProcess != null && _ffmpegProcess.HasExited) return;
_ffmpegProcess?.Kill();
_ffmpegProcess?.WaitForExit();
_ffmpegProcess?.Dispose();
_ffmpegProcess = null;
Console.WriteLine("FFMPEG process stopped.");
}
private static void CombineAudioAndVideo(string videoFilePath, string audioFilePath, string songName)
{
var combinedOutputFilePath = $"{_ffmpegFolderPath}\\{songName}_combined_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.mkv";
var ffmpegArgs =
$"-y -i \"{videoFilePath}\" -i \"{audioFilePath}\" -c:v copy -c:a aac -strict experimental \"{combinedOutputFilePath}\"";
var processInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = ffmpegArgs,
RedirectStandardOutput = false,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = Process.Start(processInfo);
if (process == null)
{
Console.WriteLine("FFmpeg failed to start.");
return;
}
var error = process.StandardError.ReadToEnd();
process.WaitForExit();
Console.WriteLine("FFmpeg Error: " + error);
Console.WriteLine(File.Exists(combinedOutputFilePath)
? "Audio and video have been successfully combined."
: "Failed to combine audio and video. Check the logs for errors.");
File.Delete(videoFilePath);
File.Delete(audioFilePath);
}
}