conceptual Android audio plugin framework

Overview

AAP: Android Audio Plugin Framework

AAP demo 20200708

disclaimer: the README is either up to date, partially obsoleted, or sometimes (but not very often) ahead of implementation (to describe the ideal state). Do not trust it too much.

What is AAP?

Android lacks commonly used Audio Plugin Framework. On Windows and other desktops, VSTs are popular. On Mac and iOS (including iPadOS) there is AudioUnit. On Linux LV2 is used, to some extent.

There is no such thing in Android. Android Audio Plugin (AAP) Framework is to fill this gap.

What AAP aims is to become like an inclusive standard for audio plugin, adoped to Android applications ecosystem. The license is permissive (MIT). It is designed to be pluggable from other specific audio plugin specifications like VST3, LV2, CLAP, and so on (not necessarily meant that we write code for them).

On the other hand it is designed so that cross-audio-plugin SDKs can support it. We have JUCE integration support, ported some plugin that uses DPF, and once iPlug2 supports Linux and Android it would become similarly possible. Namely, AAP is first designed to make use of JUCE audio plugin hosting features and JUCE-based audio plugins.

We have aap-lv2 and aap-juce repositories that achieve these objectives, to some extent. We have some plugins from these world working (to some extent) - for example: mda-lv2, Fluidsynth (as aap-fluidsynth), sfizz, Guitarix in aap-lv2, Dexed, OPNplug, OB-Xd, and JUCE AudioPluginHost in aap-juce. Note that there is no plugin UI integration support yet.

AAP features, characteristics, unique points

Android is supported, and it is the first citizen : no other audio plugin frameworks achieve that (except for AudioRoute SDK, as far as @atsushieno knows). To make it possible, we have some other characteristics explained below.

out-process model, between host activities and plugin services : AAP is designed to work for Android platform, which has strict separation on each application process space. Namely, we cannot load arbitrary shared libraries from random plugins. Thus AAP DAWs (plugin hosts) and AAPs (plugins) have to communicate through IPC mechanism. AAP uses Binder IPC through NdkBinder API which was introduced at Android 10. Also, the framework makes full use of Android shared memory (ashmem) throughout the audio/MIDI buffer processing.

AAP process model

Extensibility : plugin feature extensibility is provided through raw pointer data. But it must not contain function pointers, as Android platform does not support calling them from different apps.

Basically declarative parameter meta data : like LV2, we expect plugin metadata res/xml/aap_metadata.xml, described its ports.

C/C++ and Kotlin supported: C/C++ is supported for developing plugin and hosting, Kotlin for hosting.

Permissive licensing : It is released under the MIT license. Similar to LV2 (ISC), unlike VST3 or JUCE (GPLv3).

API unstability : unlike other audio plugin frameworks, we don't really offer API stability. What we recommend instead is to use APIs from audio plugin framework or SDKs, such as JUCE or LV2 API, and port them to AAP. AAP will be API stable "at some stage", but that is not planned.

There are some supplemental features that has not been in quality yet.

MIDI Device Service : AAP has ability to turn an instrument plugin into a MidiDeviceService.

How AAPs work: technical background

AAP distribution structure is simple; both hosts (DAWs) and plugins (instruments/effects) can be shipped as Android applications (via Google Play etc.) respectively:

  • AAP Host (DAW) developers can use AAP hosting API to query and load the plugins, then deal with audio data processed by them.
  • AAP (Plugin) developers works as a service, processes audio and MIDI messages.

From app packagers perspective and users perspective, it can be distributed like a MIDI device service. Like Android Native MIDI (introduced in Android 10.0), AAP processes all the audio stuff in native land (it still performs metadata queries and service queries in Dalvik/ART land).

AAP developers create audio plugin in native code using Android NDK, create plugin "metadata" as an Android XML resource (aap_metadata.xml), and optionally implement org.androidaudioplugin.AudioPluginService which handles audio plugin connections using Android SDK, then package them together. The metadata provides developer details, port details (as long as they are known), and feature requirement details.

TODO: The plugins and their ports can NOT be dynamically changed, at least as of the current specification stage. We should seriously reconsider this. It will be mandatory when we support so-called plugin wrappers.

AAP is similar to what AudioRoute hosted apps do. We are rather native oriented for reusing existing code. (I believe they also aim to implement plugins in native code, but not sure. The SDK is not frequently updated.)

How to create AAP plugins

Developers Guide contains some essential guide and some details on various topics.

We have a dedicated plugin development guide for (1) building from scratch, (2) importing from LV2 plugins, and (3) importing from JUCE plugins. We basically recommend (2) or (3) as our API is not quite stabilized yet. So if you take the (1) approach, then you are supposed to update source code whenever necessaey.

(We don't really assure the consistency on how we import LV2 and JUCE bits either, but their API would be mostly stable.)

Code in this repo, and build instructions

There are Android Gradle projects.

You can open java directory in Android Studio (Arctic Fox 2020.3.1 Canary is required) as the top-level project directory and build it. Or run ./gradlew there from your terminal.

There is Makefile to build all those Android modules, and install *.aars to local m2 repository. It is used by our CI builds too, including derived projects such as aap-lv2, aap-juce, and further derivative works.

androidaudioplugin

This library provides AudioPluginService which is mandatory for every AAP (plugin), as well as some hosting functionality.

androidaudioplugin-ui-compose

androidaudioplugin-ui-compose module contains PluginListActivity which can be used as a launcher activity by any audio plugin application.

It lists the plugins within the package, and when selected it can perform applying it to either some MIDI sequence or some wave inputs.

The UI is based on Jetpack Compose.

(It depends on androidaudioplugin-samples-host-engine internal component to provide in-app plugin preview (tryout) feature which will be rewritten at some stage.)

It is also used by samples/aaphostsample that lists all plugins on the system.

androidaudioplugin-midi-device-service and aap-midi-device-service

AAP instrument plugins can be extended to work as a virtual MIDI device service (software MIDI device).

aap-midi-device-service is an example application that turns any instrument plugins into MIDI device services (since it is simply possible).

At this state the audio generation feature not in usable quality at all.

aaphostsample and aapbarebonepluginsample

aaphostsample lists existing AAPs (services) that are installed on the same device, and demonstrates how they work, using some raw audio data example as well as some example MIDI input messages. As of writing this, the app does not respect any audio format and processes in fixed size, and sends some fixed MIDI messages (note on/off-s) to AAP plugins.

aapbarebonepluginsample is a sample AAP (plugin) that does "nothing". It can be rather used as a plugin template project.

Further documentation

DEVELOPERS.md contains more on plugin and host development.

PLUGIN_SCRATCH_GUIDE.md contains how to get started with plugin development using our own (unstable!) API.

HACKING.md contains how to build and hack this framework itself.

DESIGN_NOTES.md contains some technical background and design decisions that we have made in this framework.

Licenses

Android Audio Plugin Framework is released under the MIT License.

In aap-midi-device-service, there are some sources copied from jalv project included in aap-midi-device-service module, namely those files under zix directory, and they are distributed under the ISC license.

aapinstrumentsample also imports true-grue/ayumi which is distributed under the MIT License.

aap-miid-device-service also imports the following code:

Comments
  • meta: remaining MidiDeviceService tasks

    meta: remaining MidiDeviceService tasks

    (update: see https://github.com/atsushieno/android-audio-plugin-framework/issues/82#issuecomment-892459550 which updates all the statuses involved)

    (1) At this state AAPMidiProcessor can process only MIDI 2.0 messages. For MIDI 1.0 messages we have to sort out how to adjust lengths in the messages along with modified timestamps per audio processing cycles. MIDI 1.0 is especially problematic because length bytes can vary. We likely have to change how MIDI 1.0 messages are structured on the existing channels.

    (1.1) Maybe we should simply drop it and process everything in MIDI 2.0 UMPs. Then every subprojects need to reflect the protocol changes. aap-juce and all the plugins, plugin hosts, aap-lv2 based plugins (relatively easy as we already have UMP processing implementation)

    (2) At this state we assume that an AAP application contains at most one instrument plugin. It is already wrong in aap-lv2-mda as there are handful of instruments (EPiano, DX10, JX10, Piano...) in the app. A possible solution would be to create <input> port for every instrument. Then we only have to (a) create MidiReceiver instances for each, and (b) determine how to map those input entries to MidiReceivers (stable indices suffice).

    (3) there are some FIXMEs on terminating /unbinding device service.

    API design MIDI 
    opened by atsushieno 18
  • Freezes or crashes after multiple instancing on aaphostsample

    Freezes or crashes after multiple instancing on aaphostsample

    It is a typical but intermittent problem that applying a plugin on aaphostsample results in freezes. It most likely happens after 3~4 times on different plugins with LV2 plugins, or crashes with JUCE plugins.

    They may be different issues, and more likely plugin-side issues, but at this state it is looking like an overall framework or host problem. JUCE AudioPluginHost shows similar but different unstability too.

    bug blocker 
    opened by atsushieno 14
  • MIDI buffer processing abstraction

    MIDI buffer processing abstraction

    JUCE AudioPluginInstance::processBlock() tells us that it is up to the underlying audio plugin framework that interprets and processes MidiBuffer part. JUCE VST3 processor implements it in MidiEventList::toMidiBuffer() which populates Steinberg::Vst::Event instances. For AU it is MusicDeviceMIDIEvent.

    AAP needs something similar, but there is a problem here - there should be backend feature that converts those abstract MIDI messages to the backend-specifics. For LV2 backend, it is LV2 Atom.

    LV2 API design 
    opened by atsushieno 13
  • update plugins to work as AudioPluginService.V2 plugins

    update plugins to work as AudioPluginService.V2 plugins

    On main branch, aapinstrumentsample is implemented to work as MIDI 1 synth. Other plugins are either not updated or cause runtime crashes. They have to be either fixed or rewritten to work as V2 plugin.

    Though, it kinda requires updating to the new scheme for parameter changes i.e. interpret MIDI2 inputs in realtime manner. That brings in unified event processing model with MIDI1 based inputs, and not everything is ready for that yet.

    There are couple of sub-tasks:

    • [x] implement MIDI2 processing part in PluginPreview (and thus samples-host-engine and aaphostsample) to work with the new scheme if (1) there is a MIDI2 in port and (2) there are "parameters" (it was only partially implemented).
    • [x] make changes to aapinstrumentsample to also take parameter changes via UMPs.
    • [x] Once PluginPreview gets fixed and starts working fine, then it should also support (send) unified MIDI2 inputs to the instantiated plugin.
    • [x] midi-device-service should work regardless of whether the port is midi1 or midi2 (needs to insert UMP translator).
    • Once it gets working, then aap-lv2 needs to be rewritten to migrate to the new parameter changes scheme.
      • [x] get aap-ayumi working for both midi1 and midi2 modes (with conditional build)
      • [x] update aap-import-lv2-metadata to support midi2 and parameters
      • [x] get midi-device-service working
      • [x] formalize port mappings (which became inconsistent in the new design and causing run-time crash)
      • [x] get aap-lv2-mda effects working
      • [x] get aap-lv2-mda instruments working
      • [x] get aap-lv2-fluidsynth working
      • [x] get aap-lv2-sfizz working as MidiDeviceService
      • [x] get aap-lv2-string-machine MIDI output correct synthesis results
      • [ ] get LV2 patch input working
      • [ ] implement Atom output reader (and push to AAP midi2 output port)
    • The same has to go in aap-juce.
      • [x] get cmake support working
      • [x] get Projucer support working
      • [x] get plugins working
      • [x] get hosting working
    opened by atsushieno 12
  • breaking change: define extensibility controller down to AIDL

    breaking change: define extensibility controller down to AIDL

    After working on #103, I noticed that we need another bunch of breaking fundamental changes for extensibility:

    • We would still need extensibility mechanism that plugins host can "request" calls e.g. "save preset 0 as "%'#$'%$ (binary)", which is not doable just with data pointers. We need non-local function that host can call into plugin.
    • We could implement Presets extension as such, plugin service and client interacting together through binder, but that means we need another channel, which will bring in a lot of future API stability breakage. Extensibility should be realized as a generic channel.
    • An extension developer (most of the extensions would be implemented by ourselves) should be able to "message" each other (plugin to host, or host to plugin), beyond C API.
    • The most likely viable option for "general" purpose messaging is a pair of MIDI controller channels, as detailed at 21edbb9. Then an extension would not even need any extra messaging mechanism.
    • However, for operations like sending and receiving state blob that's going to be too massive and involves lots of extraneous work (splitting large binary chunk into MDS, which is what MDS is for but still we don't really need it). Therefore it would be better if an extension can make use of some shared pointer between the plugin and the client host.

    Therefore, I would tweak the existing AIDL structure and make some incompatible changes:

    • Introduce plugin service extensibility API that every extension developer needs to implement for both host client and plugin service.
    • Client hosts can invoke plugin service extension functions.
    • Plugins can only reference host extension properties through shared memory. Host cannot get notified if a plugin referenced them.
      • An extension can provide inquiry functions ("do/did you (plugin) reference my (host) property XXX?")
    • ExtensionRegistry collects the extension definitions.
    • Revamp getState() and setState() AIDL messaging: implement them along with the extensibility framework.

    Example Client that makes use of "AAP Presets" extension:

    	AndroidAudioPluginHost* host;
    	auto presetsExt =
    		(aap::PresetsPluginServiceExtension*) host->get_extension(AAP_PRESETS_EXTENSION_URI);
    	auto preset = presetsExt->get_preset(0);
    

    Example Foo Plugin that implements "AAP Presets" extension:

    	aap_preset_t foo_presets[];
    	
    	void foo_plugin_factory_initialize() {
    		// initialize foo_presets from resources here
    	}
    
    	// The actual Presets extension implementation for Foo plugin
    	aap_preset_t* foo_presets_get_preset(int32_t index) { return foo_presets[index]; }
    
    	aap_presets_extension_t foo_presets_extension { foo_presets_get_preset };
    
    	void* foo_get_extension(AndroidAudioPlugin *plugin, const char *extensionURI) {
    		switch (uri_to_id(extensionURI)) {
    		case AAP_PRESETS_EXTENSION_URI_ID:
    			return &foo_presets_extension;
    		}
    	}
    
    	AndroidAudioPlugin foo_plugin{
    		nullptr,
    		foo_prepare,
    		...
    		foo_get_extension
    	};
    	
    	AndroidAudioPlugin* foo_plugin_instantiate (...) { return &foo_plugin; };
    	
    	AndroidAudioPluginFactory foo_factory {
    		foo_plugin_instantiate, foo_plugin_release, nullptr
    	};
    	
    	bool foo_factory_initialized{false};
    	
    	AndroidAudioPluginFactory* GetAndroidAudioPluginFactory () {
    		if (!foo_factory_initialized) {
    			foo_factory_initialized = true;
    			foo_plugin_factory_initialize();
    		}
    		return &foo_factory;
    	}
    

    PluginService API (probably C, might be C++):

    	// C
    	void (*aap_service_extension_on_invoked_t) (
    		AndroidAudioPluginHost *service,
    		AndroidAudioPluginServiceExtension* serviceExtension,
    		AndroidAudioPluginExtension* data);
    
    	typedef struct {
    		const char *uri;
    		aap_service_extension_on_invoked_t on_invoked;
    	} aap_service_extension_t;
    
    	// C++
    	class AndroidAudioPluginServiceExtension {
    		virtual const char* getURI() = 0;
    		void clientInvokePluginExtension(AndroidAudioPluginExtension* ext) {
    			// transmit data via Binder
    		}
    		virtual void onServicePluginExtensionInvoked(AndroidAudioPluginExtension* ext) = 0;
    	};
    

    Presets PluginService extension implementation (probably C, might be C++):

    	// It is already in aap-core.h
    	typedef struct {
    		const char *uri;
    		int32_t transmit_size;
    		void *data;
    	} AndroidAudioPluginExtension;
    
    	class MemoryBlock {
    		void* buffer{nullptr};
    		int32_t size{0};
    		
    		~MemoryBlock() {
    			if (buffer)
    				free(buffer);
    		}
    		
    		void copyFrom(int32_t size, void *src) {
    			if (buffer == nullptr || this->size < size) {
    				if (buffer != nullptr)
    					free(buffer);
    				this->size = size;
    				buffer = calloc(1, size);
    			}
    			memcpy(result->buffer, *((int32_t*) extensionInstance->data, size);
    		}
    	}
    
    	// C
    	void presets_plugin_service_extension_on_invoked (
    		AndroidAudioPluginHost *service,
    		AndroidAudioPluginServiceExtension* serviceExtension,
    		AndroidAudioPluginExtension* data) {
    		switch (opcode) {
    		case OPCODE_GET_PRESET:
    			int32_t index = *((int32_t*) ext->data);
    			auto presetExtension = (aap_preset_extension_t*) service->get_extension(getURI());
    			auto preset = (aap_preset_t*) presetExtension->get_preset(index);
    			// write size, then data
    			*((int32_t*) ext->data) = preset->data_size;
    			memcpy(ext->data + sizeof(int32_t), preset->data, preset->data_size);
    			return;
    		}
    		assert(0); // should not reach here			
    	}
    	
    	aap_service_extension_t presets_plugin_service_extension{
    		AAP_PRESETS_EXTENSION_URI, presets_plugin_service_extension_on_invoked };
    	
    	// C++
    	class PresetsPluginServiceExtension : public AndroidAudioPluginServiceExtension {
    	public:
    		PresetsPluginServiceExtension(AndroidAudioPluginHost* host, AndroidAudioPluginExtension *extensionInstance)
    			: AndroidAudioPluginServiceExtension(host, extensionInstance) { ... }
    	
    		inline const char* getURI() override { return AAP_PRESETS_EXTENSION_URI; }
    		
    		const int32_t OPCODE_GET_PRESET = 0;
    		
    		// function for client host to implement
    		void get_preset(int32_t index, MemoryBlock *result) {
    			*((int32_t*) extensionInstance->data) = index;
    			clientInvokePluginExtension(data, OPCODE_GET_PRESET);
    			int32_t size = *((int32_t*) extensionInstance->data);
    			result->copyFrom(size, *((int32_t*) extensionInstance->data + 1);
    		}
    		
    		// function for plugin service to implement, called by AudioPluginService impl.
    		void onServicePluginExtensionInvoked(AndroidAudioPluginExtension* ext, int opcode) override {
    			switch (opcode) {
    			case OPCODE_GET_PRESET:
    				int32_t index = *((int32_t*) ext->data);
    				auto presetExtension = (aap_preset_extension_t*) host->get_extension(getURI());
    				auto preset = (aap_preset_t*) presetExtension->get_preset(index);
    				// write size, then data
    				*((int32_t*) ext->data) = preset->data_size;
    				memcpy(ext->data + sizeof(int32_t), preset->data, preset->data_size);
    				return;
    			}
    			assert(0); // should not reach here			
    		}
    	};
    

    ExtensionRegistry:

    	class ExtensionRegistry {
    		virtual AndroidAudioPluginServiceExtension* create(const char * uri, AndroidAudioPluginHost* host, AndroidAudioPluginExtension *extensionInstance) = 0;
    	};
    

    Extension service implementation needs to be compiled in the host. For plugins, only the extension header is required (the API has to be "implemented" by the plugin developer anyways).

    A host application has to prepare its own ExtensionRegistry instance, and manage whatever extensions it wants to support. StandardExtensionRegistry should come up with all those standard extensions enabled.

    API design 
    opened by atsushieno 8
  • replacement for aaphostsample based on Jetpack Compose

    replacement for aaphostsample based on Jetpack Compose

    There has been compose-samples branch for a while, but it has never been worked on for months. I started reworking on it these days.

    The idea was now that there is Jetpack Compose for Desktop, it could be reusable on desktop too. It turned out that to make org.androidaudioplugin in Kotlin MPP common package for a Compose app, it must not be an aar library package, which (at least currently) isn't.

    Should we isolate Android platform dependencies (Android platform and JNI based code), jniLibs, and prefab contents from the Kotlin core bits? So far I'm negative because It complicates the build.

    If we don't go with MPP approach, it is still possible to apply Jetpack Compose, and it is indeed desirable for future maintainability, except for current concern that Compose is supported only on Android Studio 4.2 or later.

    enhancement samples 
    opened by atsushieno 8
  • Port properties framework

    Port properties framework

    Ports should have some properties.

    For example, it is not predictable for hosts if a port is supposed to receive a lot of values continuously (e.g. filter gain, pan, pitchbend) or rarely changed (e.g. switching legato, glissando, etc.).

    LV2 seems to have such parameter kinds e.g. expensive so we might want to have something similar for better host implementation performance.

    API design 
    opened by atsushieno 8
  • remote client issues

    remote client issues

    There are two blockers that seems to prevent things:

    • [x] quite unsure if ASharedMemory FDs passed from the client to the plugin are valid. ASharedMemory_getSize() returns 0. And writing results in SEGV_ACCERR or SEGV_MAPPER.
    • [ ] AIBinder_Class created at plugin (at AAPClientContext) side is problematic. Basically it is dummy now and there is no way to retrieve AIBinder_Class for them from scratch.
    opened by atsushieno 8
  • Some official specification or design guideline on parameters or properties with input/output ports

    Some official specification or design guideline on parameters or properties with input/output ports

    Currently we don't really have "parameters" or "properties" on a plugin. Parameters are implicitly represented as in ports, and they have broad data width just like audio inputs/outputs. "Properties" (probably implying that they are not limited to floats) are also supposed to be transmitted via those ports.

    A problem here is that we shouldn't really have to require / assign ports for each single parameter. If there are 100 parameters (which is likely to happen) there will be 100 ports, 100 x 4096 bytes if the buffer size is 1024, just for one cycle. And that's too much.

    What we would like to have instead is to have one single input port to send a set of parameter changes, and output port based on user inquiry of parameter values. sfizz-lv2 has this kind of ports (manifest for input port and notification port), which would be ideal for most of us. mda-lv2, which I took to learn how plugin ports would be designed, is not designed like that.

    "Properties" would work in quite similar manner, but I would make it work like MIDI 2.0 Property Exchange (maybe without forcing JSON syntax). If it works like standard way, then plugins would be made of MIDI standard constructs. Requests would be sent to the control input port, and the results would be sent to the notification port. As of v0.7.4 there will be no property exchange support; parameter changes should be all sent via MIDI2 output notifications.

    We wouldn't need different names for parameters and properties, they can be both "property", with an optional "type" attribute (float by default). There will be no type support. Non-float properties should be set via sysex, a port, or an extension.

    Control inputs are not necessarily real-time safe. In fact most of them wouldn't be. But the inputs will be processed in real-time loop anyways, and there should be some remedy to assure that a control input does not overwrite existing control inputs. An appropriate queue without locking is required.

    API design 
    opened by atsushieno 7
  • Improve desktop experience

    Improve desktop experience

    Since prefab packaging issue has been stuck for a while and it impacts other development direction, I'm rather thinking it's time to improve desktop development experience.

    enhancement API design desktop 
    opened by atsushieno 7
  • v0.5 API cleanup

    v0.5 API cleanup

    As of v0.4 (where we have JUCE processor integration) the API became somewhat inconsistent and nasty. Especially, the plugin API now exposes shared memory FDs which should be internals. There should be some API cleanup for better code quality and hackability.

    code-quality 
    opened by atsushieno 7
  • connectedCheck fails on x86_64 and arm64

    connectedCheck fails on x86_64 and arm64

    On x86_64 emulator it fails with these logcat lines (no visible outputs on test results HTML):

    14:26:04.377  4598-4620  AudioPluginHost org...arebonepluginsample  D  bindAudioPluginService: org.androidaudioplugin.aapbarebonepluginsample | org.androidaudioplugin.AudioPluginService
    14:26:04.393  4598-4598  AudioP...ervice org...arebonepluginsample  D  onBind done
    14:26:04.394  4598-4598  AAP             org...arebonepluginsample  D  AudioPluginServiceConnector: onServiceConnected
    14:26:04.463  4598-4620  AAP.proxy       org...arebonepluginsample  E  beginPrepare() failed: (FIXME: due to Android SDK/NDK issue 219987524 we cannot retrieve failure details here)
    14:26:04.463  4598-4620  AAP.proxy       org...arebonepluginsample  E  prepareMemory() failed: (FIXME: due to Android SDK/NDK issue 219987524 we cannot retrieve failure details here)
    

    arm64 fails with somewhat different message but result are not realiable yet. To be added.

    opened by atsushieno 0
  • Sort out appropriate steps to bind service, instantiate connector, and create instances

    Sort out appropriate steps to bind service, instantiate connector, and create instances

    There seems some confusion around management. binder and proxy are now one per client-app to service-app. Service should be unbound when all connections are gone, but it seems unbinding is not appropriately handled.

    opened by atsushieno 0
  • Rename this repository to aap-core and the entire project to Audio Plugins for Android

    Rename this repository to aap-core and the entire project to Audio Plugins for Android

    It's been named as Android Audio Plugin Framework, but it will be problematic when it became really popular and causes possible Google TM issues. Can be done at any time, but I would probably do it when I create a dedicated organization.

    opened by atsushieno 1
  • find alternative approach  to eliminate AndroidAudioPluginExtensionTarget

    find alternative approach to eliminate AndroidAudioPluginExtensionTarget

    Currently whenever we need extensions we have to use AndroidAudioPluginExtensionTarget which is not very intuitive. Especially I don't like the fact that it exposes aapxs_context to extension users.

    There were handful of complaints I had when I was dealing with extensions. I will list them when I'm back to this stack.

    enhancement API design extension 
    opened by atsushieno 0
  • redesign AndroidAudioPluginBuffer?

    redesign AndroidAudioPluginBuffer?

    Current AndroidAudioPluginBuffer structure is like this:

    typedef struct AndroidAudioPluginBuffer {
    	size_t num_buffers;
    	void **buffers;
    	size_t num_frames;
    } AndroidAudioPluginBuffer;
    

    It is strongly typed, but the structure is probably too straightforward. There is no way to provide safe access to those pointers. It is not ideal.

    I have been aware of this, but the primary reason I didn't touch it was because it is part of the public API that affects everywhere. But it should be changed, probably sooner.

    API design code-quality 
    opened by atsushieno 0
  • MainActivity should function as

    MainActivity should function as "settings" for MidiDeviceService

    I have been wondering how we should configure MidiDeviceService per plugin e.g. (1) treat program changes as preset changes, or (2) treat Assignable Controllers as plugin parameter changes, or even (3) treat CCs as plugin parameter changes (then it works like MIDI mappings).

    And I was wondering if they should be configured by the plugin developer or not. At this state I think: maybe not. If it is configurable by user then that is the best.

    Those 3 options are simple checkboxes and easy to implement.

    API design MIDI 
    opened by atsushieno 0
Owner
Atsushi Eno
music software tools enthusiast. Android Audio Plugin, Linux, MML, MIDI 1.0/2.0, LV2. music tech meetup Tokyo organizer. ex mono developer. circle ginga
Atsushi Eno
AudioNotes 📙 An open source simple audio note taking app built to demonstrate android development best practices.

AudioNotes ?? A simple open source audio note-taking ?? Android application built to describe the use of Modern Android development tools. ?? . Made w

Samson Achiaga 47 Dec 15, 2022
PyCharm plugin for ruff. This plugin provides reformat code using ruff.

Ruff PyCharm Plugin A JetBrains PyCharm plugin for ruff. Help See documentation for more details. Sponsors ScreenShots Features Run ruff --fix as an a

Koudai Aono 4 Dec 17, 2022
Extensible Android mobile voice framework: wakeword, ASR, NLU, and TTS. Easily add voice to any Android app!

Spokestack is an all-in-one solution for mobile voice interfaces on Android. It provides every piece of the speech processing puzzle, including voice

Spokestack 57 Nov 20, 2022
android-trinity is tiny proactive framework with much of the scaffolding code required to start a new Android Application.

android-trinity This is tiny framework with much of the scaffolding code (with some nice utilities and prepared source code) required to start a new A

Fernando Cejas 49 Nov 24, 2022
An android Quiz App in kotlin framework and uses Appwrite as backend

Quiz App Intro An android Quiz App in kotlin framework and uses Appwrite as backend How to clone and run the project: Cloning : git clone https://gith

null 2 Oct 22, 2022
weiV(pronounced the same as wave), a new declarative UI development framework based on the Android View system.

weiV(pronounced the same as wave) 简体中文 if ("weiV" == "View".reversed()) { Log.d( "weiV", "It means Inversion of Control, you shoul

fangbing chen 69 Nov 22, 2022
The Leading Security Assessment Framework for Android.

drozer ---------------------------------------------------------------- NOTE We would like to formally announce that F-Secure has stopped further deve

WithSecure Labs 3k Jan 8, 2023
Android File Fuzzing Framework

Droid-FF : install python dependencies (setup.sh ) and you are good to go. GDB Server for android : get it from @ wget https://people.mozilla.org/~nch

xyz 81 Nov 17, 2022
The News App has been carried out within the framework of the MVVM architecture, information about news is obtained by consulting an API, it is built usisng Jetpack Copose, Coroutines, Dependency Injection with Hilt and Retrofit

Journalist The News App consists of an application that displays the latest news from EEUU from an API that provides official and updated information.

null 0 Nov 3, 2021
A framework for building native applications using React

React Native Learn once, write anywhere: Build mobile apps with React. Getting Started · Learn the Basics · Showcase · Contribute · Community · Suppor

Meta 106.9k Jan 8, 2023
Slack app example for Heroku deployment, written in Kotlin, using Bolt framework.

slack-kotlin-heroku-example Slack app example for Heroku deployment, written in Kotlin, using Bolt framework. You need to configure your Slack app to

null 0 Dec 25, 2021
Hobby-keeping - Platform to record books that you read and games you played! Made with Kotlin and Spring Framework

Hobby Keeping API to record books that you read and games you played! Made with

William Barom Mingardi 1 Jan 29, 2022
Photon Framework provides cool way to Discord Slash Commands 👩‍💻 🚧

Photon Framework provides cool way to Discord Slash Commands ??‍?? ??

Codename Photon 16 Dec 20, 2022
Simple application with some famous graph algorithm implemented by Jetpack Compose framework

GraphAlgorithm This Application was implemented by Jetpack Compose framework. The dagger-hilt library was used for dependency injection and Room libra

Amirreza lotfi 8 Aug 17, 2022
A lightweight tracking framework based on the tracking idea of Buzzvideo.(基于西瓜视频的责任链埋点思路实现的轻量级埋点框架)

Tracker English | 中文 Tracker is a lightweight tracking framework based on the tracking idea of Buzzvideo. Tracking idea Why use chain of responsibilit

DylanCai 76 Dec 22, 2022
A Modular Firebase plugin (Android) for godot

GDFirebase GDFirebase is a Modular Godot Plugin for using Firebase Depends on Godot game engine: git clone https://github.com/godotengine/godot Avail

FrogSquare 10 Dec 9, 2022
A plugin for Termux to use native Android GUI components from CLI applications.

Termux:GUI This is a plugin for Termux that enables command line programs to use the native android GUI. In the examples directory you can find demo v

Termux 345 Jan 1, 2023
simple-flank is a Gradle plugin to use Flank in Android projects with no configuration needed

simple-flank simple-flank is a new gradle plugin with a clear focus: make the setup as simple as possible. Applied to any application or library modul

Flank 8 May 10, 2022
A Android Web IDE supports code auto-completion and highlight, plugin (Supports Html, Css, JS, Json, Php etc)

WebDevOps A Android Web IDE supports code auto-completion and highlight, plugin (Supports Html, Css, JS, Json, Php etc) Join us QQ group number: 10314

SuMuCheng 22 Jan 3, 2023