webcore

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Overview

Package webcore is the Web UI's core session: the document state the browser edits, behind the coarse boundary the front end speaks. It owns canonical state and validation; the WASM glue in web/wasm only translates calls. Errors carry stable codes that cross the boundary as structured envelopes, mirrored by the TypeScript contract in web/app/src/boundary.

Index

Constants

View Source
const (
	ExtractFZV = "fzv"
	ExtractWAV = "wav"
)

Extract formats for ExtractVoiceSlot.

View Source
const (
	VerdictFits    = "fits"
	VerdictSplits  = "splits"
	VerdictWontFit = "wont-fit"

	ReasonSampleMemory = "sample-memory"
	ReasonDiskRoom     = "disk-room"
	ReasonVoiceLimit   = "voice-limit"
)

Import estimate verdicts and refusal reasons. The verdict names what the matching import call would do with the same files, rate, and channel; the reason narrows a refusal to the constraint that bites first.

View Source
const (
	ChannelLeft  = "left"
	ChannelRight = "right"
	ChannelMix   = "mix"
)

Channel names accepted by ImportWAV, mirrored by the TypeScript contract.

Variables

This section is empty.

Functions

This section is empty.

Types

type AreaSnapshot

type AreaSnapshot struct {
	VoiceSlot   int    `json:"voiceSlot"`
	VoiceName   string `json:"voiceName"`
	KeyLow      int    `json:"keyLow"`
	KeyHigh     int    `json:"keyHigh"`
	Root        int    `json:"root"`
	VelLow      int    `json:"velLow"`
	VelHigh     int    `json:"velHigh"`
	MidiChannel int    `json:"midiChannel"`
	Output      int    `json:"output"`
	OutputLabel string `json:"outputLabel"`
	Volume      int    `json:"volume"`
}

AreaSnapshot is one key split in a bank: the fields R12 makes editable. MidiChannel speaks the display scale (1 to 16) like the CLI; Output carries the raw gchn bitmask plus its rendered label.

type Audition

type Audition struct {
	SampleRate int     `json:"sampleRate"`
	Root       int     `json:"root"`
	PCM        []int16 `json:"-"`
}

Audition carries one voice's PCM for the preview path (R20 to R22): the declared sample rate, the root key the pitch maths centres on, and the decoded samples. The PCM crosses the boundary as a transferable; it stays out of JSON.

type BankSnapshot

type BankSnapshot struct {
	Name  string         `json:"name"`
	Areas []AreaSnapshot `json:"areas"`
}

BankSnapshot is one bank sector: its name and its areas in vp order.

type DiskSnapshot

type DiskSnapshot struct {
	Label         string              `json:"label"`
	UsedBytes     int                 `json:"usedBytes"`
	CapacityBytes int                 `json:"capacityBytes"`
	Disks         int                 `json:"disks"`
	MissingDisk   int                 `json:"missingDisk,omitempty"`
	Files         []FileSnapshot      `json:"files"`
	Instrument    *InstrumentSnapshot `json:"instrument,omitempty"`
}

DiskSnapshot describes the open disk set for the UI. Instrument is the parsed full dump when the disk carries one. Disks is 1 or 2: a split instrument spans a pair and the capacity figures cover both (R23). MissingDisk names the absent half when one image of a pair was opened alone (R5): 2 means "this is disk 1, disk 2 is missing".

type EffectsSnapshot

type EffectsSnapshot struct {
	BendRange int     `json:"bendRange"`
	Matrix    [][]int `json:"matrix"`
}

EffectsSnapshot is the instrument's global effect block (R19): pitch bend range plus the 3 by 7 controller modulation matrix. Rows are mod wheel, foot pedal, aftertouch; columns are LFO pitch, LFO amp, LFO filter, LFO resonance, DCA offset, DCF offset, and DCQ offset, in R19's order.

type EnvelopeSnapshot

type EnvelopeSnapshot struct {
	Sustain int   `json:"sustain"`
	End     int   `json:"end"`
	Rates   []int `json:"rates"`
	Stops   []int `json:"stops"`
}

EnvelopeSnapshot carries one envelope in the hardware display scale (0 to 99 for rates and stops), the same units the setters accept.

type Error

type Error struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	// Item names the offending file, voice, or field where one
	// exists, the way the spec's contract section promises.
	Item string `json:"item,omitempty"`
	// Detail carries the technical reason for a bug report; the
	// message above stays the one a musician reads.
	Detail string `json:"detail,omitempty"`
}

Error is a boundary error envelope: a stable machine code plus a human message. It never wraps a panic; the glue recovers those.

func (*Error) Error

func (e *Error) Error() string

type FileSnapshot

type FileSnapshot struct {
	Name      string         `json:"name"`
	Type      string         `json:"type"`
	SizeBytes int            `json:"sizeBytes"`
	Params    map[string]any `json:"params,omitempty"`
	Voice     *VoiceDetail   `json:"voice,omitempty"`
}

FileSnapshot describes one directory entry for the UI. Voice files carry their editable parameter values keyed by schema field ID, plus the bespoke-editor detail (waveform extent, loops, envelopes).

type ImportEstimate

type ImportEstimate struct {
	// Bytes is the document's full dump growth: sector padded audio
	// plus any voice header sectors the new slots need, plus the
	// dump base when the import brings the instrument into being.
	Bytes int `json:"bytes"`
	// Seconds is the batch's play time at the target rate.
	Seconds float64 `json:"seconds"`
	// RoomSeconds is how much more mono audio the document holds at
	// the target rate within its current disk count, bounded by real
	// free sectors and the sampler's sample memory. A larger import
	// may still land by splitting; the verdict reports that.
	RoomSeconds float64 `json:"roomSeconds"`
	Verdict     string  `json:"verdict"`
	// Reason narrows VerdictWontFit; empty otherwise.
	Reason string `json:"reason,omitempty"`
	// AnyStereo is set when at least one file carries two channels,
	// which is what makes the left, right, or mix question worth
	// asking.
	AnyStereo bool `json:"anyStereo"`
	// OverCapFile names the first file over the sampler's memory at
	// the target rate, with its play time in FileSeconds and the
	// longest play time that memory loads at this rate in CapSeconds.
	OverCapFile string  `json:"overCapFile,omitempty"`
	FileSeconds float64 `json:"fileSeconds,omitempty"`
	CapSeconds  float64 `json:"capSeconds,omitempty"`
	// FitsAtRates lists the rates at which the whole batch would be
	// accepted, for a refusal's way out; empty when none would.
	FitsAtRates []int `json:"fitsAtRates,omitempty"`
}

ImportEstimate is the authoritative pre-flight answer for a WAV import: what the conversion would add and whether it can land. It shares fzutil.ResampledLen with the conversion itself, so the figures cannot drift from what an accepted import then produces.

type InstrumentSnapshot

type InstrumentSnapshot struct {
	FileName string            `json:"fileName"`
	Banks    []BankSnapshot    `json:"banks"`
	Voices   []InstrumentVoice `json:"voices"`
	Effects  *EffectsSnapshot  `json:"effects,omitempty"`
}

InstrumentSnapshot is the disk's full dump as the UI edits it: banks of areas plus the voice list with unreferenced voices flagged.

type InstrumentVoice

type InstrumentVoice struct {
	Slot       int            `json:"slot"`
	Name       string         `json:"name"`
	Referenced bool           `json:"referenced"`
	Params     map[string]any `json:"params,omitempty"`
	Voice      *VoiceDetail   `json:"voice,omitempty"`
	// SharesAudio marks a slot whose audio is an earlier slot's: the
	// velocity switch clones a voice header rather than the samples,
	// so it costs no disk space. The UI says so instead of charging
	// the user twice for one sound.
	SharesAudio bool `json:"sharesAudio,omitempty"`
	// AudioKey identifies the samples this slot plays: where they
	// start and how many there are. It changes only when the audio
	// changes, so a caller can cache decoded PCM and peaks against it
	// instead of re-decoding on every parameter edit.
	AudioKey string `json:"audioKey,omitempty"`
}

InstrumentVoice is one audible voice slot with its R13 flag, plus the editable surface the voice editor renders: schema params and the bespoke detail (loops, envelopes), parsed from the slot's header bytes. Loop positions arrive voice-relative; the dump's absolute addresses rebase here, mirroring the slot setters.

type LoopSnapshot

type LoopSnapshot struct {
	Start int `json:"start"`
	End   int `json:"end"`
	XF    int `json:"xf"`
	Tm    int `json:"tm"`
}

LoopSnapshot is one row of a voice's 8-loop table, in sample frames with the spec's flag bits masked.

type SFZResult

type SFZResult struct {
	Snapshot Snapshot `json:"snapshot"`
	Rate     int      `json:"rate"`
}

SFZResult reports an import that ran the conversion pipeline: the new document snapshot plus the sample rate the core actually used, which fit to disk may have stepped down from the request (R9). The UI warns when Rate is below the asked rate.

type SchemaField

type SchemaField struct {
	ID      string   `json:"id"`
	Label   string   `json:"label"`
	Group   string   `json:"group"`
	Kind    string   `json:"kind"`
	Min     int      `json:"min"`
	Max     int      `json:"max"`
	Options []string `json:"options,omitempty"`
}

SchemaField declares one editable voice parameter: the UI renders a control from this declaration and nothing else (R14). Kind is one of "knob", "stepper", "note" (a stepper displayed as a note name), or "select". Numeric kinds carry Min and Max; select carries Options.

func Schema

func Schema() []SchemaField

Schema returns the editable parameter schema the UI renders from.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session holds one open disk set. Not safe for concurrent use; the worker serialises calls.

func NewSession

func NewSession() *Session

NewSession returns an empty session with no disk open.

func (*Session) AddArea

func (s *Session) AddArea(bank, voiceSlot int) (Snapshot, *Error)

AddArea appends an area playing an existing voice slot, with the container's default full key and velocity ranges, the voice's own root key, and every generator as its output.

func (*Session) AddBank

func (s *Session) AddBank(data []byte, slot int) (Snapshot, *Error)

AddBank places a .fzb per the placement matrix (R7). With an instrument open, the dump's bank sector lands at bank index slot (replacing that bank's mapping, or appending when slot is the first unused index); voice slots keep their meaning by index, the hardware bank load semantics. Without an instrument the .fzb itself becomes the instrument: bank plus voice headers, audio to be imported later.

func (*Session) AddVoice

func (s *Session) AddVoice(fzvData []byte) (Snapshot, *Error)

AddVoice places an .fzv per the placement matrix (R7): with no disk open it becomes a one voice instrument on a fresh disk; with no instrument it becomes a one voice instrument on the open disk; otherwise it joins the instrument's voice list. The join lands with a fresh area carrying the voice's own key range: the FZ format sizes a dump's voice area from bank references, so a voice no bank references would vanish on the next parse.

func (*Session) AuditionPCM

func (s *Session) AuditionPCM(fileName string) (*Audition, *Error)

AuditionPCM decodes a voice file's audio for preview, the same code path as the CLI's fzv extract.

func (*Session) AuditionSlot

func (s *Session) AuditionSlot(slot int) (*Audition, *Error)

AuditionSlot decodes an instrument voice slot's audio for preview, through the same unpack path as the CLI's fzf unpack.

func (*Session) BeginGesture

func (s *Session) BeginGesture()

BeginGesture opens an undo bracket: edits until CommitGesture coalesce into one history entry.

func (*Session) Close

func (s *Session) Close() (Snapshot, *Error)

Close discards the open document and its history: back to the start state, nothing written (N3: durable output is the exported file, and the UI confirms unexported changes first).

func (*Session) CommitGesture

func (s *Session) CommitGesture() bool

CommitGesture closes the bracket, landing the coalesced entry. It reports whether the gesture changed anything: a press and release with no movement lands no entry, and the UI must not call such a document dirty.

func (*Session) DeleteArea

func (s *Session) DeleteArea(bank, area int) (Snapshot, *Error)

DeleteArea removes an area, shifting later areas down (R11), and gives its voice slot back to the voice area with it: the format sizes a dump's voice area from the banks' area counts, so a voice no area references has nowhere to live. The freed voice's samples stay in the audio area until the next rebuild, and undo restores the area and the voice together.

func (*Session) DeleteFile

func (s *Session) DeleteFile(name string) (Snapshot, *Error)

DeleteFile removes a directory file from the disk. Deleting the full dump deletes the instrument; on a split pair the second disk goes with it.

func (*Session) DuplicateArea

func (s *Session) DuplicateArea(bank, area int) (Snapshot, *Error)

