Skip to content

LorisStudy.kt

src/jvmMain/kotlin/afield/audio/analysis/loris/LorisStudy.kt · 39 lines

@file:afield.runtime.completion.CompletionCategory("Audio")

package afield.audio.analysis.loris

import kotlin.math.PI
import kotlin.math.pow
import kotlin.math.sin

One worked Loris gesture, written in the same breakpoint arithmetic that LorisTransforms.kt applies one edit at a time.

spectralWhirlpool reshapes a window of a LorisModel rather than the whole of it. A sin² bell rises and falls across the window, so the edit begins and ends at zero and every breakpoint outside the window is returned unchanged. Under that bell each partial sweeps four times through ±18 semitones, its amplitude is shuttered at 12 Hz, and its noise bandwidth is pushed towards 0.85 at the peak: a vibrato that turns breathy in the middle and settles again. Pass the result to LorisClient.synthesize to hear it.

fun LorisModel.spectralWhirlpool(startSeconds: Double = 6.0, durationSeconds: Double = 3.0): LorisModel

Example gesture: smooth local ±18-semitone sweeps, 12 Hz amplitude shutters and noisy peaks.

fun LorisModel.spectralWhirlpool(startSeconds: Double = 6.0, durationSeconds: Double = 3.0): LorisModel {
    require(startSeconds.isFinite() && durationSeconds.isFinite() && startSeconds >= 0 && durationSeconds > 0)
    require(startSeconds + durationSeconds <= this.durationSeconds)
    return copy(partials = partials.map { partial -> partial.copy(points = partial.points.map { point ->
        val u = (point.timeSeconds - startSeconds) / durationSeconds
        if (u <= 0.0 || u >= 1.0) point else {
            val envelope = sin(PI * u).pow(2)
            val octaves = 1.5 * envelope * sin(2 * PI * 4 * u)
            val shutter = .25 + .75 * (.5 + .5 * sin(2 * PI * 12 * durationSeconds * u))
            point.copy(
                frequencyHz = (point.frequencyHz * 2.0.pow(octaves)).coerceAtMost(sampleRate * .49),
                amplitude = point.amplitude * (1 - envelope + envelope * shutter),
                bandwidth = point.bandwidth * (1 - envelope) + .85 * envelope,
            )
        }
    }) })
}