UGE Export Usage Guide
Overview
BeatBax exports Game Boy songs to hUGETracker v6 (.uge), including instrument subpatterns when you use macros (pitch_env, vol_env, duty_env, arp_env) or native subpat. Preview/WAV and UGE subpatterns share the same tick-program IR — see Instrument macros.
Noise instruments should set uge_note= so playback and export use the same LFSR clock. Wave tables accept 32-nibble arrays or 32-character hex strings (native hUGE format).
Quick Start
1. Write a BeatBax Song
Create a .bax file with your song:
chip gameboy
bpm 128
inst lead type=pulse1 duty=50 env=gb:12,down,1
inst bass type=pulse2 duty=25 env=gb:10,down,1
inst kick type=noise gb:width=7 uge_note=C-6 pitch_env=[0,-2,-4,-6] vol_env=[15,12,8,4]
pat melody = C4 E4 G4 C5
pat bassline = C2 . G2 .
pat drums = kick . kick .
channel 1 => inst lead pat melody
channel 2 => inst bass pat bassline
channel 4 => inst kick pat drums
2. Export to UGE
Using the BeatBax CLI:
node bin/beatbax export uge mysong.bax mysong.uge
# or
beatbax export uge mysong.bax --out mysong.uge
Output:
✓ Exported UGE v6 file: mysong.uge (68086 bytes)
CLI Options
Verbose Output - Get detailed progress information:
node bin/beatbax export uge mysong.bax mysong.uge --verbose
Output:
Exporting to UGE v6 format: mysong.uge
Processing instruments...
Instruments exported:
- Duty: 2/15 slots (lead, bass)
- Wave: 1/15 slots (arp)
- Noise: 1/15 slots (kick)
Building patterns for 4 channels...
Applying effects and post-processing...
Pattern structure:
- Channel 1: 2 patterns (128 rows total)
- Channel 2: 2 patterns (128 rows total)
- Channel 3: 1 pattern (64 rows total)
- Channel 4: 1 pattern (64 rows total)
Effects applied:
- Vibrato: 3 notes
- Note cuts: 2 occurrences
Tempo: 128 BPM (7 ticks/row in UGE)
Writing binary output...
Export complete: 68,086 bytes (66.49 KB) written
File ready for hUGETracker v6
Debug Output - Get detailed internal diagnostics:
node bin/beatbax export uge mysong.bax mysong.uge --debug
Debug output includes:
- Instrument discovery and mapping
- Wave instrument volume calculations
- Per-channel event counts
- Pattern cell construction
- Effect encoding and placement
- Binary layout verification
3. Use in hUGETracker
Open the exported .uge file in hUGETracker:
- File size will be approximately 64-70KB
- Contains 15 duty, 15 wave, and 15 noise instruments
- Patterns mapped from BeatBax channels
- Ready for Game Boy development workflow
4. Compile for Game Boy
Use uge2source.exe to convert to C code:
uge2source.exe mysong.uge mysong_data mysong.c
Include mysong.c in your Game Boy project and link with hUGEDriver.
Format Details
Channel Mapping
BeatBax channels map to Game Boy APU channels:
- Channel 1 → Pulse 1 (duty-cycle square wave)
- Channel 2 → Pulse 2 (duty-cycle square wave)
- Channel 3 → Wave (32-sample wavetable)
- Channel 4 → Noise (LFSR-based noise)
Instrument Mapping
BeatBax instruments are converted to UGE instruments based on type:
type=pulse1ortype=pulse2→ Duty instrumentstype=wave→ Wave instrumentstype=noise→ Noise instruments
Note Format
Notes use standard pitch notation:
C3,D4,E#5,Gb6, etc.- Middle C (C4) = MIDI note 60
- Rest events:
.in patterns
Pattern Structure
- Each pattern can contain up to 64 rows
- Rows contain: note, instrument, volume, effect code, effect params
- Rest cells use note value 90 with VOLUME_NO_CHANGE (0x00005A00)
Advanced Usage
Multiple Export Formats
Export to all formats at once:
node bin/beatbax export json mysong.bax mysong.json
node bin/beatbax export midi mysong.bax mysong.mid
node bin/beatbax export uge mysong.bax mysong.uge
Programmatic Export
Use the TypeScript API:
import { exportUGE } from 'beatbax/export';
import { parse } from 'beatbax/parser';
import { resolveSong } from 'beatbax/song';
import { readFileSync } from 'fs';
const src = readFileSync('mysong.bax', 'utf8');
const ast = parse(src);
const song = resolveSong(ast);
// Basic export
await exportUGE(song, 'mysong.uge');
// Export with verbose output
await exportUGE(song, 'mysong.uge', { verbose: true });
// Export with debug diagnostics
await exportUGE(song, 'mysong.uge', { debug: true });
// Export with strict GB mode (reject numeric panning)
await exportUGE(song, 'mysong.uge', { strictGb: true });
console.log('✓ Exported UGE file');
import { exportUGE } from 'packages/engine/src/export/ugeWriter';
import { parse } from 'packages/engine/src/parser';
import { resolveSong } from 'packages/engine/src/song/resolver';
import { readFileSync } from 'fs';
const source = readFileSync('mysong.bax', 'utf-8');
const ast = parse(source);
const song = resolveSong(ast);
await exportUGE(song, 'output.uge');
Validation
Effects Mapping
Supported Effects
BeatBax supports the following effects with UGE export:
- Panning (
pan,gb:pan) → NR51 terminal bits via8xxSet-Panning effect - Vibrato (
vib) →4xyeffect (x=rate, y=depth) - Portamento (
port) →3xxTone Portamento effect - Pitch Bend (
bend) →3xxTone Portamento effect (approximation with warnings) - Arpeggio (
arp) →0xyeffect (see detailed mapping below) - Volume Slide (
volSlide) →AxyVolume Slide effect - Note Cut (
cut) →E0xNote Cut effect
Unsupported Effects
The following effects are not supported in UGE export:
- Retrigger (
retrig) - hUGETracker has no native retrigger effect. When exporting songs containing retrigger effects, a warning will be displayed and the retrigger effects will be omitted from the output. Use WebAudio playback for retrigger support. - Tremolo (
trem) - Exported as metadata comment only, not as playable effect
Arpeggio (arp) mapping
BeatBax arp effect exports to hUGETracker's 0xy arpeggio effect, cycling through pitch offsets at the Game Boy frame rate (60 Hz).
- Syntax:
<arp:3,7>lists semitone offsets above the written note (do not include0; root is implicit) - Cycle behavior: Playback and hUGE always start on the root: Root → +x → +y → Root → …
- Example:
arp:3,7(C minor) cycles C → Eb → G → C at 60 Hz - Each step lasts ~16.667ms, creating a chord illusion
- Example:
- Mapping rule: First 2 offsets map to x and y nibbles of
0xyeffect codearp:3,7→0x37(offset +3 in x nibble, +7 in y nibble)arp:4,7→0x47(major chord)
- Limitations:
- UGE format supports only 2 offsets (3 notes including root)
- Arpeggios with 3+ offsets (e.g.,
arp:4,7,11) emit a warning and only export first 2
- Sustain behavior: Arpeggio effect applies to note onset row AND all sustain rows for full note duration
- Implementation: See
packages/engine/src/export/ugeWriter.ts(ArpeggioHandler, activeArp tracking)
Example presets:
effect arpMinor = arp:3,7
effect arpMajor = arp:4,7
effect arpMajor7 = arp:4,7,11 # Warning: only 4,7 exported to UGE
pat chords = C4<arpMinor>:4 F4<arpMajor>:4 G4<arpMinor>:4
Pitch Bend (bend) mapping
BeatBax bend effect exports to hUGETracker's 3xx tone portamento effect. Since hUGETracker doesn't support high-resolution pitch bends or delay parameters, the export process approximates bends with the following limitations:
-
Syntax:
<bend:semitones,curve,delay,time>in BeatBaxsemitones: Number of semitones to bend (positive = up, negative = down)curve: Bend curve shape (linear,exp,log,sine) - onlylinearapproximates well in UGEdelay: Fraction of note duration before bend starts (default 0.5) - not supported in UGEtime: Bend duration in seconds - not supported in UGE, uses full note duration
-
Mapping to
3xxeffect:- Effect code:
3(tone portamento) - Speed value (
xx): Calculated based on semitone distance:- ≤1 semitone:
32(slowest, most musical for small bends) - ≤2 semitones:
48(whole-tone intervals) - ≤5 semitones:
64(fourth/tritone intervals) - ≤7 semitones:
96(fifth intervals) -
7 semitones:
128(octave+ intervals, fastest)
- ≤1 semitone:
- Formula: hUGETracker portamento duration =
(256 - speed) / 256 × noteDuration × 0.6 - Higher speed values = faster portamento (inverse relationship)
- Effect code:
-
Export warnings issued for:
- Non-linear curves (
exp,log,sine) - UGE only supports linear portamento - Delay values other than
0.0or0.5- partial note timing not supported in UGE format - These warnings don't prevent export but indicate fidelity loss
- Non-linear curves (
-
Priority: 11 (between standard portamento and vibrato in conflict resolution)
-
Implementation: See
packages/engine/src/export/ugeWriter.ts(PitchBendHandler)
Example:
effect wholetone = bend:+2,linear,0.5 # Guitar-style whole-tone bend
effect dive = bend:-12,log,0 # Octave dive (WARNING: log curve not supported)
pat melody = C4<wholetone>:4 F4 G4<dive>:2
UGE export output:
⚠ Warning: Pitch bend effect uses non-linear curve 'log', UGE portamento is always linear
⚠ Warning: Pitch bend effect has delay parameter (0.0), UGE portamento bends across full note
Recommendation: For best results in UGE export, use linear curves (bend:+2,linear) and stick to delay values of 0 or 0.5. For complex bends, use WebAudio or MIDI export instead.
Noise Channel Note Mapping
The Game Boy noise channel doesn't use traditional musical pitches. Instead, notes control the noise generator's shift and divisor parameters. BeatBax exports noise channel notes directly to hUGETracker with NO automatic transpose applied.
- Direct 1:1 mapping: Notes are exported exactly as written in BeatBax:
C2in BeatBax → exports as index 0 (displays as C-3 in hUGETracker)C4in BeatBax → exports as index 24 (displays as C-5 in hUGETracker)C5in BeatBax → exports as index 36 (displays as C-6 in hUGETracker)C6in BeatBax → exports as index 48 (displays as C-7 in hUGETracker, typical percussion range)C8in BeatBax → exports as index 72 (displays as C-9 in hUGETracker = maximum)- Notes above
C8are clamped to index 72 - Notes below
C2are transposed up by octaves to fit in valid range
- Write in target range: For typical percussion sounds, use C5-C6 (indices 36-48), which map to common snare/hi-hat sounds in hUGETracker
- Octave Display Note: hUGETracker displays all notes ONE OCTAVE HIGHER than BeatBax's MIDI notation (C6 in BeatBax = C-7 in hUGETracker)
- Custom transpose if needed: Add
uge_transpose=Nto your noise instrument to shift all notes:inst kick type=noise env=gb:12,down,1 uge_transpose=12 # Shift up 1 octave - Round-trip behavior: When importing UGE files, noise notes remain at their hUGETracker index values (same as exported)
Example:
chip gameboy
bpm 120
# Write percussion notes in the exact range you want in hUGETracker
inst kick type=noise env=gb:12,down,1 width=15 # Use C2-C3 (deep sounds)
inst snare type=noise env=gb:10,down,2 width=7 # Use C5-C6 (mid-range)
inst hat type=noise env=gb:8,down,1 width=7 # Use C6-C7 (bright sounds)
# Direct mapping: C2→0, C4→24, C5→36, C6→48, C7→60
pat drums = C2 . C5 . C6 C7 C2 C6
channel 4 => inst kick pat drums
Important: The 1:1 mapping applies during UGE export. BeatBax's internal playback uses these as relative brightness controls for the noise generator. MIDI export maps noise notes to GM percussion (e.g., C5→Snare, C7→Hi-hat).
Vibrato (vib) mapping
When exporting BeatBax songs for Game Boy, the BeatBax vib effect is conservatively mapped into hUGETracker's compact 4xy vibrato effect so the exported .uge behaves sensibly in tracker/driver toolchains.
- Mapping rule: BeatBax vibrato rate →
x(rate nibble), depth →y(depth nibble) using a tuned scale factor. Export usesVIB_DEPTH_SCALE = 4.0to convert BeatBax depth units into theynibble. - Placement: The exporter places the
4xycommand on the same pattern row where the originating note occurs. The original Game Boy NR51 routing is preserved when possible. - Renderer parity: The offline renderer in the engine (
packages/engine/src/audio/pcmRenderer.ts) has a Game-Boy-specific emulation mode that reproduces hUGEDriver-style vibrato (mask-activated register offsets) for better audible parity with exported.ugeplayback. - Tuned constants: The engine's calibration sweep identified a practical best-fit set used in source builds:
vibDepthScale=4.0,regBaseFactor=0.04,regUnit=1. - Calibration tools: Re-run or inspect the calibration and measurement tools in
scripts/compare_vib.cjsandscripts/auto_calibrate_vib.mjsif you need to refine parity for specific material.
Example: export and analyze a song with vibrato
# export UGE then render WAV for analysis (example)
node bin/beatbax export uge songs/features/effect_demo.bax tmp/effect_demo.uge
node scripts/auto_calibrate_vib.mjs songs/features/effect_demo.bax tmp/auto_cal --sampleRate 44100
See packages/engine/src/export/ugeWriter.ts and packages/engine/src/audio/pcmRenderer.ts for implementation details.
Validate exported UGE files with the official hUGETracker tools:
Using uge2source.exe
uge2source.exe mysong.uge test_song output.c
Success: Exit code 0, output.c created
Failure: Error message displayed
Using hUGETracker
- Launch hUGETracker v1.0.11 or later
- File → Open → Select exported
.ugefile - File should load without errors
- Patterns, instruments, and order list should be visible
Troubleshooting
File Won't Open in hUGETracker
- Check file size: should be 60-70KB for typical songs
- Verify version: first 4 bytes should be
06 00 00 00(little-endian 6) - Run through uge2source.exe for detailed error messages
Missing Instruments
- UGE files contain 45 instruments (15 duty, 15 wave, 15 noise)
- BeatBax currently uses default instrument values
- Custom instrument mapping may be needed for complex songs
Pattern Length Issues
- UGE patterns have fixed 64-row length
- Shorter BeatBax patterns are padded with rests
- Longer patterns are truncated
File Format Reference
UGE v6 Binary Structure
Header:
- Version (u32): 6
- Name, Artist, Comment (shortstrings: 256 bytes each)
Instruments (45 total, 1381 bytes each):
- 15 Duty Instruments (Type 0)
- 15 Wave Instruments (Type 1)
- 15 Noise Instruments (Type 2)
Wavetables:
- 16 wavetables × 32 nibbles = 512 bytes
Patterns:
- Timing settings (9 bytes)
- Pattern count (u32)
- Pattern data (1092 bytes per pattern)
Orders:
- 4 channels × order list (variable length)
Routines:
- 16 AnsiStrings (variable length)
Pattern Cell Format (17 bytes)
Note (u32) - MIDI note number (90 = rest)
Instrument (u32) - Instrument index (0-14)
Volume (u32) - 0x00005A00 = no change
Effect Code (u32) - GB effect type
Effect Params (u8) - Effect parameters
Examples
Simple Melody
chip gameboy
bpm 128
inst lead type=pulse1 duty=50 env=gb:12,down,1
pat verse = C4 D4 E4 G4 E4 D4 C4 .
channel 1 => inst lead pat verse
Four-Channel Song
chip gameboy
bpm 140
inst lead type=pulse1 duty=50 env=gb:12,down,1
inst bass type=pulse2 duty=25 env=gb:10,down,1
inst arp type=wave wave=[0,2,3,5,6,8,9,11,12,11,9,8,6,5,3,2,0,2,3,5,6,8,9,11,12,11,9,8,6,5,3,2]
inst kick type=noise env=gb:12,down,1
pat melody = C5 E5 G5 C6
pat bassline = C3 . G2 .
pat arpeggios = C4 E4 G4 C5
pat drums = C5 . C5 C5
channel 1 => inst lead pat melody
channel 2 => inst bass pat bassline
channel 3 => inst arp pat arpeggios
channel 4 => inst kick pat drums
Resources
Support
For issues with UGE export:
- Verify beatbax is up to date:
git pull - Check test suite:
npm test - Validate with uge2source.exe
- Open an issue with example
.baxfile and error details
Notes on panning & NR51
- BeatBax maps
gb:panand snapped numericpanvalues to NR51 terminal bits for UGE export and emits8xxSet-Panning effects in pattern data when the mix changes on note onsets. - The exporter no longer appends an
[NR51=0x..]debug tag to the UGE comment; useexport jsonfor round-trip metadata if required. - Use
--strict-gbwithexport ugeto reject numeric pans instead of snapping them.