Media.Remux("in.aiff", "out.wav") writes the PCM audio of an AIFF file as a WAV file. AIFF stores samples big-endian and WAV little-endian, so each sample’s bytes are swapped; nothing else changes: no decoding, no new bit depth or sample rate. The same call takes the PCM, A-law or µ-law track of a CAF, MOV, MP4, MKV or W64 file. WAV writing is new in 0.6.0-preview.1, and past 4 GiB the file becomes RF64 by itself.
dotnet new console -o AiffToWav && cd AiffToWav dotnet add package RecursiveAI.OneMedia --version 0.6.0-preview.1 ffmpeg -v error -f lavfi -i sine=f=440:d=3:r=48000 -ac 2 -c:a pcm_s24be -metadata title="Test tone" tone.aiff
tone.aiff is three seconds of a 440 Hz tone, 24-bit stereo at 48 kHz, with a title. Put this in Program.cs and run dotnet run:
using RecursiveAI.OneMedia; using RecursiveAI.OneMedia.Remuxing; string input = args.Length > 0 ? args[0] : "tone.aiff"; string output = args.Length > 1 ? args[1] : "tone.wav"; RemuxReport report = Media.Remux(input, output); // .wav: WAVE, RF64 past 4 GiB foreach (RemuxTrackReport track in report.Tracks) if (track.OutputTrack >= 0) Console.WriteLine($"track {track.InputTrack} -> {track.OutputTrack}: {track.Packets} packets copied"); foreach (RemuxLoss loss in report.Losses) Console.WriteLine($"not carried: {loss.Kind} {loss.Subject}: {loss.Reason}"); foreach (string change in report.Changes) Console.WriteLine($"changed: {change}"); Console.WriteLine($"{report.Mux.BytesWritten:N0} bytes written"); using MediaFile wav = Media.Open(output); Console.WriteLine($"{wav.Format}: {wav.Audio?.Codec}, {wav.Audio?.Audio?.SampleRate} Hz, {wav.Audio?.Audio?.Channels} channels, {wav.Duration}, title \"{wav.Tags.Title}\"");
track 0 -> 0: 212 packets copied changed: tags carried through the common view (1 edits, 0 pictures) 880,488 bytes written wav: pcm, 48000 Hz, 2 channels, 00:00:03, title "Test tone"
The title travels through the common tag view into the WAV’s LIST INFO chunk. 24-bit stereo is written as WAVE_FORMAT_EXTENSIBLE, the record WAV uses for samples wider than 16 bits.
Check it with ffmpeg
The samples decoded from both files are the same:
ffprobe -v error -show_entries stream=codec_name,sample_rate,channels,bits_per_sample:format=format_name,duration:format_tags=title -of compact tone.wav for f in tone.aiff tone.wav; do echo "$f $(ffmpeg -v error -i $f -f s32le - | md5sum)"; done
stream|codec_name=pcm_s24le|sample_rate=48000|channels=2|bits_per_sample=24 format|format_name=wav|duration=3.000000|tag:title=Test tone tone.aiff e2631706b9a3b2520948316282c4988b - tone.wav e2631706b9a3b2520948316282c4988b -
A browser can play the result where it couldn’t play the input: headless Chrome 149 refused tone.aiff and played tone.wav.
Limits
- PCM in, PCM out. In the words of the WAV writing page: “Nothing is decoded, converted between widths or rates, or encoded.” A track in another codec with no WAV format record is refused. To get WAV from MP3, AAC or Opus, decode it: Extract the audio from a video does that with OneAudio.
- One track, seekable output. From the README: “one track, and the output must be able to seek”. The sizes in the header are written at the end, so a pipe can’t be the output.
- Broadcast Wave.
bext,cart, iXML andLIST INFOchunks are written byWavMuxerwithWavMuxOptions, and the chunks of a WAV or RF64 input travel unchanged; to edit the tags of an existing WAV, see Edit MP4, MKV and MP3 tags. - Big files. Past 4 GiB the header is rewritten as RF64 in place, without moving a sample byte; set the layout in
WavMuxOptionsto refuse that or to write RF64 or BW64 from the start.