diff --git a/CLAUDE.md b/CLAUDE.md
index 551ba2f..9e087e1 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -41,7 +41,7 @@ dotnet test src/EggEncoder.UnitTests/EggEncoder.UnitTests.csproj --configuration
- **`Transform/`** — `BitReader`, `BitWriter`, `HuffmanTable`, `Mdct` — low-level bitstream and signal-processing primitives shared by the AAC/WMA codecs
- **`Native/`** — `FlacNative.cs`/`Mp3Native.cs` (`[LibraryImport]` P/Invoke declarations), `NativeLibraryLoader.cs` (a `[ModuleInitializer]` that registers a custom `DllImportResolver` so `libFLAC`/`libmp3lame` load from `Native/win-x64/` relative to `AppContext.BaseDirectory` regardless of the consuming app's working directory)
-- **`Waveform/WaveformCalculator.cs`** — streaming peak-window calculator fed blocks during decode, used by every codec's probe path to produce `ProbeResult.WaveformResult`
+- **`Waveform/WaveformCalculator.cs`** — streaming peak-window calculator fed blocks during decode, used by every codec's probe path to produce `ProbeResult.Waveform`
- **`Results/ProbeResult.cs`** — the public `ProbeResult` DTO returned by every `Probe` call
- **`ServiceCollectionExtensions.cs`** — `AddEggEncoder()` DI registration; no options, registers `IMediaEncoder` → `NativeEncoder` (scoped)
diff --git a/docs/API-Reference.html b/docs/API-Reference.html
index 07d5dff..fef4745 100644
--- a/docs/API-Reference.html
+++ b/docs/API-Reference.html
@@ -72,7 +72,7 @@
ProbeResult
public string? TimeBase { get; init; } // e.g. "1/44100"
public int? Width { get; init; } // set for video containers (MOV/MP4), null for audio
public int? Height { get; init; }
- public string? WaveformResult { get; set; }
+ public IReadOnlyList<double>? Waveform { get; init; } // normalized peak windows in [0,1], or null
}
NativeEncoder
diff --git a/docs/Advanced-Features.html b/docs/Advanced-Features.html
index c4eb947..cd9ec19 100644
--- a/docs/Advanced-Features.html
+++ b/docs/Advanced-Features.html
@@ -43,7 +43,7 @@
Advanced Features
Waveform Generation
- Every Probe call that decodes audio (WAV, FLAC, MP3, AAC, WMA — not MOV/MP4) populates ProbeResult.WaveformResult with a JSON array of normalized peak values in [0, 1], one per window, via WaveformCalculator:
+ Every Probe call that decodes audio (WAV, FLAC, MP3, AAC, WMA — not MOV/MP4) populates ProbeResult.Waveform with a list of normalized peak values in [0, 1], one per window, via WaveformCalculator:
using EggEncoder.Waveform;
var calculator = new WaveformCalculator(totalSamplesPerChannel, channels, bitsPerSample);
@@ -71,7 +71,7 @@ MOV/MP4 Probing
var result = MovProbe.Probe("clip.mp4");
Console.WriteLine($"{result.Width}x{result.Height}, {result.DurationInSeconds}s, {result.CodecFourCc}");
- This is metadata-only — MovProbe does not decode audio or video frames. NativeEncoder.Probe routes .mov/.mp4 files here automatically and leaves ProbeResult.WaveformResult null.
+ This is metadata-only — MovProbe does not decode audio or video frames. NativeEncoder.Probe routes .mov/.mp4 files here automatically and leaves ProbeResult.Waveform null.
Native Binary Packaging
libmp3lame.dll and libFLAC.dll are packed via the NuGet contentFiles convention so they land at Native/win-x64/*.dll relative to your application's output directory — exactly where NativeLibraryLoader's custom DllImportResolver looks for them at runtime, regardless of your app's working directory. No manual copy step is required after dotnet add package EggEncoder.
diff --git a/docs/Getting-Started.html b/docs/Getting-Started.html
index 29e0c8c..491ed44 100644
--- a/docs/Getting-Started.html
+++ b/docs/Getting-Started.html
@@ -103,7 +103,7 @@ Reading a ProbeResult
public string? TimeBase { get; init; } // e.g. "1/44100"
public int? Width { get; init; } // set for video containers (MOV/MP4), null for audio
public int? Height { get; init; }
- public string? WaveformResult { get; set; } // JSON array of normalized peak windows, or null
+ public IReadOnlyList<double>? Waveform { get; init; } // normalized peak windows, or null
}
See Advanced Features for waveform generation and sample-accurate cutting details, or the API Reference for the full type list.
diff --git a/llms-full.txt b/llms-full.txt
index 4fcef59..f1efbd4 100644
--- a/llms-full.txt
+++ b/llms-full.txt
@@ -47,7 +47,7 @@ public class ProbeResult
public string? TimeBase { get; init; } // e.g. "1/44100"
public int? Width { get; init; } // set for video containers (MOV/MP4), null for audio
public int? Height { get; init; }
- public string? WaveformResult { get; set; } // JSON array of normalized peak windows in [0,1], or null
+ public IReadOnlyList? Waveform { get; init; } // normalized peak windows in [0,1], or null
}
```
diff --git a/llms.txt b/llms.txt
index 41f1907..3a1a3e6 100644
--- a/llms.txt
+++ b/llms.txt
@@ -55,7 +55,7 @@ var probeResult = await encoder.Probe("track.flac");
- `IMediaEncoder` — the abstraction (`Probe`, `ConvertFile`, `CutFile`)
- `NativeEncoder` — the sole implementation, Windows x64 only (P/Invoke to libmp3lame/libFLAC)
-- `ProbeResult` — format name/long name, file size, duration, codec name/long name, sample rate, channels, channel layout, bit depth, bitrate, dimensions (for video), waveform JSON
+- `ProbeResult` — format name/long name, file size, duration, codec name/long name, sample rate, channels, channel layout, bit depth, bitrate, dimensions (for video), normalized waveform peak windows
- `AudioCutter` — format-dispatching convert/cut, used internally by `NativeEncoder`
- `WaveformCalculator` — streams decoded blocks into normalized peak windows
diff --git a/src/EggEncoder.UnitTests/NativeEncoderTest.cs b/src/EggEncoder.UnitTests/NativeEncoderTest.cs
index 4b70b05..c3aa126 100644
--- a/src/EggEncoder.UnitTests/NativeEncoderTest.cs
+++ b/src/EggEncoder.UnitTests/NativeEncoderTest.cs
@@ -3,7 +3,6 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
-using System.Text.Json;
namespace EggEncoder.UnitTests
{
@@ -29,7 +28,7 @@ public async Task Probe_WavFile_Should_Return_Correct_Metadata_And_Waveform()
{
var probeResult = await _nativeEncoder.Probe(_wavFixturePath);
- AssertNonEmptyWaveform(probeResult.WaveformResult);
+ AssertNonEmptyWaveform(probeResult.Waveform);
probeResult.FormatName.Should().Be("wav");
probeResult.SizeBytes.Should().Be(new FileInfo(_wavFixturePath).Length);
@@ -56,7 +55,7 @@ public async Task Probe_FlacFile_Should_Return_Correct_Metadata_And_Waveform()
var probeResult = await _nativeEncoder.Probe(flacPath);
- AssertNonEmptyWaveform(probeResult.WaveformResult);
+ AssertNonEmptyWaveform(probeResult.Waveform);
probeResult.FormatName.Should().Be("flac");
probeResult.DurationSeconds.Should().BeApproximately(2, 0.1);
@@ -83,7 +82,7 @@ public async Task Probe_Mp3File_Should_Return_Correct_Metadata_And_Waveform()
var probeResult = await _nativeEncoder.Probe(mp3Path);
- AssertNonEmptyWaveform(probeResult.WaveformResult);
+ AssertNonEmptyWaveform(probeResult.Waveform);
probeResult.FormatName.Should().Be("mp3");
probeResult.DurationSeconds.Should().BeApproximately(2, 0.1);
@@ -126,7 +125,7 @@ public async Task Probe_MovFile_Should_Return_Correct_VideoMetadata()
{
var probeResult = await _nativeEncoder.Probe(_movFixturePath);
- probeResult.WaveformResult.Should().BeNull();
+ probeResult.Waveform.Should().BeNull();
probeResult.FormatName.Should().Be("mov");
probeResult.DurationSeconds.Should().BeApproximately(5, 0.1);
@@ -140,7 +139,7 @@ public async Task Probe_Mp4File_Should_Return_Correct_VideoMetadata()
{
var probeResult = await _nativeEncoder.Probe(_mp4FixturePath);
- probeResult.WaveformResult.Should().BeNull();
+ probeResult.Waveform.Should().BeNull();
probeResult.Width.Should().Be(640);
probeResult.Height.Should().Be(360);
@@ -193,12 +192,10 @@ public async Task CutFile_ToNestedMissingDirectory_Should_CreateDirectory_And_Cu
}
}
- private static void AssertNonEmptyWaveform(string? waveformResult)
+ private static void AssertNonEmptyWaveform(IReadOnlyList? waveform)
{
- waveformResult.Should().NotBeNull();
-
- var windows = JsonSerializer.Deserialize>(waveformResult!);
- windows.Should().NotBeEmpty();
+ waveform.Should().NotBeNull();
+ waveform.Should().NotBeEmpty();
}
private static string CreateTempDirectory()
diff --git a/src/EggEncoder/NativeEncoder.cs b/src/EggEncoder/NativeEncoder.cs
index cd914b3..6d23c28 100644
--- a/src/EggEncoder/NativeEncoder.cs
+++ b/src/EggEncoder/NativeEncoder.cs
@@ -127,7 +127,7 @@ private static ProbeResult ProbeWav(string filePath)
BitRate = wavReader.SampleRate * wavReader.BitsPerSample * wavReader.Channels,
DurationInSamples = wavReader.TotalSamples,
TimeBase = wavReader.SampleRate > 0 ? $"1/{wavReader.SampleRate}" : null,
- WaveformResult = JsonSerializer.Serialize(waveformCalculator.GetNormalizedWindows())
+ Waveform = waveformCalculator.GetNormalizedWindows()
};
}
@@ -159,7 +159,7 @@ private static ProbeResult ProbeFlac(string filePath)
BitRate = streamInfo.SampleRate * streamInfo.BitsPerSample * streamInfo.Channels,
DurationInSamples = streamInfo.TotalSamples,
TimeBase = streamInfo.SampleRate > 0 ? $"1/{streamInfo.SampleRate}" : null,
- WaveformResult = JsonSerializer.Serialize(waveformCalculator?.GetNormalizedWindows() ?? [])
+ Waveform = waveformCalculator?.GetNormalizedWindows() ?? []
};
}
@@ -190,7 +190,7 @@ private static ProbeResult ProbeMp3(string filePath)
BitRate = mp3ProbeResult.BitRate,
IsVariableBitRate = mp3ProbeResult.IsVariableBitRate,
TimeBase = mp3ProbeResult.SampleRate > 0 ? $"1/{mp3ProbeResult.SampleRate}" : null,
- WaveformResult = JsonSerializer.Serialize(waveformCalculator?.GetNormalizedWindows() ?? [])
+ Waveform = waveformCalculator?.GetNormalizedWindows() ?? []
};
}
@@ -222,7 +222,7 @@ private static ProbeResult ProbeAac(string filePath)
BitRate = streamInfo.SampleRate * streamInfo.BitsPerSample * streamInfo.Channels,
DurationInSamples = streamInfo.TotalSamples,
TimeBase = streamInfo.SampleRate > 0 ? $"1/{streamInfo.SampleRate}" : null,
- WaveformResult = JsonSerializer.Serialize(waveformCalculator?.GetNormalizedWindows() ?? [])
+ Waveform = waveformCalculator?.GetNormalizedWindows() ?? []
};
}
@@ -254,7 +254,7 @@ private static ProbeResult ProbeWma(string filePath)
BitRate = streamInfo.SampleRate * streamInfo.BitsPerSample * streamInfo.Channels,
DurationInSamples = streamInfo.TotalSamples,
TimeBase = streamInfo.SampleRate > 0 ? $"1/{streamInfo.SampleRate}" : null,
- WaveformResult = JsonSerializer.Serialize(waveformCalculator?.GetNormalizedWindows() ?? [])
+ Waveform = waveformCalculator?.GetNormalizedWindows() ?? []
};
}
@@ -279,7 +279,7 @@ private static ProbeResult ProbeVideo(string filePath)
CodecName = movProbeResult.CodecFourCc,
Width = movProbeResult.Width,
Height = movProbeResult.Height,
- WaveformResult = null
+ Waveform = null
};
}
diff --git a/src/EggEncoder/Results/ProbeResult.cs b/src/EggEncoder/Results/ProbeResult.cs
index de5d580..3035bc0 100644
--- a/src/EggEncoder/Results/ProbeResult.cs
+++ b/src/EggEncoder/Results/ProbeResult.cs
@@ -36,6 +36,6 @@ public class ProbeResult
public int? Height { get; init; }
- public string? WaveformResult { get; set; }
+ public IReadOnlyList? Waveform { get; init; }
}
}