Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Steps 1 and 3 are automated using the awe_generate_library.m command.  Many other MATLAB functions are internally called. Only a few other functions are documented here to help you better understand the code generation process.  Step 4 is done separately in VisualStudio and described in Generating Documentation.

awe_generate_library.m

awe_generate_library(MM, DIR, LIBNAME, USESDLLS, GENDOC)

This is the primary function for generating an audio module library.  It generates sources files for all of the modules in the library.  Arguments:

  • MM

...

  • cell array of audio module structures.

  • DIR

...

  • base directory in which the generated files should be placed.  Files are written here and in several other subdirectories.

  • LIBNAME

...

  • string specifying the name of the library.  For example, 'Advanced'.

  • USESDLLS

...

  • structure specifying other module libraries that the newly built library depends upon.  A library has a dependencies when it internally utilizes a module contained in another library.  By default, USESDLLS=[] and the library is independent of others.

  • GENDOC

...

  • integer indicating whether module documentation should be generated.  By default, GENDOC=0 and no documentation is generated.  If GENDOC=1, then a single document is created for the entire module library.  If GENDOC=2, then a separate document is created for each audio module.

Creating the Cell Array of Modules

The argument , MM, is a cell array populated with modules to be placed in the final library.  For example, it might contain:

Code Block
MM=cell(0,0);

...


MM{end+1}=downsampler_example_module('temp');

...


MM{end+1}=fader_example_fract32_module('temp');

...


MM{end+1}=fader_example_module('temp');

...


MM{end+1}=lah_limiter_example_module('temp');

...


MM{end+1}=peak_hold_example_fract32_module('temp');

...


MM{end+1}=peak_hold_example_module('temp');

...


MM{end+1}=scaler_example_module('temp');

...


MM{end+1}=scaler_smoothed_example_module('temp');

Specifying Dependencies

USESDLLS is a cell array of structures that specifies dependencies.  Each structure contains the fields:

  • .MM – cell array of audio modules in the library.

  • .str – library name.

1.5.3.    Specifying the Output Directory

The argument DIR to awe_generate_library.m specifies the directory in which to generate the modules.  There To achieve automation, each audio module library supplied with Audio Weaver has a master function which generates the library.  For example, the Advanced module library uses the script make_advancedmodulepack.m.  The script is designed so that if you request output arguments, as in

[MM, NAME]=make_advancedmodulepack;

the function will not build the library but will instead return a cell array of modules in MM and the name of the module library in NAME.  You should follow this convention when creating your own custom module libraries.  This allows you to specify dependencies in a straightforward manner.  For example, the make_examples.m script is dependent upon 3 other libraries:

Code Block
[DependMM, DependName]=make_standardmodulepack(0);
USESLIB{1}.MM=DependMM;
USESLIB{1}.str=DependName;
[DependMM, DependName]=make_deprecatedmodulepack(0);
USESLIB{2}.MM=DependMM;
USESLIB{2}.str=DependName;
[DependMM, DependName]=make_advancedmodulepack(0);
USESLIB{3}.MM=DependMM;
USESLIB{3}.str=DependName;

 If you try to build a library and do not properly account for all dependencies, then awe_make_library.m will report an error.  For example, suppose that you try and build the examples library but eliminate the dependency on the Standard/Deprecated libraries, you'll see:

Code Block
Module library has unsatisfiable or circular dependencies.
Modules with dependencies that could not be resolved:
FaderExample
	Missing ScalerSmoothed
LAHLimiter
	Missing AGCLimiterCore
	Missing AGCMultiplier
	Missing MaxAbs

The error message indicates that the FaderExample module has a dependency on ScalerSmoothed and that ScalerSmoothed is not in the dependency list.  Similarly, LAHLimiter has 3 unsatisfied dependencies.

Specifying the Output Directory

The argument DIR to awe_generate_library.m specifies the directory in which to generate the modules.  There are different ways to specify the directory including hard coding it.

Often it is useful to specify the directory relative to the location of the master library make script.  The following MATLAB code, contained in make_examples.m, shows how to determine the location of make_examples.m and then pull off the lowest level directory.  make_examples.m is located atin:

📁 <AWE>\AWEModules\Source\Examples\matlab\

The output directory is set to

📁 <AWE>\AWEModules\Source\Examples\

by the code below.

Code Block
MFILE=mfilename('fullpath');

...


[pathstr, name]=fileparts(MFILE);

...

 

% Remove the last directory level


 
% Remove the last directory level
ind=find(pathstr == filesep);

...


ind=max

...

 

...

(

...

ind

...

);

Generated Files

Let DIR be the base directory for the audio module library and let 'Examples' be the name of the library.  awe_generate_library.m creates the following files:

  • <DIR>\Examples.h

...

  • Header file containing extern definitions for each module class object.  It also has macros defining the entire list of modules and all dependencies.

  • <DIR>\Examples.sch

...

  • Overall schema file for the library.

  • <DIR>\ExamplesSchema.cpp

...

  • Compiled schema file.

  • <DIR>\Doc

...

  • Location of generated documentation

  • <DIR>\Include

...

  • Location of generated module include files

  • <DIR>\matlab

...

  • Location of the module m-files.  This also contains the master library make script.

  • <DIR>\matlab\code

...

  • Suggested location for the inner code pieces.

  • <DIR>\matlab\process

...

  • Suggested location of the MATLAB versions of the processing functions.

  • <DIR>\matlab\test

...

  • Suggested location of MATLAB test scripts which validate the operation of the modules.

  • <DIR>\Source – Location of the generated module source files.

  • <DIR>\xml – Module XML file for AWE Designer, explained in section 3.12.

SchemaBuilder.exe

You'll notice this executable within the Audio Weaver Bin directory.  This executable compiles the schema information.  That is, it takes a .sch file and compiles into a .cpp file.  The .cpp file is then included in the project for building the module DLL.  SchemaBuilder.exe is automatically called by awe_generate_library.m and there is no need to call it separately.

AWE_INFO.buildControl

The global variable AWE_INFO contain the structure .buildControl which controls the library generation process.   AWE_INFO.buildControl contains many fields which are used internally.  We point out the user settable values.

  • AWE_INFO.buildControl.combineSourceFiles

...

  • — this Boolean specifies whether each audio module should be placed into a separate source file.  By default, combineSourceFiles=0 and each module is written to a separate file.

  • AWE_INFO.buildControl.indentSourceFiles

...

  • — this Boolean specifies whether the source code should be formatted after generation using the executable indent.exe.  Formatting the code regularizes identing, line breaks, etc.  By default, indentSourceFiles=0.  Formatting the source code significantly slows down the module generation process.

Setting the Audio Module Search Path

When you are developing a custom audio module, you must add the base module directory to the Audio Weaver module search path. 

Note

Caution: It is not enough to merely modify your MATLAB path.

The path can be updated in Designer using the ‘File->Set Module Path’ menu option. To add a directory to the Audio Weaver module search path programmatically, use the command

add_module_path(DIR)

The command adds the directory

  • <DIR>\matlab

to the MATLAB search path; this directory has to exist.  It then optionally adds

  • <DIR>\matlab\test

  • <DIR>\matlab\process

if these directories exist.  In addition, the function modifies the global variable

AWE_INFO.buildControl.modulePath

to include the new directory.  This global variable is used when searching for class IDs.

The newly added module directory is placed at the end of the module search path.  You can specify that the directory be added to either the beginning or end of the module path using the optional second argument

Code Block
add_module_path(PATH, '-begin');

...


add_module_path(PATH, '-end');

Specifying Class IDs
Anchor
SpecifyingClassIDs
SpecifyingClassIDs

Every audio module in the library must have a unique class ID.  This 32-bit integer is specified in the file

<DIR>\classids.csv

This comma separated text file contains entries of the form

ClassName,ID

The file can also contain a single entry at the start of the form:

IDOFFSET=32768

The IDOFFSET is added to each ID listed in the file.  Using IDOFFSET allows you to quickly make wholesale changes to all of the modules in the library.

...

Audio Weaver provides a function for looking up classIDs across a range of audio module libraries.

ID=classid_lookup(CLASSNAME)

You pass the function a class name string and the classID is returned. The function works by using the audio module directory search path contained in

AWE_INFO.buildControl.modulePath

For each directory, the file classids.csv is opened and examined.  The function classid_lookup.m uses internal caching to speed up the search process.

...

As part of the build process, Audio Weaver reorders the variables in a module or subsystem to match the manner in which modules are instantiated by the Server.  The reordering is performed by the function

M=awe_reorder_variables(M)

where M is an @awe_module or @awe_subsystem object.  Variables are ordered according to the rules

...

Audio Weaver is frequently used in conjunction with a source code control system.  The awe_generate_library.m function overwrites generated files only if there is an actual change to the file.  If nothing has changed, the file is untouched.

...

Specifying Wiring Constraints
Anchor
SpecifyingWiringConstraints
SpecifyingWiringConstraints

The .wireAllocation field of an audio module allows you to specify wiring constraints for the routing algorithm.  There are two possible values

  • 'distinct'

...

  • None of the input wire buffers will be used as output wire buffers. That is, the outputs will be "distinct" from the inputs.

  • 'across'

...

  • The routing algorithm will try and reuse wire buffers across the module, whenever possible.  That is, the wire buffer assigned to input pin N will be reused for output pin N.  The wires are the same "across" the module.

awe_module.m sets .wireAllocation to 'distinct' by default. This is the safest approach but consumes more memory.  'across' conserves memory and is often easier to debug.  When 'distinct' is specified, the output wires will always be different compared to the inputs.  When 'across' is specified, the routing algorithm will attempt to reuse the buffers, but this may not always be the case.

The choice between 'distinct' and 'across' depends upon the internal details of your processing algorithm.  Buffer pointer usage within the algorithm has to be studied to determine if 'across' wiring is possible.  Consider the scaler module processing function shown below.  This module has N inputs and N outputs with the Nth input being scaled and written to the Nth output.

Code Block
void awe_modScalerProcess(void *pInstance)

...

{

...


{
  awe_modScalerInstance *S = (awe_modScalerInstance *)pInstance;

...


  WireInstance **pWires = ClassModule_GetWires(S);

...


  int numPins = ClassModule_GetNInWires(S);

...


  int pin;

...

       

...


        
  for(pin=0;pin<numPins;pin++)

...

    {

...


    {
      awe_vecScale((float *)(pWires[pin]->buffer), 1,

...

 
		   (float *)(pWires[numPins+pin]->buffer), 1,

...

 
		   S->gain, ClassWire_GetNumSamples(pWires[pin]));

...

    }


    }
}

It is safe to use 'across' wiring in this case. Now consider the Mixer module.  This module has 1 input pin with M channels and 1 output pin with N channels.  Each output channel is formed as a weighted sum of input channels with each output channel written in order.  In this case, you can no longer use 'across' wiring because each output channel depends upon each input channel.  If 'across' wiring were used, then writing the first output channel would overwrite the first input channel.

Note that 'across' is a suggestion to the routing algorithm.  In certain cases, 'across' wiring cannot be used and distinct buffers will be provided.  You should always write your audio module processing function anticipating that the buffer pointers may be distinct.  For example, the inner loop of the awe_modScalerExampleProcess function should not be written as:

Code Block

...

  for(pin=0;pin<numPins;pin++)

...

    {

...


    {
      awe_vecScale((float *)(pWires[pin]->buffer), 1,

...

 
		   (float *)(pWires[pin]->buffer), 1,

...

 
		   S->gain, ClassWire_GetNumSamples(pWires[pin]));

...


    }

The routing algorithm may decide to assign a distinct output buffer; always pass in the given output buffer pointer.

The most common situation when 'across' wiring cannot be applied is when a module sits between the input and output of a system.  For routing reasons, the wires attached to the input and output of a subsystem have to be distinct.  Placing a module with .wireAllocation='across' between them will still lead to distinct buffer allocation.

...

The function awe_help.m displays a list of all Audio Weaver modules on the current module search path.  In order for a module m-file to be picked up, you need to add the tag AudioWeaverModule somewhere within the m-file.  This tag allows you to differentiate between actual module m-files and other MATLAB files contained in the same directories.  The tag can be placed anywhere within the file, usually in a comment as shown below:

% AudioWeaverModule [This tag makes it appear under awe_help]

Code Markers and Template Substitution
Anchor
CodeMarkersandTemplateSubstitution
CodeMarkersandTemplateSubstitution

Template substitution is an automated method of replacing strings within a file to generate an output file.  The process starts with a template file and Audio Weaver provides separate templates for the generated .c and .h files:

  • <AWE>\matlab\module_generation\templates\awe_module_template.h

  • <AWE>\matlab\module_generation\templates\awe_module_template.c

A template file contains normal text interspersed with entries of the form

$IDENTIFIER$

where IDENTIFIER is a string.  The template substitution function is given a list of identifiers together with their replacements.  The substitutions are performed and a new output file is created.  If an identifier within a file is not defined, then it is deleted and does not appear in the output.

...

A "code marker" is an identifier together with a replacement string.  Each module and subsystem has its own list of code markers.  The MATLAB function

awe_addcodemarker(M, IDENTIFER, SUBSTITUTION)

creates a new code marker.  The function arguments are:

  • M

...

  • @awe_module or @awe_subsystem object

  • IDENTIFIER

...

  • string indicating a specific identifier in a template file.

  • SUBSTITUTION

...

  • string containing the replacement value for the identifier. 

SUBSTITUTION can be either a single string or a cell array of strings for multi-line replacements.  If you call awe_addcodemarker.m and an IDENTIFIER of the specified name already exists, then the new SUBSTITUTION string is appended as another line.  For example, the following entry located near the top of awe_module_template.c file is used to specify include files:

$srcFileInclude$

If you call

Code Block
awe_addcodemarker(M, 'srcFileInclude', '#include "file1.h"');

...


awe_addcodemarker(M, 'srcFileInclude', '#include "file2.h"');

Then the generated file will include both lines:

#include "file1.h"

#include "file2.h"

An optional 4th argument selects between appending and overwriting duplicate code markers.

awe_addcodemarker(M, IDENTIFER, SUBSTITUTION, APPEND)

By default, APPEND=1 and duplicate code markers are appended.  If you set APPEND=0, then an existing code marker of the same name is overwritten.  For example,

Code Block
awe_addcodemarker(M, 'srcFileInclude', '#include "file1.h"');

...


awe_addcodemarker(M, 'srcFileInclude', '#include "file2.h"', 0);

Since $srcFileInclude$ is already defined, the second call to awe_addcodemarker.m will overwrite the code marker and the generated file will only contain

#include "file2.h"

The call awe_addcodemarker.m adds the code marker information into the field .codeMarker of the module.  You could, for example, examine the code markers defined for the scaler_module.m as follows:

Code Block
>> M=scaler_module('fred');

...


>> M.codeMarker{1}

...

 



ans

...

 

...

 = 

    name: 'processFunction'

...


    text: 'Insert:code\InnerScaler_Process.c'

...

 



>> M.codeMarker{2}

...

 



ans

...

 

...

 = 

    name: 'discussion'

...


    text: {1x9 cell}

Inserting Files

In many cases, the SUBSTITUTION string contains many lines and it is unwieldy to represent in MATLAB.  Instead, it is easier to copy the SUBSTITUTION text from another file.  If the SUBSTITUTION string has the form "Insert:filename.txt" then the text will be copied from filename.txt.  filename.txt is referenced relative to the location of the module's m-file.  You can specify subdirectories as well.  For example:

...