Skip to content

Instantly share code, notes, and snippets.

@lostkagamine
Last active November 30, 2023 03:36
Show Gist options
  • Save lostkagamine/fb20be973e71fca522ce78026313ce6b to your computer and use it in GitHub Desktop.
Save lostkagamine/fb20be973e71fca522ce78026313ce6b to your computer and use it in GitHub Desktop.
320ifier
using System.Diagnostics;
// 320ifier - parallel automatic bulk transcoding toolkit
// 2023 nightshades-madoka
// warning: this program will use as many cores as it can, be careful
// MIT license or whatever idgaf what you do with this
namespace ThreeHundredTwentyfier;
public class Program
{
enum Mode
{
To320,
To16bit,
ToV0
}
private class WorkUnit
{
public ProcessStartInfo Info = null!;
public uint ThreadId;
public string FileName = "";
public uint WorkId;
}
static List<WorkUnit> GetPsiList(Mode mode)
{
var list = new List<WorkUnit>();
var baseDir = Path.GetFileName(Environment.CurrentDirectory);
Directory.CreateDirectory(baseDir);
var procCount = Environment.ProcessorCount;
var currentThread = 0u;
var currentId = 0u;
var dir = Directory.EnumerateFiles("input");
foreach (var f in dir)
{
var filename = Path.GetFileNameWithoutExtension(f);
var psi = mode switch
{
Mode.To320 => new ProcessStartInfo
{
FileName = @"C:\bin\ffmpeg.exe",
Arguments = $"""
-y -i "input\{filename}.flac" -ab 320k -map_metadata 0 -id3v2_version 3 "{baseDir}\{filename}.mp3"
"""
},
Mode.To16bit => new ProcessStartInfo
{
FileName = @"C:\bin\ffmpeg.exe",
Arguments = $"""
-y -i "input\{filename}.flac" -sample_fmt s16 "{baseDir}\{filename}.flac"
"""
},
Mode.ToV0 => new ProcessStartInfo
{
FileName = @"C:\bin\ffmpeg.exe",
Arguments = $"""
-y -i "input\{filename}.flac" -c:a libmp3lame -q:a 0 -map_metadata 0 -id3v2_version 3 "{baseDir}\{filename}.mp3"
"""
},
_ => throw new ArgumentOutOfRangeException(nameof(mode))
};
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
list.Add(new()
{
Info = psi,
ThreadId = currentThread++,
FileName = filename,
WorkId = currentId++
});
if (currentThread >= procCount) currentThread = 0;
}
return list;
}
public static void Main(string[] args)
{
var mode = Mode.To320;
if (args.Length > 0)
{
switch (args[0])
{
case "16bit":
Console.WriteLine("16-bit mode");
mode = Mode.To16bit;
break;
case "v0":
Console.WriteLine("v0 mode");
mode = Mode.ToV0;
break;
}
}
var processStartInfoList = GetPsiList(mode);
var fileCount = processStartInfoList.Count;
var psiSlots = Environment.ProcessorCount;
if (psiSlots > fileCount) psiSlots = fileCount;
Console.WriteLine($"Starting {psiSlots} threads");
var complete = 0;
var threads = new List<Thread>();
var timeAtStart = Stopwatch.GetTimestamp();
for (uint i = 0; i < psiSlots; i++)
{
var threadId = i;
var thread = new Thread(() =>
{
var done = new List<uint>();
while (true)
{
if (processStartInfoList.Count == 0) break;
var psi = processStartInfoList.FirstOrDefault(x =>
x.ThreadId == threadId && !done.Contains(x.WorkId));
if (psi == null) break;
Console.WriteLine($"Thread {threadId} picked up '{psi.FileName}'");
var process = Process.Start(psi.Info)!;
process.WaitForExit();
done.Add(psi.WorkId);
Console.WriteLine($"Thread {threadId} finished '{psi.FileName}' ({++complete}/{fileCount})");
}
Console.WriteLine($"Thread {threadId} exhausted work pool");
});
thread.Start();
threads.Add(thread);
}
foreach (var t in threads) t.Join();
var timeAtEnd = Stopwatch.GetElapsedTime(timeAtStart);
Console.WriteLine(timeAtEnd.TotalSeconds > 60
? $"Complete! Took {timeAtEnd.TotalMinutes}m:{timeAtEnd.TotalSeconds % 60}s."
: $"Complete! Took {timeAtEnd.TotalSeconds} seconds.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment