Impulse Tracker (.it) import corrupts volume-column pan / tone-portamento / vibrato

Renoise version tested: 3.5.4 (macOS)
Component: Impulse Tracker (.it) file import
Severity: Medium — silent data corruption on import (wrong playback + wrong pattern data)

Summary

When Renoise imports an .it module, set-volume values in the IT volume column are
imported correctly, and volume-column slide commands are silently dropped — but volume-column
set-panning, tone-portamento (Gx), and vibrato (Hx) are imported as meaningless
volume-column slide commands
. The value written is a raw-byte artifact that is unrelated to
the original command, so the song plays incorrectly and the pattern data is corrupted on those
cells.

IT volume-column commands and how each is imported

The IT volume column stores one command per cell as a single byte (per the IT format spec):

IT volume-column command byte range Renoise 3.5.4 import
set volume 0–64 :white_check_mark: correct (→ volume column)
fine volume slide up/down (a/b) 65–84 :warning: silently dropped
volume slide up/down (c/d) 85–104 :warning: silently dropped
pitch slide down/up (e/f) 105–124 :warning: silently dropped
set panning (p) 128–192 :cross_mark: corrupted (see below)
tone portamento (Gx) 193–202 :cross_mark: corrupted
vibrato (Hx) 203–212 :cross_mark: corrupted

Actual behavior

For the corrupted ranges, Renoise writes a volume-column command (shown/saved as Ixx/Oxx)
whose value is independent of which effect it was. The value is exactly:

renoise_volume_value = (raw_IT_volume_column_byte − 159) mod 32

i.e. the raw IT byte is passed straight through without being decoded. The tell-tale sign is
that tone-portamento and vibrato — two completely unrelated effects — produce one continuous
value sequence
as the underlying byte increments:

IT vol-col command raw byte Renoise <Volume> (saved .xrns)
G0 tone-portamento 193 I2
G9 202 IB
H0 vibrato 203 IC
H9 212 O5
p set-pan, byte 160 160 I1
p set-pan, byte 192 192 I1 (wraps mod 32)
p set-pan, bytes 128–159 128–159 (dropped, empty)

The continuous byte→value mapping regardless of command meaning indicates the IT volume-column
byte is being copied into Renoise’s volume column for these byte ranges instead of being
decoded into the corresponding command.

Expected behavior

  • Volume-column set-panning (p) → the note’s panning column.
  • Volume-column tone-portamento (Gx) / vibrato (Hx) → the corresponding effect-column
    command (Renoise’s glide 0G / vibrato 0V), since Renoise’s volume column has no native
    representation for them.

(At minimum, these should never produce values unrelated to the source command.)

Steps to reproduce

  1. Open an .it module that uses the volume column for panning, tone-portamento, or
    vibrato. A minimal purpose-built test module is available: voltest.it — 98 rows, one IT
    volume-column command per row, sweeping every command (set-vol, a/b/c/d/e/f
    slides, pan, porta, vibrato) across its parameter range, each on a held note so the result is
    isolated per row.
  2. In Renoise: File → Openvoltest.it.
  3. Inspect the volume column of rows ~60–88 in the pattern editor (or Save Song As .xrns
    and inspect the <Volume> entries in Song.xml).

Rows 0–59 (the slide commands) come in empty; rows 60–88 (pan/porta/vibrato) come in as the
incorrect I/O values described above; rows 89–97 (set-volume) are correct.

Impact

  • Panning, tone-portamento, and vibrato authored in the IT volume column are lost and replaced
    with incorrect volume slides → audible playback differences and corrupted pattern data, with
    no warning to the user.
  • Affects both classic Impulse Tracker .it files and OpenMPT/ModPlug-saved .it files, since the issue is inherent to decoding the IT volume-column format.

Notes

The set-volume and slide handling are fine (the latter merely unsupported/dropped, which is at
least not wrong). Only the pan/portamento/vibrato volume-column ranges are corrupted.

2 Likes