Tanpura: the waveguide string has no jawari, and what a credible one needs #104

Open
opened 2026-09-28 04:11:33 +00:00 by bcox · 0 comments
Owner

bcox asked on 2026-09-26 what Tymbal needs to get a credible tanpura. This ticket records the answer so a spec can start from it. Nothing here is built, measured or scheduled. It is an assessment from reading the tree, rechecked at 5aa82002 on main. No patch was rendered and nothing was listened to.

On the citations: every paper below is cited from memory and was not checked against the source in that session. Treat the attributions, and the two claims marked unverified, as leads to confirm before a spec leans on them.

Summary

The tanpura's identity is a nonlinearity the waveguide string does not have. Everything else it needs is already in the tree.

  • One new curve at one statement of the waveguide loop is the smallest thing that could work.
  • A distributed bridge is the version the literature says gives the real sound.
  • Resynthesis of a recorded string gets a credible drone with no engine work, and produces the reference the physical model is judged against.

What makes it a tanpura

The jawari bridge is a wide, gently curved surface, with a cotton thread under the string near it. The string is constrained on one side only. On the half-cycle toward the bridge it wraps progressively onto the curve and its effective length shortens. On the other half it swings free.

Two consequences follow, both described by Raman in 1921:

  • Every harmonic is present whatever the pluck point. A linear string plucked at its midpoint has no even partials. A tanpura string does.
  • Energy migrates upward after the pluck. The low partials feed the high ones for some hundreds of milliseconds, so the sound gets brighter before it gets darker.

That inverted brightness trajectory is the identity. The buzz is secondary. The thread's position tunes where the migration happens and how fast.

Literature, from memory:

  • Raman (1921), On some Indian stringed instruments.
  • Burridge, Kappraff and Morshedi (1982): a string against a one-sided curved obstacle, the precursor derived.
  • Valette and Cuesta, Mécanique de la corde vibrante: the tanpura and the role of the thread.
  • Van Walstijn, Bridges and Mehes (DAFx 2016), A real-time synthesis oriented tanpura model: a modal string against a curved unilateral barrier.
  • Chatziioannou and Van Walstijn (2015), and Bilbao, Torin and Chatziioannou (2015): energy-consistent collision schemes.
  • Krishnaswamy and Smith (2003): a digital waveguide against a rigid spatial obstacle.

What is already here

Need Where it lives State
The string dsp/waveguide.h, dsp/waveguide_layer.h Pluck, loss, dispersion cascade, tuning solve, two pickups. Linear for a burst drive.
A home for the nonlinearity dsp/junction.h Memoryless, table-driven curves with an exhaustive passivity test. kFelt is already a Hunt-Crossley contact.
The body modal layer at channel scope, over the cross-voice bus Exists. Fed one way, it is a body filter.
A dull excitation Drive::kInput in dsp/waveguide_layer.h Exists. Another slot can excite the line.
The four-string cycle the sequencer, or external MIDI Exists.
A just fifth on the Pa string EventKind::kTuning in fabric/event.h Exists.
Analysis of a recorded string //tools:resynth, harmonic scan Exists. One plucked string is monophonic and harmonic, which is that scan's regime.

For a burst drive the waveguide loop is entirely linear: a sign, a one-pole loss, a gain, the cascade and the tuning allpass. A linear recirculating string can only darken after the pluck. No patch of today's engine can be a tanpura, however it is tuned.

The passive bridge design already parked this item twice, in docs/superpowers/specs/2026-09-23-passive-bridge-design.md under both "Out of scope" lists: "The jawari. Still local to one voice, still not ruled out." This ticket is that item.

What it needs

1. A nonlinear reflection at the far end

In WaveguideLoop::Process, the statement that forms reflected from incident applies the bore's sign and nothing else. The smallest jawari is a curve at that point:

  • It passes the half-cycle swinging away from the bridge unchanged.
  • It compresses the half-cycle swinging into the bridge.
  • It has a knee, standing in for the thread's position, and a depth.

The property it must hold is contraction: |f(x)| <= |x| everywhere. That makes the loop passive with no further argument. Note that this is a different condition from the one junction_test.cc checks. The junction's contract is x * f(x) >= 0 and |f(x)| <= 1, which bounds a force, not a reflection. A reflection curve needs its own exhaustive sweep for contraction, whether or not it shares the junction's table machinery.

What it does dynamically: every lap converts some of the fundamental's energy into upper harmonics while the amplitude sits above the knee. As the string decays below the knee the conversion stops, and the loss filter drains the top. That is the precursor's rise and fall.

Cost: one table lookup per sample and no interior writes to the line.

What it cannot do: the progressive wrap. A memoryless curve at one point cannot make the effective length depend on amplitude within a half-cycle, and every model in the literature above is distributed. Expect a buzz with a bloom. Whether that reads as tanpura or as sitar is a listening question and is open.

Two tests, each with its injection:

  • Even partials from a symmetric pluck. Fill the line symmetrically about its midpoint. With the curve off, even-partial energy stays under a floor. With it on, it rises above one. Inject: make the curve odd-symmetric.
  • The precursor. The spectral centroid of the rendered pluck rises over the first laps and then falls. Inject: the same, or set the depth to zero.

Open risk: the curve runs inside the loop at 1x. The waveguide design deferred oversampling as a risk "to be measured, not believed", and that applies here with more force, since this curve exists to make high partials.

2. A distributed bridge, if the listen says sitar

The line lives in a store the voice owns, outside the variant, and is indexable. Displacement at a point near the far end is the sum of the two traveling waves there. In a single recirculating line those are one index and its mirror, approximately, since the loss and the allpasses are lumped at one point and not distributed.

For K points across the bridge region:

  • read both waves and form the displacement;
  • compare it against a curved profile offset by the thread's position;
  • where the string has penetrated, correct both waves.

That is the construction in Krishnaswamy and Smith. Unverified, from memory: their rigid-obstacle scheme can add energy at the contact boundary. If so, the correction needs the same discipline as every other stage in this loop, a contraction rounded toward zero, or a penalty formulation in its place.

Cost: a few line reads and writes per point per sample. K is a degradation rung in the way the cascade's depth is, and K of zero is today's loop. New patch fields would be the curvature, the thread offset and K.

The modal route is the alternative, and it is the one Van Walstijn's model takes. dsp/mode_bank.h has collocated sites and a delay-free inject. It has two sites (kModeBankSites), where a distributed barrier needs several, and its contact is explicit. Unverified, from memory: the modal collision papers use implicit energy-conserving solves because a stiff contact is unstable under an explicit one at audio rate. A bone bridge is stiff. The waveguide route looks like the better fit for fixed point, and that is a judgment, not a measurement.

3. A dull pluck

Drive::kBurst injects white noise scaled by the strike and the breath envelope. That fills the loop bright. A bright onset masks the bloom, which is the whole point of the instrument.

Drive::kInput already lets another slot excite the line, so an unvoiced layer through its own filter is a patch-level answer. Try that before adding a drive.

4. Probably nothing for loss, body or cycle

  • Loss. The top of the spectrum has to survive for seconds for the migration to be heard, so the loss coefficient will sit near transparent and the gain near one. Whether the one-pole can give a long ring and a gentle enough tilt together is untried.
  • Body. A channel-scope modal layer fed one way. The passive bridge ledger's finding, that a resonant board used as a bridge detunes its strings, is about a two-way junction and does not apply to a one-way body.
  • String-to-string coupling. Real, and secondary. Not needed for a first listen.
  • Cycle. Four notes on a track.

The shortcut: resynthesis

Resynthesize a recording of one plucked string. The per-harmonic amplitude tracks are the precursor, so the migration comes with the analysis.

Capacities, read from plugin/program.h:

Constant Value
kMaxHarmonics 512
kMaxWaves 32
kMaxFrames 512

Whether 32 waves resolve the migration well enough is untried.

What it loses:

  • A real pluck lands on a string still moving from the last cycle, so each cycle differs. A resynthesized one does not.
  • The bright region of a real tanpura is set by the bridge and stays put in frequency. A resynthesized one moves with the fundamental when retuned. Over a few semitones that should be small. An instrument an octave away wants its own analysis.

For a drone that nobody plays expressively, both are small losses.

This needs a recording the repo is allowed to carry. Per CLAUDE.md, a checked-in recording grows the bank by its size for good.

Suggested order

  1. Record and analyze one string. Measure its centroid trajectory and its even-partial levels. Those become the targets for the tests in section 1, and the resynthesized patch is a credible tanpura by itself.
  2. Build the lumped reflection curve and listen against the resynthesis.
  3. Build the distributed bridge only if step 2 sounds like a sitar.

Open questions for a spec

  • Does a lumped curve read as tanpura? Listening only.
  • How much does the curve alias at 1x, and does the loss filter's rolloff cover it?
  • Does the curve share dsp/junction.h's table and interpolation, or live beside the loop? Either way it needs a contraction sweep of its own.
  • The new fields append to the WVGD record with a minor bump. Which ones are mod destinations? The knee is the obvious candidate, since moving the thread is how a player tunes the jawari.
  • Where does a recording come from?
