Skip to content

Instantly share code, notes, and snippets.

@grappigegovert
Last active July 30, 2020 19:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grappigegovert/4cc9424351f8f8998a62a926ccf20b01 to your computer and use it in GitHub Desktop.
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.
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