Commit Graph

28 Commits

Author SHA1 Message Date
Reactorcoremeltdown
58639d07cf Add Mix Tightener effect, multi-column preset menus, louder master (v0.7.0)
Mix Tightener — a preset-driven "channel strip in a box" (Neutron-style):
parametric EQ → 3-band multiband compressor → peak limiter, all behind ONE
visible control, Strength, which morphs the whole chain from transparent (0)
to the full preset (1). Every per-band value is a hidden slot param the
presets set; the editor shows only a vertical Strength fader.
 * DSP (Effects.kt): reusable RBJ biquad; LR-style 3-way crossover; per-band
   peak-follower compressors; fast peak limiter + output make-up. Performance:
   the expensive per-band gain computation (log/pow) runs at control rate while
   the envelope follower runs per sample; Strength≈0 bypasses the whole effect,
   and flat EQ bands / ratio≈1 comp bands are skipped. Output is clamped and
   NaN-guarded so a channel FX can never poison the master.
 * 30 factory presets (MixTightenerPresets) across drums (808 Crisp, Industrial
   Drums, Trap Hats, Techno Kick, DnB Breaks, …), bass (EBM Bass, Reese, Acid
   303, Sub Bass Tame, Goth Bass, Synthwave Bass, …), synths/leads/pads
   (Synthwave Lead, Cutting Lead, Darkwave Pad, Arp Sparkle, …) and bus glue
   (Mix Glue, Loudness Max), seeded via PresetLibrary.

Global: RetroDropdown now lays long option lists (>12) into balanced columns
so preset menus grow sideways instead of into a tall vertical scroll — applies
to every dropdown in the app.

Master loudness: raise MASTER_GAIN 0.35 → 0.5 (~+3 dB) so playback sits closer
to other media apps; the soft-knee limiter still catches peaks.

Builds clean (staging + release). On-device performance profiling and install
are pending — the device dropped off wireless adb mid-session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 18:11:56 +02:00
Reactorcoremeltdown
3a180eb983 Add detailed user guide with screenshots
docs/USER_GUIDE.md: a full walkthrough of every screen — Tracker (transport,
grid, note-offs, scrolling), the punch-in keyboard, Arrangement, Mix
(channels, FX routing, recording), Toolbox (tile gestures, Synth/Sampler/
SF2-XI editors, effects, presets) and Setup (projects, audio routing, themes,
input bindings). Also covers the four input methods incl. the new
hold-to-repeat, file formats, recording, and default keyboard/gamepad/MIDI
maps. 11 on-device screenshots under docs/screenshots/, linked from README.

Also refresh the README's stale "NES synth" wording to "3-oscillator synth"
(renamed in v0.5.0).

Docs only — no code change, so no version bump or rebuild.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 15:25:58 +02:00
Reactorcoremeltdown
750d928ce1 Hold-to-repeat for navigation keys across keyboard, gamepad and MIDI (v0.6.0)
Navigation and value-step actions (Nav Up/Down/Left/Right, Next/Prev
Column, Increment/Decrement) now auto-repeat while their control is held —
fire once on press, then accelerate from 320ms to 45ms per step — and stop
the instant it's released. Implemented ONCE as a shared repeater in
InputRouter (pressRepeat/releaseRepeat, keyed by the physical control id),
so all three hardware inputs get identical behaviour:
 * Keyboard: the OS's own key auto-repeat is now ignored (held notes sustain
   instead of retriggering) and holds are driven by the shared repeater;
   key-up ends it.
 * Gamepad: buttons, D-pad hat AND analog sticks repeat (they all funnel
   through controlDown/controlUp); releasing the main control stops it,
   releasing a chord modifier does not.
 * MIDI: a momentary nav CC repeats between its press (value>=64) and
   release (value<64).

One-shots (transport, tab switch, notes, clear) are unaffected — they fire
once even when held. InputRouter takes an injectable dispatcher so the new
InputRouterTest drives the accelerating schedule on a virtual clock
(verifies repeat-while-held, stop-on-release, one-shot-fires-once, and that
an unrelated release doesn't stop the active repeat). All unit tests pass;
on-device smoke confirms single-press nav + transport still work, no crash.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 15:14:34 +02:00
Reactorcoremeltdown
e0fe89d62c Rework toolbox tile gestures: tap to pick/edit, long-press to clear (v0.5.2)
Single tap now acts immediately instead of just selecting: an empty tile
opens the device picker, a filled tile opens its settings editor (this was
the double-tap behaviour; the highlight-only single-tap and the double-tap
are gone). Long-press clears the tile — but now via a confirmation dialog
("Clear tile") rather than wiping instantly, since clearing discards the
device and its settings with no undo (matches the preset DELETE confirm).
Long-pressing an empty tile does nothing.

Removes the now-unused per-tile `selected` state/highlight and onDoubleTap.
Verified on-device: filled→editor, empty→picker, long-press→confirm(Cancel
keeps the tile).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 15:00:17 +02:00
Reactorcoremeltdown
2f671ed372 UI smoothness: distinct isPlaying flow + allocation-free grid draw (v0.5.1)
Two changes that cut playback-time UI jank (measured on-device):
- Expose a deduped isPlaying StateFlow (maps the transport flow through
  distinctUntilChanged) and collect THAT in the transport toolbar, so the
  PLAY/PAUSE button recomposes on play↔stop instead of ~12×/second as the
  playhead advances (the full transport emits every line).
- Precompute the pattern grid's fixed-width text (gutter hex, note names,
  velocity/channel) into class-load lookup tables. The grid redraws on
  every playhead line and scroll pixel, and the old toString/padStart
  formatting allocated a String per cell per frame — steady draw-phase
  garbage. The draw is now allocation-free (also improves GlyphCache hit
  rate via stable string instances).

gfxinfo over ~12s of playback on the busy track (moto g84):
  janky frames 47.2% → 32.0%; 99th-pct frame 129ms → 53ms;
  missed vsync 40 → 10; 50th 18ms → 14ms.

No engine/audio change. Remaining jank is the auto-follow scroll
redrawing the whole grid each line (everything shifts), not addressed here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 14:49:08 +02:00
Reactorcoremeltdown
cf306d25e0 Generic synth name, calmer knobs, darkwave/industrial presets (v0.5.0)
- Rename the 3-osc synth's display name "NES Synth" → "Synth" (the enum
  NES_SYNTH stays as the serialized id, so saved projects/presets are
  unaffected).
- Knobs: replace absolute finger-position control (full range mapped onto
  the 44dp knob → very touchy) with relative drag over ~280dp of travel,
  so fine tweaks are easy and the control isn't sensitive.
- Add a darkwave/industrial preset family: Pluck Bass, Acid Pluck
  (punchy plucky basses), Razor Lead, Cold Lead (cutting leads), and
  Grit Sub, Detune Wall (harsh, detuned deep basses for layering under
  bass pads).
- Preset seeding now records seeded names in the per-type marker instead
  of a single flag, so factory presets added in a later version appear on
  upgrade without resurrecting presets the user deleted (legacy "1"
  markers upgrade cleanly).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:56:52 +02:00
Reactorcoremeltdown
12c2092843 Render only active voices via per-block active-voice lists (v0.4.7)
Optimization B, superseding v0.4.6's per-bus skip. Instead of iterating
each active bus's whole POLYPHONY pool per sample (calling render() on
idle slots, which merely early-out), fillBlock now builds compact lists
of the voices that are actually sounding — once per block — and the
per-sample mix loop touches only those.

Correctness for mid-block starts: the sequencer can trigger a note part
way through a block, so the allocators queue the chosen voice into this
block's list (queueSeq/queueSmp), keeping note timing sample-accurate.
A per-voice generation stamp dedups the append so a voice that finishes
and is re-allocated within one block is never rendered twice (which would
double its amplitude). Live/bus-audition voices are snapshotted at block
start (a manual press mid-block sounds at the next block, ≤2 ms later —
imperceptible, and the pre-existing ~30 ms output buffer dwarfs it).

On-device (moto g84, 98% battery, real busy track, ~13 voices, 2.0 ms
budget), avg render callback:
  baseline 2.00 ms (xruns +250..+515) → A 1.44 ms (+0) → B ~1.00 ms (+0)
B removes ~51 inactive-slot render() calls per sample; ART's virtual
dispatch makes each ~90 ns, so this is a real ~30% gain over A (not the
~1% first estimated from the sample track — measurement corrected it).
Fast-tapping audition during playback stays ~1.0-1.3 ms, xruns +0,
voices drain cleanly (no stuck notes, no double-render).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:39:57 +02:00
Reactorcoremeltdown
d54fd27c9e Skip idle voice pools per block — fix playback dropouts (v0.4.6)
fillBlock rendered EVERY pooled voice every sample: 32+32 sequencer +
32+32 bus-audition + 16 live/audition voices, render() called ~144×
per frame × 96 frames per callback — even at idle. The bus-audition
pool (64 voices) is only used by the Toolbox MIDI piano yet ran every
sample during normal playback. This fixed cost, not active-voice count,
dominated the render budget.

Now the pools are scanned once per block into per-bus active flags, and
the per-sample mix loop skips whole idle buses and the entire bus-
audition pool when nothing is being auditioned. The sequencer sets the
flag the instant it triggers a note mid-block, so newly-started notes
still sound sample-accurately within the same block.

On-device profiling (moto g84, 98% battery, real busy track, ~11 sample
voices, 96-frame/2.0ms callback):
  before: avg 2.00 ms, xruns +250..+515 per ~500 callbacks (audible drops)
  after:  avg 1.44 ms, xruns +0 (steady)
Fast-tapping the audition keyboard during playback: avg 1.1-1.4 ms,
xruns +0, voices peaked at 16 then drained cleanly — no stuck notes.

With this, under-load render time equals idle, so voice rendering is no
longer the bottleneck; the remaining ~1.4 ms is fixed JNI + per-channel
FX overhead. A block-based per-voice refactor was evaluated and dropped
as unnecessary (it would not reduce that fixed cost).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 10:28:35 +02:00
Reactorcoremeltdown
3155caf3cc Fix stuck synth voices: release scaled by sustain level (v0.4.5)
The subtractive synth's amplitude-envelope release decremented at
`(dt / r) * s` — the rate was multiplied by the sustain level, floored
at 0.001. For any percussive patch (sustain≈0: Square Bass, Pulse Pluck,
Noise Perc), release ran ~1000× too slow, so a 0.2 s tail took minutes.
Since a voice's isActive follows its amp envelope, the voice never freed
and kept rendering (3 osc + filter) indefinitely.

That one bug produced all three reported symptoms:
 * missed note-offs / stuck notes needing panic — the note-off fires but
   the voice takes minutes to go silent, reading as ignored;
 * fast-tapping dropouts on every instrument — released-mid-envelope
   voices pile up (whole pool + 8 live audition voices) and CPU climbs
   until the audio callback overruns → xruns;
 * audition-then-play dropouts — audition notes are already stuck on the
   live pool before the sequencer stacks track voices on top.

Release now ramps to zero at a constant 1/r rate, reaching silence
within r seconds from any level (matching SampleVoice's behaviour). The
Sampler/SoundFont path already captured its release rate at note-off, so
it was less affected — but the shared synth voice pinned the CPU budget,
which is why the stutter showed across all instruments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 02:04:14 +02:00
Reactorcoremeltdown
00f52c3434 Move synth pitch modulation to control rate — fix playback hiccups (v0.4.4)
On-device profiling (worst case: 4 channels x 3 oscillators + cutoff & pitch
modulation, dense pattern at 200 BPM) showed the render callback averaging 2.8ms
against a 2.0ms budget with continuous xruns — the hiccups. The cost was the
per-sample Math.pow used for pitch modulation across 3 oscillators x many voices.

Recompute modulated oscillator pitch at control rate (every 16 samples, like the
filter cutoff) instead of per sample; LFO/envelopes move slowly enough that this
is inaudible. Render dropped to ~1.27ms avg with zero new xruns. No crashes under
load (the ConcurrentHashMap + stable SVF filter fixes from 0.4.3 hold).

Patch bump to 0.4.4 (versionCode 11).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:48:39 +02:00
Reactorcoremeltdown
78ebd88548 Fix synth crashes, dead filter, and playback hiccups (v0.4.3)
Address three issues introduced with the 3-oscillator synth:

- Crashes: ToolboxSlot.values was a plain HashMap read on the audio thread while
  the UI wrote it (knob drags vs. the synth's ~40-param-per-note reads). Made it a
  ConcurrentHashMap so get/put are atomic and reads are lock-free (RT-safe).
- Filter cutoff not affecting the sound: (a) the cheap ladder went unstable at high
  cutoff (f > 1 -> NaN -> silent voice) — replaced with a stable Cytomic TPT
  state-variable low-pass (with NaN recovery); (b) a voice captured its patch only
  at note-on, so live knob turns were inaudible — the engine now refreshes active
  voices' patches every block, so cutoff/level/mod changes affect held & playing
  notes.
- Hiccups: every note allocated a SynthPatch (+arrays) causing GC dropouts — the
  patch is now cached per bus and reparsed only on param/tempo change, so steady
  playback allocates nothing on the audio thread.

Patch bump to 0.4.3 (versionCode 10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:36:53 +02:00
Reactorcoremeltdown
74d60993f0 Inertial touch scrolling for the tracker grid (v0.4.2)
Add fling/drag vertical scrolling to the pattern grid via Modifier.scrollable
(fractional pixel offset for smooth, non-snapping motion). The view still follows
the playhead during playback (and the cursor when stopped) by default; a finger
flick detaches it into free inertial scrolling, and switching to another block
re-attaches the follow (the scroll state is keyed on the active pattern).
Tap-to-focus is unchanged. Patch bump to 0.4.2 (versionCode 9).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:15:02 +02:00
Reactorcoremeltdown
63cad6897f Rewrite the NES synth as a 3-oscillator subtractive synth; track-tied note-offs (v0.4.1)
Two changes that landed together (the note-off work was uncommitted when the synth
rewrite began):

Synth rewrite — replace the simple NES tone generator with a classic Minimoog-style
voice: 3 oscillators (+ noise) → mixer → resonant 4-pole ladder low-pass (own
contour envelope + key tracking) → amplifier (own envelope), plus a tempo-synced
LFO and a 4-slot modulation matrix.
- DSP: SynthVoice rewritten (PolyBLEP oscillators, ladder filter, two ADSRs, LFO,
  mod matrix); new SynthPatch parses a slot into an immutable snapshot per note.
  PolyBLEP + control-rate cutoff + precomputed osc tuning keep the RT cost down.
- Model: SynthDefs (shared option lists/defaults), rewritten NES_SYNTH params,
  NesSynthPresets (8 chiptune factory presets) seeded into the preset library.
- Editor: NesSynthEditor — swipeable cards (Oscillators / Filter / Amp+LFO / Mod
  Matrix) with dot pagination; new rotary Knob control (absolute, fader-like
  vertical drag: bottom = empty, top = full); enums via RetroDropdown. The shared
  slot-audition keyboard (renamed SoundFontKeyboard -> SlotAuditionKeyboard) is
  pinned at the editor bottom.
- Engine builds a SynthPatch per note. The enum id stays NES_SYNTH so existing
  projects still route; the old single `wave` param is dropped (amp envelope keys
  carry over).

Track-tied note-offs — a note-off ("===") is now tied to its track, not a channel:
it has no velocity/channel of its own (shown as ".." and non-editable) and simply
releases whatever that track last played, on the bus(es) that note routed to
(AudioEngine.trackLastChannel). `.sng` export writes "OFF .. ..".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:06:47 +02:00
Reactorcoremeltdown
9ab7660012 Justify the tracker toolbar rows edge to edge (v0.3.2)
Each transport-toolbar row now distributes its elements across the full width
(Arrangement.SpaceBetween) instead of clustering at the left: dropped the
per-row horizontalScroll (which gave the row infinite width and defeated any
distribution) and the manual landscape spacer. Rows no longer scroll — elements
fill the bar edge to edge. Patch bump to 0.3.2 (versionCode 5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:08:42 +02:00
Reactorcoremeltdown
26931dd501 Name the release APK space.rcmd.android.sizzle_<versionCode>.apk
Configure the release variant's output filename (via the legacy applicationVariants
API) so the unsigned release APK is stamped with its versionCode — e.g.
space.rcmd.android.sizzle_4.apk — to simplify uploading to the private F-Droid
repo. Staging keeps its app-staging.apk name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:21:05 +02:00
Reactorcoremeltdown
b5620b429d Pin the SF2/XI audition keyboard at the editor bottom (v0.3.1)
Lay out and place the SF2/XI loader's audition keyboard like the toolbox and
tracker keyboards: extract it into SoundFontKeyboard (an octave selector above two
weighted octaves) and pin it at the bottom of the device editor, with the controls
scrolling in a weighted region above (1.7 : 1, matching the toolbox tab) — so it's
the same height and always visible instead of scrolling away. Patch bump to 0.3.1
(versionCode 4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:45:41 +02:00
Reactorcoremeltdown
ba56b5ab9e SF2/XI loop crossfade + hold-to-repeat steppers (v0.3.0)
- SF2/XI loader: add a Loop XFade control (ms, 0-500) next to the loop toggle.
  SampleVoice crossfades the loop tail into its head for a click-free seam even
  when the file's loop points don't match; 0 ms keeps the previous hard wrap.
  Threaded through VoiceSample and all noteOn call sites.
- Add a reusable RepeatButton: fires on press then auto-repeats while held with
  an exponentially shrinking interval (stops on release/scroll-off). Applied to
  every +/- stepper (tempo, MIDI channel, arrangement repeats, audition/keyboard
  octave, and the new crossfade).
- Bump version to 0.3.0 (versionCode 3) per the minor-per-change policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:38:10 +02:00
Reactorcoremeltdown
4bd8aac4d5 Rename application identifier to space.rcmd.android.sizzle
Global rename of the app/package identifier from
com.reactorcoremeltdown.sizzletracker to space.rcmd.android.sizzle:

- build.gradle.kts: applicationId + namespace.
- Move the Kotlin source tree (main + test) and rewrite every package/import.
- native_audio.cpp: JNI symbol names (Java_space_rcmd_android_sizzle_...) so the
  Oboe callback still links against the relocated NativeAudioBridge.
- docs: package/file-map header.

The desktop-project GitHub URLs (github.com/reactorcoremeltdown/sizzletracker)
and the Reactorcoremeltdown copyright/SPDX author identity are intentionally left
unchanged. Note: the new applicationId installs as a separate package (existing
installs won't upgrade in place).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:59:51 +02:00
Reactorcoremeltdown
a5fdc7b6c4 Prepare for open-source release under GPL-3.0-or-later; docs & test maintenance
- Add GPL-3.0 LICENSE and SPDX headers (GPL-3.0-or-later) to every source file
  (58 Kotlin, the C++ file, and the sample-pack HTML tool).
- README: state the GPL-3.0-or-later license (+ badge); fix stale "256 beats"
  -> "256 bars"; fix build instructions (the Gradle wrapper is committed);
  mention master recording and the sample-pack tool.
- DEVELOPER_HANDOVER: resync with the codebase — corrected the wrapper note,
  architecture diagram, full file map, the add-instrument/effect guide, Oboe
  (now default with fallback, not an opt-in toggle), moved SF2/XI to implemented,
  and added recently shipped features with an honest remaining/optional list.
- Fix a pre-existing broken unit test: SngFormatTest called the old
  SngFormat.import(text); the API is importInto(project, text). Tests compile
  and pass again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:47:29 +02:00
Reactorcoremeltdown
3df4bb645a Extend the arrangement canvas to 256 bars
- Denominate the canvas in bars (lengthBars, default 256) instead of a raw beat
  count; canvasBeats(sig) = lengthBars * beatsPerBar, so it spans exactly 256
  bars in any signature. Backing arrays sized for the widest case (5/4 -> 1280
  beats). Playback still stops at lastFilledBeat, so the wider empty canvas adds
  no trailing silence.
- Roll: draw with viewport culling (iterate only the beats scrolled into view)
  so the ~16x-wider canvas stays cheap to redraw and scrolls smoothly.
- Persistence: store bars= (was len= in beats) and write cells only up to the
  last filled beat; old files without bars= load the default 256-bar canvas with
  their cells intact. .sng import grows the canvas in bars as needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 08:11:40 +02:00
Reactorcoremeltdown
61d7de1f25 Improve light-theme readability; drop experimental stereo FX; add sample-pack builder tool
- Theme: pick a light or dark Material base scheme by palette luminance and map
  the palette onto the container/variant/outline/secondary roles components
  actually read, so cards and secondary text stay legible on bright themes.
- Audio: remove the experimental stereo-FX rendering and the tape-delay per-head
  balance (both worked poorly); each channel runs a single mono FX chain again.
  Master output and recording remain stereo (dual-mono content).
- Tools: add tools/samplepack-builder.html, a standalone page for assembling
  Sampler .zip preset bundles (16 note-labelled drag-n-drop pads, audition,
  16-bit mono WAV re-encode, duplicate-sample detection).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:37:44 +02:00
Reactorcoremeltdown
f01a91f0ec Add experimental stereo-effects toggle; theme the system nav bar
Stereo effects (experimental), off by default and persisted:
- When on, each mono insert effect runs a second instance for the right
  channel so upstream stereo (e.g. a delay's width) carries into later
  effects like the reverb; the delay stays single-instance (nativeStereo).
- Settings toggle under Audio Devices; rebuilds the FX chains on change.

Seamless system bars:
- SizzleTheme tints the system navigation bar to the palette's surface
  (matching the bottom tab bar) and the status bar to the background, with
  light/dark icon appearance by luminance, re-applied on theme change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 16:14:17 +02:00
Reactorcoremeltdown
c630dc3694 Make playback and recording stereo; add per-head delay balance
- fillBlock now outputs interleaved stereo; AudioTrack and native Oboe
  configured for 2 channels; MasterRecorder writes a stereo WAV.
- AudioEffect gains processStereo() (default mono-collapse); the tape delay
  overrides it to pan each of its 4 heads with a level-preserving linear pan
  (feedback still uses the mono sum, so loop behaviour is unchanged).
- New per-head balance param + a horizontal balance slider under each head's
  division fader (center-detented, L/C/R readout), with a revision read so it
  redraws.

Note: only the delay produces stereo width; other insert effects stay mono
and collapse upstream stereo, so place the delay last to keep its panning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:55:33 +02:00
Reactorcoremeltdown
3a752b88ab Merge branch 'feature/recording-landscape-fx-qol'
Master recording, landscape layouts, FX/arpeggiator fixes, theme library,
and a batch of UX/persistence/cleanup improvements.
2026-07-16 11:26:07 +02:00
Reactorcoremeltdown
c8acc3827d Add master recording, landscape layout, and a batch of FX/UX improvements
Recording & audio:
- Master-bus WAV recorder: arm + FX-tail + SAF output folder on the mixer
  toolbar; allocation-free capture (ring buffer -> writer thread).
- Oboe is now the default backend; falls back to AudioTrack only on init
  error and shows a popup. Removed the manual engine toggle.
- Arpeggiator: fixed loop-boundary slip (phase-continuous), monophonic
  (no chord/voice pile-up), and a per-bar reset toggle.
- AmbienceReverb: 8-line FDN (was 16) to fit the real-time budget; per-bus
  soft-clip limiter; per-channel non-finite guard on the master sum.

UI / layout:
- Responsive landscape layouts: side nav rail; tracker grid beside the
  arrangement/keyboard; toolbox tiles beside a wider keyboard; two-column
  mixer strips; collapsed transport toolbar.
- Two-column instrument/effect editors; reverb controls paged with dot
  pagination.
- Arrangement: per-lane mute toggles; center-follow scrolling.
- Tracker toolbar: BLK dropdown, SKIP moved by OFF/KBD, glyph edit buttons.
- Sampler: normalize button, per-pad volume snap, single CLEAR.
- Import error popups; content validation on all file loaders.
- App themes: import/export/delete/dropdown library (stock themes protected).

Persistence & QoL:
- Persist keyboard MIDI channel/octave; remember loaded project in the
  settings dropdown; New Project button.

Cleanup:
- Removed dead code and fixed audio-thread allocation hot spots
  (per-sample iterators, Math.random on the RT thread, etc.).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 11:25:23 +02:00
Reactorcoremeltdown
a4c8ba2080 Big batch update: the tracker is mostly usable 2026-07-15 16:22:04 +02:00
Reactorcoremeltdown
d380f8643b UI Improvements: Piano widget, rigid sizes, proportions, etc 2026-07-14 07:37:48 +02:00
Reactorcoremeltdown
3cef1b4e85 Initial commit: Sizzletracker Android music tracker
A retro, grid-based music tracker for Android (Kotlin + Jetpack Compose,
single Activity) with four equally-capable input methods (touch, keyboard,
gamepad, MIDI) and four tabs: tracker, mixer, toolbox, settings.

Highlights:
- Tracker: Canvas-drawn 4-track pattern grid over an 8-lane arrangement roll,
  with a glyph cache and draw-phase state reads so the playhead and edits
  redraw without per-frame recomposition.
- Audio: sample-accurate sequencer feeding a shared AudioEngine, driven by
  either a Kotlin AudioTrack loop or native Oboe/AAudio via JNI (16 KB-aligned
  native libs). media3 MediaSession for lock-screen/headset transport.
- Toolbox: 16 instrument/effect slots with a 2-octave audition keyboard;
  single-tap select, double-tap edit, long-press clear.
- Note entry: long-press cell popups (piano keyboard / value steppers) plus
  keyboard/gamepad stepping that resumes from the last note/channel entered.
  Velocity capped at 0x7F, channel at 16.
- Selection/clipboard (cut/copy/paste/delete) and .sng import/export
  compatible with the reference desktop tool.
- A `profile` build type (non-debuggable, debug-signed) for realistic
  on-device performance testing.
- Developer handover documentation under docs/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:15:13 +02:00