Fuzzy Time Re-Composer — Rule-Based Temporal Recomposition

Segments a Sound, derives seven relative acoustic descriptors, maps them to fuzzy memberships, combines six weighted rules into a continuous time-warp factor plus a discrete gesture, reorders the segments, and renders a new multichannel timeline with adaptive crossfades.

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

What this does

Fuzzy Time Re-Composer treats temporal organization as a compositional variable. It does not apply one uniform stretch or one fixed granular process to the whole file. Instead, every detected segment receives its own acoustic description, fuzzy-rule activations, continuous warp factor, and winning gesture.

  1. Segment the analysis signal using adaptive onsets, a fixed grid, or accumulated pitch periods.
  2. Measure seven descriptors and normalize each descriptor across this file's segments.
  3. Map the normalized descriptors to fuzzy memberships.
  4. Evaluate six rule activations.
  5. Defuzzify all rule activations into one continuous time-warp factor, while the strongest rule supplies the discrete gesture label.
  6. Reorder the segment vector.
  7. Render preserve/dilate/compress material, shuffled micro-grains, forward+retrograde pairs, or ostinato repetitions into a new timeline.
Analysis and rendering are deliberately separated. Analysis uses a cancellation-safe mono reference and normalizes only that analysis copy to 0.5 peak. Rendering uses the untouched source channels, applies the same deterministic decisions to every channel, and preserves the source channel count.

What is fuzzy logic?

Ordinary binary classification asks whether a condition is true or false. Fuzzy logic allows graded membership between 0 and 1. In this script, a segment can simultaneously have strong transient membership, moderate entropy membership, weak sustain membership, and so on. Several rules can therefore be active at once.

The important distinction is:

A winning gesture does not erase the influence of the other rules. For example, a segment labelled Preserve can still receive a warp slightly above or below 1× because the warp is defuzzified from all six activations.

Quick start

  1. Select exactly one Sound object. The input must be at least 0.25 s long.
  2. Run Fuzzy_Time_Recomposer.praat.
  3. Choose Manual or one of the five presets.
  4. Choose the segmentation mode and set the target Segment_length_ms.
  5. Set Pitch_floor and Pitch_ceiling for pitch-period segmentation, pitch/motion analysis, and PSOLA warping.
  6. Adjust Fuzzy_sensitivity, Fragmentation, Dilation, and Mirroring.
  7. Choose a reorder mode and optional macro trajectory.
  8. Use a fixed Random_seed for repeatable results. Set it to 0 for an unpredictable run; the concrete seed is printed so that run can still be reproduced later.
  9. Run the script. The output is named <source>_fuzzytime.

Segmentation

Adaptive onsets

The analysis Intensity contour is sampled every 5 ms. The script measures the positive frame-to-frame intensity derivative and computes:

onsetThreshold = mean(positive derivatives) + 1.0 × SD(positive derivatives)

A boundary is accepted at a local positive-derivative peak only when it is sufficiently separated from the previous accepted onset and stays away from the file edges. Fuzzy_sensitivity does not alter this onset threshold.

Fixed grid

Boundaries are placed at the requested Segment_length_ms interval, with a final segment covering the remaining material.

Pitch periods

Praat creates a periodic PointProcess between Pitch_floor and Pitch_ceiling. Pitch-period intervals are accumulated until they reach approximately the requested segment length, then a boundary is created. This is therefore period-aligned segmentation at the requested timescale, not one segment per pitch period.

Zero-crossing snapping

Adaptive-onset and fixed-grid interior boundaries are moved to the nearest channel-1 zero crossing only when the moved boundary remains safely between both neighboring boundaries. Pitch-period boundaries are not re-snapped.

The script supports up to 400 segments. If the cap is reached, later material is absorbed into the final segment rather than silently disappearing.

Seven descriptors & fuzzy memberships

MembershipRaw descriptorImplemented measurement
TransientAttack strengthEarly intensity peak minus intensity near the segment onset.
SustainHarmonicityMean HNR from Praat Harmonicity (cc).
MotionPitch motionF0 standard deviation in semitones.
HFHigh-frequency ratioEnergy from 2000 Hz to Nyquist divided by total spectral energy.
EntropyCoarse spectral entropyNormalized Shannon entropy over up to seven broad frequency bands.
LowLow-frequency ratioEnergy from 0–500 Hz divided by total spectral energy.
RiseEnergy slopeLate-segment mean intensity minus early-segment mean intensity.

Self-calibration

Each raw descriptor is min–max normalized across the segments of the current file:

n = (x - fileMinimum) / (fileMaximum - fileMinimum)

If a descriptor is effectively constant across the file, all segments receive the neutral value 0.5. These values are relative positions within the descriptor range, not percentile ranks; for example, 0.9 does not mean “top 10% of segments.”

Membership ramp

muWidth = 0.80 - 0.60 × Fuzzy_sensitivity muLo = 0.50 - muWidth / 2 muHi = 0.50 + muWidth / 2 mu(x) = 0 if x <= muLo (x-muLo)/(muHi-muLo) between muLo and muHi 1 if x >= muHi

Fuzzy_sensitivity is clamped to 0–1. Higher values narrow the ramp around 0.5 and make low/high evidence separate more sharply.

Rule base & continuous warp

The three user biases are not probabilities and do not need to sum to 1. They scale rule activations together with the macro trajectory.

Trajectory-dependent bias weights

fragW = (0.4 + 1.2×Fragmentation) × (0.7 + 0.6×chaos) dilW = (0.4 + 1.2×Dilation) × (1.3 - 0.6×chaos) mirW = 0.4 + 1.2×Mirroring ostW = (0.4 + 1.2×(1-Fragmentation)) × 0.75 prsW = 1.4 - 0.8×Fragmentation

Six rule activations

Preserve = muSustain × (1-muTransient) × prsW Dilate = muSustain × (1-muMotion) × dilW Compress = muHF × (1-0.60×muTransient) × fragW Granulate = muTransient × (0.50+0.50×muEntropy) × fragW × (0.80+0.60×Fragmentation) Retrograde= min(muRise, 1-muMotion) × mirW Ostinato = muSustain × muLow × ostW × (1-0.75×Dilation)

Log-domain defuzzification

Each rule contributes a target time factor:

warp = exp( [aDilate×ln(Max_dilation) + aCompress×ln(Max_compression) + aGranulate×ln(max(Max_compression,0.7))] / sum(all six activations) )

The result is finally clamped to the requested Max_compression ... Max_dilation range. The discrete gesture is the activation argmax. The visualization also compares the winner with the runner-up; a lead below 25% is shown as a ring rather than a filled marker.

What the six gestures actually render

GestureAdditional structural action
PreserveNo additional structural action; render one segment after applying the continuously defuzzified warp.
DilateSame one-segment render path. The Dilate rule strongly pulls the continuous warp toward Max_dilation.
CompressSame one-segment render path. The Compress rule pulls the continuous warp toward Max_compression.
GranulateWarp first, then split into micro-grains and Fisher–Yates shuffle their order. Micro-grain length also contracts as the macro trajectory becomes more chaotic.
RetrogradeWarp first, then emit two items: the forward segment followed by a reversed copy. The joint fade allowance is 1.6× the segment's normal fuzzy fade.
OstinatoWarp first, then repeat the segment 2–4 times according to Sustain membership.

Reordering & macro trajectory

ModeExact behavior
SequentialKeep source segment order.
Fuzzy nearest-profile chainStart at source segment 1, then greedily choose the nearest unused segment in the seven-dimensional fuzzy-membership space.
ShuffleFisher–Yates permutation.
Retrograde orderReverse the segment order. Segment audio itself is not reversed unless its discrete gesture is Retrograde.
Interleave (odd then even)Order = 1,3,5,… followed by 2,4,6,… . It does not alternate odd/even one by one.

Trajectory

Trajectory is evaluated over each segment's output slot after reordering:

The trajectory does not add random swaps by itself. It progressively changes the rule biases — most notably increasing the fragmentation weighting while reducing the dilation weighting as chaos rises.

Five presets

PresetSegmentFrag.DilationMirrorWarp rangeReorderTrajectoryMicro grain
Micro-shuffle90 ms0.900.150.100.45–1.6×SequentialNone16 ms
Frozen resonance200 ms0.100.950.100.60–8.0×SequentialNone30 ms
Directional mirroring160 ms0.200.350.950.50–2.5×SequentialNone24 ms
Poly-temporal interleave110 ms0.550.450.200.35–3.0×Odd then evenNone20 ms
Entropy injection130 ms0.600.400.250.30–3.5×Nearest-profileOrder → chaos18 ms
Presets override segment length, fuzzy sensitivity, the three rule-bias controls, warp range, reorder mode, trajectory, and internal micro-grain/fade values. They do not override Segment_mode, Pitch_floor/ceiling, Random_seed, Safety_peak, Draw_analysis, or Play_result.

Time warping, rendering & crossfades

PSOLA time warping

Warping uses Praat Lengthen (overlap-add). A single pass is kept between 0.25× and 3×; larger requested factors are reached by chaining passes. Before rendering, the script computes the number of passes required by the requested warp range and rejects settings requiring more than eight passes.

Segments shorter than:

minimumWarpDuration = 4 / Pitch_floor

are left unwarped and reported. This is a duration transformation; it is not a pitch-transposition stage.

Multichannel rendering

Each original channel is rendered independently with the same segment order, fuzzy decisions, PSOLA seeds, and micro-grain permutations. The channels are then recombined in their original order. This preserves inter-channel relationships while avoiding the mono-only limitation of Praat's PSOLA command.

Adaptive crossfades

requestedFade = Fade_min + (Fade_max - Fade_min) × muSustain × (1-muTransient)

At each joint the actual overlap is limited by the fade requests of both adjacent items and by 45% of each grain duration, then sample-aligned. The active law is a raised-cosine equal-gain fade. The timeline is assembled by writing the windowed grains into pre-allocated buffers at sample-aligned offsets; Praat Concatenate is not used.

Visualization

When Draw_analysis is enabled, the Picture window shows the complete inference-to-render chain:

Output behavior & limits

ItemBehavior
Input minimum0.25 s.
Segment cap400 segments.
Grain cap6000 rendered items. If reached, additional items are dropped and reported.
Output duration guardProjected output above 900 s stops before assembly.
Channel countPreserved.
Output time domainStarts at 0 s because the timeline is newly composed.
Object name<source>_fuzzytime.

Safety peak

Safety_peak is clamped to 0–1. A value of 0 disables it. Otherwise the completed result is attenuated only when its Sinc70 peak exceeds the requested ceiling:

if peak > Safety_peak: gain = Safety_peak / peak else: gain = 1

There is no upward normalization. Quiet output remains quiet.