This page describes Audio Weaver's MATLAB functions related to module generation.  Audio Weaver makes heavy use of MATLAB’s object oriented features.  An object in MATLAB is a data structure with associated functions or methods.  Each type of object is referred to as a class, and Audio Weaver uses separate classes to represent variable, modules, and subsystems.  The class functions are stored in directories that start with the “@” symbol.  Under <AWE>\matlab\ are 3 class directories:

@awe_variable\

@awe_module\

@awe_subsystem\

It is important to understand how to use and manipulate these classes in order to properly use all of the features of Audio Weaver.  We begin by describing each class and then document additional MATLAB commands used for constructing modules.

@awe_variable

A variable in Audio Weaver represents a single scalar or array variable on the target processor.  Even if you are not developing modules, it is good to understand the @awe_variable object so that you can fully utilize variables.

A new scalar variable is created by the call:

awe_variable(NAME, TYPE, VALUE, USAGE, DESCRIPTION, ISHIDDEN, ISCOMPLEX)

Scalar is a mathematical term and refers to a variable containing only a single value, not to be confused with Audio Weaver scaler modules.


Variables in Audio Weaver have a close correspondence to variables in the C language.  The arguments are:


The awe_variable.m function is typically not used directly.  Rather, the function is automatically called when you add variables to modules or subsystems using the add_variable.m or add_array.m functions.

Only 32-bit data types are supported.  Allowable types are: 'float', 'int', 'uint', 'and 'fract32'.

We now look carefully at one of the variables in the agc_example.m system.  At the MATLAB prompt, type:

SYS=agc_example;
struct(get_variable(SYS.agc.core, 'targetLevel'))

The get_variable.m command extracts a single variable from a module and returns the @awe_module object.  (If you instead try to access SYS.agc.core.targetLevel you'll only get the value of the variable, not the actual structure.)  The MATLAB struct.m command turns an object into a data structure revealing each of its internal fields.  You'll see:

                    name: 'targetLevel'
           hierarchyName: 'agc.core.targetLevel'
                   value: -20
                    size: [1 1]
                    type: 'float'
               isComplex: 0
                   range: [-50 50 0.1000]
              isVolatile: 0
                   usage: 'parameter'
             description: 'Target audio level.'
               arrayHeap: 'AWE_HEAP_FAST2SLOW'
           memorySegment: 'AWE_MOD_FAST_ANY_DATA'
    arraySizeConstructor: ''
         constructorCode: ''
                 guiInfo: [1x1 struct]
                  format: '%g'
                   units: 'dB'
                  isLive: 1
                isHidden: 0
                 presets: [1x1 struct]
                 isArray: 0
              targetInfo: [1x1 struct]
                isLocked: 1
                   isPtr: 0
                 ptrExpr: ''
         isRangeEditable: 1
          isTuningSymbol: 0
             isTextLabel: 1
                isPreset: 1
             isDisplayed: 1
            classVersion: 29341
                   class: 'awe_variable'
              fieldNames: {32x1 cell}

Many of the fields are set when the variable was added to the module or at build time.  Some of them can be set after a module has been built.


The .isHidden field can be used to hide variable that the user typically does not need to know about.  For example, allocate a 2nd order Biquad filter:

>> M=biquad_module('filter')
filter = Biquad // 2nd order IIR filter

  b0: 1          // First numerator coefficient
  b1: 0          // Second numerator coefficient
  b2: 0          // Third numerator coefficient
  a1: 0          // Second denominator coefficient
  a2: 0          // Third denominator coefficient

All 5 of the tunable filter coefficients are shown.  After the filter is built, state variables are added.  You can see them by typing:

>> M.state

ans =

     0
     0

Of course, this assumes that you know that this module has a variable named .state.  To show all hidden variables in the MATLAB output window, set

AWE_INFO.displayControl.showHidden=1;

Then, looking at the Biquad filter, the hidden .state variable will be shown as well:

filter = Biquad // 2nd order IIR filter

     b0: 1          // First numerator coefficient
     b1: 0          // Second numerator coefficient
     b2: 0          // Third numerator coefficient
     a1: 0          // Second denominator coefficient
     a2: 0          // Third denominator coefficient
  state: 0
          0]

@awe_module

This class represents a single primitive audio processing function on the target.  A module consists of the following components: a set of names, input and output pins, variables, and functions.  All of these items exist in MATLAB and many of them have duals on the target itself.

Class Object

To create an audio module object, call the function M=awe_module(CLASSNAME, DESCRIPTION)

The first argument, CLASSNAME, is string specifying the class of the module.  Each module must have a unique class name, and modules on the Server are instantiated by referencing their class name.  The second argument, DESCRIPTION, is a short description of the function of the module.  The DESCRIPTION string is used when displaying the module or requesting help. 

note

Note: The function classid_lookup.m can be used to determine if a class name is already in use.  Refer to Specifying Class IDs for more information.

Note: The function classid_lookup.m can be used to determine if a class name is already in use.  Refer to Specifying Class IDs for more information.

After the module class is created, set the particular name of the module:

M.name='moduleName';

Note that there is a distinction between the CLASSNAME and the .name of a module.  The CLASSNAME is the unique identifier for the type of the module.  For example, there are different class names for scalers and biquad filters.  The .name identifies the module within a system and must be unique within the current level of hierarchy in the system.  At this point, we have a bare module without inputs, outputs, variables, or associated functions.  These must each be added.

We'll now look more closely at the fields within the @awe_module object.  Instead of looking at a bare module as returned by awe_module.m, we'll look at a module that is part of a system and has already been built. We'll choose the Automatic Gain Control Core module:

struct(agc_core_module)

MATLAB displays the following:

                         name: ''
                    className: 'AGCCore'
                  description: 'Slowly varying RMS based gain computer'
                      classID: []
          constructorArgument: {}
            showArgumentPopup: 0
                  defaultName: ''
                moduleVersion: 30797
                 classVersion: 32465
                 isDeprecated: 0
           nativePCProcessing: 1
                     heapUsed: [3×1 double]
                      version: []
                 propertyInfo: [0×1 containers.Map]
                      modVars: [0×1 containers.Map]
                isInterpreted: 0
                    mfilePath: [1x62 char]
               mfileDirectory: [1x44 char]
                    mfileName: 'agc_core_module.m'
                         mode: 'active'
                 clockDivider: []
                     inputPin: {}
                    outputPin: {}
                   scratchPin: {}
               inputPinLookup: [0×1 containers.Map]
              outputPinLookup: [0×1 containers.Map]
                     variable: {}
                 variableName: {}
                      control: {}
               wireAllocation: 'distinct'
                      getFunc: []
                      setFunc: []
                enableSetFunc: 1
                  processFunc: []
                   bypassFunc: @generic_bypass
                     muteFunc: @generic_mute
                 preBuildFunc: []
                postBuildFunc: []
              testHarnessFunc: []
                  profileFunc: @profile_simple_module
                  pinInfoFunc: []
    propagateChannelNamesFunc: @generic_propagate_channel_names
           propagateDelayFunc: @generic_propagate_delay
                     isHidden: 0
                     isPreset: 1
                       isMeta: 0
                      isDebug: 0
                 metaFuncName: []
              requiredClasses: {}
             consArgPatchFunc: []
                moduleBrowser: [1×1 struct]
                textLabelFunc: []
                     textInfo: [1×1 struct]
                    shapeInfo: [1×1 struct]
                 freqRespFunc: []
           bypassFreqRespFunc: []
            prepareToSaveFunc: []
                  copyVarFunc: @generic_copy_variable_values
                  inspectFunc: []
                inspectHandle: []
              inspectPosition: []
              inspectUserData: [1×1 struct]
         numericInspectHandle: []
       numericInspectPosition: []
               freqRespHandle: []
             freqRespPosition: []
                    isTunable: 1
                         view: [1×1 struct]
                       isLive: 0
                hierarchyName: ''
                     hasFired: 0
                   targetInfo: [1×1 struct]
                       coreID: 0
                multicoreInfo: [1×1 struct]
              nProcessorGroup: 0
           allocationPriority: 0
                     objectID: []
                  objectAlias: ''
              procSIMDSupport: {}
                   codeMarker: {}
                   isTopLevel: 0
          executionDependency: {}
                      presets: [1×1 struct]
                  presetTable: [1×1 struct]
                   numPresets: 0
                      guiInfo: [1×1 struct]
                     drawInfo: [1×1 struct]
                 isControlled: 0
            controllingModule: ''
                      uccInfo: [1×1 struct]
                      docInfo: [1×1 struct]
           savePropertiesFunc: []
           loadPropertiesFunc: []
                     isLocked: 1
                        class: 'awe_module'
                  isNavigable: 1
                 passwordHash: ''
                       module: {}
                 moduleLookup: [0×1 containers.Map]
                   connection: {}
               flattenOnBuild: 1
                    isVirtual: 0
           targetSpecificInfo: [1×1 struct]
                   isPrebuilt: 0
               preProcessFunc: []
              postProcessFunc: []
                    buildFunc: []
        reusableSubsystemName: ''
                oriSubSysName: ''
                   fieldNames: {109×1 cell}

The above fields correspond to the following:


MATLAB Functions for Constructing Modules

The following functions are commonly used for constructing audio modules.  They are briefly mentioned here and are documented later in this guide.

add_variable.m — adds a scalar variable to an audio module object.

add_array.m — adds an indirect array to an audio module object.

add_pin.m — adds an input, output, or scratch pin to an audio module object.

add_codemarker.m — adds information related to code generation.

add_control.m — exposes a single control on the inspector.

set_variable.m — replaces an existing module variable.

get_variable.m — extracts a variable from a module and returns an @awe_variable object.

Internal Module Functions

Every module in Audio Weaver has an associated MATLAB function file that constructs an @awe_module object describing the module.  Audio Weaver uses the convention that these module constructor functions end in "_module.m".   In addition to configuring the module’s input and output pins, and defining instance variables, the constructor function also assigns a number of method functions, and these functions are typically contained as sub-functions in the constructor file.

There is a close coupling between the module related C functions the module subfunctions on the target processor.  The table below gives a brief description of the functions available.

MATLAB Function Name

Target Function Name

 Purpose

*_module.m

*_Constructor()

Constructs an instance of a module class.  The function allocates memory for the base instance structure.  The MATLAB function sets default values while on the target default values are passed into the constructor.  Required.

.processFunc

*_Process()

Real-time processing function.  Required for C code.  Optional for MATLAB.

.setFunc

*_Set()

Implements the module's control functionality.  The function converts high-level interface parameters to lower-level derived values.  This function should be called whenever a variable in a module is modified.  Optional. 

.getFunc

*_Get()

The counterpart to the _Set() function but is used when module variables are read.  Most modules do not use this function. When a module instance variable is read, this function is first called and then the instance variable is returned.  It could be used, for example, to convert a measurement from energy to dB.

.bypassFunc

*_Bypass()

Implements the module's bypass functionality.  This is an optional function; when it is empty, a generic bypass function is used instead.

.preBuildFunc

N/A

This function exists only in MATLAB and it is called as part of the build procedure.  The prebuild function propagates pin information and may update array sizes.  The prebuild function is useful for setting variable values that depend upon the pin type.  The prebuild function is optional; if it doesn't exist, the pin type information from the module's first input pin is propagated to the output.

.freqRespFunc

 

N/A

This optional function exists only in MATLAB and it computes the frequency response of the module.

.profileFunc

 

N/A

This optional function exists only in MATLAB and it profiles the module for various settings like different block size and etc.

.textLabelFunc

 

N/A

This optional function exists only in MATLAB and it draws the text label for the AWE Designer GUI.

MATLAB Module M-File

The MATLAB m-file associated with an audio module creates and returns an instance of the module.  The typical function signature for a module m-file is

function M=new_module(NAME, ARG1, ARG2,...)

where NAME is a string module name and ARG1, ARG2, are optional arguments.  Good practice is to set default values for all arguments except NAME.  For example:

if (nargin < 2)
	ARG1=1;
end
if (nargin < 3)
	ARG2=3;
end

The main difference between the MATLAB instantiation function and the C constructor is that the C constructor is responsible for all memory allocation.  MATLAB, on the other hand, has the option of doing memory allocation in the prebuild function.

.processFunc

This function provides a MATLAB implementation of the module's processing function.  The function signature is:

function [M, WIRE_OUT]=new_process(M, WIRE_IN)

where:

Note that M serves as both an input argument and output result of the function.  The function modifies the state variables within M and then returns the updated object.  WIRE_OUT is a cell array containing the output data.

Each element within the WIRE_IN cell array is an MxN matrix representing the input data at a particular pin.  Each column of the input data represents a different channel.  The audio data is processed and the result written to WIRE_OUT.  For example, the processing function for the scaler_module.m computes

function [M, WIRE_OUT]=scaler_process(M, WIRE_IN)

WIRE_OUT=cell(size(WIRE_IN));
 
for i=1:length(WIRE_OUT)
    WIRE_OUT{i}=WIRE_IN{i}*M.gain;
end
 
return;

One difference between MATLAB and C is that the wires in MATLAB serve only to pass data in and out of the processing function.  Pin information is determined in MATLAB from fields in the audio module structure.  For example, to determine the sample rate of the first input, use

M.inputPin{1}.type.sampleRate

whereas in C, you would use

SR = (float) ClassWire_GetSampleRate(pWires[0]);

.setFunc

Implements the main control functional for a module.  This function is called whenever a variable within the audio module is updated.  The function signature is:

function M=new_set(M)

where:

Note that the MATLAB set function does not have a MASK argument indicating which variable changed.

.getFunc

Implements secondary control functionality.  This function is called when a variable in the module is read.  The function signature is identical to the .setFunc

function M=new_get(M)

where:

.bypassFunc

Provides a custom bypass function for a module.  The function signature is identical to the .processFunc

function [M, WIRE_OUT]=new_bypass(M, WIRE_IN)

where:

.preBuildFunc

This function is called when a system is built for execution on a target.  The function updates any internal variables which depend upon the sample rate, block size, or number of channels.  The function also implements non-standard pin propagation functionality.  The function signature is:

function M=new_prebuild(M)

where:

The function updates and returns the module object M.

.freqRespFunc

This function computes the frequency response of the system. The function signature is:

function H_OUT=new_freq_response(M, H_IN, W)

where:

.textLabelFunc

This is an optional function which returns text labels to be displayed in AWE Designer. The function signature is:

Function L=sof40_cascade_text_label(M)

where:

@awe_subsystem

This class represents both systems and subsystems; they are equivalent and no distinction is made in Audio Weaver.  A subsystem has all of the characteristics of an audio module:  a class name, input and output pins, variables, and associated sub-functions.  In addition, a subsystem contains two other items:

  1. Internal modules.

  2. Connections between the modules and the subsystem input and output pins.

Key subsystem functions are described next.

ClassObject

The call to create a new empty subsystem is similar to the call to create a new module described in Class Object.

SYS=awe_subsystem(CLASSNAME, DESCRIPTION);

You have to provide a unique class name and a short description of the function of the subsystem. (To be precise, the CLASSNAME only has to be unique if you are generating C code using the new subsystem class.  Then, the CLASSNAME has to be unique among all of the subsystems and audio modules.)

After the subsystem is created, set the particular name of the subsystem:

SYS.name='subsystemName';

At this point, we have an empty subsystem; no modules, pins, variables, or connections.


As before, we'll look at the internal fields of a subsystem and we'll use a running subsystem as an example.  The @awe_subsystem object is derived from an @awe_module object.  In fact, the first 36 fields of a subsystem are identical to a module object.  The only new fields are:

            isPrebuilt: 1
        preProcessFunc: []
       postProcessFunc: []
             buildFunc: @pcserver_build
              drawFunc: []
                module: {1x6 cell}
            moduleName: {1x6 cell}
            connection: {1x7 cell}
        flattenOnBuild: 1
    targetSpecificInfo: [1x1 struct]

MATLAB Functions for Constructing Subsystems

All of the functions listed in Section 3.2.2 which are used to construct modules also apply to subsystems.  Additional commands for constructing subsystems are:

These commands are documented in MATLAB Scripting API.

Internal Subsystem Functions

Each subsystem has a set of subfunctions that mirror the module functions described in 3.2.3.  Fortunately, for most subsystems, generic or generated versions of the functions can be used.  The table below describes the operation of each of the functions in the context of generated code.

MATLAB Function Name

Target Function Name

 Purpose

*_module.m

*_Constructor()

This MATLAB code must be manually written as described above.  Modules are manually added, configured, and connected.

The C code version of this function is automatically generated.  The function calls the base module constructor function and then calls the _Constructor() function for all internal modules.  The internal modules parameters are set according to their values at build time.

.processFunc

*_Process()

Audio Weaver automatically generates the MATLAB and C versions of these functions.

.setFunc

*_Set()

In all cases, this must be manually written.

.getFunc

*_Get()

In all cases, this must be manually written.

.bypassFunc

*_Bypass()

The generic function utilized by modules can also be used here.

.preBuildFunc

N/A

Not needed.  The existing pin propagation function can propagate the information through subsystems.

Top-Level Systems

Thus far, we have been using the terms system and subsystem interchangeably.  There is a slight difference, though, that you need to be aware of.  The highest level system object that is passed to the build command must be a top-level system created by the function:

SYS=target_system(CLASSNAME, DESCRIPTION, RT)

The top-level system is still an @awe_subsystem object but it is treated differently by the build process.  The main difference is how the output pin properties are handled.  In a top-level system, the output pins properties are explicitly specified and not derived from pin propagation.  As an added check, the pin propagation algorithm verifies that the wires attached to a top-level system's output pins match the properties of each output pin.  In contrast, internal subsystems are created by calls to

SYS=awe_subsystem(CLASSNAME, DESCRIPTION)

and the type information for output pins is determined by pin propagation.

new_pin_type.m

This function returns a data structure representing a pin.  The internal structure of a pin can be seen by examining the pin data structure.  At the MATLAB command prompt type:

new_pin_type


Be sure to leave off the trailing semicolon — this causes MATLAB to display the result of the function call.  We see:

ans = 

         numChannels: 1
           blockSize: 32
          sampleRate: 48000
            dataType: 'float'
           isComplex: 0
    numChannelsRange: []
      blockSizeRange: []
     sampleRateRange: []
       dataTypeRange: {'float'}
      isComplexRange: 0

The first 5 fields of the data structure specify the current settings of the pin; the last 5 fields represent the allowable ranges of the settings.  The range information is encoded using the following convention:


By default, the new_pin_type.m function returns a pin with no constraints on the sampleRate, blockSize, and numChannels.  The dataType is floating-point and the data real.

Additional flexibility is built into the range information.  Instead of just a row vector, the range can have a matrix of values.  Each row is interpreted as a separate allowable range.  For example, suppose that a module can only operate at the sample rates 32 kHz, 44.1 kHz, and 48 kHz.  To enforce this, set the sampleRateRange to [32000; 44100; 48000].  Note the semicolons which place each sample rate constraint on a new row.

Audio Weaver also interprets NaN’s in the matrix as if they were blank.  For example, suppose a module can operate at exactly 32 kHz or in the range 44.1 to 48 kHz.  To encode this, set sampleRateRange=[32000 NaN; 44100 48000].

The new_pin_type.m function accepts a number of optional arguments:

new_pin_type(NUMCHANNELS, BLOCKSIZE, SAMPLERATE, DATATYPE, ISCOMPLEX);

These optional arguments allow you to properties of the pin.  For example, the call

new_pin_type(2, 32, 48000)

returns the pin

ans = 

         numChannels: 2
           blockSize: 32
          sampleRate: 48000
            dataType: 'float'
           isComplex: 0
    numChannelsRange: 2
      blockSizeRange: 32
     sampleRateRange: 48000
       dataTypeRange: {'float'}
      isComplexRange: 0

This corresponds to exactly 2 channels with 32 samples each at 48 kHz.  Note, that the pin type is represented using a standard MATLAB structure.  You can always change the type information after new_pin_type.m is called.  For example,

PT=new_pin_type;
PT.sampleRateRange=[32000; 44100; 48000];

The current values and ranges of values are both provided in Audio Weaver for a number of reasons.  First, the range information allows an algorithm to represent and validate the information in a consistent manner.  Second, the pin information is available to the module at design time, allocation time, and at run-time.  For example, the sample rate can be used to compute filter coefficients given a cutoff frequency in Hz.  Third, most modules in Audio Weaver are designed to operate on an arbitrary number of channels.  The module's run-time function interrogates its pins to determine the number of channels and block size, and processes the appropriate number of samples.

Consider the look ahead limiter subsystem introduced in Specifying Module Constructor Arguments in Subsystems.  It can be connected to mono, stereo, or 5.1 channel signals and all of the wiring and buffer allocation will be automatically handled.  This generality allows you to design algorithms which operate on an arbitrary number of channels with little added complexity.

Usage of new_pin_type.m is slightly more complicated than described above.  In fact, the 5 arguments passed to the function actually specify the ranges of the pin properties and the current values are taken as the first item in each range.  For example, consider a module that can operate on even block sizes in the range from 32 to 64.  This is specified as:

new_pin_type([], [32 64 2])

ans = 

         numChannels: 1
           blockSize: 32
          sampleRate: 48000
            dataType: 'float'
           isComplex: 0
    numChannelsRange: []
      blockSizeRange: [32 64 2]
     sampleRateRange: []
       dataTypeRange: {'float'}
      isComplexRange: 0

Note that the first argument, the number of channels, is empty.  An empty matrix places no constraints on the item.  Notice also that the current blockSize equals the first value, 32, in the range of allowable block sizes.  Additional examples highlight other ways to use this function.

You can also specify that a pin can hold either floating-point or fract32 data.  Pass in a cell array of strings as the 4th argument to new_pin_type:

>> P=new_pin_type([], [], [], {'float', 'fract32'})

P = 

         numChannels: 1
           blockSize: 32
          sampleRate: 48000
            dataType: 'float'
           isComplex: 0
    numChannelsRange: []
      blockSizeRange: []
     sampleRateRange: []
       dataTypeRange: {'float'  'fract32'}
      isComplexRange: 0

Some modules do nothing more than move data around.  Examples include the interleave_module.m and the delay_module.m.  These modules do not care about the data type, only that it is 32-bits wide.  The new_pin_type.m function uses the shorthand '*32' to represent all 32-bit data type.  This currently includes 'float', 'fract32', and 'int':

>> PT=new_pin_type([], [], [], '*32')

PT = 

         numChannels: 1
           blockSize: 32
          sampleRate: 48000
            dataType: 'float'
           isComplex: 0
    numChannelsRange: []
      blockSizeRange: []
     sampleRateRange: []
       dataTypeRange: {'float'  'int'  'fract32'}
      isComplexRange: 0

add_pin.m

Pins are added to a module by the add_pin.m function.  Each pin has an associated Pin Type as described in new_pin_type.m.  After creating the Pin Type, call the function

add_pin(M, USAGE, NAME, DESCRIPTION, TYPE, numPinArray)

for each input, output, or scratch pin you want to add.


The arguments are as follows:


M.inputPin, M.outputPin, and M.scratchPin are cell arrays that describe the pins.  Each call to add_pin.m adds an entry to one of these arrays, depending upon whether it is an input, output, or scratch pin.  For example, to determine the number of input pins that a module has, use

length(M.inputPin)

or to print out all of the names of the output pins

for i=1:length(M.outputPin)
	fprintf(1,'%s\n', M.outputPin{i}.name);
end

Some processing functions require temporary memory storage.  This memory is only needed while processing is active and does not need to be persisted between calls.  (On the other hand, memory that needs to be persisted by a module between calls to the processing function appears in the instance structure and has usage "state".)  Allocating temporary memory and sharing it between modules is accomplished by scratch pins.  Scratch pins are added to a module via add_pin.m with the USAGE argument set to 'scratch'.  Scratch pins are typically not used by audio modules, but are often required by subsystems. For subsystems, the routing algorithm automatically determines scratch pins at build time.

In some cases, you want to add a pin to a subsystem that is of the same type as an internal module.  For example, the Hilbert subsystem uses the same pin type as the Biquad filter.  This can be achieved programmatically:

 

add_module(SYS, biquad_module('bq11'));
pinType=SYS.bq11.inputPin{1}.type;

add_pin(SYS, 'input', 'in', 'Audio Input', pinType);
add_pin(SYS, 'output', 'out', 'Audio output', pinType);

Rules to add_pin.m:

add_variable.m

M=add_variable(M, VAR)

Adds a single scalar variable to an @awe_module or @awe_subsystem object and returns the updated object.


The arguments are as follows:


Alternatively, you can construct the variable on the fly as:

M=add_variable(M, ARGS...)

where ARGS are arguments which get passed to @awe_variable.  In this case, the arguments are ordered as:

M=add_variable(M, NAME, TYPE, DATA, USAGE, DESCRIPTION, ISHIDDEN, ...
    ISCOMPLEX)

When called with no output arguments, as in

add_variable(M, VAR);

the input module M is updated in the calling environment.

After adding a variable to an audio module, it is a good idea to also specify its range and units.  The range field is used when drawing inspectors (sliders and knobs, in particular) and also for validating variable assignments.  The units string reminds the user of what units the variable represents.  You set these as:

M.variableName.range=[min max];
M.variableName.units='msec';

Note that after a variable is added to a module, it appears as a field within the module’s structure and the name of the field equals the name of the variable.  Attributes of an individual variable are referenced using the “.” structure notation.  Refer to Audio Module Instance Structure to see the correspondence between variables in the MATLAB object and variables in the C type definition.

add_argument.m

M = add_argument(M, VAR, varargin)

Adds an input argument descriptor to the module/subsystem object. These are not accessible via the . operator, but are kept in the constructorArguments container. Arguments are variables used at construction time for operations such as: sizing internal arrays based on something other than channels / blockSize and disabling or enabling additional pins. For constructor arguments to be available in the structure they should be tracked by an additional ‘const’ module variable added using add_variable().


The arguments are as follows:


add_array.m

M=add_array(M, VAR)

Adds an array to an @awe_module or @awe_subsystem object and returns the updated object.


The arguments are as follows:


Alternatively, you can construct the array on the fly as:

M=add_array(M, ARGS...)

where ARGS are arguments which get passed to @awe_variable.  In this case, the arguments are ordered as:

M=add_array(M, NAME, TYPE, DATA, USAGE, DESCRIPTION, ISHIDDEN, ...
     ISCOMPLEX)

The size of the array is specified when it is first added.  You can also subsequently change the array size in the module's prebuild function.  When doing so, you need to change the .size field of the variable and then the data itself.  For example, the state variables used by the Biquad module are stored in a matrix of size 2 x numChannels.  However, numChannels is not known until the module is built and therefore code within the Biquad prebuild function is used to update the array size:

M.state.size=[2 M.inputPin{1}.type.numChannels];
M.state=zeros(2, M.inputPin{1}.type.numChannels);

Audio Weaver supports 1- and 2-dimensional arrays.  The 2 dimensional array representation is maintained in MATLAB.  On the target, however, a 2-dimensional array is flattened into a 1-dimensional array on a column-by-column basis.  Both scalar and array variables are represented as @awe_variable objects.

Array variables appear as pointers in the module instance structure.  For example, an FIR filter has separate arrays for coefficients and state variables:

add_variable(M, 'numTaps', 'int', L, 'const', 'Length of the filter');
M.numTaps.range=[1 5000 1];
M.numTaps.units='samples';
 
add_array(M, 'coeffs', 'float', [1; zeros(L-1,1)], 'parameter', 'Coefficient array');
M.coeffs.arrayHeap='AE_HEAP_FAST2SLOW';
M.coeffs.arraySizeConstructor='S->numTaps * sizeof(float)';
 
add_variable(M, 'stateIndex', 'int', 0, 'state', 'Index of the oldest state variable in the array of state variables');
M.stateIndex.isHidden=1;
 
% Set to default value here.  This is later updated by the pin function
add_array(M, 'state', 'float', zeros(L, 1), 'state', 'State variable array');
M.state.arrayHeap='AE_HEAP_FASTB2SLOW';
M.state.arraySizeConstructor='ClassWire_GetChannelCount(pWires[0]) * S->numTaps * sizeof(float)';
M.state.isHidden=1;

The C type definition for this FIR filter is found in the file ModFIR.h:

typedef struct _awe_modFIRInstance
{
  ModuleInstanceDescriptor instance;
    	int       	numTaps;             // Length of the filter
    	int       	stateIndex;          // Index of the oldest state variable
    	float*    	coeffs;              // Coefficient array
    	float*    	state;               // State variable array
} awe_modFIRInstance; 

Using indirect arrays enables arrays to have variable length and also to be placed in distinct memory banks.  This is last time is useful, for example, on the SHARC processor to enable simultaneous memory reads.

add_pointer.m

add_pointer(M, NAME, CTYPE, [USAGE], [DESCRIPTION], [ISHIDDEN]))

Adds a pointer variable to an Audio Weaver module. A pointer variable is used to point to a data structure which is defined outside of the module itself.  Use pointers, for example, to point a data structure used by a third party algorithm.  When you add a pointer to an Audio Weaver module, the pointer variable is added to the module’s instance structure but memory for it is not allocated.  You must separately allocate memory for the pointer using custom code in the module’s constructor function.


The arguments are as follows:


note

Note: you will not be able to examine the contents of pointer variables from MATLAB, since is unaware of the data type of the variable.

Note: you will not be able to examine the contents of pointer variables from MATLAB, since is unaware of the data type of the variable.

add_module.m

Modules are added to subsystems one at a time using the function

add_module(SYS, MOD)

The first argument is the subsystem while the second argument is the module (or subsystem) to add.  Once a module is added to a subsystem, it appears as a field within the object SYS.  The name of the field is determined by the module's name.  Modules can be added to subsystems in any order.  The run-time execution order is determined by the routing algorithm.

As modules are added to the subsystem, they are appended to the .module field.  You can use standard MATLAB programming syntax to access this information.  For example, to determine the number of modules in a subsystem:

count=length(SYS.module);

Or, to print out the names of all of the modules in a subsystem:

for i=1:length(SYS.module)
      fprintf(1, '%s\n', SYS.module{i}.name);
end

connect.m

Creates a connection between two pins in a subsystem.  The general form is:

connect(SYS, SRCPIN, DSTPIN);

where SRCPIN and DSTPIN specify the source and destination pins, respectively.  Pins are specified as strings to the connect.m command using the syntax:

moduleName.pinName

Consider the system shown below and contained within multiplexor_example.m.

To connect the output of the sine generator1 to the second input of the multiplexor, use the command:

connect(SYS, 'sine1.out', 'mult.in2');

The module names "sine1" and "mult" are obvious because they were specified when the modules were created.  The pins names may not be obvious since they appear within the module's constructor function.  To determine the names of a module's pins, you can either utilize the detailed help function awe_help (recommended)

awe_help multiplexor_smoothed_module

or have MATLAB draw the module

M=multiplexor_smoothed_module('foo');
draw(M)

Several shortcuts exists to simplify use of the connect.m command: 

Output pins are permitted to fan out to an arbitrary number of input pins.  Input pins, however, can only have a single connection.

Audio Weaver is set up to handle several special cases of connections at build time.  First, if an input pin has no connections, Audio Weaver inserts a source module and connects it to the unconnected input.  Either a source_module.m (float) or source_fract32_module.m is added based on the first input to the system (Audio Weaver is guessing at the type of pin that needs to be connected).  If a module has an output pin without a connection, Audio Weaver inserts a sink module based on the data type of the unconnected pin. This is always correct since the pin type is inherited from the module's output pin.

sink_module.m                  Floating-point data
sink_fract32_module.m          Fixed-point data=
sink_int_module.m              Integer data

If the subsystem has a direct connection from an input pin to an output pin, then a copier_module is inserted.  If a module output fans out to N outputs of the subsystem, then N-1 copier modules are inserted. This is required since each output of the subsystem is stored in a separate buffer.

get_variable.m and set_variable.m

Extract and assign individual @awe_variable objects within an audio module.  This is needed because of the object-oriented nature of the Audio Weaver interface.  When accessing a variable "var" within a module "M", as in:

M.var

Audio Weaver returns the value of the variable, not the variable object itself.  If you wish to gain access to the actual variable object, use the call:

V=get_variable(M, NAME)

where:

The function returns an @awe_variable object.  For example, the following code gets the underlying @awe_variable object for the "gain" variable of a smoothed scaler module:

M=scaler_smoothed_module('scaler');
V=get_variable(M, 'gain');

Similary, the set_variable.m command can be used to replace an existing variable with a given @awe_variable object.  "Replace" is used because the variable must already exist within the audio module.  The syntax for this command is:

M=set_variable(M, NAME, VAR)

where:

The get_variable.m and set_variable.m functions are rarely used in practice.  You can always access the internal fields of a @awe_variable object even when it is part of an audio module.  For example, to access the "range" field of the variable "gain", use:

range=M.gain.range;

note

Note: get_variable.m and set_variable.m also apply to @awe_subsystem objects.

Note: get_variable.m and set_variable.m also apply to @awe_subsystem objects.

Module for AWE Designer

This section focus on the module browser information related to AWE Designer. Every module must initialize the moduleBrowser and shapeInfo structure of the MATLAB module object. Below is the example settings of downsampler_example_module.m module. Refer the section 3.2 for complete information of these structures.

M.moduleBrowser.path = 'Examples';
M.moduleBrowser.image = '../images/E.bmp';
M.moduleBrowser.searchTags = 'decimation';
M.shapeInfo.svgImage = '../../images/DownSampler.svg';
M.shapeInfo.size = [6 6];

When the module is generated by executing the make script, for example make_examples.m, the XML file is generated which is used by the AWE Designer. XML file holds the complete information about the module to AWE Designer, like browser image file path, svg image file path for module shape, help document path, module arguments and other settings for AWE Designer. The information in XML file is extracted from the module file from the moduleBrowser and shapeInfo fields. Below is the example XML file of downsampler_example_module.m module.

<designer>
    <module type='Examples' width='6' height='6'>
        <browsername>Downsampler Example</browsername>
        <legend></legend>
        <image>../images/E.bmp</image>
        <svgimage>../../images/DownSampler.svg</svgimage>
        <targetclasses>ModuleDownsamplerExample</targetclasses>
        <datatype>float,fract32,int</datatype>
        <searchtags>decimation</searchtags>
        <mfile>downsampler_example_module</mfile>
        <helpfile>../Doc/DownsamplerExample.html</helpfile>
        <examples></examples>
        <property propsheet = 'Build Settings' name='allocationPriority' type='int' value='0' min = '0'/>
        <property propsheet = 'Build Settings' name='objectID' type='int' value='' min = '1'/>
        <property propsheet = 'Build Settings' name='clockDivider' type='int' value='' isValueEditable='0'/>
        <property propsheet = 'Build Settings' name='isTunable' type='bool' value='1' min = '0'/>
        <property propsheet = 'Variables' name='D' description='Decimation factor.  1 out of every D samples is output' type='int' min='1' max='1024' step='1' isRangeEditable='1' value='2' format='%g' hide='1' isComplex='0'/>
        <property propsheet = 'Arguments' name='D' description='Decimation factor' type='int'  value='2' format='%g' />
        <pin name='in' legend='in' usage='input' pos='left' show='0' hide='0'  />
        <pin name='out' legend='out' usage='output' pos='right' show='0' hide='0'  />
        Downsampler (or decimator) - keeps 1 out of every D samples
        <shape color='#000000' fill='#ffffff' style='0'>
        <poly>
            <point x='0' y='0' />
            <point x='0' y='6' />
            <point x='6' y='6' />
            <point x='6' y='0' />
        </poly>
        </shape>
    </module>
</designer>

Every module must be instantiated in the make script with complete arguments. Otherwise the arguments related information will not be extracted in to XML file and AWE Designer won’t have access to arguments. For example downsampler_example_module.m require two arguments, NAME and D (decimation factor). It must be instantiated as

MM{end+1}=downsampler_example_module('temp', 2);    % 100% instantiation

in the make_example.m script. Here ‘temp’ is the name and 2 is the default decimation factor. Similarly lah_limiter_example_module.m require two arguments, NAME and MAXDELAY. This must be instantiated as

MM{end+1}=lah_limiter_example_module('temp', 5);    % 100% instantiation

Here ‘temp’ is the name and 5 is the default max delay time.