Skip to content

Commit

Permalink
ongoing extensibility support in host/plugin interface from AIDL level.
Browse files Browse the repository at this point in the history
NOTE: this is still regressive; audio processing fails even with mda-lv2 ports.

partial context: #44
  • Loading branch information
atsushieno committed Apr 25, 2020
1 parent 2f467c0 commit b4c4ead
Show file tree
Hide file tree
Showing 21 changed files with 226 additions and 52 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ What AAP aims is to become like an inclusive standard for audio plugin, adoped t

On the other hand it is designed so that cross-audio-plugin frameworks can support it. We have [JUCE](http://juce.com/) integration support, and once [iPlug2](https://iplug2.github.io/) supports Linux and Android it would become similarly possible. Namely, AAP is first designed so that JUCE audio processor can be implemented and JUCE-based audio plugins can be easily imported to AAP world.

Extensibility is provided like what LV2 does (but without RDF and Turtle complication). VST3-specifics, or AAX-specifics, can be represented as long as it can be represented through raw pointer of any type (`void*`) i.e. cast to any context you'd like to have, associalted with a URI. Those extensions can be used only with supported hosts. A host can query each plugin whether it supports certain feature or not and disable those not-supported plugins, and a plugin can query the host which features it provides.
AAP is extensible to some extent; plugins can query host for extension data which is represented as a pointer on shared memory (explained later), associated with certain extension URI. It is similar to how LV2 extensions works, but in AAP it is limited to **data** pointers (also without RDF and Turtle complication). Those extensions can be used only with supported hosts. A host can query each plugin whether it supports certain feature or not and disable those not-supported plugins, and a plugin can query the host which features it provides. It is limited to data i.e. no runnable code, because code in host application is simply not runnable on the plugin application.

VST3 specifics, LV2 specifics, etc. should be processed by (so-called) plugin wrappers, with some help with extensions, if provided by anyone.

Android is the first citizen in AAP, but we also support Linux desktop so that actual plugin development can be achieved on the desktop.

Expand Down
1 change: 1 addition & 0 deletions aidl/org/androidaudioplugin/AudioPluginInterface.aidl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package org.androidaudioplugin;
interface AudioPluginInterface {

int create(String pluginId, int sampleRate);
void addExtension(int instanceID, String uri, in ParcelFileDescriptor sharedMemoryFD, int size);

boolean isPluginAlive(int instanceID);

Expand Down
1 change: 1 addition & 0 deletions java/.idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions java/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions java/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion java/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.androidaudioplugin

public class AudioPluginExtensionData(val uri: String, val data: ByteArray)
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class AudioPluginHost(private var applicationContext: Context) {
internal var connectedServices = mutableListOf<PluginServiceConnection>()
var instantiatedPlugins = mutableListOf<AudioPluginInstance>()

val extensions = mutableListOf<AudioPluginExtensionData>()

fun bindAudioPluginService(service: AudioPluginServiceInformation) {
var intent = Intent(AudioPluginHostHelper.AAP_ACTION_NAME)
intent.component = ComponentName(
Expand Down Expand Up @@ -79,7 +81,7 @@ class AudioPluginHost(private var applicationContext: Context) {

private fun instantiatePlugin(pluginInfo: PluginInformation, conn: PluginServiceConnection)
{
var instance = conn.instantiatePlugin(pluginInfo, sampleRate)
var instance = conn.instantiatePlugin(pluginInfo, sampleRate, extensions)
instantiatedPlugins.add(instance)
pluginInstantiatedListeners.forEach { l -> l (instance) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public static class Default implements org.androidaudioplugin.AudioPluginInterfa
{
return 0;
}
@Override public void addExtension(int instanceID, java.lang.String uri, android.os.ParcelFileDescriptor sharedMemoryFD, int size) throws android.os.RemoteException
{
}
@Override public boolean isPluginAlive(int instanceID) throws android.os.RemoteException
{
return false;
Expand Down Expand Up @@ -98,6 +101,26 @@ public static org.androidaudioplugin.AudioPluginInterface asInterface(android.os
reply.writeInt(_result);
return true;
}
case TRANSACTION_addExtension:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
android.os.ParcelFileDescriptor _arg2;
if ((0!=data.readInt())) {
_arg2 = android.os.ParcelFileDescriptor.CREATOR.createFromParcel(data);
}
else {
_arg2 = null;
}
int _arg3;
_arg3 = data.readInt();
this.addExtension(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
return true;
}
case TRANSACTION_isPluginAlive:
{
data.enforceInterface(descriptor);
Expand Down Expand Up @@ -264,6 +287,34 @@ public java.lang.String getInterfaceDescriptor()
}
return _result;
}
@Override public void addExtension(int instanceID, java.lang.String uri, android.os.ParcelFileDescriptor sharedMemoryFD, int size) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(instanceID);
_data.writeString(uri);
if ((sharedMemoryFD!=null)) {
_data.writeInt(1);
sharedMemoryFD.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeInt(size);
boolean _status = mRemote.transact(Stub.TRANSACTION_addExtension, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addExtension(instanceID, uri, sharedMemoryFD, size);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public boolean isPluginAlive(int instanceID) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
Expand Down Expand Up @@ -487,16 +538,17 @@ public java.lang.String getInterfaceDescriptor()
public static org.androidaudioplugin.AudioPluginInterface sDefaultImpl;
}
static final int TRANSACTION_create = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_isPluginAlive = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_prepare = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_prepareMemory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_activate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_process = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_deactivate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_getStateSize = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_setState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
static final int TRANSACTION_destroy = (android.os.IBinder.FIRST_CALL_TRANSACTION + 10);
static final int TRANSACTION_addExtension = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_isPluginAlive = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_prepare = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_prepareMemory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_activate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_process = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_deactivate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getStateSize = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_getState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
static final int TRANSACTION_setState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 10);
static final int TRANSACTION_destroy = (android.os.IBinder.FIRST_CALL_TRANSACTION + 11);
public static boolean setDefaultImpl(org.androidaudioplugin.AudioPluginInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
Expand All @@ -509,6 +561,7 @@ public static org.androidaudioplugin.AudioPluginInterface getDefaultImpl() {
}
}
public int create(java.lang.String pluginId, int sampleRate) throws android.os.RemoteException;
public void addExtension(int instanceID, java.lang.String uri, android.os.ParcelFileDescriptor sharedMemoryFD, int size) throws android.os.RemoteException;
public boolean isPluginAlive(int instanceID) throws android.os.RemoteException;
public void prepare(int instanceID, int frameCount, int portCount) throws android.os.RemoteException;
public void prepareMemory(int instanceID, int shmFDIndex, android.os.ParcelFileDescriptor sharedMemoryFD) throws android.os.RemoteException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package org.androidaudioplugin
import android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder
import android.os.ParcelFileDescriptor
import android.os.SharedMemory
import android.util.Log

// FIXME: make it internal
Expand All @@ -22,9 +24,14 @@ class PluginServiceConnection(var serviceInfo: AudioPluginServiceInformation, va
override fun onServiceDisconnected(name: ComponentName?) {
}

fun instantiatePlugin(pluginInfo : PluginInformation, sampleRate: Int) : AudioPluginInstance {

var instanceId = AudioPluginInterface.Stub.asInterface(binder!!).create(pluginInfo.pluginId, sampleRate)
fun instantiatePlugin(pluginInfo : PluginInformation, sampleRate: Int, extensions: List<AudioPluginExtensionData>) : AudioPluginInstance {
val aapSvc = AudioPluginInterface.Stub.asInterface(binder!!)
var instanceId = aapSvc.create(pluginInfo.pluginId, sampleRate)
var extensionSharedMemoryList = extensions.associateBy({ ext -> ext.uri}, { ext ->
val shm = SharedMemory.create(null, ext.data.size)
shm.mapReadWrite().put(ext.data)
shm })
extensionSharedMemoryList.forEach { ext -> aapSvc.addExtension(instanceId, ext.key, ParcelFileDescriptor.fromFd(ext.value.describeContents()), ext.value.size) }
var instance = AudioPluginInstance(instanceId, serviceInfo.plugins.first { p -> p.pluginId == pluginInfo.pluginId}, this)
instances.add(instance)
return instance
Expand Down
4 changes: 2 additions & 2 deletions java/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.61'
ext.kotlin_version = '1.3.72'
ext.dokkaVersion = '0.9.17'
repositories {
google()
jcenter()
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0-alpha01'
classpath 'com.android.tools.build:gradle:4.1.0-alpha07'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokkaVersion"
// NOTE: Do not place your application dependencies here; they belong
Expand Down
4 changes: 2 additions & 2 deletions java/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Mon Mar 09 01:52:42 JST 2020
#Sun Apr 26 00:33:37 JST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
1 change: 1 addition & 0 deletions java/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
rootProject.name = 'AndroidAudioPlugin'
include ':androidaudioplugin', ':androidaudioplugin-lv2', ':aapbarebonepluginsample', ':aaphostsample', ':localpluginsample', ':aaplv2plugins', ':aappluginsample'
project(':aaplv2plugins').projectDir = new File('samples/aaplv2plugins')
project(':aapbarebonepluginsample').projectDir = new File('samples/aapbarebonepluginsample')
Expand Down
27 changes: 21 additions & 6 deletions native/androidaudioplugin/android/src/AudioPluginInterfaceImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ class AudioPluginInterfaceImpl : public aidl::org::androidaudioplugin::BnAudioPl
*_aidl_return = host->createInstance(in_pluginId.c_str(), in_sampleRate);
auto instance = host->getInstance(*_aidl_return);
auto shm = new SharedMemoryExtension();
shm->getSharedMemoryFDs().resize(instance->getPluginInformation()->getNumPorts());
AndroidAudioPluginExtension ext{SharedMemoryExtension::URI, shm};
shm->getPortBufferFDs().resize(instance->getPluginInformation()->getNumPorts());
AndroidAudioPluginExtension ext{SharedMemoryExtension::URI, 0, shm};
instance->addExtension(ext);
buffers.resize(*_aidl_return + 1);
auto & buffer = buffers[*_aidl_return];
Expand All @@ -51,6 +51,21 @@ class AudioPluginInterfaceImpl : public aidl::org::androidaudioplugin::BnAudioPl
return ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus addExtension(int32_t in_instanceID, const std::string& in_uri, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD, int32_t in_size) override
{
assert(in_instanceID < host->getInstanceCount());
AndroidAudioPluginExtension extension;
extension.uri = in_uri.c_str();
auto shmExt = host->getInstance(in_instanceID)->getSharedMemory();
assert(shmExt != nullptr);
auto dfd = dup(in_sharedMemoryFD.get());
shmExt->getExtensionFDs().emplace_back(dfd);
extension.transmit_size = in_size;
extension.data = mmap(nullptr, in_size, PROT_READ | PROT_WRITE, MAP_SHARED, dfd, 0);
host->getInstance(in_instanceID)->addExtension(extension);
return ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus isPluginAlive(int32_t in_instanceID, bool* _aidl_return) override
{
assert(in_instanceID < host->getInstanceCount());
Expand All @@ -64,8 +79,8 @@ class AudioPluginInterfaceImpl : public aidl::org::androidaudioplugin::BnAudioPl
{
assert(in_instanceID < host->getInstanceCount());
auto shmExt = host->getInstance(in_instanceID)->getSharedMemory();
if (shmExt != nullptr)
shmExt->getSharedMemoryFDs()[in_shmFDIndex] = dup(in_sharedMemoryFD.get());
assert(shmExt != nullptr);
shmExt->getPortBufferFDs()[in_shmFDIndex] = dup(in_sharedMemoryFD.get());
return ndk::ScopedAStatus::ok();
}

Expand All @@ -81,7 +96,7 @@ class AudioPluginInterfaceImpl : public aidl::org::androidaudioplugin::BnAudioPl
void freeBuffers(PluginInstance* instance, AndroidAudioPluginBuffer& buffer)
{
if (buffer.buffers)
for (int i = 0; i < instance->getSharedMemory()->getSharedMemoryFDs().size(); i++)
for (int i = 0; i < instance->getSharedMemory()->getPortBufferFDs().size(); i++)
if (buffer.buffers[i])
munmap(buffer.buffers[i], buffer.num_buffers * sizeof(float));
}
Expand All @@ -100,7 +115,7 @@ class AudioPluginInterfaceImpl : public aidl::org::androidaudioplugin::BnAudioPl
int resetBuffers(PluginInstance* instance, AndroidAudioPluginBuffer& buffer, int frameCount)
{
int nPorts = instance->getPluginInformation()->getNumPorts();
auto& FDs = instance->getSharedMemory()->getSharedMemoryFDs();
auto& FDs = instance->getSharedMemory()->getPortBufferFDs();
if (FDs.size() != nPorts) {
freeBuffers(instance, buffer);
FDs.resize(nPorts, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ void resetBuffers(AAPClientContext *ctx, AndroidAudioPluginBuffer* buffer)
int n = buffer->num_buffers;

auto prevBuf = ctx->previous_buffer;
auto &fds = ctx->shared_memory_extension->getSharedMemoryFDs();
auto &fds = ctx->shared_memory_extension->getPortBufferFDs();

// close extra shm FDs that are (1)insufficient in size, or (2)not needed anymore.
if (prevBuf != nullptr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class IAudioPluginInterface : public ::ndk::ICInterface {
static bool setDefaultImpl(std::shared_ptr<IAudioPluginInterface> impl);
static const std::shared_ptr<IAudioPluginInterface>& getDefaultImpl();
virtual ::ndk::ScopedAStatus create(const std::string& in_pluginId, int32_t in_sampleRate, int32_t* _aidl_return) = 0;
virtual ::ndk::ScopedAStatus addExtension(int32_t in_instanceID, const std::string& in_uri, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD, int32_t in_size) = 0;
virtual ::ndk::ScopedAStatus isPluginAlive(int32_t in_instanceID, bool* _aidl_return) = 0;
virtual ::ndk::ScopedAStatus prepare(int32_t in_instanceID, int32_t in_frameCount, int32_t in_portCount) = 0;
virtual ::ndk::ScopedAStatus prepareMemory(int32_t in_instanceID, int32_t in_shmFDIndex, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD) = 0;
Expand All @@ -37,6 +38,7 @@ class IAudioPluginInterface : public ::ndk::ICInterface {
class IAudioPluginInterfaceDefault : public IAudioPluginInterface {
public:
::ndk::ScopedAStatus create(const std::string& in_pluginId, int32_t in_sampleRate, int32_t* _aidl_return) override;
::ndk::ScopedAStatus addExtension(int32_t in_instanceID, const std::string& in_uri, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD, int32_t in_size) override;
::ndk::ScopedAStatus isPluginAlive(int32_t in_instanceID, bool* _aidl_return) override;
::ndk::ScopedAStatus prepare(int32_t in_instanceID, int32_t in_frameCount, int32_t in_portCount) override;
::ndk::ScopedAStatus prepareMemory(int32_t in_instanceID, int32_t in_shmFDIndex, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD) override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class BpAudioPluginInterface : public ::ndk::BpCInterface<IAudioPluginInterface>
virtual ~BpAudioPluginInterface();

::ndk::ScopedAStatus create(const std::string& in_pluginId, int32_t in_sampleRate, int32_t* _aidl_return) override;
::ndk::ScopedAStatus addExtension(int32_t in_instanceID, const std::string& in_uri, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD, int32_t in_size) override;
::ndk::ScopedAStatus isPluginAlive(int32_t in_instanceID, bool* _aidl_return) override;
::ndk::ScopedAStatus prepare(int32_t in_instanceID, int32_t in_frameCount, int32_t in_portCount) override;
::ndk::ScopedAStatus prepareMemory(int32_t in_instanceID, int32_t in_shmFDIndex, const ::ndk::ScopedFileDescriptor& in_sharedMemoryFD) override;
Expand Down
Loading

0 comments on commit b4c4ead

Please sign in to comment.