DirectAccess
DirectAccess is the low-level host access layer of the MIDI Remote API.
Use it when normal mappings are not enough and you need runtime traversal, object metadata, or parameter-level inspection/write access.
For standard control assignments, prefer normal high-level bindings (makeValueBinding(...)).
Related chapters:
A DirectAccess instance must be activated in `page.mOnActivate` and deactivated in `page.mOnDeactivate`.
1. When to use DirectAccess
Use DirectAccess when you need one of these:
- Traverse runtime host objects by
ObjectID - Inspect runtime metadata (
getObjectTitle,getObjectTypeName,getObjectUniqueIDString) - Enumerate/query parameters dynamically
- Use plugin slot collection/assignment via
mPluginManager
Avoid DirectAccess when the same task can be solved by standard value bindings.
Reference:
2. Creating and wiring DirectAccess
var driver = midiremote_api.makeDeviceDriver('ExampleCompany', 'DirectAccessDevice', 'Example Author')
var page = driver.mMapping.makePage('DirectAccess Page')
var hostObject = page.mHostAccess.mTrackSelection.mMixerChannel
// API 1.2+
if (page.mHostAccess.makeDirectAccess) {
var da = page.mHostAccess.makeDirectAccess(hostObject)
page.mOnActivate = function (activeDevice, activeMapping) {
da.activate(activeMapping)
}
page.mOnDeactivate = function (activeDevice, activeMapping) {
da.deactivate(activeMapping)
}
}
Reference:
3. Runtime object traversal model
DirectAccess methods live on da.
Sub-nodes are addressed by runtime ObjectID values.
Typical traversal flow:
getBaseObjectID(activeMapping)getNumberOfChildObjects(activeMapping, objectID)getChildObjectID(activeMapping, objectID, index)- Read metadata for each node
Reference:
function visitSubtree(da, activeMapping, objectID, depth) {
var title = da.getObjectTitle(activeMapping, objectID)
var typeName = da.getObjectTypeName(activeMapping, objectID)
console.log(' '.repeat(depth) + '[' + objectID + '] ' + title + ' (' + typeName + ')')
var childCount = da.getNumberOfChildObjects(activeMapping, objectID)
for (var i = 0; i < childCount; ++i) {
var childObjectID = da.getChildObjectID(activeMapping, objectID, i)
visitSubtree(da, activeMapping, childObjectID, depth + 1)
}
}
function logTree(da, activeMapping) {
var rootObjectID = da.getBaseObjectID(activeMapping)
if (rootObjectID < 0) return
visitSubtree(da, activeMapping, rootObjectID, 0)
}
4. Parameter discovery and conversion
ObjectID and parameterTag are the core lookup keys.
function logParameters(da, activeMapping, objectID) {
var count = da.getNumberOfParameters(activeMapping, objectID)
for (var i = 0; i < count; ++i) {
var parameterTag = da.getParameterTagByIndex(activeMapping, objectID, i)
var parameterTitle = da.getParameterTitle(activeMapping, objectID, parameterTag, 128)
var display = da.getParameterDisplayValue(activeMapping, objectID, parameterTag)
var units = da.getParameterDisplayUnits(activeMapping, objectID, parameterTag)
var typeName = da.getParameterProcessValueType(activeMapping, objectID, parameterTag)
var automatable = da.isParameterAutomatable(activeMapping, objectID, parameterTag)
console.log(parameterTitle + ' = ' + display + units + ' [' + typeName + '] automatable=' + automatable)
}
}
Process value and plain value conversion:
var processValue = 0.5
var plainValue = da.convertParameterProcessValueToPlain(activeMapping, objectID, parameterTag, processValue)
var backToProcess = da.convertParameterPlainToProcessValue(activeMapping, objectID, parameterTag, plainValue)
Reference:
5. Writing parameters safely
function setParameterHalf(da, activeMapping, objectID, parameterTag) {
da.setParameterProcessValue(activeMapping, objectID, parameterTag, 0.5)
}
If your script writes multiple parameters in a compact interaction flow, test and use edit-lock APIs where appropriate:
var wasLocked = da.getParameterEditLockState(activeMapping, objectID, parameterTag)
da.setParameterEditLockState(activeMapping, objectID, parameterTag, true)
// ... perform coordinated changes ...
da.setParameterEditLockState(activeMapping, objectID, parameterTag, wasLocked)
6. DirectAccess callbacks
DirectAccess supports callback hooks for incremental updates:
mOnObjectChangemOnObjectWillBeRemovedmOnParameterChange
Reference:
Use them for cache invalidation and targeted refresh logic:
da.mOnObjectWillBeRemoved = function (activeDevice, activeMapping, objectID) {
delete objectCache[objectID]
}
7. Plugin manager (advanced)
The plugin manager is available through da.mPluginManager and enables slot-level plugin browsing and assignment.
var collectionCount = da.mPluginManager.getNumberOfPluginCollections(activeMapping, pluginSlotObjectID)
var activeCollectionIndex = da.mPluginManager.getIndexOfActivePluginCollection(activeMapping, pluginSlotObjectID)
if (activeCollectionIndex >= 0 && activeCollectionIndex < collectionCount) {
var collection = da.mPluginManager.getPluginCollectionByIndex(activeMapping, pluginSlotObjectID, activeCollectionIndex)
if (collection.mEntries.length > 0) {
var pluginUID = collection.mEntries[0].mPluginUID
da.mPluginManager.trySetSlotPlugin(activeMapping, pluginSlotObjectID, pluginUID, true)
}
}
Reference:
8. Compatibility strategy (API 1.2 and 1.3)
DirectAccess is an API 1.2+ feature. Guard usage with feature checks if your script still supports older host versions.
Example guard pattern:
var driver = midiremote_api.makeDeviceDriver('ExampleCompany', 'DirectAccessDevice', 'Example Author')
var page = driver.mMapping.makePage('Main')
var hostObject = page.mHostAccess.mTrackSelection.mMixerChannel
if (page.mHostAccess.makeDirectAccess) {
var da = page.mHostAccess.makeDirectAccess(hostObject)
}
For API 1.3 additions (for example platform detection filters or extended parameter inspection), check method availability before calling:
if (da.getParameterProcessValueType) {
// API 1.3+ method usage
}
See also: