Skip to content

Instantly share code, notes, and snippets.

@Sciss
Created April 2, 2019 18:45
Show Gist options
  • Save Sciss/b870f41f1e2c3eba98e2fa3fca5984c9 to your computer and use it in GitHub Desktop.
Save Sciss/b870f41f1e2c3eba98e2fa3fca5984c9 to your computer and use it in GitHub Desktop.
// version 02-Apr-2019 - Mellite 2.33.0
// written by Hanns Holger Rutz
// license: CC BY-SA 4.0
val in0 = ImageFileIn("in")
val w = in0.width
val h = in0.height
val hm = h - 1
val in1 = in0.out(0)
val dur = "dur" .attr(10)
val numVoices = "maxVoices" .attr(128).min(h)
val gainType = "gain-type" .attr(0)
val gainDb = "gain-db" .attr(0.0)
val gainAmt = gainDb.dbAmp
val fileType = "out-type" .attr(0)
val smpFmt = "out-format".attr(1)
val sampleRate = "sampleRate".attr(44100.0)
val minFreq = "minFreq" .attr(100)
val maxFreq = "maxFreq" .attr(18000)
val fadeTime = "fadeTime" .attr(0.1)
val numFrames = (dur * sampleRate).floor.max(2)
val fadeFrames = (fadeTime * sampleRate).floor.max(1)
val inverted = "inverted".attr(0) > 0
val in = in1 * (1 - inverted) + (-in1 + (1: GE)) * inverted
val minAmp = "minLevel".attr(-60.0).dbAmp
val maxAmp = "maxLevel".attr( 0.0).dbAmp
val framesXY = numFrames * numVoices
val x0 = LFSaw(1.0/numFrames).linLin(-1, 1, 0, numFrames)
val frame = x0.take(framesXY)
val xs = w / numFrames
val x = frame * xs
val voiceIdx = (Frames(x) / numFrames).floor
val ys = h / numVoices
val y0 = voiceIdx * ys
val y = y0 + WhiteNoise(0.5)
val freq = y .linExp(0, hm, maxFreq, minFreq)
val amp0 = ScanImage(in, width = w, height = h, x = x, y = y,
wrap = 0, zeroCrossings = 0)
val amp1 = amp0.linExp(0, 1, minAmp, maxAmp)
val fdIn = frame.clip(0, fadeFrames) / fadeFrames
// N.B.: this is still a problem in FScape 2.16: BinaryOp is
// not commutative with respect to signal lengths!
val fdOut = (-frame + numFrames).clip(0, fadeFrames) / fadeFrames
val fade = fdIn * fdOut
val amp = amp1 * fade
val osc = (SinOsc(freq/sampleRate) * amp).take(framesXY)
val synthesized = Frames(osc)
def mkProgress(frames: GE, n: GE, label: String): Unit =
Progress(frames / n, Metro(sampleRate) | Metro(n - 1),
label)
mkProgress(synthesized, framesXY, "synthesized")
// XXX TODO --- this is a limitation of using overlap-add,
// we have to shift at least one frame per window...
val mix = OverlapAdd(osc, size = numFrames, step = 1)
val framesOut = numFrames + (numVoices - 1)
val sig0 = mix
val sig = If (gainType sig_== 0) Then {
val rsmpBuf = BufferDisk(sig0)
val rMax = RunningMax(Reduce.max(sig0.abs))
val read = Frames(rMax)
mkProgress(read, framesOut, "analyze")
val maxAmp = rMax.last
val div = maxAmp + (maxAmp sig_== 0.0)
val gainAmtN = gainAmt / div
rsmpBuf * gainAmtN
} Else {
sig0 * gainAmt
}
val written = AudioFileOut("out", sig, fileType = fileType,
sampleFormat = smpFmt, sampleRate = sampleRate)
mkProgress(written, framesOut, "written")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment