[orx-envelopes] Add trigger id and object functions
This commit is contained in:
@@ -18,7 +18,8 @@ kotlin {
|
|||||||
@Suppress("UNUSED_VARIABLE")
|
@Suppress("UNUSED_VARIABLE")
|
||||||
val jvmDemo by getting {
|
val jvmDemo by getting {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":orx-shapes"))
|
implementation(project(":orx-envelopes"))
|
||||||
|
implementation(project(":orx-noise"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ data class ADSR(
|
|||||||
val decayDuration: Double,
|
val decayDuration: Double,
|
||||||
val sustainValue: Double,
|
val sustainValue: Double,
|
||||||
val releaseDuration: Double
|
val releaseDuration: Double
|
||||||
) : Envelope{
|
) : Envelope() {
|
||||||
override fun value(t: Double, tOff: Double): Double {
|
override fun value(t: Double, tOff: Double): Double {
|
||||||
return adsr(attackDuration, decayDuration, sustainValue, releaseDuration, t, tOff)
|
return adsr(attackDuration, decayDuration, sustainValue, releaseDuration, t, tOff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun position(t: Double, tOff: Double): Double {
|
||||||
|
return adsrPosition(attackDuration, decayDuration, releaseDuration, t, tOff)
|
||||||
|
}
|
||||||
|
|
||||||
override fun isActive(t: Double, tOff: Double): Boolean {
|
override fun isActive(t: Double, tOff: Double): Boolean {
|
||||||
return !(t - tOff > releaseDuration)
|
return !(t - tOff > releaseDuration)
|
||||||
}
|
}
|
||||||
@@ -26,12 +30,21 @@ fun adsr(
|
|||||||
t: Double,
|
t: Double,
|
||||||
tOff: Double = 1E10
|
tOff: Double = 1E10
|
||||||
): Double {
|
): Double {
|
||||||
|
|
||||||
val da = t / attackDuration
|
val da = t / attackDuration
|
||||||
val dc = (t - attackDuration) / decayDuration
|
val dc = (t - attackDuration) / decayDuration
|
||||||
|
|
||||||
val vOn = mix(min(1.0, da), sustainValue, dc.coerceIn(0.0..1.0))
|
val vOn = mix(min(1.0, da), sustainValue, dc.coerceIn(0.0..1.0))
|
||||||
|
|
||||||
return mix(vOn, 0.0, ((t - tOff) / releaseDuration).coerceIn(0.0..1.0))
|
return mix(vOn, 0.0, ((t - tOff) / releaseDuration).coerceIn(0.0..1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun adsrPosition(
|
||||||
|
attackDuration: Double,
|
||||||
|
decayDuration: Double,
|
||||||
|
releaseDuration: Double,
|
||||||
|
t: Double,
|
||||||
|
tOff: Double
|
||||||
|
): Double {
|
||||||
|
val ta = (t / attackDuration).coerceIn(0.0..1.0)
|
||||||
|
val td = ((t - attackDuration) / decayDuration).coerceIn(0.0..1.0)
|
||||||
|
val tr = ((t - tOff) / releaseDuration).coerceIn(0.0..1.0)
|
||||||
|
return (ta + td + tr) / 3.0
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package org.openrndr.extra.envelopes
|
package org.openrndr.extra.envelopes
|
||||||
|
|
||||||
interface Envelope {
|
abstract class Envelope {
|
||||||
fun value(t: Double, tOff: Double): Double
|
abstract fun value(t: Double, tOff: Double): Double
|
||||||
|
|
||||||
fun isActive(t: Double, tOff:Double): Boolean
|
abstract fun position(t: Double, tOff: Double): Double
|
||||||
|
|
||||||
|
abstract fun isActive(t: Double, tOff: Double): Boolean
|
||||||
|
|
||||||
|
var objectFunction: ((time: Double, value: Double, position: Double) -> Unit) = { _, _, _ -> }
|
||||||
}
|
}
|
||||||
3
orx-envelopes/src/commonMain/kotlin/MPPSynchronize.kt
Normal file
3
orx-envelopes/src/commonMain/kotlin/MPPSynchronize.kt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
package org.openrndr.extra.envelopes
|
||||||
|
|
||||||
|
expect fun <V> mppSynchronized(lock:Any, f:()->V) : V
|
||||||
@@ -5,42 +5,62 @@ package org.openrndr.extra.envelopes
|
|||||||
import org.openrndr.Clock
|
import org.openrndr.Clock
|
||||||
import org.openrndr.extra.parameters.DoubleParameter
|
import org.openrndr.extra.parameters.DoubleParameter
|
||||||
|
|
||||||
class Trigger(val on: Double, var off: Double, val envelope: Envelope)
|
class Trigger(val id: Int, val on: Double, var off: Double, val envelope: Envelope)
|
||||||
|
|
||||||
class TrackerValue(val time: Double, val value: Double)
|
class TrackerValue(
|
||||||
abstract class Tracker<T : Envelope>(val clock: Clock) {
|
val time: Double,
|
||||||
|
val value: Double,
|
||||||
val triggers = mutableListOf<Trigger>()
|
val position: Double,
|
||||||
|
val envelope: Envelope
|
||||||
|
) {
|
||||||
protected abstract fun createEnvelope(): T
|
operator fun invoke() {
|
||||||
|
draw()
|
||||||
fun triggerOn() {
|
|
||||||
val t = clock.seconds
|
|
||||||
triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) }
|
|
||||||
triggers.add(Trigger(clock.seconds, 1E30, createEnvelope()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun triggerOff() {
|
fun draw() {
|
||||||
val t = clock.seconds
|
envelope.objectFunction(time, value, position)
|
||||||
triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) }
|
}
|
||||||
triggers.lastOrNull()?.let {
|
}
|
||||||
it.off = clock.seconds
|
|
||||||
|
abstract class Tracker<T : Envelope>(val clock: Clock) {
|
||||||
|
val triggers = mutableListOf<Trigger>()
|
||||||
|
|
||||||
|
protected abstract fun createEnvelope(objectFunction: (time: Double, value: Double, position: Double) -> Unit): T
|
||||||
|
|
||||||
|
fun triggerOn(
|
||||||
|
triggerId: Int = 0,
|
||||||
|
objectFunction: (time: Double, value: Double, position: Double) -> Unit = { _, _, _ -> }
|
||||||
|
) {
|
||||||
|
mppSynchronized(triggers) {
|
||||||
|
val t = clock.seconds
|
||||||
|
triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) }
|
||||||
|
triggers.add(Trigger(triggerId, clock.seconds, 1E30, createEnvelope(objectFunction)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun triggerOff(triggerId: Int = 0) {
|
||||||
|
mppSynchronized(triggers) {
|
||||||
|
val t = clock.seconds
|
||||||
|
triggers.removeAll { !it.envelope.isActive(t - it.on, it.off - it.on) }
|
||||||
|
triggers.findLast { it.id == triggerId }?.let {
|
||||||
|
it.off = clock.seconds
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun values(): List<TrackerValue> {
|
fun values(): List<TrackerValue> {
|
||||||
val t = clock.seconds
|
val t = clock.seconds
|
||||||
return triggers.mapNotNull {
|
return mppSynchronized(triggers) {
|
||||||
val tOn = t - it.on
|
triggers.mapNotNull {
|
||||||
val tOff = it.off - it.on
|
val tOn = t - it.on
|
||||||
|
val tOff = it.off - it.on
|
||||||
|
|
||||||
if (it.envelope.isActive(tOn, tOff)) {
|
if (it.envelope.isActive(tOn, tOff)) {
|
||||||
val v = it.envelope.value(tOn, tOff)
|
val v = it.envelope.value(tOn, tOff)
|
||||||
|
TrackerValue(t, v, it.envelope.position(tOn, tOff), it.envelope)
|
||||||
TrackerValue(t, v)
|
} else {
|
||||||
} else {
|
null
|
||||||
null
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,21 +68,27 @@ abstract class Tracker<T : Envelope>(val clock: Clock) {
|
|||||||
fun value(): Double {
|
fun value(): Double {
|
||||||
return values().sumOf { it.value }
|
return values().sumOf { it.value }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ADSRTracker(clock: Clock): Tracker<ADSR>(clock) {
|
|
||||||
|
class ADSRTracker(clock: Clock) : Tracker<ADSR>(clock) {
|
||||||
|
|
||||||
@DoubleParameter("attack", 0.0, 20.0, order = 1)
|
@DoubleParameter("attack", 0.0, 20.0, order = 1)
|
||||||
var attack: Double = 0.1
|
var attack: Double = 0.1
|
||||||
|
|
||||||
@DoubleParameter("decay", 0.0, 20.0, order = 2)
|
@DoubleParameter("decay", 0.0, 20.0, order = 2)
|
||||||
var decay: Double = 0.1
|
var decay: Double = 0.1
|
||||||
|
|
||||||
@DoubleParameter("sustain", 0.0, 1.0, order = 3)
|
@DoubleParameter("sustain", 0.0, 1.0, order = 3)
|
||||||
var sustain: Double = 0.9
|
var sustain: Double = 0.9
|
||||||
|
|
||||||
@DoubleParameter("release", 0.0, 20.0, order = 4)
|
@DoubleParameter("release", 0.0, 20.0, order = 4)
|
||||||
var release: Double = 0.9
|
var release: Double = 0.9
|
||||||
|
|
||||||
override fun createEnvelope(): ADSR {
|
override fun createEnvelope(objectFunction: (time: Double, value: Double, position: Double) -> Unit): ADSR {
|
||||||
return ADSR(attack, decay, sustain, release)
|
return ADSR(attack, decay, sustain, release).apply {
|
||||||
|
this.objectFunction = objectFunction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
orx-envelopes/src/jsMain/kotlin/MPPSynchronize.kt
Normal file
5
orx-envelopes/src/jsMain/kotlin/MPPSynchronize.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package org.openrndr.extra.envelopes
|
||||||
|
|
||||||
|
actual fun <V> mppSynchronized(lock: Any, f: () -> V): V {
|
||||||
|
return f()
|
||||||
|
}
|
||||||
46
orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker02.kt
Normal file
46
orx-envelopes/src/jvmDemo/kotlin/DemoADSRTracker02.kt
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import org.openrndr.application
|
||||||
|
import org.openrndr.draw.loadFont
|
||||||
|
import org.openrndr.extra.envelopes.ADSRTracker
|
||||||
|
import org.openrndr.extra.noise.uniform
|
||||||
|
import org.openrndr.shape.Rectangle
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
application {
|
||||||
|
program {
|
||||||
|
val tracker = ADSRTracker(this)
|
||||||
|
tracker.attack = 1.0
|
||||||
|
tracker.decay = 0.2
|
||||||
|
tracker.sustain = 0.8
|
||||||
|
tracker.release = 2.0
|
||||||
|
|
||||||
|
keyboard.keyDown.listen {
|
||||||
|
if (it.name == "t") {
|
||||||
|
val center = drawer.bounds.uniform(distanceToEdge = 30.0)
|
||||||
|
tracker.triggerOn(0) { time, value, position ->
|
||||||
|
drawer.circle(center, value * 100.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (it.name == "r") {
|
||||||
|
val center = drawer.bounds.uniform(distanceToEdge = 30.0)
|
||||||
|
tracker.triggerOn(1) { time, value, position ->
|
||||||
|
val r = Rectangle.fromCenter(center, width = value * 100.0, height = value * 100.0)
|
||||||
|
drawer.rectangle(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyboard.keyUp.listen {
|
||||||
|
if (it.name == "t")
|
||||||
|
tracker.triggerOff(0)
|
||||||
|
if (it.name == "r")
|
||||||
|
tracker.triggerOff(1)
|
||||||
|
}
|
||||||
|
extend {
|
||||||
|
tracker.values().forEach {
|
||||||
|
it()
|
||||||
|
}
|
||||||
|
drawer.fontMap = loadFont("demo-data/fonts/IBMPlexMono-Regular.ttf", 16.0)
|
||||||
|
drawer.text("press and hold 't' and/or 'r'", 20.0, height - 20.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
orx-envelopes/src/jvmMain/kotlin/MPPSynchronize.kt
Normal file
7
orx-envelopes/src/jvmMain/kotlin/MPPSynchronize.kt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package org.openrndr.extra.envelopes
|
||||||
|
|
||||||
|
actual fun <V> mppSynchronized(lock: Any, f: () -> V): V {
|
||||||
|
return synchronized(lock) {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user