Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ var probeResult = await encoder.Probe("track.flac");
| WMA | ✅ | ✅ | ❌ |
| MOV/MP4 (metadata only) | ✅ | ❌ | ❌ |

`IMediaEncoder.CutFile` supports WAV, FLAC, MP3, and AAC — sample-accurate, no re-encode of the untouched region.
`IMediaEncoder.CutFile` decodes any supported source (WAV, FLAC, MP3, AAC, WMA) and can cut into any supported destination format, including converting as it trims — sample-accurate, no re-encode of the untouched region.

## License

Expand Down
28 changes: 21 additions & 7 deletions docs/API-Reference.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,26 @@ <h2>IMediaEncoder</h2>
}</code></pre>

<h2>ProbeResult</h2>
<p>Only fields this library can genuinely compute are populated — no ffprobe-style placeholders (e.g. no <code>probe_score</code>, no disposition flags).</p>
<pre><code class="language-csharp">public class ProbeResult
{
public int DurationInSeconds { get; set; }
public int? BitsPerSample { get; set; }
public int? BitRate { get; set; }
public int? SampleRate { get; set; }
public int? Height { get; set; }
public int? Width { get; set; }
public string? Result { get; set; }
public string FormatName { get; init; } // e.g. "wav", "flac", "mp3", "aac", "asf", "mov", "mp4"
public string FormatLongName { get; init; } // e.g. "WAV / WAVE (Waveform Audio)"
public long SizeBytes { get; init; }
public double DurationSeconds { get; init; }
public string CodecType { get; init; } // "audio" or "video"
public string? CodecName { get; init; } // e.g. "pcm_s16le", "flac", "mp3", "aac", "wmav2"
public string? CodecLongName { get; init; }
public int? SampleRate { get; init; }
public int? Channels { get; init; }
public string? ChannelLayout { get; init; } // "mono", "stereo", or "{n} channels"
public int? BitsPerSample { get; init; }
public int? BitRate { get; init; }
public bool? IsVariableBitRate { get; init; } // MP3 only
public long? DurationInSamples { get; init; }
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; }
}</code></pre>

Expand All @@ -86,6 +97,7 @@ <h2>AudioCutter</h2>
public static void Convert(string sourceFilePath, string destFilePath);
public static bool Cut(string sourceFilePath, string destFilePath, int startInSeconds, int endInSeconds);
}</code></pre>
<p><code>Cut</code> decodes any supported source (<code>.wav</code>, <code>.flac</code>, <code>.mp3</code>, <code>.aac</code>, <code>.wma</code>) and writes any supported destination extension — source and destination don't need to match, so it can transcode while it trims. Returns <code>false</code> (and writes no file) if the requested range is entirely outside the source's duration.</p>

<h2>WaveformCalculator</h2>
<p>Namespace: <code>EggEncoder.Waveform</code>.</p>
Expand Down Expand Up @@ -148,6 +160,7 @@ <h3>WAV — <code>EggEncoder.Codecs.Wav</code></h3>
public int SampleRate { get; }
public int BitsPerSample { get; }
public long TotalSamples { get; }
public bool IsFloatFormat { get; }
public int ReadInterleavedSamples(int[] buffer, int maxSamplesPerChannel);
}

Expand All @@ -156,6 +169,7 @@ <h3>WAV — <code>EggEncoder.Codecs.Wav</code></h3>
public static WavWriter Create(string destFilePath, int channels, int sampleRate, int bitsPerSample, long totalFrames);
public void WriteInterleavedSamples(int[] buffer, int frameCount);
}</code></pre>
<p>Supports 8-bit unsigned, 16/24/32-bit signed integer, and (read-only) 32-bit IEEE float PCM.</p>

<h3>WMA — <code>EggEncoder.Codecs.Wma</code></h3>
<pre><code class="language-csharp">public static class WmaDecoder
Expand Down
4 changes: 2 additions & 2 deletions docs/Advanced-Features.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ <h2>Waveform Generation</h2>
<p><code>WaveformCalculator</code> streams — call <code>AddBlock</code> as many times as you like across however many decode callbacks the codec produces; it does not need the full signal in memory at once.</p>

<h2>Sample-Accurate Cutting</h2>
<p><code>NativeEncoder.CutFile</code> (backed by <code>AudioCutter.Cut</code>) trims WAV, FLAC, MP3, and AAC files without re-encoding the untouched region where the format allows frame-boundary slicing:</p>
<p><code>NativeEncoder.CutFile</code> (backed by <code>AudioCutter.Cut</code>) decodes any supported source (WAV, FLAC, MP3, AAC, WMA) and writes any supported destination format (WAV, FLAC, MP3, AAC) without re-encoding the untouched region where the format allows frame-boundary slicing:</p>
<pre><code class="language-csharp">using EggEncoder.Codecs;

bool produced = AudioCutter.Cut(sourcePath, destPath, startInSeconds: 30, endInSeconds: 90);
// false means the requested range was entirely outside the source's duration — no file was written</code></pre>
<p><code>Cut</code> requires the source and destination extensions to match — it trims in place, it does not transcode. Use <code>AudioCutter.Convert</code> (or <code>NativeEncoder.ConvertFile</code>) for format conversion.</p>
<p>Source and destination extensions no longer need to match — <code>Cut</code> can transcode while it trims (e.g. cut a WAV directly to MP3). Use <code>AudioCutter.Convert</code> (or <code>NativeEncoder.ConvertFile</code>) when you just need format conversion with no trimming.</p>

<h2>MOV/MP4 Probing</h2>
<p><code>MovProbe</code> walks the ISO base media container's atom tree directly — no native dependency — to read duration, dimensions, and the video codec's four-character code:</p>
Expand Down
30 changes: 20 additions & 10 deletions docs/Getting-Started.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,30 @@ <h2>Without DI</h2>
IMediaEncoder encoder = new NativeEncoder(NullLogger&lt;NativeEncoder&gt;.Instance);
var probeResult = await encoder.Probe("track.flac");

Console.WriteLine($"{probeResult.SampleRate}Hz, {probeResult.DurationInSeconds}s");</code></pre>
Console.WriteLine($"{probeResult.SampleRate}Hz, {probeResult.DurationSeconds}s");</code></pre>

<h2>Reading a ProbeResult</h2>
<p><code>Probe</code> always returns the same <code>ProbeResult</code> shape:</p>
<p><code>Probe</code> always returns the same <code>ProbeResult</code> shape. Only fields this library can genuinely compute are populated:</p>
<pre><code class="language-csharp">public class ProbeResult
{
public int DurationInSeconds { get; set; }
public int? BitsPerSample { get; set; }
public int? BitRate { get; set; }
public int? SampleRate { get; set; }
public int? Height { get; set; } // set for video containers (MOV/MP4), null for audio
public int? Width { get; set; }
public string? Result { get; set; } // raw JSON detail from the probing engine
public string? WaveformResult { get; set; } // JSON array of normalized peak windows, or null
public string FormatName { get; init; } // e.g. "wav", "flac", "mp3", "aac", "asf", "mov", "mp4"
public string FormatLongName { get; init; } // e.g. "WAV / WAVE (Waveform Audio)"
public long SizeBytes { get; init; }
public double DurationSeconds { get; init; }
public string CodecType { get; init; } // "audio" or "video"
public string? CodecName { get; init; } // e.g. "pcm_s16le", "flac", "mp3", "aac", "wmav2"
public string? CodecLongName { get; init; }
public int? SampleRate { get; init; }
public int? Channels { get; init; }
public string? ChannelLayout { get; init; } // "mono", "stereo", or "{n} channels"
public int? BitsPerSample { get; init; }
public int? BitRate { get; init; }
public bool? IsVariableBitRate { get; init; } // MP3 only
public long? DurationInSamples { get; init; }
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
}</code></pre>
<p>See <a href="Advanced-Features.html">Advanced Features</a> for waveform generation and sample-accurate cutting details, or the <a href="API-Reference.html">API Reference</a> for the full type list.</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ <h2>Fully Native</h2>
<tr><td><strong>External process</strong></td><td class="yes">None</td></tr>
<tr><td><strong>Setup</strong></td><td class="yes">Nothing — DLLs ship in the package</td></tr>
<tr><td><strong>Format coverage</strong></td><td class="yes">AAC, FLAC, MP3, WAV, WMA; MOV/MP4 metadata</td></tr>
<tr><td><strong>Sample-accurate cut</strong></td><td class="yes">WAV, FLAC, MP3, AAC — no re-encode of the untouched region</td></tr>
<tr><td><strong>Sample-accurate cut</strong></td><td class="yes">Any source (incl. WMA) to any dest format — no re-encode of the untouched region</td></tr>
</tbody>
</table>
</div>
Expand Down Expand Up @@ -112,7 +112,7 @@ <h2>Key Features</h2>
<div class="feature-card">
<div class="feature-icon">&#9986;</div>
<div class="feature-title">Sample-Accurate Cutting</div>
<p class="feature-desc">Trim WAV/FLAC/MP3/AAC without a full decode&rarr;encode round trip.</p>
<p class="feature-desc">Trim any supported source, into any supported destination format, without a full decode&rarr;encode round trip on the untouched region.</p>
</div>
<div class="feature-card">
<div class="feature-icon">&#128269;</div>
Expand Down
42 changes: 29 additions & 13 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,29 @@ public interface IMediaEncoder
```csharp
public class ProbeResult
{
public int DurationInSeconds { get; set; }
public int? BitsPerSample { get; set; }
public int? BitRate { get; set; }
public int? SampleRate { get; set; }
public int? Height { get; set; } // set for video containers (MOV/MP4), null for audio
public int? Width { get; set; }
public string? Result { get; set; } // raw JSON detail from the probing engine
public string? WaveformResult { get; set; } // JSON array of normalized peak windows in [0,1], or null
public string FormatName { get; init; } // e.g. "wav", "flac", "mp3", "aac", "asf", "mov", "mp4"
public string FormatLongName { get; init; } // e.g. "WAV / WAVE (Waveform Audio)"
public long SizeBytes { get; init; }
public double DurationSeconds { get; init; }
public string CodecType { get; init; } // "audio" or "video"
public string? CodecName { get; init; } // e.g. "pcm_s16le", "flac", "mp3", "aac", "wmav2"
public string? CodecLongName { get; init; }
public int? SampleRate { get; init; }
public int? Channels { get; init; }
public string? ChannelLayout { get; init; } // "mono", "stereo", or "{n} channels"
public int? BitsPerSample { get; init; }
public int? BitRate { get; init; }
public bool? IsVariableBitRate { get; init; } // MP3 only
public long? DurationInSamples { get; init; }
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
}
```

Only fields this library can genuinely compute are populated -- nothing is filled with ffprobe-style placeholders (e.g. no `probe_score`, no disposition flags).

## Dependency Injection

```csharp
Expand Down Expand Up @@ -96,7 +108,7 @@ public class TranscodeWorker(IServiceProvider serviceProvider)
| WMA | yes | yes | no |
| MOV/MP4 (metadata only) | yes | no | no |

`AudioCutter.Cut` (used by `NativeEncoder.CutFile`) supports WAV, FLAC, MP3, and AAC — sample-accurate, no re-encode of the untouched region. `WmaDecoder` is decode-only and mono-only: real-world WMAv2 stereo encoders (including ffmpeg's) default to mid/side stereo coding, which is unsupported and throws `NotSupportedException`.
`AudioCutter.Cut` (used by `NativeEncoder.CutFile`) decodes any supported source format (WAV, FLAC, MP3, AAC, WMA) and writes to any supported destination format, including WAV, FLAC, MP3, or AAC — it can transcode while trimming; source and destination extensions no longer need to match. Sample-accurate, no re-encode of the untouched region. `WmaDecoder` is decode-only and mono-only: real-world WMAv2 stereo encoders (including ffmpeg's) default to mid/side stereo coding, which is unsupported and throws `NotSupportedException`.

## Codec Reference

Expand Down Expand Up @@ -169,14 +181,17 @@ public sealed class WavReader : IDisposable
public int SampleRate { get; }
public int BitsPerSample { get; }
public long TotalSamples { get; }
public bool IsFloatFormat { get; }
public int ReadInterleavedSamples(int[] buffer, int maxSamplesPerChannel);
// throws NotSupportedException for unsupported bit depths
// 8-bit unsigned, 16/24/32-bit signed, and 32-bit IEEE float PCM are supported;
// throws NotSupportedException for anything else
}

public sealed class WavWriter : IAudioSink, IDisposable
{
public static WavWriter Create(string destFilePath, int channels, int sampleRate, int bitsPerSample, long totalFrames);
public void WriteInterleavedSamples(int[] buffer, int frameCount);
// writes 8-bit unsigned or 16/24/32-bit signed integer PCM
}
```

Expand Down Expand Up @@ -224,9 +239,10 @@ public static class AudioCutter
// dispatches by extension: .wav/.flac/.mp3/.aac/.wma source -> any supported dest extension

public static bool Cut(string sourceFilePath, string destFilePath, int startInSeconds, int endInSeconds);
// requires source and dest extensions to match (trims, does not transcode)
// decodes any supported source (.wav/.flac/.mp3/.aac/.wma) and writes any supported dest
// extension (.wav/.flac/.mp3/.aac) -- source and dest extensions no longer need to match,
// so Cut can transcode while it trims
// returns false (and writes no file) if the requested range is entirely outside the source duration
// supports .wav, .flac, .mp3, .aac
}
```

Expand Down Expand Up @@ -267,7 +283,7 @@ Every codec's `Decode` method streams blocks through the same `AudioBlockDecoded

- **Windows x64 only.** `NativeEncoder` and its codec bindings P/Invoke into bundled `win-x64` binaries. There is no cross-platform build or fallback engine — this library does not run on Linux/macOS/ARM.
- **WMA is decode-only and mono-only.** Genuine stereo WMA files (mid/side coded, the real-world default) throw `NotSupportedException` by design rather than decoding incorrectly.
- **`AudioCutter.Cut` requires matching extensions.** It trims, it does not transcode; mismatched source/dest extensions throw `NotSupportedException`. Use `Convert` (or `ConvertFile`) to change format.
- **`AudioCutter.Cut` can transcode while it trims.** Source and destination extensions no longer need to match -- it decodes any supported source and writes any supported destination format.
- **AAC encoding is mono-only** and restricted to a fixed set of MPEG-4 sample rates (see `AacTables.SampleRates`); other combinations throw `NotSupportedException`.

## License
Expand Down
4 changes: 2 additions & 2 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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` — duration, sample rate, 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), waveform JSON
- `AudioCutter` — format-dispatching convert/cut, used internally by `NativeEncoder`
- `WaveformCalculator` — streams decoded blocks into normalized peak windows

Expand All @@ -67,7 +67,7 @@ WAV, FLAC, MP3, AAC, WMA (decode-only, mono-only); MOV/MP4 metadata probing via

- Native codec binaries are Windows x64 only — there is no cross-platform fallback
- `WmaDecoder` does not support mid/side stereo coding (the default for most real-world WMAv2 encoders) — throws `NotSupportedException` for genuine stereo files
- `AudioCutter.Cut` requires matching source/destination extensions (trims, does not transcode) — use `Convert` for format changes
- `AudioCutter.Cut` decodes any supported source and can write any supported destination format — it can transcode while trimming, source and destination extensions no longer need to match

## Links

Expand Down
Loading