bcox asked on 2026-09-26 what Tymbal needs to get a credible tanpura. This ticket records the answer so a spec can start from it. **Nothing here is built, measured or scheduled.** It is an assessment from reading the tree, rechecked at `5aa82002` on `main`. No patch was rendered and nothing was listened to. **On the citations:** every paper below is cited from memory and was not checked against the source in that session. Treat the attributions, and the two claims marked *unverified*, as leads to confirm before a spec leans on them. ## Summary The tanpura's identity is a nonlinearity the waveguide string does not have. Everything else it needs is already in the tree. - One new curve at one statement of the waveguide loop is the smallest thing that could work. - A distributed bridge is the version the literature says gives the real sound. - Resynthesis of a recorded string gets a credible drone with no engine work, and produces the reference the physical model is judged against. ## What makes it a tanpura The jawari bridge is a wide, gently curved surface, with a cotton thread under the string near it. The string is constrained on one side only. On the half-cycle toward the bridge it wraps progressively onto the curve and its effective length shortens. On the other half it swings free. Two consequences follow, both described by Raman in 1921: - **Every harmonic is present whatever the pluck point.** A linear string plucked at its midpoint has no even partials. A tanpura string does. - **Energy migrates upward after the pluck.** The low partials feed the high ones for some hundreds of milliseconds, so the sound gets brighter before it gets darker. That inverted brightness trajectory is the identity. The buzz is secondary. The thread's position tunes where the migration happens and how fast. Literature, from memory: - Raman (1921), *On some Indian stringed instruments*. - Burridge, Kappraff and Morshedi (1982): a string against a one-sided curved obstacle, the precursor derived. - Valette and Cuesta, *Mécanique de la corde vibrante*: the tanpura and the role of the thread. - Van Walstijn, Bridges and Mehes (DAFx 2016), *A real-time synthesis oriented tanpura model*: a modal string against a curved unilateral barrier. - Chatziioannou and Van Walstijn (2015), and Bilbao, Torin and Chatziioannou (2015): energy-consistent collision schemes. - Krishnaswamy and Smith (2003): a digital waveguide against a rigid spatial obstacle. ## What is already here | Need | Where it lives | State | |---|---|---| | The string | `dsp/waveguide.h`, `dsp/waveguide_layer.h` | Pluck, loss, dispersion cascade, tuning solve, two pickups. Linear for a burst drive. | | A home for the nonlinearity | `dsp/junction.h` | Memoryless, table-driven curves with an exhaustive passivity test. `kFelt` is already a Hunt-Crossley contact. | | The body | modal layer at channel scope, over the cross-voice bus | Exists. Fed one way, it is a body filter. | | A dull excitation | `Drive::kInput` in `dsp/waveguide_layer.h` | Exists. Another slot can excite the line. | | The four-string cycle | the sequencer, or external MIDI | Exists. | | A just fifth on the Pa string | `EventKind::kTuning` in `fabric/event.h` | Exists. | | Analysis of a recorded string | `//tools:resynth`, harmonic scan | Exists. One plucked string is monophonic and harmonic, which is that scan's regime. | For a burst drive the waveguide loop is entirely linear: a sign, a one-pole loss, a gain, the cascade and the tuning allpass. A linear recirculating string can only darken after the pluck. **No patch of today's engine can be a tanpura, however it is tuned.** The passive bridge design already parked this item twice, in `docs/superpowers/specs/2026-09-23-passive-bridge-design.md` under both "Out of scope" lists: "The jawari. Still local to one voice, still not ruled out." This ticket is that item. ## What it needs ### 1. A nonlinear reflection at the far end In `WaveguideLoop::Process`, the statement that forms `reflected` from `incident` applies the bore's sign and nothing else. The smallest jawari is a curve at that point: - It passes the half-cycle swinging away from the bridge unchanged. - It compresses the half-cycle swinging into the bridge. - It has a knee, standing in for the thread's position, and a depth. **The property it must hold is contraction:** `|f(x)| <= |x|` everywhere. That makes the loop passive with no further argument. Note that this is a different condition from the one `junction_test.cc` checks. The junction's contract is `x * f(x) >= 0` and `|f(x)| <= 1`, which bounds a force, not a reflection. A reflection curve needs its own exhaustive sweep for contraction, whether or not it shares the junction's table machinery. What it does dynamically: every lap converts some of the fundamental's energy into upper harmonics while the amplitude sits above the knee. As the string decays below the knee the conversion stops, and the loss filter drains the top. That is the precursor's rise and fall. Cost: one table lookup per sample and no interior writes to the line. What it cannot do: the progressive wrap. A memoryless curve at one point cannot make the effective length depend on amplitude within a half-cycle, and every model in the literature above is distributed. Expect a buzz with a bloom. Whether that reads as tanpura or as sitar is a listening question and is open. Two tests, each with its injection: - **Even partials from a symmetric pluck.** Fill the line symmetrically about its midpoint. With the curve off, even-partial energy stays under a floor. With it on, it rises above one. *Inject: make the curve odd-symmetric.* - **The precursor.** The spectral centroid of the rendered pluck rises over the first laps and then falls. *Inject: the same, or set the depth to zero.* Open risk: the curve runs inside the loop at 1x. The waveguide design deferred oversampling as a risk "to be measured, not believed", and that applies here with more force, since this curve exists to make high partials. ### 2. A distributed bridge, if the listen says sitar The line lives in a store the voice owns, outside the variant, and is indexable. Displacement at a point near the far end is the sum of the two traveling waves there. In a single recirculating line those are one index and its mirror, approximately, since the loss and the allpasses are lumped at one point and not distributed. For K points across the bridge region: - read both waves and form the displacement; - compare it against a curved profile offset by the thread's position; - where the string has penetrated, correct both waves. That is the construction in Krishnaswamy and Smith. *Unverified, from memory:* their rigid-obstacle scheme can add energy at the contact boundary. If so, the correction needs the same discipline as every other stage in this loop, a contraction rounded toward zero, or a penalty formulation in its place. Cost: a few line reads and writes per point per sample. K is a degradation rung in the way the cascade's depth is, and K of zero is today's loop. New patch fields would be the curvature, the thread offset and K. The modal route is the alternative, and it is the one Van Walstijn's model takes. `dsp/mode_bank.h` has collocated sites and a delay-free inject. It has two sites (`kModeBankSites`), where a distributed barrier needs several, and its contact is explicit. *Unverified, from memory:* the modal collision papers use implicit energy-conserving solves because a stiff contact is unstable under an explicit one at audio rate. A bone bridge is stiff. The waveguide route looks like the better fit for fixed point, and that is a judgment, not a measurement. ### 3. A dull pluck `Drive::kBurst` injects white noise scaled by the strike and the breath envelope. That fills the loop bright. A bright onset masks the bloom, which is the whole point of the instrument. `Drive::kInput` already lets another slot excite the line, so an unvoiced layer through its own filter is a patch-level answer. Try that before adding a drive. ### 4. Probably nothing for loss, body or cycle - **Loss.** The top of the spectrum has to survive for seconds for the migration to be heard, so the loss coefficient will sit near transparent and the gain near one. Whether the one-pole can give a long ring and a gentle enough tilt together is untried. - **Body.** A channel-scope modal layer fed one way. The passive bridge ledger's finding, that a resonant board used as a bridge detunes its strings, is about a two-way junction and does not apply to a one-way body. - **String-to-string coupling.** Real, and secondary. Not needed for a first listen. - **Cycle.** Four notes on a track. ## The shortcut: resynthesis Resynthesize a recording of one plucked string. The per-harmonic amplitude tracks are the precursor, so the migration comes with the analysis. Capacities, read from `plugin/program.h`: | Constant | Value | |---|---| | `kMaxHarmonics` | 512 | | `kMaxWaves` | 32 | | `kMaxFrames` | 512 | Whether 32 waves resolve the migration well enough is untried. What it loses: - A real pluck lands on a string still moving from the last cycle, so each cycle differs. A resynthesized one does not. - The bright region of a real tanpura is set by the bridge and stays put in frequency. A resynthesized one moves with the fundamental when retuned. Over a few semitones that should be small. An instrument an octave away wants its own analysis. For a drone that nobody plays expressively, both are small losses. This needs a recording the repo is allowed to carry. Per CLAUDE.md, a checked-in recording grows the bank by its size for good. ## Suggested order 1. **Record and analyze one string.** Measure its centroid trajectory and its even-partial levels. Those become the targets for the tests in section 1, and the resynthesized patch is a credible tanpura by itself. 2. **Build the lumped reflection curve** and listen against the resynthesis. 3. **Build the distributed bridge** only if step 2 sounds like a sitar. ## Open questions for a spec - Does a lumped curve read as tanpura? Listening only. - How much does the curve alias at 1x, and does the loss filter's rolloff cover it? - Does the curve share `dsp/junction.h`'s table and interpolation, or live beside the loop? Either way it needs a contraction sweep of its own. - The new fields append to the `WVGD` record with a minor bump. Which ones are mod destinations? The knee is the obvious candidate, since moving the thread is how a player tunes the jawari. - Where does a recording come from?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
bcox/tymbal#104
No description provided.