Created
April 2, 2019 18:45
-
-
Save Sciss/b870f41f1e2c3eba98e2fa3fca5984c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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