Markov Soundscape Weaver — User Guide

Markov-based audio resynthesis using overlapping acoustic analysis, k-means texture states, first- or second-order transition models, state-conditioned grain selection, temporal variation, stereo decorrelation, and normalized overlap-add rendering.

Author: Shai Cohen Affiliation: Department of Music, Bar-Ilan University, Israel Version: 0.8 (2026) License: MIT License Repo: https://github.com/ShaiCohen-ops/Praat-plugin_AudioTools
Contents:

What this does

This script analyzes one Sound as a sequence of overlapping acoustic grains, groups those grains into texture states with k-means, and models state-to-state transitions with either a first-order or second-order Markov chain. A new state sequence is then generated from the learned transition model, and each state selects source grains for resynthesis.

Key Features:

Model scope: the transition model describes local sequential relationships between acoustic texture states derived from the source. It does not explicitly model harmony, counterpoint, semantics, or long-range formal structure.

Technical Implementation: (1) Convert the source to mono. (2) Extract overlapping 4D analysis grains. (3) Z-score each feature independently. (4) Run k-means with empty-state reseeding. (5) Build either a first-order transition matrix or a second-order transition tensor with first-order backoff. (6) Generate one event list containing state, source grain, source position, varispeed ratio, and output onset. (7) Derive the right-channel event list according to Stereo_decorrelation. (8) Render each channel into audio and envelope buffers. (9) Divide by the accumulated envelope, trim to the exact duration, apply boundary fades, combine stereo if required, and peak-scale to 0.99.

Quick start

  1. Select exactly one Sound object in Praat.
  2. Run Markov_Soundscape_Weaver.praat.
  3. Choose a preset or Manual.
  4. For Manual, set Grain_size_ms, Analysis_overlap, Number_of_states, Markov_order, Randomness, Output_duration_sec, Synthesis_overlap, Varispeed_scatter_semitones, Position_jitter, Density_variation, Stereo_decorrelation, and Random_seed.
  5. Enable visualization if you want to inspect the generated state path, transition matrix, state populations, and waveform comparison.
  6. Click OK. Output is named <source>_MarkovWeave_<preset>.
Quick tip: First-order modeling conditions the next state on the current state. Second-order modeling conditions it on the previous-state/current-state pair, with first-order backoff for unseen contexts.
Channel behavior: the source is converted to mono before analysis and rendering. Stereo output is generated from two event streams controlled by Stereo_decorrelation: values below 0 produce mono, 0 duplicates the event sequence, and 1 generates an independent right-channel sequence.

Markov Theory

First-Order Chain

P(S[t+1] = j | S[t] = i) States = k-means texture clusters One transition step = one generated event

Observed state-to-state transitions in the source are counted and each row is normalized. Empty target states are excluded. A row with no usable observed transitions falls back to a distribution over active states.

Second-Order Chain

P(S[t+1] = k | S[t-1] = i, S[t] = j)

The second-order model uses a K×K context pair and a successor state. Generation is seeded from a state pair that actually occurred in the source sequence. If a context pair was never observed, the script backs off to the learned first-order distribution conditioned on the current state. If that row is also unavailable, active-state occupancy is used.

Randomness

For every transition: with probability Randomness: ignore the Markov row jump to an active state sampled in proportion to source-state occupancy otherwise: sample from the learned first- or second-order transition

Therefore Randomness = 0 follows the learned grammar exclusively, while higher values progressively inject occupancy-weighted state jumps.

Initial State

First-order generation starts from an active state sampled by state occupancy. Second-order generation starts from an observed adjacent state pair rather than two unrelated random states.

Grammar Time Scale

analysisHop = grainSize * (1 - Analysis_overlap) synthesisHop = grainSize * (1 - Synthesis_overlap) grammar time scale = synthesisHop / analysisHop

If the two overlaps differ, the learned sequence is played at a different event rate. A ratio below 1 compresses the grammar in time; above 1 expands it. This is an intentional compositional parameter interaction, not an error.

Analysis Phase

Overlapping Grain Grid

