Skip to content

Commit 661ab84

Browse files
committed
Sync from local GitLab
1 parent 41c755e commit 661ab84

File tree

9 files changed

+1206
-1
lines changed

9 files changed

+1206
-1
lines changed

.gitignore

Lines changed: 484 additions & 0 deletions
Large diffs are not rendered by default.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2026 Vincent
3+
Copyright (c) 2026 Vincent/gizmowhatsit
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Program.cs

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
namespace QOATool;
2+
3+
class Program
4+
{
5+
static void Main(string[] args)
6+
{
7+
if (args.Length < 2)
8+
{
9+
PrintUsage();
10+
return;
11+
}
12+
13+
string command = args[0].ToLower();
14+
string inputPath = args[1];
15+
16+
bool mono = args.Any(a => a == "-m" || a == "--mono");
17+
18+
try
19+
{
20+
switch (command)
21+
{
22+
case "wav2qoa":
23+
if (args.Length < 3) { Console.WriteLine("Usage: wav2qoa <input.wav> <output.qoa> [-m]"); return; }
24+
string outputPath = args[2];
25+
ConvertWavToQoa(inputPath, outputPath, mono);
26+
break;
27+
28+
case "qoa2wav":
29+
if (args.Length < 3) { Console.WriteLine("Usage: qoa2wav <input.qoa> <output.wav>"); return; }
30+
ConvertQoaToWav(inputPath, args[2]);
31+
break;
32+
33+
default:
34+
PrintUsage();
35+
break;
36+
}
37+
}
38+
catch (Exception ex)
39+
{
40+
Console.WriteLine($"Error: {ex.Message}");
41+
Console.WriteLine(ex.StackTrace);
42+
}
43+
}
44+
45+
static void PrintUsage()
46+
{
47+
Console.WriteLine("QOA CLI Tool");
48+
Console.WriteLine("Usage:");
49+
Console.WriteLine(" wav2qoa <input.wav> <output.qoa> [-m] Convert WAV to QOA (optional -m for mono)");
50+
Console.WriteLine(" qoa2wav <input.qoa> <output.wav> Convert QOA to WAV");
51+
}
52+
53+
static void ConvertWavToQoa(string wavPath, string qoaPath, bool mixDownToMono)
54+
{
55+
Console.WriteLine($"Reading WAV: {wavPath}");
56+
var wav = WavFile.Read(wavPath);
57+
58+
if (mixDownToMono && wav.Channels > 1)
59+
{
60+
Console.WriteLine($"Mixing down {wav.Channels} channels to Mono...");
61+
62+
int originalChannels = wav.Channels;
63+
int totalFrames = wav.Samples.Length / originalChannels;
64+
short[] monoSamples = new short[totalFrames];
65+
66+
for (int i = 0; i < totalFrames; i++)
67+
{
68+
int sum = 0;
69+
for (int c = 0; c < originalChannels; c++)
70+
{
71+
sum += wav.Samples[i * originalChannels + c];
72+
}
73+
monoSamples[i] = (short)(sum / originalChannels);
74+
}
75+
76+
wav.Samples = monoSamples;
77+
wav.Channels = 1;
78+
}
79+
80+
Console.WriteLine($"Encoding QOA: {qoaPath} ({wav.SampleRate}Hz, {wav.Channels}ch, {wav.Samples.Length / wav.Channels} samples)");
81+
82+
using (var fs = File.Create(qoaPath))
83+
{
84+
var encoder = new QOAStreamEncoder(fs);
85+
86+
int totalSamples = wav.Samples.Length / wav.Channels;
87+
88+
if (!encoder.WriteHeader(totalSamples, wav.Channels, wav.SampleRate))
89+
throw new Exception("Failed to write QOA header");
90+
91+
int cursor = 0;
92+
int remaining = totalSamples;
93+
short[] buffer = wav.Samples;
94+
95+
while (remaining > 0)
96+
{
97+
int chunkCount = Math.Min(remaining, QOABase.MaxFrameSamples);
98+
int bufferOffset = cursor * wav.Channels;
99+
100+
short[] frameBuffer = new short[chunkCount * wav.Channels];
101+
Array.Copy(buffer, bufferOffset, frameBuffer, 0, frameBuffer.Length);
102+
103+
if (!encoder.WriteFrame(frameBuffer, chunkCount))
104+
{
105+
throw new Exception("Failed to write QOA frame");
106+
}
107+
108+
cursor += chunkCount;
109+
remaining -= chunkCount;
110+
}
111+
}
112+
Console.WriteLine("Conversion complete.");
113+
}
114+
115+
static void ConvertQoaToWav(string qoaPath, string wavPath)
116+
{
117+
Console.WriteLine($"Reading QOA: {qoaPath}");
118+
using (var fs = File.OpenRead(qoaPath))
119+
{
120+
var decoder = new QOAStreamDecoder(fs);
121+
if (!decoder.ReadHeader())
122+
{
123+
throw new Exception("Invalid QOA header");
124+
}
125+
126+
int totalSamples = decoder.GetTotalSamples();
127+
int channels = decoder.GetChannels();
128+
int sampleRate = decoder.GetSampleRate();
129+
130+
Console.WriteLine($"Decoding: {sampleRate}Hz, {channels}ch, {totalSamples} samples");
131+
132+
List<short> allSamples = new List<short>(totalSamples * channels);
133+
short[] frameBuffer = new short[QOABase.MaxFrameSamples * channels];
134+
135+
while (!decoder.IsEnd())
136+
{
137+
int samplesDecoded = decoder.ReadFrame(frameBuffer);
138+
if (samplesDecoded < 0) break;
139+
140+
for (int i = 0; i < samplesDecoded * channels; i++)
141+
{
142+
allSamples.Add(frameBuffer[i]);
143+
}
144+
}
145+
146+
Console.WriteLine($"Writing WAV: {wavPath}");
147+
WavFile.Write(wavPath, allSamples.ToArray(), channels, sampleRate);
148+
}
149+
Console.WriteLine("Conversion complete.");
150+
}
151+
}

0 commit comments

Comments
 (0)