Recording movements / automation

is it possible to record and playback fader and knob movements at all? i guess if this functionality is not native, maybe it’s possible to code it as a plugin?

i want to be able to record movements, and play them back. so i guess a kind of automation.
“press record, move a fader, let go, and it will play the movement back” - that sort of thing

AFAIK this is possible with lemur using a “roboknob” plugin…?

That’s very unlikely to be implemented (the app’s main focus is direct user interaction), but I guess it could be possible to code something like this with a custom module.

is anyone have found a solution for this suggestion ?
I will take a look if something has be done

It should be doable, i think of a javascript object, where every time a fader is touched, it is stored in a object. On a practical view there are several things to consider - and it has to be generic so other peeps could use it.
When it ends up in a node module it could be used in other project. In my big project(keko kemper control) i have implemented a dynamic object, that is written to hdd and loaded on startup... -> I am a beginner, so there might be better solutions.

@abstrus thank you I will take a look

we are always young padawan :slight_smile:

I fiddled a little with the idea, here is a basic implementation example (as a custom module). It's far from complete and is not going to be implemented as a core feature, but it could help you nonetheless:

class Automation {

    constructor() {

        // osc message store
        this.events = []

        // record state
        this.recordStartTime = 0
        this.recording = false

        // play state
        this.playStartTime = 0
        this.playing = false
        this.playbackCursor = 0

        // looping state
        this.looping = false

        // playback process
        this.interval = 10
        this.intervalCallback = null

    }

    startRecording() {

        // clear osc message store
        this.events = []

        // enable recording
        this.recording = true

        // save timestamp
        this.recordStartTime = Date.now()

    }

    stopRecording() {

        // stop recording
        this.recording = false

    }

    startPlaying() {

        // start or restart playback
        if (this.playing) this.stopPlaying()
        this.playing = true

        // save timestamp
        this.playStartTime = Date.now()

        // start playback process
        this.intervalCallback = setInterval(this.processEvents.bind(this), this.interval)

    }

    stopPlaying() {

        // stop playback
        this.playing = false

        // reset playback cursor
        this.playbackCursor = 0

        // stop playback process
        clearInterval(this.intervalCallback)

    }

    setLooping(v) {

        // set looping state
        this.looping = !!v

    }

    addEvent(data) {

        if (!this.recording) return

        // store osc message with relative timestamp
        this.events.push({
            timestamp: Date.now() - this.recordStartTime,
            data: data
        })

    }

    processEvents() {

        // playback process

        // compute elapsed time
        var playtime = Date.now() - this.playStartTime

        while (this.playing) {

            // retrieve event at playback cursor
            var event = this.events[this.playbackCursor]

            if (!event) {
                this.stopPlaying()
                return
            }

            // retrieve osc message data and timestamp
            var {data, timestamp} = event

            if (playtime >= timestamp) {
                // if elapsed time is greater than relative timestamp

                // send osc message at playback cursor
                send(data.host, data.port, data.address, ...data.args)
                // and send it back to the clients to update the gui
                receive(data.host, data.port, data.address, ...data.args)

                // increment playback cursor
                this.playbackCursor += 1

                if (this.playbackCursor == this.events.length) {
                    // if last event was sent, stop playback or loop

                    if (this.looping) {
                        this.startPlaying()
                        break
                    } else {
                        this.stopPlaying()
                        break
                    }

                }

            } else {
                // else wait for next process call (in this.interval milliseconds)

                break

            }



        }

    }

}

var automation = new Automation()


module.exports = {

    oscOutFilter: (data)=>{

        var {host, port, address, args} = data

        if (address === '/record') {

            if (args[0].value) automation.startRecording()
            else automation.stopRecording()

            return

        } else if (address === '/play') {

            if (args[0].value) automation.startPlaying()
            else automation.stopPlaying()

            return

        } else if (address === '/looping') {

            automation.setLooping(args[0].value)

            return

        } else {

            automation.addEvent(data)

        }


        return data
    }

}