Documentation
¶
Overview ¶
Package voiceedit provides in-place byte patching for FZV and FZF voice parameters. Patches are applied atomically: the file is read, modified in memory, and written back via fileutil.WriteAtomic.
Index ¶
- Constants
- Variables
- func ApplyToFZFSlotBytes(data []byte, slot int, patches []Patch) error
- func ApplyToFZFVoice(path string, voiceName string, patches []Patch) error
- func ApplyToFZV(path string, patches []Patch) error
- func ApplyToFZVBytes(data []byte, patches []Patch) error
- func ValidateByte(name string, val, lo, hi int) error
- func ValidateWaveform(val int) error
- func WaveformIndex(name string) (int, bool)
- type Patch
- func BuildDCAPatches(sustain, end int, rates, stops [disk.EnvelopeStages]int, ...) ([]Patch, error)
- func BuildDCFPatches(sustain, end int, rates, stops [disk.EnvelopeStages]int, ...) ([]Patch, error)
- func BuildFilterPatches(cutoff, resonance int) ([]Patch, error)
- func BuildKeyRangePatch(keyLow, keyHigh, root int) ([]Patch, error)
- func BuildLFOPatches(wave, rate, delay, attack, pitch, amp, filter, q int, origLFOName uint8) ([]Patch, error)
- func BuildLoopAttrPatch(index, xf, tm int) ([]Patch, error)
- func BuildLoopPatch(index int, start, end uint32, origSt, origEd uint32) ([]Patch, error)
- func BuildLoopSelectPatch(sustain, release int) ([]Patch, error)
- func BuildModulationPatches(...) ([]Patch, error)
- func BuildNamePatch(name string) ([]Patch, error)
- func BuildPlaybackModePatch(mode string) ([]Patch, error)
- func BuildTunePatch(tune int) ([]Patch, error)
Constants ¶
const ( // Unchanged is the sentinel value for parameters that should not be // modified. It must be outside all valid parameter ranges. Unchanged = -1000 )
Variables ¶
var ( ErrNotVoiceFile = errors.New("voiceedit: file does not appear to be a voice file") ErrUnsupportedPatch = errors.New("voiceedit: unsupported patch size") ErrFileTooSmall = errors.New("voiceedit: file too small") )
Sentinel errors. Wrap with %w; match with errors.Is, not on message text.
Functions ¶
func ApplyToFZFSlotBytes ¶ added in v0.5.0
ApplyToFZFSlotBytes applies patches to one voice slot's header inside FZF full dump bytes, in place: the in-memory, slot-addressed sibling of ApplyToFZFVoice. Key-range patches mirror into every bank site that references the slot, exactly as the file-path edit does.
func ApplyToFZFVoice ¶
ApplyToFZFVoice reads the FZF file at path, locates the voice by name, applies patches to that voice's header, and writes back atomically. It takes the same cross-process lock as ApplyToFZV.
func ApplyToFZV ¶
ApplyToFZV reads the FZV file at path, applies patches to the voice header, and writes the result back atomically. Offsets are relative to the start of the voice header (byte 0 of the file). The read-modify-write sequence is serialised across processes via fileutil.WithFileLock so concurrent writers can't lose each other's edits.
func ApplyToFZVBytes ¶ added in v0.5.0
ApplyToFZVBytes applies patches to FZV voice file bytes in place: the same validation and patching as ApplyToFZV with no filesystem.
func ValidateByte ¶
ValidateByte checks that val is within the given range.
func ValidateWaveform ¶
ValidateWaveform checks that val is a valid LFO waveform index.
func WaveformIndex ¶
WaveformIndex returns the index for the named LFO waveform and whether it was found.
Types ¶
type Patch ¶
type Patch struct {
Offset int
Size int // 1 or 2 (ignored when Bytes is set)
Value uint16 // ignored when Bytes is set
Bytes []byte // multi-byte payload; when non-nil takes precedence over Size/Value
}
Patch describes a modification to a voice header. When Bytes is non-nil it is written verbatim at Offset and Size/Value are ignored. Otherwise Size (1 or 2) bytes from Value are written as little-endian.
func BuildDCAPatches ¶
func BuildDCAPatches(sustain, end int, rates, stops [disk.EnvelopeStages]int, origRates [disk.EnvelopeStages]uint8) ([]Patch, error)
BuildDCAPatches creates patches for DCA envelope parameters. Pass Unchanged for sustain/end, or for an individual rate/level element, to leave it alone. Rates and levels use the hardware display scale (0 to 99). origRates carries the original rate bytes so the sign bit (envelope direction) survives a magnitude-only change.
func BuildDCFPatches ¶
func BuildDCFPatches(sustain, end int, rates, stops [disk.EnvelopeStages]int, origRates [disk.EnvelopeStages]uint8) ([]Patch, error)
BuildDCFPatches creates patches for DCF envelope parameters, under the same conventions as BuildDCAPatches: Unchanged skips a field or element, rates and levels use the 0 to 99 display scale, and origRates preserves the envelope-direction sign bit.
func BuildFilterPatches ¶
BuildFilterPatches creates patches for filter cutoff and resonance. Both use the hardware display scale: cutoff 0 to 127, resonance 0 to 127. The resonance byte is stored directly (the full byte is used by the hardware, not just the upper nibble as the spec suggests). Pass Unchanged to leave a parameter unmodified.
func BuildKeyRangePatch ¶
BuildKeyRangePatch creates patches for the key range (key-low, key-high, root). Each value is a MIDI note number (0 to 127). Pass Unchanged for any parameter to leave it unmodified.
func BuildLFOPatches ¶
func BuildLFOPatches(wave, rate, delay, attack, pitch, amp, filter, q int, origLFOName uint8) ([]Patch, error)
BuildLFOPatches creates patches for LFO parameters. Pass Unchanged for any parameter to leave it unmodified. origLFOName is the current value of the lfo_name byte (spec offset 0x9E): bits 0-6 hold the waveform index, bit 7 is the phase-sync flag. It is used to preserve the phase-sync flag when only the waveform index changes; see disk.LFOWaveformMask / LFOPhaseFlag.
func BuildLoopAttrPatch ¶ added in v0.5.0
BuildLoopAttrPatch creates patches for loop index's cross-fade and multi-loop time attributes (loopxf and looptm, spec §2-1). Both are 16-bit little-endian entries; xf ranges 0 to disk.MaxLoopXF (0 disables the cross-fade) and tm ranges 0 to disk.MaxLoopTm (fresh voices carry 0, so 0 passes even though the spec's lower bound is 1).
func BuildLoopPatch ¶ added in v0.5.0
BuildLoopPatch creates patches setting loop index's start and end sample addresses. The spec reserves flag bits inside both cells (the loop-fine byte in the upper 8 bits of loopst, the skip flag in the MSB of looped); origSt and origEd carry the current cell values so those bits survive the write. start must be below end, and end must fit the 24-bit loopst address space so the pair stays addressable.
func BuildLoopSelectPatch ¶ added in v0.5.0
BuildLoopSelectPatch creates patches for the sustain and release loop designations (loop_sus, loop_end). Valid values are 0 to 7 for a loop index, or disk.NoSustainLoop (8) for none.
func BuildModulationPatches ¶
func BuildModulationPatches(dcaKF, dcaRS, dcfKF, dcfRS, velDCAKF, velDCFKF, velDCQKF, velDCARS, velDCFRS int) ([]Patch, error)
BuildModulationPatches creates patches for modulation routing parameters. KF parameters (dcaKF, dcaRS, dcfKF, dcfRS) use the hardware display scale (-15 to +15). All five velocity-modulation parameters (velDCAKF, velDCFKF, velDCQKF, velDCARS, velDCFRS) are signed -127 to +127 per spec §2-1 and are stored as two's-complement bytes. Pass Unchanged for any parameter to leave it unmodified.
func BuildNamePatch ¶
BuildNamePatch creates a patch for the voice name (max 12 characters). The 12-byte padded name is followed by the two zero bytes the FZ voice header layout requires.
Case is stored verbatim. The FZ-1 supports mixed-case names (factory disks carry "All Voices"), and upper-casing on commit mutates a field the user only tabbed through. findVoiceIndex matches case-insensitively, so lookups still work either way.
func BuildPlaybackModePatch ¶
BuildPlaybackModePatch creates a patch for the voice playback mode. The mode name is matched case-insensitively. Valid modes: Normal, Reverse, Cue, Synth.
func BuildTunePatch ¶
BuildTunePatch creates a patch for the voice tuning (DCP field). The value is in 1/256-semitone units and stored as a uint16 (two's complement).