DuplicateArea appends a clone of an area: the velocity switch workflow (R11). The clone's voice header is copied into a fresh slot at the end of the voice area (growing it by a sector when full), so the two areas share audio and the PCM footprint is unchanged.

func (*Session) EstimateImport

func (s *Session) EstimateImport(files map[string][]byte, rate uint32, channel string) (*ImportEstimate, *Error)

EstimateImport reports what importing files at rate would do to the document, without touching it: the size and play time the batch becomes, the room left, and whether the import fits, splits the instrument across two disks, or is refused. It reads every file the way the conversion would, so an unreadable WAV is refused here with the same error the import would give.

func (*Session) ExportImage

func (s *Session) ExportImage() ([]byte, *Error)

ExportImage returns a copy of disk 1's image bytes. Opening then exporting is byte identical.

func (*Session) ExportImageAt

func (s *Session) ExportImageAt(index int) ([]byte, *Error)

ExportImageAt returns a copy of one image of the document: index 0 is disk 1, index 1 is disk 2 of a split pair (R25).

func (*Session) ExtractFile

func (s *Session) ExtractFile(name string) ([]byte, *Error)

ExtractFile returns a copy of a directory file's bytes for saving. The full dump of a split pair comes back stitched, the whole instrument in one file.

func (*Session) ExtractVoiceSlot

func (s *Session) ExtractVoiceSlot(slot int, format string) ([]byte, string, *Error)