grainSec = Grain_size_ms / 1000 analysisHop = grainSec * (1 - Analysis_overlap) nGrains = floor((duration - grainSec) / analysisHop) + 1 centre(i) = grainSec/2 + (i - 1) * analysisHop

Analysis_overlap is clamped to 0–0.95. The source must be at least four grain lengths long and must provide at least twice as many analysis grains as requested states.

4D Feature Space

Spectral Centroid

Centre of gravity of a Gaussian-windowed spectrogram slice. The spectrogram is analyzed up to 8 kHz.

Spectral Bandwidth

Spectral standard deviation around the centroid. This is a bandwidth descriptor, not a direct noisiness metric.

Pitch

F0 from Praat Pitch, 75–600 Hz. Undefined or unvoiced grains receive 0 Hz.

Harmonicity

Praat Harmonicity (cc). Undefined HNR receives -50 dB.

Z-Score Normalization

for each feature d: mean_d = mean(feature_d) std_d = population standard deviation if std_d < 0.001: std_d = 1 normalized_d = (feature_d - mean_d) / std_d
Voiced/unvoiced handling: unvoiced grains use F0 = 0 and undefined HNR = -50 before z-score normalization. These values can therefore contribute strongly to state separation.

K-Means Texture States

Initialize K centroids from randomly selected grains. Repeat up to 15 iterations: 1. assign every grain to nearest centroid in normalized 4D Euclidean space 2. recompute each non-empty centroid 3. if a cluster is empty: move its centroid to the currently worst-fitting assigned grain and reassign that grain 4. stop when no assignments change

After clustering, any state that somehow remains empty is pruned from the Markov chain: it cannot be an initial state, successor state, or grain-emitting state.

Synthesis Phase

State-Conditioned Grain Selection

For current state s: 1. find all analysis grains assigned to state s 2. choose one uniformly at random 3. start from that grain's original source centre 4. optional Position_jitter: centre += Uniform(-1,1) * Position_jitter * grainSec 5. clamp centre so a full grain remains inside source

Position_jitter therefore changes where the source is read. It does not change the output onset.

Varispeed Scatter

scatterSemitones ~ Uniform(-range, +range) ratio = 2^(scatterSemitones / 12) render: Resample to fs * ratio Override sampling frequency back to fs

This is varispeed: pitch, playback speed, and grain duration change together.

Density Variation

nominal output onset: (g - 1) * synthesisHop actual onset: nominal + Uniform(-1,1) * Density_variation * synthesisHop

Density_variation changes the output event timing. Position_jitter changes the source read position. They are different controls.

Stereo Decorrelation

ValueBehavior
< 0Mono output
0Stereo output with right event list copied exactly from left
0…1That approximate fraction of right-channel events is re-decided; the rest is copied from left
1Fully independent right-channel Markov walk and grain/event decisions

Normalized Overlap-Add Rendering

For each channel: create: audio buffer envelope-sum buffer for each event: extract source grain with Hann window optional varispeed apply additional short linear edge taper place grain at its actual output onset audioBuffer += processed grain envelopeBuffer += analytic Hann envelope * same edge taper after all events: output = audioBuffer / (envelopeBuffer + 1e-6) trim to exact Output_duration_sec apply short final head/tail fade

The renderer supports arbitrary overlap, density jitter, and variable grain durations after varispeed. The old block-based concatenate system no longer exists.

Final Output

Stereo: combine independently rendered L/R channels Mono: keep left render only name: _MarkovWeave_ final peak: 0.99

Parameters & Presets

Manual Parameters

ParameterDefaultMeaning / validation
Grain_size_ms80Analysis/render grain duration
Analysis_overlap0.5Clamped 0–0.95; determines analysis hop
Number_of_states5K-means state count
Markov_orderFirst OrderFirst or Second Order
Randomness0Clamped 0–1; probability of occupancy-weighted state jump
Output_duration_sec15Exact requested duration
Synthesis_overlap0.5Clamped 0–0.95; determines synthesis hop
Varispeed_scatter_semitones0Negative values become 0
Position_jitter0.1Clamped 0–1; source-centre perturbation as fraction of grain duration
Density_variation0Clamped 0–1; output-onset perturbation as fraction of synthesis hop
Stereo_decorrelation0.3<0 = mono; 0 = identical stereo; 1 = independent right walk
Random_seed00 = unpredictable; positive integer = reproducible stochastic run
Draw_visualizationOnDraw v0.8 diagnostic page
Play_resultOnPlay output after generation

Script-Level Edge Taper

crossfade_ms is not a form parameter. Manual mode begins from a script-level 10 ms edge taper. Presets can override this internally. Because the accumulated analytic envelope is normalized afterward, this parameter shapes grain edges without acting as a simple mix-gain control.

Built-in Presets

PresetGrainAna. ovlpKOrderRand.OutputSynth ovlpVarispeedPos. jitterDensityStereo dec.
Ambient Flow100 ms60%41st.1020 s60%0.10.05.40
Rhythmic Pulse50 ms30%61st.0512 s30%000.20
Dense Texture40 ms70%82nd.1515 s70%±.3 st.20.10.50
Sparse Minimal150 ms40%31st.2025 s40%0.15.10.30
Evolving Landscape120 ms50%52nd.1030 s60%±.2 st.10.08.60
Chaotic Transitions60 ms50%101st.5012 s50%±.8 st.30.15.70
Example: Evolving Landscape uses a 60 ms analysis hop and a 48 ms synthesis hop, giving a grammar time-scale ratio of 0.8. The generated state sequence therefore advances at a faster event rate than the analysis grid.

Visualization

v0.8 Diagnostic Page

HEADER source | preset | Markov order | K | analysis-grain count GENERATED STATE TRAJECTORY x = nominal synthesis time y = generated state color = destination-state index TRANSITION MATRIX first-order mode: actual first-order matrix second-order mode: first-order projection derived from input state sequence visualization only synthesis still uses the second-order tensor STATE POPULATION number of analysis grains assigned to each k-means state INPUT / RESULT WAVEFORM grey = input blue = result shared amplitude scale dashed vertical line = end of input when result is longer SUMMARY STRIP grain / analysis overlap / analysis-grain count / K / Markov order randomness / synthesis overlap / source-position jitter density variation / varispeed scatter mono or stereo-decorrelation amount scheduled events / requested duration / rendered duration / peak
State-trajectory timing: the trajectory panel uses the nominal synthesis grid. Density-jittered event onsets are used for audio rendering but are not reflected in the trajectory's horizontal positions.

Applications

Texture-State Resequencing

Generate new arrangements of a source's acoustic texture states while retaining transition tendencies learned from that source.

Soundscape Variation

Environmental, instrumental, vocal, or mixed recordings can produce useful generative textures when their local spectral/pitch/HNR patterns form recurring state transitions.

First- vs Second-Order Grammar Experiments

Compare the same source under first-order and second-order transition models to hear how conditioning on the previous two states changes local continuity.

Temporal Rescaling of Learned Grammar

Changing Analysis_overlap and Synthesis_overlap independently changes the event-rate relationship between learned and rendered sequences without altering the source feature model itself.

Stereo Texture Design

Stereo_decorrelation provides a controlled continuum from duplicated mono-in-stereo to independent probabilistic walks.

Scope: this is an offline acoustic-state analysis and resynthesis tool. Its Markov model describes local transitions between learned texture states; it does not explicitly model harmonic, semantic, or long-range formal structure.

Troubleshooting

Not enough grains for the requested state count:
The script requires at least 2 × Number_of_states analysis grains. Reduce K, reduce grain size, increase analysis overlap, or use a longer source.
Output is too repetitive:
Increase Randomness, try a different K, or use a source with more state diversity. Very strong self-transition probabilities in the learned chain will naturally repeat.
Output is too chaotic:
Reduce Randomness, lower Stereo_decorrelation, reduce Density_variation, or use a more structured source.
Pitch changes also change timing:
Varispeed_scatter_semitones changes pitch, playback speed, and grain duration together.
Stereo_decorrelation = 0 produces no stereo divergence:
Both channels use the same event list. Use a value above 0 for increasing channel divergence, or a negative value for mono output.
Random_seed affects the complete stochastic process:
It controls k-means initialization, state transitions, grain selection, jitter, varispeed, and stereo-decorrelation decisions.