Add additive synth minim example (#231)

This commit is contained in:
Abe Pazos
2022-03-16 21:11:03 +01:00
committed by GitHub
parent e22e81b912
commit bf4ce7dbb3
2 changed files with 109 additions and 1 deletions

View File

@@ -6,10 +6,14 @@ A minimal Minim support library
Add `orx-minim` to `orxFeatures`
Check the [example](src/demo/kotlin/DemoFFT01.kt)
Check the examples:
* [FFT visualization of live audio input](src/demo/kotlin/DemoFFT01.kt)
* [Additive synthesizer](src/demo/kotlin/DemoAdditive01.kt)
## Further reading
* [Minim website](https://code.compartmental.net/tools/minim/)
* [Minim's UGens](https://code.compartmental.net/minim/index_ugens.html)
* [Minim Github](https://github.com/ddf/Minim)
* [Minim Processing examples](https://github.com/ddf/Minim/tree/master/examples)

View File

@@ -0,0 +1,104 @@
import ddf.minim.ugens.Oscil
import ddf.minim.ugens.Pan
import org.openrndr.application
import org.openrndr.color.rgb
import org.openrndr.extra.minim.minim
import org.openrndr.math.Polar
import kotlin.math.pow
import kotlin.random.Random
/**
* Random drone generator and visualizer with 20 stereo voices.
* Hold the mouse button to randomize the frequencies.
* Press keys 'a' or 'b' for less random frequencies.
*/
fun main() {
application {
program {
val minim = minim()
val out = minim.lineOut
// generates a random frequency value biased down
fun randomFreq() = 20f + Random.nextFloat().pow(3) * 1000
// If one didn't want to visualize or control the synths we
// wouldn't need a data structure to store them. Here we store
// Pairs, so we have access both to the frequency of the wave
// and the current amplitude defined by the lfo (low frequency
// oscillator).
val synths = List(20) {
// By default Oscil creates sine waves, but it can be changed.
val lfo = Oscil(
Random.nextFloat() * 0.1f + 0.005f,
0.05f
).apply {
// Here we set the center of the lfo to 0.05f.
// Since the amplitude is also 0.05f, it moves between
// 0.00f and 0.10f.
offset.lastValue = 0.05f
// Have the sine waves to not start in sync.
//phase.lastValue = Random.nextFloat() * 6.28f
}
val wave = Oscil(randomFreq(), 0f)
// The `lfo` Oscil controls the `wave` Oscil's amplitude.
lfo.patch(wave.amplitude)
// Random pan to avoid a mono sound.
val pan = Pan(Random.nextFloat() * 2 - 1)
wave.patch(pan)
pan.patch(out)
// Store a [Pair] in `synths`.
Pair(wave, lfo)
}
val bgColor = rgb(0.094, 0.188, 0.349)
val lineColor = rgb(0.992, 0.918, 0.671)
extend {
drawer.clear(bgColor)
drawer.translate(drawer.bounds.center)
drawer.rotate(seconds)
// A CircleBatchBuilder for faster drawing of circles.
drawer.circles {
// For each synth draw a circle.
synths.forEachIndexed { i, (wave, lfo) ->
stroke = lineColor.opacify(Random.nextDouble(0.4) + 0.6)
fill = lineColor.opacify(Random.nextDouble() * 0.04)
// A Polar arrangement centered on the screen.
// Higher pitch circles are farther away from the center.
val pos = Polar(
360.0 * i / synths.size,
50.0 + wave.frequency.lastValue * 0.2
).cartesian
// The size of the circle depends on the current volume
// set by the lfo.
circle(pos, 500 * lfo.lastValues.last().toDouble())
}
}
if (mouse.pressedButtons.isNotEmpty()) {
synths.random().first.setFrequency(randomFreq())
}
}
keyboard.keyDown.listen { key ->
when (key.name) {
"a" -> {
// make all frequencies close to a base frequency
// (circular arrangement)
val baseFreq = 20 + Random.nextFloat() * 200
synths.forEach {
it.first.setFrequency(baseFreq + Random.nextFloat() * 20)
}
}
"b" -> {
// make all frequencies follow an exponential series
// (spiral arrangement)
val inc = Random.nextFloat() * 0.1f
synths.forEachIndexed { i, (wave, _) ->
wave.setFrequency(25f.pow(1f + i * inc))
}
}
}
}
}
}
}