chovy-sign/GameBuilder/Atrac3/Atrac3ToolEncoder.cs

175 lines
5.8 KiB
C#

using Li.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace GameBuilder.Atrac3
{
public class Atrac3ToolEncoder : IAtracEncoderBase
{
[DllImport("libc")]
private static extern int setenv(string name, string value, bool overwrite);
private static Random rng = new Random();
private static string TOOLS_DIRECTORY = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "tools");
private static string AT3TOOL_WIN = Path.Combine(TOOLS_DIRECTORY, "at3tool.exe");
private static string AT3TOOL_LINUX = Path.Combine(TOOLS_DIRECTORY, "at3tool.elf");
private static string TEMP_DIRECTORY = Path.Combine(Path.GetTempPath(), "at3tool_tmp");
private static string LD_LIBRARY_PATH = "LD_LIBRARY_PATH";
// random name so that can generate multiple at once if wanted ..
private string TEMP_WAV;
private string TEMP_AT3;
public Atrac3ToolEncoder()
{
string rdmPart = rng.Next().ToString("X");
TEMP_WAV = Path.Combine(TEMP_DIRECTORY, rdmPart + "_tmp.wav");
TEMP_AT3 = Path.Combine(TEMP_DIRECTORY, rdmPart + "_tmp.at3");
}
private static string AT3TOOL_LOCATION
{
get
{
if (OperatingSystem.IsWindows())
return AT3TOOL_WIN;
else if (OperatingSystem.IsLinux())
return AT3TOOL_LINUX;
else
throw new PlatformNotSupportedException("No at3tool binary for your platform");
}
}
private string setupLibaryPath()
{
string? libaryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
if (libaryPath is null) libaryPath = TOOLS_DIRECTORY;
else libaryPath += ";" + TOOLS_DIRECTORY;
Environment.SetEnvironmentVariable(libaryPath, libaryPath);
setenv(LD_LIBRARY_PATH, libaryPath, true);
return libaryPath;
}
private void runAtrac3Tool()
{
using(Process proc = new Process())
{
if (OperatingSystem.IsLinux())
proc.StartInfo.Environment.Add(LD_LIBRARY_PATH, setupLibaryPath());
proc.StartInfo.FileName = AT3TOOL_LOCATION;
proc.StartInfo.Arguments = "-br 132 -e \"" + TEMP_WAV + "\" \"" + TEMP_AT3 + "\"";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.Start();
proc.WaitForExit();
string stdout = proc.StandardOutput.ReadToEnd();
if (!stdout.Contains("Total Encoded Bytes"))
throw new Exception(stdout);
}
}
private byte[] stripAtracHeader()
{
using(FileStream at3Stream = File.OpenRead(TEMP_AT3))
{
StreamUtil at3Util = new StreamUtil(at3Stream);
at3Stream.Seek(0x4C, SeekOrigin.Begin);
int at3Len = at3Util.ReadInt32();
return at3Util.ReadBytes(at3Len);
}
}
private void makeWav(byte[] pcmData)
{
using (FileStream wavStream = File.Open(TEMP_WAV, FileMode.Create))
{
// CD-AUDIO standard settings
int fileSize = pcmData.Length;
int samplerate = 44100;
short channels = 2; // channels
short format = 16; // signed, 16 bit PCM
StreamUtil wavUtil = new StreamUtil(wavStream);
wavUtil.WriteStr("RIFF");
wavUtil.WriteInt32((fileSize + (0x2C - 8)));
wavUtil.WriteStr("WAVE");
wavUtil.WriteStr("fmt ");
wavUtil.WriteInt32(format);
wavUtil.WriteInt16(1);
wavUtil.WriteInt16(channels);
wavUtil.WriteInt32(samplerate);
wavUtil.WriteInt32((samplerate * format * channels) / 8);
wavUtil.WriteInt16(Convert.ToInt16(format * channels));
wavUtil.WriteInt16(format);
wavUtil.WriteStr("data");
wavUtil.WriteInt32(fileSize);
wavUtil.WriteBytes(pcmData);
}
}
private void ensureFilesAvailable()
{
if (!Directory.Exists(TEMP_DIRECTORY))
Directory.CreateDirectory(TEMP_DIRECTORY);
if (!Directory.Exists(TOOLS_DIRECTORY))
Directory.CreateDirectory(TOOLS_DIRECTORY);
if (OperatingSystem.IsWindows())
{
if (!File.Exists(AT3TOOL_WIN))
{
throw new FileNotFoundException("Cannot find at3tool at " + AT3TOOL_WIN);
}
}
else if(OperatingSystem.IsLinux())
{
if (!File.Exists(AT3TOOL_LINUX))
{
throw new FileNotFoundException("Cannot find at3tool at " + AT3TOOL_LINUX);
}
}
}
private void cleanup()
{
if (File.Exists(TEMP_WAV)) File.Delete(TEMP_WAV);
if (File.Exists(TEMP_AT3)) File.Delete(TEMP_AT3);
}
public byte[] EncodeToAtrac(byte[] pcmData)
{
ensureFilesAvailable();
makeWav(pcmData);
runAtrac3Tool();
byte[] rawAtracData = stripAtracHeader();
cleanup();
return rawAtracData;
}
}
}