Last active
July 30, 2020 19:47
-
-
Save grappigegovert/4cc9424351f8f8998a62a926ccf20b01 to your computer and use it in GitHub Desktop.
Code for my .TUN Converter. A wrapper around SoX for handling LEGO® Racers .TUN and .PCM files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace tunconverter | |
{ | |
class Program | |
{ | |
public static readonly int chunklength = 1; | |
static void Main(string[] args) | |
{ | |
if (args.Length < 2) | |
{ | |
usage(); | |
return; | |
} | |
else | |
{ | |
try | |
{ | |
int width = Console.WindowWidth; | |
Console.Write(new string('*', width)); | |
Console.CursorLeft += width / 2 - 7; | |
Console.WriteLine("Fancy interface"); | |
Console.Write(new string('*', width)); | |
} | |
catch (System.IO.IOException) { } | |
string input; | |
string ext; | |
try | |
{ | |
input = Path.GetFullPath(args[0]); | |
ext = Path.GetExtension(input); | |
} | |
catch (ArgumentException) | |
{ | |
usage(); | |
return; | |
} | |
if (ext.ToLower() == ".tun" || ext.ToLower() == ".pcm") | |
{ | |
string channel1 = Path.GetRandomFileName(); | |
string channel2 = Path.GetRandomFileName(); | |
int channels; | |
int samplerate; | |
using (FileStream inputstream = new FileStream(input, FileMode.Open, FileAccess.Read)) | |
using (FileStream output1 = new FileStream(channel1, FileMode.Create, FileAccess.ReadWrite)) | |
using (FileStream output2 = new FileStream(channel2, FileMode.Create, FileAccess.ReadWrite)) | |
{ | |
split(inputstream, output1, output2, out channels, out samplerate); | |
} | |
ProcessStartInfo ps = new ProcessStartInfo(); | |
ps.FileName = "SoX\\sox.exe"; | |
if (channels > 1) | |
ps.Arguments = " -M -t ima -r " + samplerate + " " + channel1 + " -t ima -r " + samplerate + " " + channel2 + " " + "\"" + Path.ChangeExtension(input, args[1]) + "\""; | |
else | |
ps.Arguments = " -t ima -r " + samplerate + " " + channel1 + " " + "\"" + Path.ChangeExtension(input, args[1]) + "\""; | |
ps.UseShellExecute = false; | |
Process p = Process.Start(ps); | |
p.WaitForExit(); | |
if (!(args.Length == 3 && args[2] == "nodelete")) | |
{ | |
File.Delete(channel1); | |
File.Delete(channel2); | |
} | |
else | |
{ | |
Console.WriteLine("Channel 1 is stored at " + channel1); | |
Console.WriteLine("Channel 2 is stored at " + channel2); | |
} | |
} | |
else | |
{ | |
int samplerate; | |
int channels; | |
getInfo(input, out channels, out samplerate); | |
if (args[1] == "tun" || args[1] == "pcm") | |
{ | |
if (args[1] == "tun") | |
{ | |
samplerate = 22050; | |
} | |
ProcessStartInfo ps1 = new ProcessStartInfo(); | |
ps1.FileName = "SoX\\sox.exe"; | |
if (channels == 1) | |
ps1.Arguments = "\"" + input + "\" -r " + samplerate.ToString() + " -t ima -"; | |
else | |
ps1.Arguments = "\"" + input + "\" -r " + samplerate.ToString() + " -t ima - remix 1"; | |
ps1.UseShellExecute = false; | |
ps1.RedirectStandardOutput = true; | |
FileStream output = new FileStream(Path.ChangeExtension(input, args[1]), FileMode.Create, FileAccess.Write); | |
Process p1 = Process.Start(ps1); | |
Stream input1 = p1.StandardOutput.BaseStream; | |
if (channels == 1) | |
{ | |
if (args[1] == "tun") | |
merge(input1, Stream.Null, output, 1); | |
else | |
merge(input1, Stream.Null, output, 1, samplerate); | |
} | |
else | |
{ | |
ProcessStartInfo ps2 = new ProcessStartInfo(); | |
ps2.FileName = "SoX\\sox.exe"; | |
ps2.Arguments = "\"" + input + "\" -r " + samplerate.ToString() + " -t ima - remix 2"; | |
ps2.UseShellExecute = false; | |
ps2.RedirectStandardOutput = true; | |
Process p2 = Process.Start(ps2); | |
Stream input2 = p2.StandardOutput.BaseStream; | |
if (args[1] == "tun") | |
merge(input1, input2, output, 2); | |
else | |
merge(input1, input2, output, 2, samplerate); | |
p2.Dispose(); | |
input2.Dispose(); | |
} | |
output.Dispose(); | |
p1.Dispose(); | |
input1.Dispose(); | |
} | |
else | |
{ | |
usage(); | |
return; | |
} | |
} | |
Console.WriteLine("Conversion successful."); | |
} | |
} | |
static void split(Stream input, Stream output1, Stream output2, out int channels, out int samplerate) | |
{ | |
int bytesread = 1; | |
long length = input.Length; | |
byte[] buffer = new byte[chunklength]; | |
input.Seek(4, SeekOrigin.Begin); | |
byte[] buf = new byte[4]; | |
input.Read(buf, 0, 4); | |
int offset = BitConverter.ToInt32(buf, 0); | |
input.Seek(0x7, SeekOrigin.Current); | |
input.Read(buf, 0, 1); | |
channels = Convert.ToInt32(buf[0]); | |
if (offset == 0xC) | |
{ | |
input.Read(buf, 0, 4); | |
samplerate = BitConverter.ToInt32(buf, 0); | |
} | |
else | |
samplerate = 22050; | |
while (bytesread > 0 && length > 0) | |
{ | |
bytesread = input.Read(buffer, 0, chunklength); | |
output1.Write(buffer, 0, bytesread); | |
length -= bytesread; | |
if (channels > 1) | |
{ | |
bytesread = input.Read(buffer, 0, chunklength); | |
output2.Write(buffer, 0, bytesread); | |
length -= bytesread; | |
} | |
} | |
} | |
static void merge(Stream in1, Stream in2, Stream output, int channels, int samplerate = 0) | |
{ | |
int bytesread = 1; | |
byte[] header; | |
if (samplerate == 0) | |
{ | |
header = new byte[] { 0x41, 0x4C, 0x50, 0x20, 0x08, 0x00, 0x00, 0x00, 0x41, 0x44, 0x50, 0x43, 0x4D, 0x00, 0x00, Convert.ToByte(channels) }; | |
} | |
else | |
{ | |
header = (new byte[] { 0x41, 0x4C, 0x50, 0x20, 0x0C, 0x00, 0x00, 0x00, 0x41, 0x44, 0x50, 0x43, 0x4D, 0x00, 0x00, Convert.ToByte(channels) }).Concat(BitConverter.GetBytes(samplerate)).ToArray(); | |
} | |
output.Write(header, 0, header.Length); | |
byte[] buffer = new byte[chunklength]; | |
while (bytesread > 0) | |
{ | |
bytesread = in1.Read(buffer, 0, chunklength); | |
output.Write(buffer, 0, bytesread); | |
if (channels > 1) | |
{ | |
bytesread = in2.Read(buffer, 0, chunklength); | |
output.Write(buffer, 0, bytesread); | |
} | |
} | |
} | |
static void getInfo(string inputfile, out int channels, out int samplerate) | |
{ | |
ProcessStartInfo ps = new ProcessStartInfo(); | |
ps.FileName = "SoX\\sox.exe"; | |
ps.Arguments = "--i " + "\"" + inputfile + "\""; | |
ps.UseShellExecute = false; | |
ps.RedirectStandardOutput = true; | |
channels = 1; | |
samplerate = 22050; | |
using (Process proc = Process.Start(ps)) | |
using (StreamReader stream = proc.StandardOutput) | |
{ | |
while (!stream.EndOfStream) | |
{ | |
string line = stream.ReadLine(); | |
string value = line.Substring(line.IndexOf(':') + 1); | |
if (line.StartsWith("Channels")) | |
{ | |
channels = int.Parse(value); | |
} | |
else if (line.StartsWith("Sample Rate")) | |
{ | |
samplerate = int.Parse(value); | |
} | |
} | |
} | |
} | |
static void usage() | |
{ | |
string[] lines = {"Usage:", | |
//"Open GUI:", | |
//" tunconverter", | |
"Convert to tun file:", | |
" tunconverter input.ext tun", | |
"Convert to pcm file:", | |
" tunconverter input.ext pcm", | |
"Convert to ogg file:", | |
" tunconverter input.tun ogg", | |
" OR ", | |
" tunconverter input.pcm ogg",}; | |
foreach (string str in lines) | |
{ | |
Console.WriteLine(str); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment