Skip to content

Add Renoise instrument (XRNI) format support (read + write)#149

Open
douglas-carmichael wants to merge 8 commits into
git-moss:mainfrom
douglas-carmichael:add-renoise-xrni-support
Open

Add Renoise instrument (XRNI) format support (read + write)#149
douglas-carmichael wants to merge 8 commits into
git-moss:mainfrom
douglas-carmichael:add-renoise-xrni-support

Conversation

@douglas-carmichael

Copy link
Copy Markdown
Contributor

Summary

Adds read and write support for the Renoise instrument format (.xrni) — a ZIP archive containing an Instrument.xml description plus FLAC samples under SampleData/. Created files use document version 34 (Renoise 3.5); document versions 24–34 can be read.

What is translated

  • Key & velocity mapping, root note, tuning (transpose + fine-tune)
  • Volume (linear gain) and panning
  • Loops (off / forward / backward / ping-pong)
  • Amplitude envelope (AHDSR volume modulation device)
  • Per-sample sampler filter — type (LP/HP/BP/band-stop), cutoff and resonance, plus a cutoff envelope
  • Pitch envelope
  • Round-robins (the Cycle/Random keyzone overlapping mode)
  • The instrument comment ↔ description metadata

The filter is written as the native sampler filter inside the modulation set, including the required SampleMixerModulationDevice (which backs the modulation "input" view) so the instruments open correctly in Renoise rather than crashing the editor.

Also included

  • Fix: FLAC or OGG samples stored inside a ZIP archive could fail to decompress with a mark/reset not supported error. AudioFileUtils.decompressToWav now wraps non-markable streams in a BufferedInputStream. This also benefits the existing discoDSP Bliss and DecentSampler library import.

Implementation

  • New package format/renoise: RenoiseDetector, RenoiseCreator, RenoiseTag, RenoiseValueConverter, RenoiseFilterType.
  • Registered the detector and creator in ConverterBackend.
  • Documented in documentation/README-FORMATS.md; CHANGELOG.md updated under 18.2.0.

Known limitations (documented)

  • 32-bit FLAC source samples cannot be transcoded (the bundled FLAC decoder handles ≤24-bit), so such samples are skipped with an error.
  • The cutoff-frequency↔Hz curve is an approximation (Renoise's normalized-cutoff to frequency curve is internal); the filter type, resonance and envelopes are exact.

Verification

  • Reads the Renoise 3.5 factory instruments without errors.
  • Round-trips with mapping, tuning, loops, panning and filter preserved; the generated Instrument.xml validates against the official RenoiseInstrument34.xsd.
  • Confirmed to load in Renoise 3.5.4.

Adds a detector and creator for the Renoise instrument format (.xrni), a
ZIP archive containing an Instrument.xml description and FLAC samples in a
SampleData folder. Created files use document version 34 (Renoise 3.5);
document versions 24-34 can be read.

Translated: key/velocity mapping, root note, tuning, volume, panning,
loops, the amplitude envelope, a per-sample sampler filter (type, cutoff
and resonance with a cutoff envelope), a pitch envelope and round-robins.
The filter is written as the native sampler filter, including the required
SampleMixerModulationDevice so the instruments load in Renoise.

Also fixes a latent issue where FLAC or OGG samples stored inside a ZIP
archive could fail to decompress with a "mark/reset not supported" error;
AudioFileUtils.decompressToWav now wraps non-markable streams (this also
benefits the existing discoDSP Bliss and DecentSampler library import).

Verified against the Renoise 3.5 factory instruments (read, round-trip and
load in Renoise) and the generated Instrument.xml validates against the
official RenoiseInstrument34 schema.
Renoise has no loop cross-fade parameter, so by default loops are written
exactly (faithful). When the loop cross-fade processing option is enabled,
the cross-fade is now baked into the looped sample audio so that loops with
a discontinuity (e.g. some SoundFonts) wrap seamlessly.
@douglas-carmichael

Copy link
Copy Markdown
Contributor Author

Added a second commit with an opt-in loop cross-fade, kept separate so it's easy to judge on its own.

Default is unchanged and fully faithful — loops are written exactly as they come from the source. Renoise has no loop cross-fade parameter, so only when the existing "loop cross-fade" processing option is enabled does the Renoise creator bake the cross-fade into the looped sample audio.

Motivation: some sources define loops with a built-in discontinuity (e.g. a SoundFont whose loop-offset generators land the loop on a non-matching point). That's reproduced faithfully by default, but it clicks in Renoise's sample-accurate looping; the option gives a way to get seamless loops without changing the faithful default. Left entirely optional for your call.

The written instrument structure is valid for document version 33 (Renoise
3.3) and validates against the v33 and v34 schemas alike. Using 33 lets the
created files load in newer Renoise versions as well as in the Renoise Redux
plug-in, whose own instruments use document version 33.
Some samples trigger an ArrayIndexOutOfBoundsException in the bundled FLAC
encoder library (seen e.g. with a 24-bit auto-sampled Logic EXS24 instrument).
Instead of aborting the whole instrument, such a sample is now stored as an
uncompressed WAV file - which Renoise reads as well - while the remaining
samples stay FLAC encoded.
Renoise attaches a filter to virtually every instrument through its
always-present mixer device. When that filter is parked at the transparent
extreme - a high-pass at the bottom of its range or a low-pass at the top -
it removes nothing audible, but writing it out as an active filter is at
best pointless and at worst harmful: the Waldorf Quantum/Iridium muted a
patch whose high-pass was parked at its lowest cutoff, so the converted
instrument produced no sound at all.

The detector now returns no filter for such transparent settings (high-pass
cutoff <= 40 Hz, low-pass cutoff >= 18 kHz). Because this happens while
reading the Renoise file, every output format benefits - verified that the
transparent filter is dropped for Waldorf, Akai MPC, SFZ, tx16wx,
DecentSampler and others, while an audible filter (e.g. a 632 Hz low-pass)
is still written correctly.
@douglas-carmichael

Copy link
Copy Markdown
Contributor Author

A couple of notes for merging:

1. Complements #150. Reading an XRNI decompresses its (stereo FLAC) samples, which trips a pre-existing bug that halved stereo compressed samples on decompress — fixed separately in #150. This PR stands on its own, but converting XRNIs to uncompressed destinations only yields full-length samples with #150 merged as well, so the two go well together.

2. Small overlap with #150 in AudioFileUtils.decompressToWav. Both PRs touch that method but on different lines — this PR adds the mark/reset stream wrap near the top of the method, while #150 fixes the frame-count line ~20 lines below. Each is clean against main individually; whichever lands second may need a trivial one-line rebase. Their CHANGELOG entries also sit under different version headings (18.2.0 here, 18.1.2 in #150) — versioning is of course your call.

douglas-carmichael and others added 3 commits June 21, 2026 17:01
… a corrupt value

A zero (or sub-0.06 second) attack, decay or release was converted to an
out-of-range QPAT parameter value - exactly zero produced negative infinity -
and written into the preset as a corrupt float. On the Quantum/Iridium this
could cause a click at the start of every note. Such times are now clamped to
the shortest representable value [0..1].
A Renoise amplitude envelope with no attack and no decay but a sustain below
full level (e.g. attack 0, decay 0, sustain 51%) was written verbatim. On the
Quantum/Iridium that snaps the amplifier to the 100% attack peak and then
instantly drops to the sustain level at the start of every note, which is
audible as a pop. Such an envelope is meant to be flat at the sustain level, so
it is now written with a full sustain and the sustain level is folded into the
per-zone gain instead - the steady-state loudness is identical but there is no
longer an instantaneous level step.
@git-moss

Copy link
Copy Markdown
Owner

Many thanks! Can you point me to test files? Also for the Polyend format? PM me on KVR.

@git-moss

Copy link
Copy Markdown
Owner

Thanks! I you can remove it again if you want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants