Skip to content

touchdesigner: run message objects once per cook + fire impulses only on pulse#136

Closed
jcelerier wants to merge 1 commit into
mainfrom
fix/td-message-cook-and-pulse
Closed

touchdesigner: run message objects once per cook + fire impulses only on pulse#136
jcelerier wants to merge 1 commit into
mainfrom
fix/td-message-cook-and-pulse

Conversation

@jcelerier

Copy link
Copy Markdown
Member

Two related CHOP_MESSAGE binding bugs, found by the golden differential (#134) on avnd_test_impulse (a bang → counter: TD read Count=3, the raw-C++ oracle Count=0).

Bug 1 — the object ran multiple times per cook

message_processor::getGeneralInfo — a metadata callback TD invokes several times per cook — called invoke_effect, i.e. ran the object's operator(). Proven by the op's own total_cooks info channel: total_cooks = 1 (one execute) yet operator() ran . This mis-advances every stateful object (counters, oscillators, RNG); stateless ones were merely idempotent, so it hid. Moved the input-read + run into run_effect(), called once from execute(); getGeneralInfo is now metadata-only. (audio_processor was already correct — it runs in execute().)

Bug 2 — impulses fired on every cook

The generic per-cook update() fallback called pulse(field, name), and pulse() for an optional-valued port (impulse_button) does field.value = {std::in_place}engaging it every cook. So a bang fired on every frame instead of only when clicked. Impulses must engage solely via the onPulse callback (already wired at message_processor → parameter_update::pulse); the per-cook update must not. Emptied the generic update fallback.

Result

avnd_test_impulse Count 3 → 0 (matches the oracle); differential match 50 → 51. Beyond the test: stateful message objects no longer advance N× per cook, and bang/impulse buttons no longer trigger continuously in normal TD use.

🤖 Generated with Claude Code

… on pulse

Two related CHOP_MESSAGE binding bugs, found via the golden differential
(feature/td-golden-differential) on avnd_test_impulse (Count read 3, golden 0):

1. The object ran multiple times per cook. message_processor::getGeneralInfo --
   a metadata callback TD invokes several times per cook -- called invoke_effect
   (i.e. ran the object's operator()). Confirmed via the op's own total_cooks
   info channel: total_cooks=1 (one execute) but operator() ran 3x. This
   mis-advances every STATEFUL object (counters, oscillators, RNG); stateless
   ones were merely idempotent so it went unnoticed. Moved the input-read + run
   into run_effect(), called exactly once from execute(); getGeneralInfo is now
   metadata-only (audio_processor was already correct -- it runs in execute()).

2. Impulses fired on every cook. The generic per-cook update() fallback called
   pulse(field, name), which for an optional-valued port (impulse_button) does
   field.value = {std::in_place} -- engaging it. So a bang fired on every frame
   instead of only when clicked. Impulses must engage solely via the onPulse
   callback (already wired: message_processor -> parameter_update::pulse); the
   per-cook update must not. Emptied the generic update fallback.

Result: avnd_test_impulse Count 3 -> 0 (matches the raw-C++ oracle); differential
match 50 -> 51. Beyond the test, this fixes stateful objects advancing N x per
cook and bang/impulse buttons triggering continuously in real TD use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011s7huWR2wFsLFiMJPjx1z2
@jcelerier

Copy link
Copy Markdown
Member Author

Superseded by #137, which combines all outstanding work into a single PR.

@jcelerier jcelerier closed this Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant