In-Place Paulstretch Slicer — Multi-channel

Extracts random source slices, turns each into a phase-randomized Paulstretch texture, places the stretched material back around its original temporal midpoint, and mixes the wet slices into a dry multichannel layout.

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

What this does

In-Place Paulstretch Slicer creates a hybrid dry/wet texture from randomly selected moments of a Sound. The dry signal keeps its original channel layout. The wet path is derived from a mono fold of the source, and each stretched slice is routed to one output channel in round-robin order.

  1. Choose a random slice duration and random source position.
  2. Extract that slice with a rectangular window.
  3. Apply phase-randomized FFT overlap-add time stretching.
  4. Apply explicit raised-cosine fade-in and fade-out to the stretched slice.
  5. Place the result around the source slice's original midpoint when possible.
  6. Route successive wet slices round-robin across the output channels.
  7. Mix the wet field with the untouched zero-based dry channels.
Wet analysis/render source: the Paulstretch slices are always mono textures. Dry source: the original channel layout is retained. The script therefore spatializes the wet material by channel assignment rather than by preserving the source's original spatial image inside each wet slice.

What “in-place” means

For a selected source slice from sliceStart to sliceEnd:

sourceCenter = sliceStart + sourceDuration / 2 stretchedDuration = sourceDuration × Stretch_factor targetStart = sourceCenter - stretchedDuration / 2 targetStart = max(0, targetStart)

The normal case therefore expands the slice around its original midpoint. It is not an edit that replaces only the original slice span, and it does not force the final Sound to keep the source duration.

If centering would place the stretched slice before time 0, its start is clamped to 0. In that case the stretched slice is no longer exactly centred on the original midpoint. At the other end, wet material is allowed to extend past the original duration and the final output grows to contain it.

Paulstretch stage

Effective FFT window

Window_size_s is a request. The script converts it to samples and rounds up to the next power of two:

requestedSamples = round(Window_size_s × sampleRate) effectiveSamples = smallest power of 2 >= requestedSamples effectiveWindow = effectiveSamples / sampleRate

The effective window must contain at least four samples. When rounding changes the requested duration by more than 0.5 ms, the actual value is reported in the Info window.

Analysis and synthesis hops

overlap = Overlap_percent / 100 hopOut = effectiveWindow × (1 - overlap) hopIn = hopOut / Stretch_factor requested stretched duration = sliceDuration × Stretch_factor

A factor above 1 expands time; a factor below 1 contracts it. The script accepts any positive stretch factor.

Frame processing

Every analysis frame is Hann-windowed, transformed to a Spectrum, and assigned independent random phases while retaining the frame's original spectral magnitudes. DC and the final spectral bin are left unchanged. The modified spectrum is converted back to Sound, multiplied by another Hann window, and overlap-added into the stretched buffer.

Boundary frames are zero-padded to the complete FFT window. The output overlap-add also accumulates a synthesis-window normalization buffer; after all frames have been added, the audio accumulator is divided by that buffer wherever it is non-zero. This prevents the overlap percentage from acting as an unintended gain control.

Exact returned duration: the internal overlap-add workspace includes one extra window, but it is trimmed before return. Each wet slice is therefore returned at exactly source slice duration × Stretch_factor.

What “Paulstretch” means here

The implementation uses the characteristic Paulstretch idea of very slow frame progression combined with spectral phase randomization and overlap-add. It is an offline spectral time-stretch process; it is not Praat PSOLA and does not use a DurationTier.

Quick start

  1. Select exactly one Sound object.
  2. Run In-Place_Paulstretch_Slicer__Multi-channel.praat.
  3. Choose Custom or one of the five named presets.
  4. For Custom, set the number and duration range of the slices, stretch factor, FFT-window request, overlap, fades, and dry/wet mix.
  5. Run the script. Random slices are processed independently and the result is created as <source>_PSslice_<preset>.
There is no Random_seed control. Any run that uses the same settings can still choose different source slices and different spectral phases.

Presets

PresetSlicesSource durationStretchWindow requestOverlapFade in/outWet
Subtle Shimmer30.15–0.40 s0.20 s60%0.08 / 0.15 s30%
Frozen Texture50.20–0.60 s0.30 s50%0.10 / 0.20 s50%
Extreme Stretch30.30–0.80 s10×0.40 s70%0.15 / 0.30 s60%
Glitch Clouds80.05–0.20 s0.15 s40%0.02 / 0.05 s70%
Ambient Wash40.40–1.00 s0.35 s65%0.20 / 0.40 s40%
Custom defaults40.10–0.50 s0.25 s50%0.05 / 0.10 s50%

Named presets overwrite all synthesis and mix controls in the table. Draw_visualization and Play_result remain user-controlled.

Channel routing

Mono source

A mono source produces a two-channel result. The dry mono signal is copied equally to L and R. Wet slices alternate round-robin:

Slice 1 → L Slice 2 → R Slice 3 → L Slice 4 → R ...

Stereo or multichannel source

The output channel count equals the source channel count. The dry path preserves channel 1, channel 2, … in place. Wet slices are mono and cycle through those existing channels:

Slice i → channel ((i - 1) mod outputChannels) + 1

For example, with four source channels, slices route 1→Ch1, 2→Ch2, 3→Ch3, 4→Ch4, 5→Ch1, and so on.

The current script does not create one new output channel per wet slice. Increasing Number_of_slices increases the number of wet events, not the channel count.

Random extraction & wet placement

Source selection

sliceDuration = uniform(Min_duration_s, Max_duration_s) sliceStart = uniform(0, sourceDuration - sliceDuration)

All wet slices are extracted from the mono source copy. The input time domain is first shifted to start at 0, so non-zero Praat source times do not alter the random-position or placement arithmetic.

Explicit slice fades

Source extraction itself is rectangular. After Paulstretch, the script applies raised-cosine edge fades:

effectiveFadeIn = min(Fade_in_s, stretchedDuration / 2) effectiveFadeOut = min(Fade_out_s, stretchedDuration / 2)

This prevents an over-long requested fade from consuming more than half of the stretched slice. Setting a fade to 0 disables that edge fade.

Overlapping wet slices

Each stretched slice is added into its assigned wet channel. Slices assigned to the same channel can overlap in time, in which case their samples sum.

Dry/wet mix & level handling

Mix law

Dry_wet_mix is clamped to 0–1:

dryGain = 1 - Dry_wet_mix wetGain = Dry_wet_mix output[channel] = dry[channel] × dryGain + wet[channel] × wetGain

For a mono source, the same dry mono channel is used for both output channels. For a multichannel source, each dry channel remains on its corresponding output channel.

Safety ceilings, not normalization

Each stretched slice is attenuated only if its Sinc70 peak exceeds 0.95. After the full dry/wet mix is built, the complete output receives the same downward-only safety check:

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

Quiet slices and quiet mixes are not boosted. There is no per-channel wet normalization and no target normalization to 0.99.

Parameters & limits

ParameterCustom defaultBehavior / validation
PresetCustomCustom plus five named presets.
Number_of_slices41–256 random wet slices.
Min_duration_s0.1Must be positive and ≤ Max_duration_s.
Max_duration_s0.5Must be positive and may not exceed the source duration.
Stretch_factor4.0Any value > 0. Above 1 expands; below 1 contracts.
Window_size_s0.25Requested Paulstretch FFT window; internally rounded upward to a power-of-two number of samples.
Overlap_percent500 ≤ overlap < 100. Affects the Paulstretch OLA hop, not overlap between source slices.
Fade_in_s0.05Non-negative; effective value capped at half the stretched-slice duration.
Fade_out_s0.10Non-negative; effective value capped at half the stretched-slice duration.
Dry_wet_mix0.5Silently clamped to 0–1.
Draw_visualizationOnDraw the slice map, process explanation, source regions, all output channels and summary.
Play_resultOnPlay the finished Sound.

Visualization

The v0.6 Picture output shows the actual multichannel structure:

Source and all output lanes share one amplitude scale. For more than eight output channels, the channel palette repeats after channel 8.

The slice-map midpoint line shows the intended temporal anchor. For a stretched slice whose ideal start would be negative, the darker placement rectangle begins at 0, visibly showing that the midpoint could not be preserved exactly.

Output behavior

Duration

The script initially allocates conservative workspace, but the delivered result is trimmed to:

finalDuration = max(originalDuration, latest actual wet-slice end)

The output therefore never becomes shorter than the dry source, but it can extend beyond it when a stretched slice rings past the original end.

Channels

Naming

<source name>_PSslice_<presetName>

The original Sound remains unchanged. Temporary source, wet, frame, spectrum, matrix and visualization objects are removed after use.