ExtractVoiceSlot returns one instrument voice as a standalone file: the unpacked .fzv (the CLI's fzf unpack output), or a 16-bit mono WAV at the voice's declared rate (the CLI's fzv extract output). The returned name is the voice's, for the download filename.

func (*Session) ImportSFZ

func (s *Session) ImportSFZ(files map[string][]byte, sfzPath string, rate int, fitToDisk, split bool, channel string) (SFZResult, *Error)

ImportSFZ converts an SFZ instrument (its folder crossing the boundary as path to bytes pairs) through the CLI's pipeline and places it as the document's instrument, replacing any existing one (R7, R9). sfzPath names the .sfz inside files; empty means the one .sfz the folder holds. With split, the CLI's two disk build runs and the document becomes an image pair; otherwise fitToDisk steps the rate down to fit one disk, and a dump that still outgrows one disk splits automatically (R10 rejects only past two).

channel is ChannelLeft, ChannelRight, or ChannelMix, as it is for ImportWAVFolder: the UI asks once and the answer covers every stereo sample the instrument references.

func (*Session) ImportWAV

func (s *Session) ImportWAV(filename string, wavData []byte, rate uint32, channel string) (Snapshot, *Error)

ImportWAV converts a WAV to an FZ voice and adds it to the open disk: the browser side of J1. The voice name derives from the filename the way the CLI derives it. channel is ChannelLeft, ChannelRight, or ChannelMix and only matters for stereo input.

func (*Session) ImportWAVFolder

func (s *Session) ImportWAVFolder(files map[string][]byte, rate int, fitToDisk bool, channel string) (SFZResult, *Error)

ImportWAVFolder converts a folder of WAVs through the CLI's zero SFZ pipeline (sorted names, sequential keys up the keyboard, R8) and places the result as the document's instrument. channel is ChannelLeft, ChannelRight, or ChannelMix; the UI asks once and the answer covers every file in the batch.

func (*Session) ImportWAVToInstrument

func (s *Session) ImportWAVToInstrument(filename string, wavData []byte, rate uint32, channel string) (Snapshot, *Error)

ImportWAVToInstrument converts a WAV through the CLI's importer and places the voice with AddVoice's matrix behaviour: the instrument grows by one voice, or comes into being when absent.

func (*Session) LoadFZF

func (s *Session) LoadFZF(data []byte) (Snapshot, *Error)

LoadFZF places a full dump per the placement matrix (R7): with no disk open it becomes the instrument of a fresh disk; otherwise it replaces the open disk's instrument. A dump too large for one disk splits into a two disk document (R25). The UI owns the replace prompt; the core just places.

func (*Session) MapVoice

func (s *Session) MapVoice(voiceSlot int) (Snapshot, *Error)

MapVoice maps a voice into the first bank with room, in one action (R13). The new area arrives with the container's playable defaults.

func (*Session) NewDisk

func (s *Session) NewDisk(label string) (Snapshot, *Error)

NewDisk replaces the session's disk with a blank formatted image.

func (*Session) NewInstrument

func (s *Session) NewInstrument(name string) (Snapshot, *Error)

NewInstrument creates an empty instrument on the open disk (R4), or on a fresh disk when none is open. The format cannot express a dump with no voices (a dump's voice area is sized from bank references), so the empty instrument is one bank whose single area references a silent placeholder slot; the first voice that joins fills it.

func (*Session) OpenImage

func (s *Session) OpenImage(data []byte) (Snapshot, *Error)

OpenImage replaces the session's disk with the given image after validating that the core can parse it. The bytes are copied, so the caller's buffer (often a transferable from the UI) is never aliased.

func (*Session) OpenImagePair

func (s *Session) OpenImagePair(a, b []byte) (Snapshot, *Error)

OpenImagePair opens a two disk split instrument as one document (R5). The images may arrive in either order: the directory entry's disk number byte says which is which.

func (*Session) Peaks

func (s *Session) Peaks(fileName string, startFrame, endFrame, buckets int) ([]int16, *Error)

Peaks returns interleaved min/max sample pairs for the requested frame window of a voice, bucketed for display (R17). The window clamps to the voice's frame count; buckets must be positive. The PCM decode is the same code path as the CLI's fzv extract.

func (*Session) Redo

func (s *Session) Redo() (Snapshot, *Error)

Redo restores the state most recently undone.

func (*Session) RenameBank

func (s *Session) RenameBank(bank int, name string) (Snapshot, *Error)

RenameBank sets a bank's 12-character printable ASCII name (R11).

func (*Session) RenameDisk

func (s *Session) RenameDisk(label string) (Snapshot, *Error)

RenameDisk sets the open disk's 12-character printable ASCII label. On a split pair, disk 1 carries the document's name; disk 2 keeps the derived label it was formatted with.

func (*Session) RenameVoiceSlot

func (s *Session) RenameVoiceSlot(slot int, name string) (Snapshot, *Error)

RenameVoiceSlot sets a slot's 12-character printable ASCII name.

func (*Session) SetAreaField

func (s *Session) SetAreaField(bank, area int, field string, value int) (Snapshot, *Error)

SetAreaField edits one Area field (R12). Numeric fields clamp to their ranges; midiChannel speaks the display scale (1 to 16); voiceSlot re-points the area at another voice.

func (*Session) SetBendRange

func (s *Session) SetBendRange(value int) (Snapshot, *Error)

SetBendRange writes the pitch bend range, clamped to the block's 0..127 byte.

func (*Session) SetEffectCell

func (s *Session) SetEffectCell(controller, target, value int) (Snapshot, *Error)

SetEffectCell writes one modulation matrix cell (R19): controller 0 to 2 (mod wheel, foot pedal, aftertouch) to target 0 to 6 in R19's destination order, clamped to 0..127.

func (*Session) SetEnvelope

func (s *Session) SetEnvelope(fileName, which string, sustain, end int, rates, stops []int) (Snapshot, *Error)

SetEnvelope sets a whole envelope (which is "dca" or "dcf"): the sustain and end stage designations plus all eight rates and stops in the hardware display scale, clamped (R16).

func (*Session) SetGeneration

func (s *Session) SetGeneration(fileName string, startFrame, endFrame int) (Snapshot, *Error)

SetGeneration sets a voice file's generation window (R14's generation start and end) in sample frames, clamped to the voice's own extent. A standalone voice starts at sample zero, so the cells already hold the frames the boundary speaks and no rebase applies.

func (*Session) SetLoop

func (s *Session) SetLoop(fileName string, index, startFrame, endFrame int) (Snapshot, *Error)

SetLoop sets loop index's start and end on a voice, clamped to the voice's frame count. The snapshot reports the frames the core confirmed (R17).

func (*Session) SetLoopSelect

func (s *Session) SetLoopSelect(fileName string, sustain, release int) (Snapshot, *Error)

SetLoopSelect sets the sustain and release loop designations, clamped to 0..8 where 8 means none.

func (*Session) SetParamNumber

func (s *Session) SetParamNumber(fileName, fieldID string, value int) (Snapshot, *Error)

SetParamNumber sets a numeric schema field on a voice file, clamping the value to the field's declared range (R14).

func (*Session) SetParamOption

func (s *Session) SetParamOption(fileName, fieldID, option string) (Snapshot, *Error)

SetParamOption sets a select schema field on a voice file.

func (*Session) SetSlotEnvelope

func (s *Session) SetSlotEnvelope(slot int, which string, sustain, end int, rates, stops []int) (Snapshot, *Error)

SetSlotEnvelope sets a whole envelope on an instrument voice slot: the same display-scale contract as the loose-file setter (R16).

func (*Session) SetSlotGeneration

func (s *Session) SetSlotGeneration(slot, startFrame, endFrame int) (Snapshot, *Error)

SetSlotGeneration sets an instrument voice slot's generation window (R14's generation start and end) in voice-relative frames, clamped to the slot's own frame count. The cells hold absolute addresses, so the write rebases; without that a window written as frames would address another voice's samples.

func (*Session) SetSlotLoop

func (s *Session) SetSlotLoop(slot, index, startFrame, endFrame int) (Snapshot, *Error)

SetSlotLoop sets loop index's start and end on an instrument voice slot, in voice-relative frames clamped to the slot's frame count; the cells store absolute addresses, so the write rebases (R17).

func (*Session) SetSlotLoopAttr

func (s *Session) SetSlotLoopAttr(slot, index, xf, tm int) (Snapshot, *Error)

SetSlotLoopAttr sets loop index's cross-fade and multi-loop time on an instrument voice slot, clamped to the format's ranges.

func (*Session) SetSlotLoopSelect

func (s *Session) SetSlotLoopSelect(slot, sustain, release int) (Snapshot, *Error)

SetSlotLoopSelect sets the sustain and release loop designations on an instrument voice slot, clamped to 0..8 where 8 means none.

func (*Session) SetSlotParamNumber

func (s *Session) SetSlotParamNumber(slot int, fieldID string, value int) (Snapshot, *Error)

SetSlotParamNumber sets a numeric schema field on an instrument voice slot, clamping to the field's declared range (R14).

func (*Session) SetSlotParamOption

func (s *Session) SetSlotParamOption(slot int, fieldID, option string) (Snapshot, *Error)

SetSlotParamOption sets a select schema field on an instrument voice slot.

func (*Session) SlotPeaks

func (s *Session) SlotPeaks(slot, startFrame, endFrame, buckets int) ([]int16, *Error)

SlotPeaks returns interleaved min/max sample pairs for a frame window of an instrument voice slot, the slot-addressed sibling of Peaks (R17).

func (*Session) Snapshot

func (s *Session) Snapshot() Snapshot

Snapshot returns the state the UI renders from.

func (*Session) SwapAreas

func (s *Session) SwapAreas(bank, a, b int) (Snapshot, *Error)

SwapAreas reorders two areas within a bank (R11).

func (*Session) Undo

func (s *Session) Undo() (Snapshot, *Error)

Undo restores the previous state. The restored snapshot carries a fresh revision; a changed revision is the only change signal.

type Snapshot

type Snapshot struct {
	Revision int           `json:"revision"`
	Disk     *DiskSnapshot `json:"disk"`
	CanUndo  bool          `json:"canUndo"`
	CanRedo  bool          `json:"canRedo"`
}

Snapshot is the state the UI renders from. Revision is a monotonic per-session token; a changed revision means changed state.

type VoiceDetail

type VoiceDetail struct {
	Frames      int              `json:"frames"`
	SampleRate  int              `json:"sampleRate"`
	GenStart    int              `json:"genStart"`
	GenEnd      int              `json:"genEnd"`
	LoopSustain int              `json:"loopSustain"`
	LoopRelease int              `json:"loopRelease"`
	Loops       []LoopSnapshot   `json:"loops"`
	Dca         EnvelopeSnapshot `json:"dca"`
	Dcf         EnvelopeSnapshot `json:"dcf"`
}

VoiceDetail is the bespoke-editor surface of a voice: the waveform extent, the generation window, the loop table, and both envelopes (R14's Sample group, R16, R17).

GenStart and GenEnd are R14's generation start and end, in voice-relative sample frames like every other position the boundary carries. They sit here rather than in the schema because their bounds are this voice's own frame count: a schema field declares one static range for every voice, and a gened past waved would play the samples of whatever follows in the audio area.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL