NVIDIA Tegra
DRIVE 5.0 Linux Open Source Software

Development Guide
5.0.10.3 Release


 
Audio Processing Modules
 
Initialization
Closing
Handling Parameters
Processing Data
Interface
invfx_t
nvfx_t
nvfx_pin_state_t
nvfx_shared_state_t
nvfx_process_state_t
nvfx_mem_t
nvfx_mem_init
nvfx_buffer_t
nvfx_buffer_init
nvfx_buffer_bytes_to_end
nvfx_buffer_valid_bytes
nvfx_buffer_valid_bytes_contiguous
nvfx_buffer_bytes_free
nvfx_buffer_bytes_free_contiguous
nvfx_buffer_copy_in
nvfx_buffer_copy_out
nvfx_buffer_add_bytes
nvfx_buffer_consume_bytes
nvfx_get_sys_ts
nvfx_get_exec_ts
nvfx_get_ts_diff
nvfx_set_instance
nvfx_get_instance
Toolchain Support for Compilation and Linking
Unified ADSP Image
Build Unified ADSP Firmware
Running the Sample Plugins
Dynamic Plugin Parameter Change
APM Priority
This section describes execution flow and interfaces for audio processing modules (APMs) interacting in and with the ADSP framework.
APMs exist in one of two states: Uninitialized or Ready. When an APM is loaded onto the DSP, the APM is in an uninitialized state. After loading, the APM calls the vendor library’s invfx_t::init function and transitions to the Ready state. Once the APM is in the Ready state, it waits for an external parameter setting, buffer ready signal, or unload before performing any additional processing.
Initialization
APMs exist in one of two states: Uninitialized or Ready. When it is loaded onto the DSP, an APM is in an uninitialized state. After it has been loaded, the APM calls the vendor library’s invfx_t::init function and transition to the Ready state as illustrated in the figure below. Once the APM is in the Ready state, it waits for an external parameter setting, buffer ready signal, or unload before performing any additional processing.
Closing
When an APM is unloaded, the audio framework calls the vendor audio library’s invfx_t::close function. At this time, the vendor audio library performs any mandatory cleanup. This function should often be treated as a no-op by the library.
Handling Parameters
When an APM receives an external message, the message may indicate a parameter change in the vendor audio library. When this happens, the APM calls the vendor library’s invfx_t::call function with the parameters specified in the message from the CPU.
The audio framework makes several assumptions regarding parameters to a vendor audio library:
All parameters sets can complete successfully.
Erroneous or invalid parameters are filtered prior to calling the APM.
Any parameters passed to the APM are cached for read operations by the CPU layer to prevent (to the degree possible) unnecessary get-parameter types of operations.
Any status information that must be read by the CPU from the APM is defined and made available in a non-cached data area.
Each library must support several required methods:
The framework calls the plugin method nvfx_method_reset when the plugin must return to its initial state. The plugin should flush any internal buffers containing processed data, and return any parameters to default values.
The framework calls the plugin method nvfx_method_set_state when the plugin must enter either an active or inactive state. When the plugin is active, it processes all data normally. When the plugin is inactive, the plugin flushes process data to the output buffer, but does not generate new audio data.
Begin custom methods to set internal parameters with the nvfx_method_external_start value.
Processing Data
When an APM receives a data ready/needed signal, it might call the invfx_t::process function of a vendor audio library. The function is only called if there is space in both the input buffer feeding the APM and the output buffer where the APM writes processed data. If there is considerable delay between the time the consumer APM signals its need for more audio data, and the time the APM produces more audio data, an audible artifact may be produced as the consumer starves. The framework sends data needed and data ready signals as data is consumed and produced by the APMs. These signals are sent to up and down stream APMs respectively.
Interface
invfx_t
The audio plugin exports a statically initialized invfx_t structure for the APM to link against.
struct invfx_t {
uint32_t size;
nvid_t id;
uint32_t version;
uint32_t type;
 
uint32_t instance_mem_size;
uint32_t shared_mem_size;
uint32_t fast_shared_mem_size;
uint32_t internal_mem_size;
uint32_t req_internal_mem_size;
 
uint32_t max_call_params_size;
 
uint32_t num_input_pins;
uint32_t num_output_pins;
 
uint32_t max_process_time;
uint32_t period;
 
/* Function Pointers */
nvfx_init_t init;
nvfx_close_t close;
nvfx_call_t call;
nvfx_process_t process;
};
Members
size
Size of the current used invfx_t structure.
id
A unique ID for the plugin. A GUID generator can be used to generate this.
typedef struct {
uint32_t data1;
uint32_t data2;
uint32_t data3;
uint32_t data4;
} nvid_t;
version
The version of the plugin.
type
The processing type flags that describe the processing present in the vendor audio library. These flags may be combined in OR-style logic:
NVFX_TYPE_HARDWARE No software intervention (e.g. HW Module)
NVFX_TYPE_IN_PORT Brings data in (e.g. Stream)
NVFX_TYPE_OUT_PORT Returns data out (e.g. Stream, DMA)
NVFX_TYPE_N_INPUT Requires n input buffers (e.g. Mix)
NVFX_TYPE_N_OUTPUT Requires n output buffers (e.g. Splitter)
NVFX_TYPE_IN_PLACE In place processing (e.g. Volume, Mute)
NVFX_TYPE_REFORMAT Reformats the data (e.g. Decoder, SRC)
NVFX_TYPE_MULTIPASS Multiple processing passes (e.g. AEC)
NVFX_TYPE_NONLINEAR Non-linear processing (e.g. Reverb)
instance_mem_size
The amount of memory declared with the NVFX_MEM_INSTANCE memory attribute. Minimum value is equal to sizeof(nvfx_t) and this structure must be located at the beginning of the memory area. Except for the nvfx_t structure within this memory area, this memory is private internal storage for the plugin and can not accessed externally.
shared_mem_size
The amount of memory declared with the NVFX_MEM_SHARED memory attribute. Minimum value is sizeof(nvfx_shared_state_t) and this structure must be located at the beginning of the memory area. This memory is used to share state with external clients.
Access to this memory is non-cached and so its use should be limited. Treat the shared-state memory area as write-only. Store any variables to read in instance or internal memory for faster access.
fast_shared_mem_size
The amount of memory declared with the NVFX_MEM_FAST_SHARED memory attribute. This holds output data samples and should be twice the size of the plugin’s maximum output frame size, for each output pin. It is possible to use one frame at a time to minimize latency, but specify two frames in case the library is attached to an output port (e.g. DMA).
By convention, output buffers are assumed to be at the start of the fast, shared memory area. You can specify additional shared memory after any output buffer storage for parameter blocks that would exceed the maximum message size.
internal_mem_size
The amount of memory declared with the NVFX_MEM_INTERNAL memory attribute. If available, this memory is faster than normal instance memory. As this is a limited resource, it might not be provided at initialization time. If it is not provided, the instance memory size covers the additional amount and the memory space follows the normal instance memory.
req_internal_mem_size
The amount of memory declared with the NVFX_MEM_REQ_INTERNAL memory attribute. If required internal memory is not available, the plugin is no loaded.
max_call_params_size
The size of a union of all supported parameters. The minimum size of this member is equal to sizeof(nvfx_call_params_t) and the maximum size is NVFX_MAX_CALL_PARAMS_SIZE.
num_input_pins
The number of input pins supported by the plugin. These inputs might be required or optional, depending on the plugin design.
num_output_pins
The number of output buffers supported by the plugin. These outputs might be required or optional, depending on the plugin design.
max_process_time
The worst case timestamp or execution cycle count required for any processing pass. This number is specific to the hardware platform. Determine this number by calling nvfx_ts_diff with start and end timestamps returned from calling nvfx_get_exec_ts and the beginning and end respectively of the worst case processing call.
max_process_time
The worst case timestamp or execution cycle count required for any processing pass. This number is specific to the hardware platform. Determined this number by calling nvfx_ts_diff with start and end timestamps returned from calling nvfx_get_exec_ts and the beginning and end respectively of the worst case processing. The likely scheduling period in microseconds of one processing pass of the library. This number is based on the output frame size.
Function Pointers
For detailed explanations of the plugin functions listed here, please refer to the APM Execution Flow.
init
Pointer to the plugin’s init function. Parameters include the pointer to the plugins interface and requested memory areas.
typedef void (*nvfx_init_t)(
const invfx_t* ifx,
nvfx_t* fx,
nvfx_shared_state_t* shared_state,
void* fast_shared_mem,
void* internal_mem,
void* req_internal_mem
);
To aid in the initialization of the plugins base member fields, a utility function is also provided. If you use the utility function, call it immediately upon entry to init.
inline static void nvfx_init(nvfx_t* fx, const invfx_t* ifx,
nvfx_shared_state_t* cached_state,
nvfx_shared_state_t* noncached_state,
nvfx_buffer_t** input_buffer,
nvfx_buffer_t* output_buffer)
close
Pointer to the plugin’s close function.
typedef void (*nvfx_close_t)(nvfx_t* fx);
call
Pointer to the plugin call function for setting parameters.
typedef struct {
uint32_t size;
uint32_t method;
/* custom params */
} nvfx_call_params_t;
 
typedef void (*nvfx_call_t)(nvfx_t* fx, nvfx_call_params_t* params);
The call function has several predefined, required methods with predefined parameters:
enum {
nvfx_method_reset = 0,
nvfx_method_set_state,
 
nvfx_method_external_start = 65536,
};
 
typedef struct
{
nvfx_call_params_t call;
} nvfx_reset_params_t;
 
 
enum {
nvfx_state_inactive = 0,
nvfx_state_active,
 
nvfx_state_external_start = 65536,
};
 
typedef struct
{
nvfx_call_params_t call;
int32_t state;
} nvfx_set_state_params_t;
process
Pointer to the plugin’s process function for data processing.
typedef void (*nvfx_process_t)(nvfx_t* fx);
Similar to the init function, the process function has two utility functions that can be called at the start and end to aid in proper setting of timing and process-related variables.
inline static void nvfx_process_start(nvfx_t* fx, uint32_t* ts_start)
 
inline static void nvfx_process(nvfx_t* fx, uint32_t ts_start,
uint32_t period)
nvfx_t
The nvfx_t type describes the minimum required instance data for every plugin. The memory space of any custom instance space immediately follows this structure.
typedef struct {
const invfx_t* ifx;
nvfx_shared_state_t state;
nvfx_shared_state_t* shared_state;
nvfx_buffer_t* input_buffer[NVFX_MAX_INPUT_BUFFERS];
nvfx_buffer_t output_buffer[NVFX_MAX_OUTPUT_BUFFERS];
/* custom params */
} nvfx_t;
Members
ifx
Constant data and function pointer interface exposed by the plugin.
state
Cached plugin state information.
shared_state
Pointer to shared, externally exposed state information. Treat this area as write-only. Variables used to calculate state information should be read from a cached memory area.
input_buffer
Array of buffers pointers that feed the plugin data. The pointers are filled in by the framework. If the amount of data supplied by the input buffer is too small, plugins should cache the input data until enough data is provided to produce an output frame. Each plugin may support a maximum of NVFX_MAX_INPUT_BUFFERS input buffers.
output_buffer
Array of buffers that contain the output data buffers of the plugin. Typically, the output buffers are owned by the plugin as it knows its output frame size. However, plugins may be assigned output buffers by the framework depending on the functionality supplied by the plugin. Each plugin may support a maximum of NVFX_MAX_OUTPUT_BUFFERS output buffers.
It is recommended the plugin’s instance memory space begin with an nvfx_t type. This way the nvfx_t pointer passed into the infx_t::init function can just be recast to the plugins instance type as follows:
typedef struct {
nvfx_t nvfx;
/* Plugin instance variables */
} plugin_t;
 
static plugin_t s_plugin NVFX_MEM_INSTANCE;
 
void plugin_init(..., nvfx_t* fx, ...) {
plugin_state_t* plugin = (plugin_t*)(fx);
...
Alternatively, the plugins instance data may also directly follow the nvfx_t structure if instance memory is exposed as follows:
typedef struct {
/* Plugin instance variables */
} plugin_t;
 
static nvfx_t s_nvfx NVFX_MEM_INSTANCE;
static plugin_t s_plugin NVFX_MEM_INSTANCE;
 
void plugin_init(..., nvfx_t* fx, ...) {
plugin_t* plugin = (plugin_t*)(fx + 1);
...
nvfx_pin_state_t
The nvfx_pin_state_t type describes position related information for input and output pins. The information is externally exposed.
typedef struct {
uint64_t bytes;
uint32_t frames;
} nvfx_pin_state_t;
Members
bytes
The total number of bytes consumed or produced by the plugin for the pin. For an input pin, increment this variable calling nvfx_buffer_consume_bytes on the buffer. For an output pin, increment this number after calling nvfx_buffer_add_byes on the buffer.
frames
The number of frames produced or consumed by the plugin’s pin. The relevance of this value is dependent on the data flow direction and format and might not always be used.
nvfx_shared_state_t
The nvfx_shared_state_t type describes the minimum required state data for every plugin. The memory space of any custom state in non-cached memory the plugin wishes to expose immediately follows this structure.
typedef struct {
nvfx_process_state_t process;
nvfx_pin_state_t input[NVFX_MAX_INPUT_PINS];
nvfx_pin_state_t output[NVFX_MAX_OUTPUT_PINS];
/* custom params */
} nvfx_shared_state_t;
Members
process
State related to the invfx_t::process function.
input
The array containing information of each input pin.
output
The array containing information of each output pin.
Do not expose pointers in any shared state. To share pointers, store the pointer in a variant_t type and use the variant_t in the shared structure to support any 64/32-bit processor address space crosses.
As with nvfx_t, begin the shared memory space of the plugin with an nvfx_shared_state_t type. This way the nvfx_shared_state_t pointer passed into the infx_t::init function can just be recast to the plugins shared state information. This also allows the nvfx_t::shared_state pointer to be recast to the shared memory area of the plugin without requiring maintenance of a duplicate pointer. Sample code to access the shared pointer from invfx_t::init follows:
typedef struct {
nvfx_shared_state_t nvfx_state;
/* Plugin shared state variables */
} plugin_state_t;
 
plugin_state_t plugin_state NVFX_MEM_SHARED;
 
void plugin_init(..., nvfx_shared_state_t* shared_state, ...) {
plugin_state_t* plugin_state = (plugin_state_t*)(state);
...
Alternatively, the shared state of the plugin can also directly follow the nvfx_shared_state_t structure if shared memory is exposed as follows:
typedef struct {
/* Plugin shared state variables */
} plugin_state_t;
 
nvfx_shared_state_t nvfx_state NVFX_MEM_SHARED;
plugin_state_t plugin_state NVFX_MEM_SHARED;
 
 
void plugin_init(..., nvfx_shared_state_t* shared_state, ...) {
plugin_state_t* plugin_state = (plugin_state_t*)(state + 1);
...
nvfx_process_state_t
The nvfx_process_state_t type describes values related to the invfx_t::process call. Every plugin is required to maintain these values to ensure proper scheduling.
typedef struct {
int32_t state;
uint32_t count;
uint64_t time_total;
uint32_t time_high;
uint32_t time_low;
uint32_t time_last;
uint32_t period;
} nvfx_process_state_t;
Members
state
The current plugin state passed in by the invfx_t::call nvfx_method_set_state function.
count
The total number of times invfx_t::process has been called.
time_total
The total amount of time spent in all invfx_t::process calls.
time_high
The highest measured time spent in a single invfx_t::process call.
time_low
The lowest measured time spent in a single invfx_t::process call.
time_last
The measured time spent the most recent invfx_t::process call.
period
The scheduling period between process calls. Typically, this is the amount of time to consume one frame of output data.
nvfx_mem_t
The nvfx_mem_t type describes a generic plugin memory buffer:
typedef struct {
variant_t v;
int32_t size;
uint32_t flags;
} nvfx_mem_t;
Members
v
The variant pointer to the data buffer.
typedef union {
uint64_t addr;
 
/* std C */
void* pvoid;
char* pchar;
short* pshort;
long* plong;
int* pint;
long long* pllong;
unsigned char* puchar;
unsigned short* pushort;
unsigned long* pulong;
unsigned int* puint;
unsigned long long* pullong;
float* pfloat;
double* pdouble;
 
/* stdint.h */
int8_t* pint8;
uint8_t* puint8;
int16_t* pint16;
uint16_t* puint16;
int32_t* pint32;
uint32_t* puint32;
} variant_t;
size
The size of the data buffer in bytes.
flags
The following flags describing the data buffer These flags may be combined in OR-style logic:
/* Buffer cannot be used for in-place transform. */
#define NVFX_MEM_READ_ACCESS 0x1
 
/* Buffer can only be written to. */
#define NVFX_MEM_WRITE_ACCESS 0x2
 
/* Buffer can read from or written to. */
#define NVFX_MEM_ALL_ACCESS \
(NVFX_MEM_READ_ACCESS | NVFX_MEMWRITE_ACCESS)
 
/* Buffer has been mapped. */
#define NVFX_MEM_MAPPED 0x4.
nvfx_mem_init
This function is used to initialize the nvfx_mem_t type.
Syntax
void nvfx_mem_init(
nvfx_mem_t* mem,
variant_t addr,
const int32_t size,
const uint32_t flags
);
Parameters
mem
The nvfx_mem_t instance.
addr
The base pointer to the buffer in the current address space.
size
The size of the buffer in bytes.
flags
The processing flags describing the data buffer.
nvfx_buffer_t
The nvfx_buffer_t type describes a circular data buffer for providing input data to a plugin for processing, and to return processed data from the plugin for the next audio processing module in the chain. Treat this type as read only and only modify it with the provided thread-safe functions.
typedef struct {
nvfx_mem_t mem;
volatile int32_t read;
volatile int32_t write;
volatile int32_t valid_bytes;
} nvfx_buffer_t;
Parameters
mem
Memory instance space.
read
The read byte offset. The default value is 0.
write
The write byte offset. The default value is 0.
valid_bytes
The number of valid bytes in the buffer. This is used for determining whether the buffer is full or empty, when the read offset is equal to write offset. The default value is 0.
nvfx_buffer_init
This function is used to initialize the nvfx_buffer_t type.
Syntax
void nvfx_buffer_init(
nvfx_buffer_t* buffer,
void* buffer,
const int32_t size,
const uint32_t flags
);
Parameters
nvfx_buffer
The nvfx_buffer_t instance.
addr
The base pointer to the buffer in the current address space.
size
The size of the buffer in bytes.
flags
The processing flags describing the data buffer.
nvfx_buffer_bytes_to_end
Returns the number of bytes from the offset to the end of the buffer.
Syntax
int32_t nvfx_buffer_bytes_to_end(nvfx_buffer_t* buffer, const int32_t offset);
Parameters
buffer
The nvfx_buffer_t instance.
offset
The offset into the buffer.
nvfx_buffer_valid_bytes
This function returns the number of valid bytes in the buffer.
Syntax
int32_t nvfx_buffer_valid_bytes(nvfx_buffer_t* buffer);
Parameters
buffer
The nvfx_buffer_t instance.
nvfx_buffer_valid_bytes_contiguous
This function returns the number of contiguous valid bytes in the buffer.
Syntax
int32_t nvfx_buffer_valid_bytes(nvfx_buffer_t* buffer);
Parameters
buffer
The nvfx_buffer_t instance.
nvfx_buffer_bytes_free
This function returns the number of free bytes in the buffer.
Syntax
int32_t nvfx_buffer_bytes_free(nvfx_buffer_t* buffer);
Parameters
buffer
The nvfx_buffer_t instance.
nvfx_buffer_bytes_free_contiguous
This function returns the number of free contiguous bytes in the buffer.
Syntax
int32_t nvfx_buffer_bytes_free(nvfx_buffer_t* buffer);
Parameters
buffer
The nvfx_buffer_t instance.
nvfx_buffer_copy_in
This function copies data from the source buffer to the NVFX buffer, updates the written offset, and increments the number of valid bytes in the internal data buffer.
Syntax
void nvfx_buffer_copy_in(
nvfx_buffer_t* buffer,
const void* source,
const int32_t num_bytes
);
Parameters
buffer
The nvfx_buffer_t instance.
source
The source data buffer to copy.
num_bytes
The number of bytes to copy from the source data buffer.
This function wraps back to the beginning of the NVFX buffer if the size if the number of bytes to copy exceeds the number of bytes free at the end of the internal data buffer.
nvfx_buffer_copy_out
This function copies data from the NVFX buffer to the destination buffer, updates the read offset, and decrements the number of valid bytes in the internal data buffer.
Syntax
void nvfx_buffer_copy_out(
nvfx_buffer_t* buffer,
const void* destination,
const int32_t num_bytes
);
Parameters
buffer
The nvfx_buffer_t instance.
destination
The destination data buffer to copy to.
num_bytes
The number of bytes to copy to the destination data buffer.
This function wraps back to the beginning of the NVFX buffer if the size if the number of bytes to copy exceeds the number of valid bytes at the end of the internal buffer.
nvfx_buffer_add_bytes
This function updates the write offset with the number of bytes added.
Syntax
void nvfx_buffer_add_bytes(nvfx_buffer_t* buffer, const int32_t num_bytes);
Parameters
buffer
The nvfx_buffer_t instance.
num_bytes
The number of bytes added to the internal data buffer.
This function also increments the number of valid bytes in the internal counter, after updating the write offset.
nvfx_buffer_consume_bytes
This function updates the read offset with the number of bytes consumed.
Syntax
void nvfx_buffer_consume_bytes(
nvfx_buffer_t* buffer,
const int32_t num_bytes
);
Parameters
buffer
The nvfx_buffer_t instance.
num_bytes
The number of bytes consumed from the internal data buffer.
This function also decrements the number of valid bytes from the internal counter, after updating the read offset.
nvfx_get_sys_ts
This function returns the current system timestamp. Use this timestamp to note the last time a process was called, to coordinate with the time on the CPU.
Syntax
uint64_t nvfx_get_sys_ts(void);
nvfx_get_exec_ts
This function returns an execution timestamp. Use this timestamp should be used in all execution time-related calculations.
Syntax
uint32_t nvfx_get_exec_ts(void);
nvfx_get_ts_diff
This function returns the difference between two timestamps, taking rollover into account.
Syntax
inline static uint32_t nvfx_ts_diff(uint32_t start, uint32_t end)
nvfx_set_instance
This function stores an instance pointer in thread local storage.
Syntax
void nvfx_set_instance(void* ptr);
nvfx_get_instance
This function returns the instance pointer from thread local storage.
Syntax
void* nvfx_get_instance(void);
Toolchain Support for Compilation and Linking
The adsp tool chain is a bare-metal toolchain that does not provide standard C headers or standard C library or its alternatives. It provides libgcc and libgcc headers. Support for C standard libraries and headers such as stdio.h is available in the NvFX library. Consequently, all the plugins developed must be compiled using the NvFX framework, see the example for building the wire plugin.
Unified ADSP Image
ADSP boot, code are set of features added to APE under security features’ umbrella. These allow running trusted firmware on ADSP to protect firmware code from external access. SDK comes with pre-packaged ADSP static library (libadsp.a). One can write audio plugins and link with ADSP OS library to get a unified firmware [ADSP OS + Audio Plugins].
Build Unified ADSP Firmware
1. On the host system enter the following commands:
$ cd <top>/drive-t186ref-linux/samples/ape/
$ make clean
$ make
2. On successful compilation you should see adsp firmware(adsp-fw.bin) getting created under:
<top>/drive-t186ref-linux/samples/ape/make/target
3. Copy the new adsp firmware bin to lib-target directory at location:
<top>/drive-t186ref-linux/lib-target
4. Flash the target.
The flashing script picks the ADSP firmware from lib-target.
To add new plugin to ADSP firmware
1. Place plugin source in the following directory.
<top>/drive-t186ref-linux/samples/ape/plugins
2. Update the makefile to include the plugin in unified image.
drive-t186ref-linux/samples/ape/plugins/Makefile
3. Copy prebuilt dependency static libraries under prebuilt directory to be included in unified adsp firmware build at locatation:
drive-t186ref-linux/samples/ape/make/prebuilts/lib
4. Follow the instruction in Build Unified ADSP Firmware.
To add prebuilt objects/static library to the unified ADSP firmware
1. If you already have objects file for audio plugins, create the static library using the following commands, and copy the newly created library to prebuilt directory.
<top>/toolchains/arm-eabi-4.8/bin/arm-eabi-ar rcs <out library> <object files>
2. Verify that all dependent libraries are copied to the prebuilts/lib directory.
For example, include sample wire plugin static library into adsp firmware:
#export SAMPLE=<top>/drive-t186ref-linux/samples/ape/
#cd $(SAMPLE)/plugins/wire
#arm-eabi-ar rcs libnvwirefx.a wire.o
#cp libnvwirefx.a $(SAMPLE)/make/prebuilts/libs
#make
#cp $(SAMPLE)/make/target/adsp-fw.bin drive-t186ref-foundation/firmware/t186/
Running the Sample Plugins
Starting ADSP and tracing the logs
ADSP should already be started at boot. To check if it is already started, look at the ADSP log files.
# cat /sys/kernel/debug/tegra_ape/adsp_logger&
 
[ADSP OS][ 7224913]Initialised debug UART 
[ADSP OS][ 7224913]LIC supports security extension 
[ADSP OS][ 7224913]LIC supports 21 interrupts 
[ADSP OS][ 7224913]enabling LIC interrupts 
[ADSP OS][ 7224913]enabling AGIC interrupts 
[ADSP OS][ 7224913]setting up TKE timer 
[ADSP OS][ 7224913]L2 cache enabled 
[ADSP OS][ 7224913]L310 cache controller enabled: 
[ADSP OS][ 7224913] 8 ways, CACHE_ID 0x410004c9, AUX_CTRL 0x7e420000 size = 131072 B 
[ADSP OS][ 7224913]exiting idle(WFI) state. os resumed 
[ADSP OS][ 7224918]entering idle(WFI) state. os suspended
If ADSP is not started:
# amixer -c 0 cset name=’ADSP init’ 1
To find the ADSP Interface device number for playback and capture:
ADSP ALSA Device Number is calculated as follows
#aplay -l //On Virtualized Linux Configuration
card 0: tegravirtpcmvm2 [tegra-virt-pcm-vm2], device 32: ADSP PCM1 ADSP-FE1-32 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: tegravirtpcmvm2 [tegra-virt-pcm-vm2], device 33: ADSP PCM2 ADSP-FE2-33 []
 
#aplay -l //On Native Linux
card 0: tegra186sndp238 [tegra186-snd-p2382], device 154: ADSP PCM1 ADSP-FE1-154 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 0: tegra186sndp238 [tegra186-snd-p2382], device 155: ADSP PCM2 ADSP-FE2-155 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
The above indicates that you can use [ hw:0,32 and hw:0,33] or [hw:0,154 and hw:0,155] as ALSA devices for ADSP based Playback/Capture.
Audio playback example via Reverb plugin
The Reverb plugin runs in an APM and produces a reverberation effect for the input audio signal.
To set mixer routes and play audio with the Reverb effect
1. Set the mixer routes for ADSP with the following commands:
$ amixer -c 0 cset name='APM-IN1 MUX' 'ADSP-FE1'
$ amixer -c 0 cset name='REVERB MUX' 'APM-IN1'
$ amixer -c 0 cset name='ADMA1 MUX' 'REVERB'
$ amixer -c 0 cset name='APM-OUT1 MUX' 'ADMA1'
$ amixer -c 0 cset name='ADSP-ADMAIF1 MUX' 'APM-OUT1'
2. Set the mixer routes for AHUB with the following commands:
$ amixer -c 0 cset name='AMX1-1 Mux' 'ADMAIF1'
$ amixer -c 0 cset name='AMX1-2 Mux' 'ADMAIF2'
$ amixer -c 0 cset name='AMX1-3 Mux' 'ADMAIF3'
$ amixer -c 0 cset name='AMX1-4 Mux' 'ADMAIF4'
$ amixer -c 0 cset name='I2S4 Mux' 'AMX1'
3. Play an audio file with the following command:
$ aplay -D hw:0,32 <sample.wav>
-OR-
$ aplay -D hw:0,154 <sample.wav>
The Wire plugin, also referred to as the copy plugin, produces no effect/change in the output as compared with the input signal. The plugin merely performs memcpy from input to the output.
Audio record example with the Wire plugin
To record Audio via the wire plugin
1. Set the mixer routes for ADSP with the following commands:
$ amixer -c 0 cset name='APM-IN2 MUX' 'ADSP-ADMAIF1'
$ amixer -c 0 cset name='ADMA2 MUX' 'APM-IN2'
$ amixer -c 0 cset name='WIRE MUX' 'ADMA2'
$ amixer -c 0 cset name='APM-OUT2 MUX' 'WIRE'
$ amixer -c 0 cset name='ADSP-FE1 MUX' 'APM-OUT2'
2. Enter the following commands to set the mixer routes for AHUB:
$ amixer -c 0 cset name='ADMAIF1 Mux' 'ADX1-1'
$ amixer -c 0 cset name='ADMAIF2 Mux' 'ADX1-2'
$ amixer -c 0 cset name='ADMAIF3 Mux' 'ADX1-3'
$ amixer -c 0 cset name='ADMAIF4 Mux' 'ADX1-4'
$ amixer -c 0 cset name='ADX1 Mux' 'I2S4'
3. Record the audio file with the following command:
$ arecord -D hw:0,32 -f dat <sample.wav>
-OR-
$ arecord -D hw:0,154 -f dat <sample.wav>
Virtual ADMAIF
Using ADSP, you can use ADSP FEs to act as virtual ADMAIFs, where applications that are not concurrent can open these devices and at the underlying hardware level use the same ADMAIF.
For example, Application1 opens ADSP-FE1 and Application2 opens ADSP-FE2. Both the ADSP-FEs share the same ADMAIF. In the example below, Application1 uses ADSP-FE1, which internally uses ADMAIF1.
$ amixer -c 0 cset name='ADMA1 MUX' 'APM-IN1'
$ amixer -c 0 cset name='APM-OUT1 MUX' 'ADMA1'
$ amixer -c 0 cset name='ADSP-ADMAIF1 MUX' 'APM-OUT1'
$ amixer -c 0 cset name='APM-IN1 MUX' 'ADSP-FE1'
$ amixer -c 0 cset name='AMX1-1 Mux' 'ADMAIF1'
$ amixer -c 0 cset name='I2S3 Mux' 'AMX1'
$ aplay -D ADSP_OUT0 <sample.wav>
$ arecord -D hw:0,32 -f dat <sample.wav>
-OR-
$ arecord -D hw:0,154 -f dat <sample.wav>
Now if Application2 comes in and wants to use ADMAIF1:
$ amixer -c 0 cset name='ADMA1 MUX' 'APM-IN1'
$ amixer -c 0 cset name='APM-OUT1 MUX' 'ADMA1'
$ amixer -c 0 cset name='ADSP-ADMAIF1 MUX' 'APM-OUT1'
$ amixer -c 0 cset name='APM-IN1 MUX' 'ADSP-FE2'
$ amixer -c 0 cset name='AMX1-1 Mux' 'ADMAIF1’
$ amixer -c 0 cset name='I2S3 Mux' 'AMX1'
$ arecord -D hw:0,33 -f dat <sample.wav>
-OR-
$ arecord -D hw:0,155 -f dat <sample.wav>
This is applicable only for non-concurrent use cases (i.e., if ADSP-FE1 playback is already in progress on ADMAIF1, you will not be able to play via ADSP-FE1 for the case mentioned above).
Dynamic Plugin Parameter Change
The parameters for the Reverb plugin include delay, gain, forward gain, word length, and number of channels. The Reverb plugin is designed to work with 16-bit stereo audio files. However, one can easily control the rest of the parameters as shown in the following example amixer command:
$ amixer cset name=’REVERB set params’ <method_id><#params><param1><..>…..<paramN>
To dynamically control the parameters of the Reverb plugin
Set plugin parameters by entering the following command on the host system:
$ amixer cset name=’REVERB set params’ 65536,5,2,2,24000,24000,16384
Where:
65536: Reverb method initialization
5: Number of parameters
2: Number of channels
2: Word length
24000: Delay in samples
24000: Gain in Q15 format
16384: Forward Gain in Q15 format
Reverb effect is perceptibly more pronounced.
Further modify parameters with the following command:
$ amixer cset name=’REVERB set params’ 65536,5,2,2,0,0,16384
65536: Reverb method initialization
5: Number of parameters
2: Number of channels
2: Word length
0: Delay in samples
0: Gain in Q15 format
16384: Forward Gain in Q15 format
When delay and Gain are set to 0, the Reverb plugin has no effect; it behaves like a wire plugin, copying the input signal at the output. For the full parameter list for the Reverb plugin, see the following file:
<top>/drive-t186ref-linux/samples/ape/plugins/reverb/reverb.h
APM Priority
When there are 2 or more APMs running, each has its own thread. Context switching between 2 threads depends on the priority level of the APMs. The max priority level for APM is 58. You can choose to program the APM priority using the following command:
$ amixer cset name=”APMx Priority” y
where x is is the APM ID and y is a integer value between 0-58.
If not provided, all APMs run at 48, the default priority level.
It is your responsibility to set the priorities of APM based on concurrency, processing time taken by thread, and frequency of process calls for plugins attached to an APM.