Phase Magnet — User Guide

Weaves two mono source streams into one output by alternating or probabilistically switching between time-domain segments, choosing destination splice positions from proportional alignment, local waveform matching, and controlled random displacement.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 1.8 (2026) License: MIT License Repo: GitHub
Contents:

What this does

Phase Magnet keeps one cursor in Sound A and one in Sound B. It copies a segment from the active source, creates a short transition into the other source around a predicted destination position, advances both cursors, and then either changes source or continues according to the selected alternation mode.

Current channel contract: processing uses channel 1 only from each selected Sound. The result is always mono. Stereo or multichannel spatial information is not preserved.

If the source sample rates differ, Sound B is resampled to the sample rate of Sound A with Praat precision 50. Both internal processing copies are shifted to a zero-based time domain before sample/time conversions are performed.

What “phase-aware” means here

The word phase in this tool refers to time-domain waveform compatibility at splice points: zero crossings, crossing direction, or sample-amplitude/slope similarity.

This is not spectral phase analysis, a phase vocoder, FFT cross-synthesis, or phase-locking of sinusoidal components. No Spectrum object is used by the engine.

The three matching modes therefore operate directly on waveform samples and Praat zero-crossing queries.

Quick start

  1. Select exactly two Sound objects.
  2. Run Phase_Magnet.praat.
  3. Choose Custom or one of the four named presets.
  4. Choose which source starts, and whether source changes are strict or probabilistic.
  5. For Custom, set crossfade, patience, magnetism, segment range, matching mode and randomness.
  6. Run the script. The output is named PhaseMagnet_<A>_<B>.
Named presets overwrite crossfade, patience, magnetism, segment range, matching mode, randomness and alternation mode. They do not overwrite Start_source, Play_result or Draw_visualization.

Segment & cursor engine

1. Choose a target segment length

At every iteration the script draws a new target length uniformly between the effective minimum and maximum segment lengths:

targetSegment = round( effectiveMin + uniform(0,1) × (effectiveMax - effectiveMin) )

This random segment-length draw happens regardless of the Randomness parameter. Consequently, Randomness = 0 does not make the tool deterministic.

2. Establish the active-source endpoint

For Zero Crossing and Same-Slope Zero Crossing modes, the requested endpoint is first moved to the nearest zero crossing only if that crossing remains inside:

If no acceptable crossing is found, the original target endpoint is retained. In Nearest Amplitude Match mode, the target endpoint is used directly.

3. Advance the output

Each loop writes:

steady segment + one equal-power transition block

After the transition, both source cursors are advanced beyond the audio consumed by that iteration.

Magnetism & randomness

After the active-source endpoint has been chosen, Phase Magnet calculates where the corresponding point would lie in the other source if both recordings were aligned by proportional progress.

If A is active: idealTarget = (timeInA / durationA) × durationB If B is active: idealTarget = (timeInB / durationB) × durationA

The script also knows the current cursor of the other source, called the drift target. In sample coordinates:

searchCenter = Magnetism × proportionalTarget + (1 - Magnetism) × otherCurrentCursor

where Magnetism is expressed internally from 0 to 1.

Magnetism does not force the final splice to equal the proportional position. It chooses the centre of the matching search. The selected zero crossing or amplitude match can still move away from that centre within the permitted search region.

Randomness

Randomness adds an integer jitter to that search centre:

maxJitter = Randomness / 100 × effectivePatienceSamples searchCenter += randomInteger(-maxJitter, +maxJitter)

This parameter controls destination-position jitter only. It does not control the always-random segment-length selection or the random decision used by Probabilistic alternation.

There is no Random_seed control, so repeated runs can differ even with identical settings.

Three splice modes

1. Zero crossing

The other source is queried with Praat Get nearest zero crossing at the predicted search centre. The crossing is accepted only if it lies inside the effective ±Patience interval and leaves enough samples for the transition. Otherwise the predicted search centre itself is used.

Praat's nearest-zero-crossing command uses linear interpolation between samples. The script then converts the returned time back to its working sample index.

2. Same-slope zero crossing

This mode first obtains a nearest zero crossing as a fallback, then searches for a crossing whose local slope has the same sign as the endpoint of the active-source segment.

The bounded probe strategy is:

within first 100 ms of Patience: probe every 1 ms beyond 100 ms, if Patience is larger: probe every 10 ms each probe: ask Praat for its nearest zero crossing choose: nearest candidate with matching slope sign

If no same-slope candidate is found, the mode falls back to the ordinary nearest zero crossing when one was valid; otherwise it falls back to the predicted centre. Thus “same-slope” is preferred but is not an absolute requirement when no matching crossing is available.

3. Nearest amplitude match

This mode scans samples directly around the predicted position and minimizes:

score = |candidateAmplitude - sourceEndpointAmplitude| + slopePenalty + 0.5 × normalizedDistance slopePenalty = 0.0 if slope signs match 0.2 otherwise normalizedDistance = |candidateIndex - searchCenter| / (effectivePatienceSamples + 1)

The scan itself is limited to ±100 ms, even when Patience is larger. This keeps the scalar sample search bounded.

Equal-power transition block

Phase Magnet does not call Praat Concatenate with overlap. It writes its own transition region immediately after every steady segment.

u = 0 ... 1 across the transition output = sqrt(1-u) × currentSource + sqrt(u) × otherSource

The first transition sample is entirely the current source; the last is entirely the other source. The squared gains sum to 1 at every point, giving an equal-power coefficient law.

This is a transition block appended to the output timeline, not an overlap that shortens the montage. Every iteration contributes its steady segment duration plus the transition duration.

No final peak limiter or normalization follows the assembly. Equal-power mixing can therefore create peaks above either source when the two transition signals reinforce each other.

Alternation modes

Strict A-B-A-B

After every segment/transition block, the active source changes unconditionally:

A → B → A → B → ...

Probabilistic

The chance of changing source depends on the actual segment length relative to the effective segment range:

P(switch) = (segmentLength - effectiveMin + 1) / (effectiveMax - effectiveMin + 1)

The value is capped at 1. Longer segments are therefore more likely to trigger a source change; shorter ones are more likely to continue with the same active source.

Probabilistic mode does not mean a fixed 50/50 source choice. It is a length-dependent decision made after each rendered segment.

Four named presets + Custom

PresetCrossfadePatienceMagnetismSegment requestMatchingRandomnessAlternation
Glitch Stutter (Fast)2 ms20 ms95%10–45 msZero crossing50%Strict
Ambient Morph (Slow)150 ms500 ms60%400–1200 msSame-slope ZC10%Probabilistic
Rhythmic Alternation5 ms50 ms100%125–250 msSame-slope ZC0%Strict
Chaotic Scatter15 ms300 ms20%20–800 msAmplitude match100%Probabilistic
Custom defaults10 ms100 ms80%50–500 msSame-slope ZC20%Strict
Rhythmic Alternation contains no BPM or beat grid. Its 125–250 ms segment lengths are still randomly selected in milliseconds. The preset is rhythmically regular only in the sense of strict source alternation and a comparatively narrow duration range.

Parameters & effective limits

ParameterCustom defaultExact role
Crossfade_length_ms10Requested equal-power transition duration; must be ≥ 0.
Patience_ms100Requested destination-search half-range; must be ≥ 0.
Magnetism80%Interpolation between proportional target and the other source's current cursor.
Minimum_segment_ms50Positive requested lower segment limit.
Maximum_segment_ms500Positive requested upper limit; must be ≥ requested minimum.
Matching_modeSame-slope ZCZero crossing, same-slope zero crossing, or amplitude/slope score.
Randomness20%0–100%; controls only random displacement of the destination search centre.
Start_sourceAActive source for the first steady segment.
Alternation_modeStrictAlways switch or use the length-dependent probability.
Play_resultOnPlay the completed mono result.
Draw_visualizationOnDraw the Phase Magnet process view.

Sample-domain adjustments

The public values are converted to samples at Sound A's sample rate:

effectiveCrossfadeSamples = max(2, round(requestedCrossfade × sr)) effectivePatienceSamples = max(1, round(requestedPatience × sr)) effectiveMinSegment = max(effectiveCrossfadeSamples + 2, round(requestedMin × sr)) effectiveMaxSegment = max(effectiveMinSegment + 2, round(requestedMax × sr))

Consequently:

Both sources must be long enough to contain the effective maximum segment plus transition reserve, otherwise the script stops before rendering.

Visualization

The v1.8 Picture view contains:

  1. Source A waveform in blue.
  2. Source B waveform in orange.
  3. Hybridized output waveform in neutral dark gray.
  4. Splice-source timeline: colored blocks identify the active source of each steady segment.
  5. Summary strip: preset, matching mode, magnetism, randomness, splice counts, crossfade request, segment request and output duration.
The source waveform panels draw the original selected Sound objects, while the DSP engine uses only channel 1. For multichannel inputs the pictures therefore show the source objects, not a dedicated display of the mono processing streams.

The three waveform panels use Praat's automatic amplitude ranges independently. They should not be read as a shared-scale level comparison.

The colored timeline blocks cover only the steady portions. The equal-power transition blocks lie between them and are not assigned a single A/B color because both sources contribute there.

Output behavior

Output duration

The initial output buffer has capacity equal to the combined input sample counts, but that is only a safety allocation. The delivered result is trimmed to the samples actually written.

actualOutputSamples = Σ over rendered iterations (steadySegmentSamples + effectiveCrossfadeSamples)

Rendering stops as soon as either source cursor no longer has enough remaining material for the effective maximum segment and transition reserve, or the output-capacity guard is reached. The output duration is therefore not defined as the sum of the two source durations.

Further reading