English | 日本語
A complete C# port of WORLD, the vocoder-based speech analysis, manipulation and synthesis system by M. Morise.
Every stage runs without a single managed allocation, so the garbage collector never observes the analysis or the synthesis path.
All scratch memory comes from a native arena, every hot routine is written with unsafe pointers, and the whole library is annotated for Native AOT.
Correctness is not asserted from reading the source: each stage is compared against golden data produced by the original C++ compiled with MSVC.
- Overview
- Requirements
- Installation
- Features
- API Reference
- Limitations
- Notes
- Disclaimer
- Third-Party Licenses
- License
WorldNet ports the eleven source files of WORLD to C#. Fundamental frequency estimation is provided by Dio, Harvest and StoneMask, spectral envelope estimation by CheapTrick, aperiodicity estimation by D4C, waveform generation by Synthesis and by a sequential real-time synthesizer, and low-dimensional representation by the spectral envelope codec. WAV input and output and the analysis parameter file format are ported as well.
The public surface is modern C#. Waveforms and parameters are passed as ReadOnlySpan<double> and Span<double>, options are readonly struct types with init accessors and a Default factory, and each algorithm is a static class. The pointer-based internals are not exposed.
All working memory is served by WorldArena, a bump allocator backed by NativeMemory.AlignedAlloc with 64-byte alignment. The arena is a chain of chunks, so growing it never invalidates a pointer that was already handed out. Scratch buffers are declared once as a Layout method on a dedicated type; a source generator reads that method's signature and emits both the size query and the binding call, so the reported size and the actual consumption cannot drift apart.
The original C++ is not vendored into this repository. The reference harness under reference/ clones WORLD, builds it with MSVC, and dumps the input and output of every stage as raw doubles. The test suite loads those dumps and compares them against the C# results.
| Item | Requirement |
|---|---|
| OS | Windows, Linux or macOS supported by .NET 10 |
| SDK | .NET SDK 10.0 |
| Language | C# 14 or later (LangVersion is set to latest) |
| Unsafe code | Required in the consuming project only when WorldArena.FromNativeMemory is used |
| Reference data | MSVC C++ toolset and Git, required only to regenerate the golden data used by the tests |
- Clone the repository and add
WorldNet/WorldNet.csprojas a project reference, or build it and reference the resulting assembly. - Create a
WorldArenaonce and reuse it for every call. The arena grows on first use and performs no further allocation afterwards. - To run the test suite, generate the reference data first by executing
reference/build.bat, which clones WORLD, builds it with MSVC and writes the dumps toreference/data. - To produce a Native AOT binary of the sample application, run
publish-aot.bat.
Dio estimates the F0 contour from the periodicity of band-passed signals and returns both the temporal positions and the contour. Its Speed option enables the decimation path, which lowers the sampling rate before the search. Harvest estimates the contour from instantaneous frequency candidates and produces a more robust result at a higher cost. StoneMask refines an existing contour using instantaneous frequency and is normally applied to the output of Dio.
Dio, Harvest and StoneMask all reproduce the original bit for bit on the reference waveform, including the decimation path of Dio.
CheapTrick estimates the spectral envelope with an F0-adaptive window and pitch-synchronous smoothing. The FFT size is derived from the sampling rate and the F0 floor through CheapTrick.GetFftSize, and CheapTrick.GetF0Floor returns the inverse relation.
D4C estimates band aperiodicity and includes the D4C LoveTrain stage. Its result agrees with the original to within one unit in the last place; the difference originates in Math.Pow, which is not required to be correctly rounded and does not always return the same value as the MSVC runtime.
Synthesis.Synthesize generates the waveform from the F0 contour, the spectrogram and the aperiodicity in one call. WorldSynthesizer implements the sequential real-time synthesizer, which accepts parameter chunks through AddParameters and produces output through Synthesize while managing an internal ring buffer.
The real-time synthesizer reproduces the original bit for bit. The batch synthesizer agrees to within 64 units in the last place, inherited from the same Math.Pow difference and amplified by the overlap-add accumulation.
The codec converts the spectral envelope and the aperiodicity into a low-dimensional representation on the mel scale and back. Codec.GetNumberOfAperiodicities returns the number of coefficients for a given sampling rate.
Coding the aperiodicity, coding the spectral envelope and decoding the spectral envelope reproduce the original bit for bit. Decoding the aperiodicity agrees to within one unit in the last place, again through Math.Pow.
WorldArena hands out 64-byte aligned blocks from a chain of native chunks. BeginScope records the current position and restores it on disposal, so scratch taken inside a loop is released without freeing anything. FromNativeMemory wraps a buffer the caller already owns; such an arena never grows and throws when exhausted.
Every scratch requirement is declared once as a Layout method that is generic over an allocator. Running it with the measuring allocator yields the required byte count without touching memory, and running it with the arena allocator performs the actual binding. The source generator emits GetRequiredArenaBytes and Bind from that single signature.
The absence of managed allocation is measured, not asserted. GC.GetAllocatedBytesForCurrentThread reports a delta of zero bytes across the full analysis and synthesis pipeline and across the real-time synthesizer.
The test suite compares against dumps produced by the original C++ built with MSVC. The following table records the agreement that the tests enforce.
| Stage | Agreement |
|---|---|
| Ooura FFT, all four transforms, sizes 8 to 4096 | Bit-exact |
| matlabfunctions, common | Bit-exact |
| Dio, including the decimation path | Bit-exact |
| StoneMask, Harvest, CheapTrick | Bit-exact |
| Real-time synthesizer, WAV I/O, parameter file I/O | Bit-exact |
| Coding of aperiodicity and spectral envelope, decoding of spectral envelope | Bit-exact |
| D4C, decoding of aperiodicity | Within 1 ULP |
| Batch synthesis | Within 64 ULP |
The transcendental functions are measured separately. Math.Cos, Math.Sin, Math.Log, Math.Exp and Math.Log10 return exactly the same doubles as the MSVC runtime over the sampled ranges. Math.Pow(10, v) and the squaring v * v differ from the MSVC pow by at most one unit in the last place on fewer than one percent of the sampled inputs, and the remaining tolerances above follow from this.
Beyond equivalence, the suite covers degenerate input such as silence, direct current and white noise, extremely short input, determinism across repeated runs, thread safety with one arena per thread, operation on a caller-supplied arena, and full release of the arena after the pipeline. The suite contains 332 tests and all of them pass.
The tables compare the original C++ compiled with MSVC at /O2 against this port published with Native AOT. Both are compiled ahead of time, so the comparison is like for like. Each table measures both builds back to back in one session, so the ratio is the meaningful quantity while the absolute values move with the machine.
The first table is a fixed record taken on a dedicated workstation. It is not regenerated.
Intel Core i7-1360P under Windows 11, sample application published for an x86-64-v3 baseline, best of 12 runs in milliseconds, analysing the 22050 Hz reference waveform of 17500 samples with a 5 ms frame period.
| Stage | C++ with MSVC | This port with Native AOT | Ratio |
|---|---|---|---|
| Dio | 9.81 | 3.86 | 2.54x |
| StoneMask | 9.40 | 3.03 | 3.10x |
| CheapTrick | 7.84 | 8.66 | 0.91x |
| D4C | 33.28 | 29.00 | 1.15x |
| Synthesis | 9.78 | 8.98 | 1.09x |
| Harvest | 161.49 | 122.36 | 1.32x |
The second table is regenerated by the benchmark workflow on a GitHub Actions runner. The hardware differs from the workstation, which makes it an independent check that the ratios do not depend on one particular machine.
Measured by CI on a GitHub Actions windows-latest runner with AMD EPYC 7763 64-Core Processor. Figures are the best of 20 runs in milliseconds, analysing the 22050 Hz reference waveform of 17500 samples with a 5 ms frame period. Recorded on 2026-07-20 from commit 5cfba94.
| Stage | C++ with MSVC | This port with Native AOT | Ratio |
|---|---|---|---|
| Dio | 9.51 | 4.53 | 2.10x |
| StoneMask | 9.81 | 3.69 | 2.66x |
| CheapTrick | 8.95 | 9.31 | 0.96x |
| D4C | 35.68 | 34.58 | 1.03x |
| Synthesis | 10.97 | 9.98 | 1.10x |
| Harvest | 156.05 | 123.21 | 1.27x |
A ratio above 1.00 means this port is faster than the original C++.
Figures obtained through the just-in-time compiler are deliberately absent. The benchmark repeats each stage only twice, which is not enough for tiered compilation to settle. Disabling tiering with DOTNET_TieredCompilation=0 brings the just-in-time figures back in line with the Native AOT column, which shows that the gap is a warm-up artefact of the measurement rather than a property of the port.
| Member | Description |
|---|---|
Dio.GetSamplesForDio(fs, xLength, framePeriod) |
Returns the number of frames the contour will contain. |
Dio.Estimate(x, fs, option, temporalPositions, f0, arena) |
Estimates the F0 contour and the temporal positions. |
Harvest.GetSamplesForHarvest(fs, xLength, framePeriod) |
Returns the number of frames the contour will contain. |
Harvest.Estimate(x, fs, option, temporalPositions, f0, arena) |
Estimates the F0 contour and the temporal positions. |
StoneMask.Refine(x, fs, temporalPositions, f0, refinedF0, arena) |
Refines an existing contour using instantaneous frequency. |
CheapTrick.GetFftSize(fs, f0Floor) |
Returns the FFT size implied by the sampling rate and the F0 floor. |
CheapTrick.GetF0Floor(fs, fftSize) |
Returns the F0 floor implied by the sampling rate and the FFT size. |
CheapTrick.Estimate(x, fs, option, temporalPositions, f0, spectrogram, arena) |
Estimates the spectral envelope. |
D4C.Estimate(x, fs, option, temporalPositions, f0, fftSize, aperiodicity, arena) |
Estimates the band aperiodicity. |
| Member | Description |
|---|---|
Synthesis.Synthesize(f0, spectrogram, aperiodicity, fftSize, framePeriod, fs, y, arena) |
Generates the whole waveform in one call. |
new WorldSynthesizer(arena, fs, framePeriod, fftSize, bufferSize, numberOfPointers, maxFramesPerAdd) |
Creates the sequential real-time synthesizer. |
WorldSynthesizer.AddParameters(f0, spectrogram, aperiodicity) |
Queues one chunk of parameters and reports whether it was accepted. |
WorldSynthesizer.Synthesize() |
Produces the next block and reports whether output is available. |
WorldSynthesizer.Buffer |
Exposes the current output block. |
WorldSynthesizer.IsLocked |
Reports whether the internal buffer is full. |
WorldSynthesizer.Refresh() |
Clears the queued parameters and the internal state. |
| Member | Description |
|---|---|
Codec.GetNumberOfAperiodicities(fs) |
Returns the number of aperiodicity coefficients. |
Codec.CodeAperiodicity(aperiodicity, f0Length, fs, fftSize, codedAperiodicity) |
Reduces the aperiodicity to coefficients. |
Codec.DecodeAperiodicity(codedAperiodicity, f0Length, fs, fftSize, aperiodicity) |
Restores the aperiodicity from coefficients. |
Codec.CodeSpectralEnvelope(spectrogram, f0Length, fs, fftSize, dimensions, coded) |
Reduces the spectral envelope on the mel scale. |
Codec.DecodeSpectralEnvelope(coded, f0Length, fs, fftSize, dimensions, spectrogram) |
Restores the spectral envelope. |
| Member | Description |
|---|---|
new WorldArena() |
Creates an arena that grows on demand. |
new WorldArena(initialCapacityInBytes) |
Creates an arena with an initial chunk. |
WorldArena.FromNativeMemory(buffer, capacityInBytes) |
Wraps a caller-owned 64-byte aligned buffer. The arena cannot grow. |
WorldArena.GetReservedBytes(count, elementSize) |
Returns the aligned reservation for one allocation. |
WorldArena.EnsureCapacity(byteCount) |
Adds a chunk so that at least this many bytes are free. |
WorldArena.BeginScope() |
Records the position; disposing the returned scope restores it. |
WorldArena.Reset() |
Releases every allocation while keeping the chunks. |
WorldArena.Capacity, WorldArena.Used |
Report the totals across all chunks. |
| Member | Description |
|---|---|
WaveFile.GetLength(path) |
Returns the number of samples in the file. |
WaveFile.Read(path, destination, out sampleRate, out bitDepth) |
Reads a WAV file into a span. |
WaveFile.Write(path, x, sampleRate) |
Writes a 16-bit WAV file. |
ParameterFile.WriteF0, ParameterFile.ReadF0 |
Write and read the F0 contour in the analysis parameter format. |
ParameterFile.WriteSpectralEnvelope, ParameterFile.ReadSpectralEnvelope |
Write and read the spectral envelope. |
ParameterFile.WriteAperiodicity, ParameterFile.ReadAperiodicity |
Write and read the aperiodicity. |
ParameterFile.GetHeaderInformation(path, parameter) |
Reads one header field from a parameter file. |
| Option | Property | Default | Description |
|---|---|---|---|
DioOption |
F0Floor |
71.0 | Lower bound of the search range in hertz. |
DioOption |
F0Ceil |
800.0 | Upper bound of the search range in hertz. |
DioOption |
ChannelsInOctave |
2.0 | Number of band-pass filters per octave. |
DioOption |
FramePeriod |
5.0 | Frame shift in milliseconds. |
DioOption |
Speed |
1 | Decimation ratio from 1 to 12. Higher values are faster and coarser. |
DioOption |
AllowedRange |
0.1 | Threshold used when fixing the contour. |
HarvestOption |
F0Floor |
71.0 | Lower bound of the search range in hertz. |
HarvestOption |
F0Ceil |
800.0 | Upper bound of the search range in hertz. |
HarvestOption |
FramePeriod |
5.0 | Frame shift in milliseconds. |
CheapTrickOption |
Q1 |
-0.15 | Parameter of the spectral recovery. |
CheapTrickOption |
F0Floor |
71.0 | F0 floor that determines the FFT size. |
CheapTrickOption |
FftSize |
derived | Computed from the sampling rate by CheapTrickOption.Create. |
D4COption |
Threshold |
0.85 | Threshold of the D4C LoveTrain stage. |
DioOption.Default, HarvestOption.Default and D4COption.Default return the values above. CheapTrickOption.Create(fs) is used instead because the FFT size depends on the sampling rate. All four are readonly struct types with init accessors, so a modified copy is produced with a with expression.
- The batch synthesizer agrees with the original to within 64 units in the last place rather than exactly. D4C and the decoding of aperiodicity agree to within one unit in the last place. Both follow from
Math.Pow, which neither the .NET runtime nor the MSVC runtime is required to round correctly. - Bit-exactness has been verified against WORLD compiled with MSVC on Windows x64. Other compilers, other runtimes and other architectures may round the transcendental functions differently, and the agreement above is not claimed for them.
WorldArenais not thread-safe. Concurrent analysis requires one arena per thread, which the test suite exercises.- An arena created by
FromNativeMemorycannot grow. Determine the size by running once with a growing arena and readingUsed, or by summing theGetRequiredArenaBytesof the stages you invoke. - Running the comparison tests requires the reference data. Without
reference/datathose tests cannot execute. - The sample application under
WorldNet.Examplesreads and writes files and therefore allocates managed memory. The zero-allocation guarantee applies to the library.
- Processing cost: Harvest is by far the most expensive stage, followed by D4C. Dio with a higher
Speedis the cheapest way to obtain a contour. The figures in the performance table are indicative of one machine and vary between runs.WorldNet.Examples bench <input.wav>reports the per-stage timings on your own hardware, and the reference harness prints the same figures for the original C++ whenWORLD_BENCH_ONLYis set. - Arena reuse: the arena grows during the first call and performs no further allocation. Creating a new arena for every call defeats the purpose and reintroduces native allocation.
- Determinism: the pipeline produces identical output across repeated runs. The pseudo-random generator used by D4C and by the synthesizer is the xorshift generator of the original, reseeded to the same state, and it reproduces the original sequence and its final state exactly.
- Scratch layout: a type marked with
[ScratchLayout]must expose aLayoutmethod generic overIScratchAllocator. The generator emitsGetRequiredArenaBytesandBindwith a matching parameter list, and skips whichever of the two the type already declares. - Native AOT: the library sets
IsAotCompatible, which enables the trim, single-file and AOT analyzers.publish-aot.batpublishes the sample application forwin-x64and requires the MSVC toolset for the native linker. - Regenerating reference data:
reference/build.batclones WORLD intoreference/world-src, builds it, and writes the dumps. Both directories are excluded from version control.
This library is published under the MIT License.
This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement.
The author accepts no liability for any damage arising from the use of or the inability to use this library. Use it at your own risk.
WorldNet is a derivative work of the software below. The full license texts are stored in the repository under .github/LICENSE/WORLD.txt and .github/LICENSE/OouraFFT.txt.
WORLD is distributed under the modified BSD license, which requires that redistributions of source code retain its copyright notice, the list of conditions and the disclaimer. Those files carry that text unmodified. No third-party source code is vendored into this repository, and the reference harness downloads WORLD on demand.
| Software | Purpose | License | Copyright |
|---|---|---|---|
| WORLD | Origin of every ported algorithm and of the reference implementation used for verification | Modified BSD License | Copyright (c) 2010 M. Morise |
| Ooura FFT | Fast Fourier transform carried by WORLD and ported here | Free use permitted by the author | Copyright Takuya OOURA, 1996-2001 |