AppModel

interface AppModel

Host side of uapmd_app::AppModel (tools/uapmd-app-model) — the façade uapmd-app itself renders against, wrapped by c-api/uapmd-c-app.h.

It is a process-wide singleton: call instantiateAppModel once, before any UI exists, then reach it with getAppModel. It owns its RealtimeSequencer, so the instance returned by sequencer must not be closed.

A host must install an event loop (see uapmd_set_event_loop, which the JVM/Android bindings do via initJvmEventLoop() / initAndroidEventLoop()) before instantiating: AppModel marshals plugin deactivation and history completions through remidy::EventLoop, and without one they never run.

Inheritors

AndroidAppModel
JsAppModel
JvmAppModel
NativeAppModel
WasmJsAppModel

Properties

Link copied to clipboard
Link copied to clipboard

AppModel's own blocklist — the list the Plugin Selector shows. A standalone ScanTool keeps a separate one. BlocklistEntry.timestamp in uapmd has no counterpart in uapmd_blocklist_entry_t, so it is not carried across.

Link copied to clipboard
abstract val devices: List<DeviceEntry>

UMP devices the model has instantiated.

Link copied to clipboard
abstract val historyState: UndoState

Also reports busy while an asynchronous plug-in mutation is still capturing state, so shortcuts cannot race the capture.

Link copied to clipboard

Turning this off is not a plain stopAudio(): the model stops the transport, mutes the output, lets release/reverb tails drain inaudibly, then deactivates plugins on the main thread and resets processing state, so a restart cannot resume stale voices.

Link copied to clipboard
abstract val isScanning: Boolean
Link copied to clipboard

The last scanning error, or null.

Link copied to clipboard

Empty when the last clipboard call succeeded.

Link copied to clipboard
Link copied to clipboard
abstract val masterTempoMap: TempoMap

The project's tempo curve, as the engine derived it from the master track. Read this rather than assembling one from the master tempo points, so that display and playback can never be working from different maps.

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
abstract val sampleRate: Int
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Owned by the model; do not close.

Link copied to clipboard

Progress of the slow scan. isScanning alone cannot tell a long scan from a stuck one, which is why uapmd-app's selector shows these.

Link copied to clipboard
Link copied to clipboard

The span the timeline's content actually occupies.

Link copied to clipboard
abstract val timelineTrackCount: UInt
Link copied to clipboard
abstract val trackCount: UInt
Link copied to clipboard

Functions

Link copied to clipboard
abstract fun addClipToTrack(trackIndex: Int, position: TimelinePosition, reader: AudioFileReader, filepath: String): ClipAddResult

AppModel::addClipToTrack. Ownership of reader transfers to the engine, so do not close it afterwards - a successful call consumes it, and a failed one reports reader not found or already consumed if the same reader is offered twice.

Link copied to clipboard
abstract fun addDeviceInputToTrack(trackIndex: Int, channelIndices: List<UInt>): Int

AppModel::addDeviceInputToTrack, returning the new source node id, or a negative value on failure. The undoable counterpart is ProjectCommands.addDeviceInputToTrack, which takes the node id this returns; prefer that one from a UI.

Link copied to clipboard
abstract fun addMidiClipFromData(trackIndex: Int, position: TimelinePosition, umpEvents: List<UInt>, tickTimestamps: List<ULong>, tickResolution: UInt, clipTempo: Double = 120.0, tempoChanges: List<MidiTempoChange> = emptyList(), timeSignatureChanges: List<MidiTimeSignatureChange> = emptyList(), clipName: String = "", needsFileSave: Boolean = true): ClipAddResult

AppModel::addMidiClipFromData, for a clip built in memory rather than read from a file - what a generator or a step sequencer produces.

Link copied to clipboard
abstract fun addMidiClipToTrack(trackIndex: Int, position: TimelinePosition, filepath: String): ClipAddResult

AppModel::addMidiClipToTrack, reading an SMF or SMF2 from disk.

Link copied to clipboard
abstract fun addTrack(callback: (trackIndex: Int, error: String?) -> Unit)
Link copied to clipboard
abstract fun addUmpEventToClip(trackIndex: Int, clipId: Int, tick: Long, words: UIntArray): Boolean
Link copied to clipboard
abstract fun cancelPluginScanning()
Link copied to clipboard
abstract fun cancelRenderToFile()
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
abstract fun clearPluginBlocklist()
Link copied to clipboard
abstract fun clearTimelineClipboard()
Link copied to clipboard
Link copied to clipboard
abstract fun closePianoRollSession(trackIndex: Int, clipId: Int)
Link copied to clipboard
abstract fun connectTrackGraph(trackIndex: Int, connection: GraphConnection): OpResult
Link copied to clipboard

False leaves the reason in lastTimelineClipError.

Link copied to clipboard
abstract fun createEmptyMidiClip(trackIndex: Int, positionSamples: Long, tickResolution: UInt, bpm: Double = 120.0): ClipAddResult

Creates an empty MIDI 2.0 clip; tickResolution is ticks per quarter.

Link copied to clipboard
abstract fun createPluginInstance(format: String, pluginId: String, trackIndex: Int, config: PluginInstanceConfig = PluginInstanceConfig(), callback: (PluginInstanceResult) -> Unit)

Instantiates pluginId and attaches it to trackIndex; a negative index creates a new track. The callback runs once, on the thread that finishes instantiation.

Link copied to clipboard

cut copies the selection before deleting it.

Link copied to clipboard
abstract fun deviceForInstance(instanceId: Int): DeviceEntry?

The device hosting a plug-in instance, or null when it has none.

Link copied to clipboard
abstract fun disableUmpDevice(instanceId: Int)
Link copied to clipboard
abstract fun disconnectTrackGraphConnection(trackIndex: Int, connectionId: Long): OpResult
Link copied to clipboard
abstract fun enableUmpDevice(instanceId: Int, deviceName: String)

Registers the instance as a virtual MIDI 2.0 device where the platform supports it.

Link copied to clipboard
abstract fun ensureTrackUsesEditorGraph(trackIndex: Int): Boolean

Switches the track from the simple linear chain to the editable graph.

Link copied to clipboard
abstract fun findPianoRollSession(trackIndex: Int, clipId: Int): PianoRollSession?
Link copied to clipboard
abstract fun generateScanReport(): String
Link copied to clipboard
abstract fun getClipAudioEvents(trackIndex: Int, clipId: Int): ClipAudioEventsResult

Reading counterpart to ProjectCommands.setClipMarkers/setClipAudioWarps.

Link copied to clipboard
abstract fun getInstanceGroup(instanceId: Int): UByte
Link copied to clipboard
abstract fun getMidiClipUmpEvents(trackIndex: Int, clipId: Int): UmpEventsResult
Link copied to clipboard
Link copied to clipboard
abstract fun getTimelineTrack(index: UInt): TimelineTrack
Link copied to clipboard
Link copied to clipboard
abstract fun getTrackGraphNodes(trackIndex: Int): GraphNodesResult
Link copied to clipboard
abstract fun hidePluginUi(instanceId: Int)
Link copied to clipboard
abstract fun importMidiTracksFromFile(filepath: String, callback: (Boolean, String?, Int) -> Unit)

Imports a possibly multi-track SMF, one new track per SMF track. callback receives success, an error when it failed, and how many tracks were created.

Link copied to clipboard
abstract fun isTimelineClipSelected(trackIndex: Int, clipId: Int): Boolean
Link copied to clipboard
abstract fun isTrackHidden(trackIndex: Int): Boolean

A hidden track is skipped by paste and by the track list, but still plays.

Link copied to clipboard
abstract fun isTrackMuted(trackIndex: Int): Boolean
Link copied to clipboard
abstract fun isTrackSolo(trackIndex: Int): Boolean
Link copied to clipboard
abstract fun loadPluginState(instanceId: Int, filepath: String, callback: (PluginStateResult) -> Unit)

Plug-in state to and from a file. Prefer these over the *Sync variants: a plug-in's state can be slow to produce, and on Android reading it from the main thread can deadlock.

Link copied to clipboard
abstract fun loadPluginStateSync(instanceId: Int, filepath: String): PluginStateResult

The blocking variants, for tools and tests that have no loop to post a completion to. See the warning on loadPluginState.

Link copied to clipboard
abstract fun loadProject(filePath: String): AppProjectResult
Link copied to clipboard

Android's document-picker path: resolves a content:// handle token.

Link copied to clipboard
abstract fun markPluginInstanceTrackDirty(instanceId: Int)

Marks the track owning this instance dirty, so the next save rewrites it.

Link copied to clipboard

Discards the current project and starts an empty one. Asks nothing and always replaces: whether unsaved changes may be discarded is the caller's decision. Unlike TimelineFacade.newProject this also tears down what the outgoing project instantiated and rebuilds the model's view of the result.

Link copied to clipboard
Link copied to clipboard
abstract fun notifyUiReady()

Startup lifecycle; uapmd-app calls both once the UI exists.

Link copied to clipboard
abstract fun openPianoRollSession(trackIndex: Int, clipId: Int): PianoRollSession?

Owned by the model and shared between callers; do not close.

Link copied to clipboard
abstract fun pasteTimelineClips(trackIndex: Int, positionSeconds: Double, originalTracks: Boolean): TimelinePasteResult
Link copied to clipboard
abstract fun performPluginScanning(forceRescan: Boolean = false, mode: ScanMode = ScanMode.InProcess, remoteTimeoutSeconds: Double = 20.0, requireFastScanning: Boolean = false)

Runs asynchronously; watch isScanning. ScanMode.Remote launches a separate scanner process on desktop and is unavailable on WebAssembly.

Link copied to clipboard
abstract fun pianoRollClipSnapshot(trackIndex: Int, clipId: Int, fallbackDurationSeconds: Double = 0.01): PianoRollSnapshot?

The caller owns the snapshot and must close it.

Link copied to clipboard
Link copied to clipboard
abstract fun recordPianoRollCommitSource(trackIndex: Int, clipId: Int)

Commit-source tracking.

Link copied to clipboard
abstract fun redo(callback: (error: String?) -> Unit? = null)
Link copied to clipboard

Master-track tempo map. getTimelineState().tempo is a single value and cannot describe a project whose tempo changes; a beats view needs these. Rebuild once with refreshMasterTempoMap, then read the lists — they stay valid until the next refresh.

Link copied to clipboard
abstract fun removeAllTracks(callback: (error: String?) -> Unit)
Link copied to clipboard
abstract fun removeClipFromTrack(trackIndex: Int, clipId: Int): Boolean
Link copied to clipboard
abstract fun removePluginInstance(instanceId: Int)
Link copied to clipboard
abstract fun removeTrack(trackIndex: Int, callback: (trackIndex: Int, error: String?) -> Unit)
Link copied to clipboard
abstract fun removeUmpEventFromClip(trackIndex: Int, clipId: Int, eventIndex: Int): Boolean
Link copied to clipboard
abstract fun requestShowInstanceDetails(instanceId: Int)
Link copied to clipboard
abstract fun requestShowPluginUi(instanceId: Int)
Link copied to clipboard
abstract fun requestShowTrackGraph(trackIndex: Int)

Asks the host to bring up this track's graph editor.

Link copied to clipboard
abstract fun revertTrackToSimpleGraph(trackIndex: Int): Boolean
Link copied to clipboard
abstract fun savePluginState(instanceId: Int, filepath: String, callback: (PluginStateResult) -> Unit)
Link copied to clipboard
abstract fun savePluginStateSync(instanceId: Int, filepath: String): PluginStateResult
Link copied to clipboard
abstract fun saveProject(filePath: String, callback: (AppProjectResult) -> Unit)
Link copied to clipboard
abstract fun saveProjectSync(filePath: String): AppProjectResult
Link copied to clipboard
abstract fun selectTimelineClips(clips: List<TimelineClipTarget>, additive: Boolean = false, toggle: Boolean = false)

Replaces the selection, adds to it (additive), or flips each clip's state within it (toggle) — what a click, a shift-click and a ctrl-click respectively produce. An empty list with both false clears it.

Link copied to clipboard
abstract fun selectTimelineMidiClip(trackIndex: Int, clipId: Int): Boolean

The MIDI clip an editor is open on, tracked separately from the selection.

Link copied to clipboard
abstract fun setAudioEngineEnabled(enabled: Boolean)
Link copied to clipboard
abstract fun setClipAudioEvents(trackIndex: Int, clipId: Int, markers: List<ClipMarkerData>, warps: List<AudioWarpPointData>): OpResult
Link copied to clipboard
abstract fun setInstanceGroup(instanceId: Int, group: UByte): Boolean
Link copied to clipboard

Writes the master markers and reports why a rejected set was rejected, which ProjectCommands.setMasterTrackMarkers reduces to a boolean. This path is not undoable.

Link copied to clipboard
abstract fun setTrackMuted(trackIndex: Int, muted: Boolean): Boolean
Link copied to clipboard
abstract fun setTrackSolo(trackIndex: Int, solo: Boolean): Boolean
Link copied to clipboard

False when a render is already running or the settings are unusable.

Link copied to clipboard
abstract fun timelinePasteDestinations(trackIndex: Int, originalTracks: Boolean): List<Int>

Which tracks a paste would land on, without performing it. originalTracks pastes each clip back onto the track it came from instead of onto trackIndex.

Link copied to clipboard
abstract fun toggleAudioEngine()
Link copied to clipboard
abstract fun unblockPlugin(entryId: String): Boolean
Link copied to clipboard
abstract fun undo(callback: (error: String?) -> Unit? = null)
Link copied to clipboard
abstract fun updateAudioDeviceSettings(sampleRate: Int, bufferSize: UInt)
Link copied to clipboard
abstract fun updateDeviceLabel(instanceId: Int, label: String)