Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions OpenUtau.Core/Format/FFmpegWaveReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using NAudio.Wave;

namespace OpenUtau.Core.Format {
public class FFmpegWaveReader(string filepath) : WaveStream {
private readonly WaveFileReader inner = new(DecodeToMemory(filepath));
private static MemoryStream DecodeToMemory(string filepath) {
using var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "ffmpeg",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
}
};
proc.StartInfo.ArgumentList.Add("-i");
proc.StartInfo.ArgumentList.Add(filepath);
proc.StartInfo.ArgumentList.Add("-f");
proc.StartInfo.ArgumentList.Add("wav");
proc.StartInfo.ArgumentList.Add("pipe:1");
var stderrLog = new StringBuilder();
proc.ErrorDataReceived += (_, e) => { if (e.Data != null) stderrLog.AppendLine(e.Data); };
try {
proc.Start();
} catch (Exception) {
throw new Exception("ffmpeg not found. Install ffmpeg and ensure it is on PATH.");
}
proc.BeginErrorReadLine();
var memStream = new MemoryStream();
proc.StandardOutput.BaseStream.CopyTo(memStream);
proc.WaitForExit();
if (proc.ExitCode != 0) {
throw new Exception($"ffmpeg failed to decode '{Path.GetFileName(filepath)}'.\n{stderrLog}");
}
// ffmpeg cannot seek back to fix WAV chunk sizes when writing to a non-seekable
// pipe, leaving them as 0 or 0xFFFFFFFF. Patch them now that the full size is known.
PatchWavHeader(memStream);
return memStream;
}

private static void PatchWavHeader(MemoryStream stream) {
int total = (int)stream.Length;
// Fix RIFF chunk size at offset 4
stream.Position = 4;
stream.Write(BitConverter.GetBytes(total - 8), 0, 4);
// Scan sub-chunks starting after "RIFF" + size + "WAVE"
stream.Position = 12;
var id = new byte[4];
var sz = new byte[4];
while (stream.Position <= stream.Length - 8) {
stream.Read(id, 0, 4);
stream.Read(sz, 0, 4);
if (id[0] == 'd' && id[1] == 'a' && id[2] == 't' && id[3] == 'a') {
stream.Position -= 4;
stream.Write(BitConverter.GetBytes(total - (int)stream.Position - 4), 0, 4);
break;
}
int chunkSize = BitConverter.ToInt32(sz, 0);
if (chunkSize < 0) break;
stream.Position += chunkSize;
}
stream.Position = 0;
}

public override WaveFormat WaveFormat => inner.WaveFormat;
public override long Length => inner.Length;
public override long Position {
get => inner.Position;
set => inner.Position = value;
}
public override int Read(byte[] buffer, int offset, int count) => inner.Read(buffer, offset, count);

protected override void Dispose(bool disposing) {
if (disposing) {
inner.Dispose();
}
base.Dispose(disposing);
}
}
}
9 changes: 9 additions & 0 deletions OpenUtau.Core/Format/Wave.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ public static WaveStream OpenFile(string filepath) {
if (ext == ".aiff" || ext == ".aif" || ext == ".aifc") {
return new AiffFileReader(filepath);
}
// M4A/AAC: MP4 container has a "ftyp" box at bytes 4–7
string ftyp = System.Text.Encoding.ASCII.GetString(buffer.AsSpan(4, 4));
if (ftyp == "ftyp" || ext == ".m4a" || ext == ".mp4") {
#if WINDOWS
return new MediaFoundationReader(filepath);
#else
return new FFmpegWaveReader(filepath);
#endif
}
throw new Exception("Unsupported audio file format.");
}

Expand Down
2 changes: 1 addition & 1 deletion OpenUtau/FilePicker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class FilePicker {
Patterns = new[] { "*.musicxml" },
};
public static FilePickerFileType AudioFiles { get; } = new("Audio Files") {
Patterns = new[] { "*.wav", "*.mp3", "*.ogg", "*.opus", "*.flac" },
Patterns = new[] { "*.wav", "*.mp3", "*.ogg", "*.opus", "*.flac", "*.m4a" },
};
public static FilePickerFileType WAV { get; } = new("WAV") {
Patterns = new[] { "*.wav" },
Expand Down
2 changes: 1 addition & 1 deletion OpenUtau/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ void OnPointerPressed(object? sender, PointerPressedEventArgs args) {
async void OnDrop(object? sender, DragEventArgs args) {
string[] ProjectExts = { ".ustx", ".ust", ".vsqx", ".ufdata", ".musicxml", ".mid", ".midi" };
string[] ArchiveExts = { ".zip", ".rar", ".uar" };
string[] AudioExts = { ".mp3", ".wav", ".ogg", ".flac" };
string[] AudioExts = { ".mp3", ".wav", ".ogg", ".flac", ".m4a" };
string[] SupportedExts = ProjectExts
.Concat(ArchiveExts)
.Concat(AudioExts)
Expand Down
Loading