Skip to content

Processing

This guide explains how to add a custom Audio Processing component with MimiCore inside MSDK, providing your users with "Mimified" audio.

Architecture Overview

The Processing APIs can be divided into four main components:

  • Processing Controller - The root entry point and context for Processing API access.
  • Processing Session - The provider of access to processing Processing Parameters when activated.
  • Processing Parameters - Individual parameter objects that provides state, data and mutation.
  • Processing Applicators - Responsible for applying the Processing Parameter values to external systems.

The following section describes the components in the Processing APIs.

Processing System Overview

Figure: Processing System Components

ProcessingController

Overview

The ProcessingController is a controller owned and initialized by MimiCore.

It is responsible for the activation and deactivation of Processing Sessions, and therefore can be used as the source of truth for the active ProcessingSession instance.

Responsibilities

  • Centralized location for accessing Processing APIs.
  • Owner of active Processing Session
  • Provides activation and deactivation of Processing Session.

Activation

As the ProcessingController owns the ProcessingSession status; it is the location where processing can be "activated" or "deactivated".

Once there is an activated ProcessingSession, it is possible to access the MimiProcessingParameters and configure them appropriately for use.

Lifecycle

The ProcessingController is responsible for handling the ProcessingSession lifecycle in terms of activation and deactivation.

  • Activating a session will set the activeSession property on the ProcessingController allowing the session to be easily referenced.
  • Only a single session can be activated at one time.
  • Deactivation clears the activeSession property. It does not directly affect the applied Mimi processing.
  • Activating and deactivating a session triggers MimiObserver notifications to observers.

ProcessingSession

Provides access to the Processing Parameters.

Overview

As described in the previous section, a ProcessingSession is owned by a ProcessingController.

The ProcessingSession itself owns the MimiProcessingParameters, and therefore is the gateway to parameter access.

Responsibilities

  • Owner of all Processing Parameters, providing access to; isEnabled, intensity and preset.
  • Exposes the current data source configuration for the preset parameter.
  • Provides the ability for processing to be "interrupted" and automatically disabled due to external events (such as the hearing test launching).

Upon activation, the ProcessingSession will be initialized and the previously applied parameter values are restored to their respective parameters. At this point the parameters are ready for use.

Processing Parameter

The focal point for state.

Overview

A MimiProcessingParameter represents an individual parameter that can be used to control Mimi processing functionality. It encompasses a value that can be mutated and applied asynchronously to subscribed components called Applicators.

Responsibilities

In general terms, a MimiProcessingParameter is responsible for:

  • Providing access to the current parameter value.
  • Applying values to a collection of subscribed Applicators using the value application sequence.
  • Loading values from remote data sources asynchronously (such as preset data).
  • Providing state and value updates to a collection of subscribed observers.

MSDK ProcessingParameter instances

The ProcessingSession defines the following MimiProcessingParameter instances to provide access and control of the Mimi processing:

  • isEnabled - Whether Mimi processing is enabled or disabled.
  • intensity - Intensity of Mimi processing.
  • preset - Preset data model for use by a Mimi processor.

Parameter value application sequence summary

The internal process of updating a MimiProcessingParameter value or transferring the existing value to Applicators follows a specific sequence as executed by the MimiProcessingParameter:

  1. The Applicators are iterated through and are requested to apply the new value.
    • If any Applicator fails to apply the value (by returning MimiApplicatorResult.Failure), the application sequence is abandoned with an error and observers are notified of the failure.
    • If successful (each Applicator returns MimiApplicatorResult.Success), the value is updated and observers are notified of the new value.
  2. A return result value is given for the request.

Note: If no Applicators are present - then value is immediately updated and the result is successful.

Important: A given MimiProcessingParameter executes each value application sequence sequentially - it does not perform concurrent updates.

Applicator

Arguably the most relevant Processing API component for integrators is the Applicator.

An Applicator is an accessory of a MimiProcessingParameter that is capable of performing the value application logic for its associated MimiProcessingParameter.

In simple terms, its role is to apply MimiProcessingParameter values to an external system. When there is a request to update a MimiProcessingParameter value, its Applicators are requested to apply this value.

While an Applicator appears quite similar to an Observer, the two components have distinct roles:

  • An Applicator is responsible for the value application logic of a MimiProcessingParameter - the result of which influences the MimiProcessingParameter state.
  • An Observer is only informed of events reflecting committed changes to the MimiProcessingParameter state; it cannot influence the MimiProcessingParameter state.

In the Android MSDK, the concept of the Applicator is represented by the MimiParameterApplicator type.

Responsibilities

  • Provides an apply function which will allow the Applicator to asynchronously apply a value your processing system, returning the appropriate MimiApplicatorResult instance.
  • Ability to be removed from a MimiProcessingParameter.
  • Ability to set a “Delivery Timeout” to specify a time duration which if exceeded is considered erroneous and will cause a value application sequence to fail.

Getting Started

This section highlights some important APIs used to integrate your processing system with the MSDK.

Configuration and Activation

When activating a ProcessingSession, you need to provide a MimiProcessingConfiguration to define its configuration.

Example

Activate a ProcessingSession with a MimiProcessingConfiguration

First define a MimiProcessingConfiguration instance:

val configuration = MimiProcessingConfiguration(TODO("Define your configuration here"))

Then activate the ProcessingSession:

val processingController : ProcessingController = MimiCore.processingController
val session : ProcessingSession = processingController.activateSession(configuration)

MimiProcessingConfiguration Overview

The MimiProcessingConfiguration defines the configuration of a ProcessingSession and provides the ability to configure the personalization preset data source.

It can be expressed directly through class instantiation, or using the fluent DSL syntax.

Example

Define MimiProcessingConfiguration with "Fine Tuning" personalization:

val configuration = MimiProcessingConfiguration(
    personalization = PersonalizationConfiguration(
        mode = FineTuning(Fitting(TODO("Fitting is defined by your Processing system")))
    )
)

Alternatively, using the Processing Configuration DSL:

val configuration : MimiProcessingConfiguration = mimiProcessingConfiguration {
    personalization { 
        fineTuning { fitting = Fitting(TODO("Fitting is defined by your Processing system")) }
    }
}

Warning

The Processing Configuration DSL is experimental and requires opt-in, as it may have unannounced changes in future releases.

Read more in the Preset Parameter Data Sources guide.

Defining and adding Applicators

After activation, you will need to define and add Applicators to each of the MimiProcessingParameters: isEnabled, intensity and preset.

To add an Applicator, use the addApplicator function which adds an "out-of-date" Applicator.

Note

When adding an Applicator, it will not automatically receive the current value and likewise; it has no effect on the current MimiProcessingParameter value.

Example

Adding Applicators to each MimiProcessingParameter:

// Define a timeout for the value application sequence
// TODO Example only: tune this value to your processing system
val applyTimeout = Duration.ofSeconds(15) 

val isEnabledApplicator : MimiParameterApplicator = session.isEnabled.addApplicator(applyTimeout) { value ->
    // TODO - Implement your logic to apply the given isEnabled value in your processing system
    //   For example: Send Bluetooth commands to your device to set the isEnabled state.
    MimiApplicatorResult.Success
}

val presetApplicator : MimiParameterApplicator = session.preset.addApplicator(applyTimeout) { value ->
   // TODO - Implement your logic to apply the given preset value in your processing system
   //   For example: Send Bluetooth commands to your device to set the preset.
   MimiApplicatorResult.Success
}

val intensityApplicator : MimiParameterApplicator = session.intensity.addApplicator(applyTimeout) { value ->
   // TODO - Implement your logic to apply the given intensity value in your processing system
   //   For example: Send Bluetooth commands to your device to set the intensity.
   MimiApplicatorResult.Success
}

// Make each Applicator receive the current value from their respective ProcessingParameter
val isEnabledSyncResult : ProcessingParameterResult = session.isEnabled.synchronizeApplicators()
val presetSyncResult : ProcessingParameterResult = session.preset.synchronizeApplicators()
val intensitySyncResult : ProcessingParameterResult = session.intensity.synchronizeApplicators()

Common MimiProcessingParameter operations

MimiProcessingParameter Value operations

The following operations are provided on the MimiProcessingParameter and execute the value application sequence, and return a ProcessingParameterResult indicating whether the operation was successful.

Function Behavior Notes
apply(value) The apply function attempts to update the MimiProcessingParameter with the given value by updating all registered, out-of-date Applicator instances.
synchronizeApplicators() The synchronizeApplicators function attempts to update all registered, out-of-date Applicators instances with the current MimiProcessingParameter value.
load() The load function will attempt to refresh the value from the data source, and then automatically execute the value application sequence with that value. Only available for applicable if the MimiProcessingParameter has a MimiProcessingParameterDataSource

Reading the MimiProcessingParameter state

Property/Function Behavior Notes
value The value property holds the latest successfully applied Parameter value. It is updated at the end of the value application sequence
observe() An Observer which provides callbacks for observing and value and state changes related to the MimiProcessingParameter. It has the following events: Applying - the request value is currently being applied to the Applicators. Failed - the most recent value application failed. Applied - the most recent value application was successful. Loading - the value is being retrieved from the data source. Important: You should not use observe to apply values to your processing system; that is the responsibility of an Applicator.

Example

Get the current parameter value:

val activeSession : ProcessingSession = requireNotNull(MimiCore.processingController.activeSession.state)
val isEnabled : Boolean = activeSession.isEnabled.value 

Example

Observe changes in the MimiProcessingParameter state:

// Acquire the active ProcessSession (assumes already activated!)
val activeSession : ProcessingSession = requireNotNull(MimiCore.processingController.activeSession.state)

// Observer for a MimiProcessingParameter
activeSession.isEnabled.processingParameter.observe {
   when(val updateState = it.updateState) {
      ProcessingParameterUpdateState.Applied -> TODO() // add your code here to handle when value application succeeds
      is ProcessingParameterUpdateState.Applying -> TODO() // add your code here to handle when a value application is in progress
      is ProcessingParameterUpdateState.Failed -> TODO() // add your code here to handle when a value application fails
      ProcessingParameterUpdateState.Loading -> TODO() // add your code here to handle when a value application is loading from the data source
   }
}