Hi Beads fans,
I've been using Beads this week for the first time and really enjoying it. I'd like to make a UGen that produces live control data from a joystick (the GameTrak). I implemented one but it had some audible artifacts, so I wrote a very simple patch using MouseResponder to see how it behaved. I just tied the mouse x position to the pitch of a sine wave, and I can hear discrete pitch steps when I move the mouse left and right, which is unexpected/undesirable. This is with the default buffer of 512. A larger buffer (1024, 2048) eliminates the discontinuities, but now with noticeable lag. Smaller buffers result in more discontinuities. I realize that to an extent, this is just part of the game, but I'm wondering what I can do to improve it.
For example, I see in the implementation that MouseResponder interpolates linearly between positions read once per buffer. Maybe more frequent reads and a small buffer would do the trick? I tried something in a custom UGen using an internal Clock to fill bufOut once per sample tick, but it results in audible circular buffer overlaps, presumably because messageReceived and calculateBuffer are not mutually synchronized.
Here is my simple demo patch (in Kotlin) using Beads 3.2:
import net.beadsproject.beads.core.AudioContext
import net.beadsproject.beads.ugens.Plug
import net.beadsproject.beads.ugens.MouseResponder
import net.beadsproject.beads.ugens.Gain
import net.beadsproject.beads.data.Buffer
import net.beadsproject.beads.ugens.Function
import net.beadsproject.beads.ugens.WavePlayer
fun main() {
val ac = AudioContext(1024)
val mouseX = Plug(ac, MouseResponder(ac), 0)
val freq = object : Function(mouseX) {
override fun calculate() = scale(x[0], 0f, 1f, 200f, 600f)
}
val wp = WavePlayer(ac, freq, Buffer.SINE)
val g = Gain(ac, 1, 0.5f)
g.addInput(wp)
ac.out.addInput(g)
ac.start()
}
inline fun scale(n: Float, x1: Float, x2: Float, y1: Float, y2: Float) = (((n-x1)/(x2-x1)) * (y2-y1)) + y1
and here is my stab at using messageReceived.
import net.beadsproject.beads.core.AudioContext
import net.beadsproject.beads.core.Bead
import net.beadsproject.beads.core.UGen
import net.beadsproject.beads.data.Buffer
import net.beadsproject.beads.ugens.*
import net.beadsproject.beads.ugens.Function
import java.awt.MouseInfo
import java.awt.Toolkit
class MouseTick @JvmOverloads constructor(private val ac: AudioContext = getDefaultContext()) :
UGen(ac, 2) {
private val clock = Clock(ac, ac.samplesToMs(1.0).toFloat())
private val width = Toolkit.getDefaultToolkit().screenSize.width.toFloat()
private val height = Toolkit.getDefaultToolkit().screenSize.height.toFloat()
init {
ac.out.addDependent(clock)
clock.addMessageListener(this)
}
// Works, but not like you want it to, because this isn't synchronized with calculateBuffer
override fun messageReceived(message: Bead) {
val i = (clock.count % bufferSize).toInt()
val point = MouseInfo.getPointerInfo().location
bufOut[0][i] = point.x.toFloat() / width
bufOut[1][i] = point.y.toFloat() / height
}
override fun calculateBuffer() = Unit
}
fun main() {
val ac = AudioContext(2048)
val mouse = MouseTick(ac)
val mouseX = Plug(ac, mouse, 0)
val freq = object : Function(mouseX) { override fun calculate() = scale(x[0], 0f, 1f, 200f, 600f) }
val wp = WavePlayer(ac, freq, Buffer.SINE)
val g = Gain(ac, 1, 0.5f)
g.addInput(wp)
ac.out.addInput(g)
ac.start()
}
inline fun scale(n: Float, x1: Float, x2: Float, y1: Float, y2: Float) = (((n-x1)/(x2-x1)) * (y2-y1)) + y1
Thanks for any suggestions!
Curtis