[Image]  APEX AUDIO SYSTEM
Instruction Manual

intro | getting started | conv2aas | example | faq
api : index | general | sfx | mod | misc | mixer


:: Introduction

Three simple examples of how to use AAS are included in the "AASExample", "AASExample2" and "AASExample_c++" folders that come with the SDK. "AASExample" and "AASExample_c++" work using the standard interrupt system, although this does mean that sound quality may suffer if it used in conjunction with other CPU-intensive interrupts. "AASExample2" uses a modified interrupt system that will work in all cases, although it does require more care to set up correctly. Feel free to modify the example code for use in your own projects.

The suggested structure for your project folder is as follows:


:: Makefile And Includes

In order for your programs to compile successfully they must be linked to the AAS library. To do this, the following options should be passed to the linker:

-Laas/lib : Adds the "aas/lib" subfolder to the list of folders that will be searched for library files.

-lAAS : Links the AAS library ("libAAS.a") to your project.

In order for the compiler to be able to find AAS's include files, the follow option needs to be specified at the compilation stage:

-Iaas/include : Adds the specified subfolder ("aas/include") to the list of folders that will be searched for include files.

Both of these options are specified in the Makefile included in the project. It may also be necessary to change the "CROSS =" line in the Makefile depending on the location and file names of the GBA tools on your system.

NOTE: the Makefiles in the individual example directories aren't selfcontaining for maintainability reasons. They include example.make which includes common.make. Both are found in ‹root›/make. To make a single makefile out of these, just recursively replace the include directive with the actual content of the included file.

Once the Makefile has been configured appropriately, it is safe to include the AAS header files in your project, as shown in the example code:

#include "AAS.h"
#include "AAS_Data.h"

The "AAS_Data.h" header file contains information specific to your project and is generated by the Conv2AAS program. The makefile included with the example code automatically calls Conv2AAS each time the program is compiled so that your project will always have an up-to-date header file and will be linked to the most recent version of the files in the "AAS_Data" folder.


:: Interrupt Handling And crt0.s

A crt0.s or similar file is required for any GBA project and, amongst other things, it specifies how hardware interrupts will be handled. AAS can be made to work with all these different methods, provided that a Timer 1 interrupt results in a call to AAS_Timer1InterruptHandler.

In "AASExample" and "AASExample_c++", the crt0.s file has been configured to use "Fast Interrupts", which means that any interrupt will automatically call a function named InterruptProcess() and it is then up to the user to implement this routine. In order for this work with AAS, your InterruptProcess() code must automatically call "AAS_Timer1InterruptHandler()" when a Timer 1 interrupt occurs. The C code to do this is shown below. The C++ code used in "AASExample_c++" is much the same except InterruptProcess() has to be declared "extern "C"" to prevent name mangling.

#define REG_IE (*(volatile AAS_u16 *)0x4000200)
#define REG_IF (*(volatile AAS_u16 *)0x4000202) 

void InterruptProcess()
{
  AAS_u16 intr_bits = REG_IE & REG_IF;

  // It's best to test for AAS's Timer 1 interrupt first
  if ( intr_bits & 0x10 ) // Timer 1
    AAS_Timer1InterruptHandler();
	
  // Process other interrupts here by testing appropriate bits of "intr_bits"

  // Clear the interrupt flags
  REG_IF |= REG_IF;
}

This routine simply creates a bit mask (intr_bits) that indicates which interrupts have occurred (there may be more than one) and then tests for the Timer 1 interrupt that is used by AAS. If it occured then it calls AAS_Timer1InterruptHandler. You can add additional code to service any other interrupts you may be using in your project. Once all interrupts have been serviced, it is necessary to clear the interrupt flags as the last action before returning from the function.


:: Using AAS With Other CPU-Intensive Interrupts

A complication not mentioned in the explanation above is that AAS requires that its interrupt be processed very quickly to avoid audible gaps in the sound. This may cause problems if your code uses other CPU-intensive interrupts. In this situation, you should use the special "AAS_MultipleInterrupts" mode that has been added to the custom crt0.s included with the SDK. In this mode, a Timer 1 interrupt automatically calls AAS_FastTimer1InterruptHandler, which is a simple routine that is designed to return quickly so as not to interfere with your own code. However, unlike AAS_Timer1InterruptHandler, AAS_FastTimer1InterruptHandler does not mix the next batch of audio so a seperate call to AAS_DoWork is also required. This must be done at least 50 times per second, although it is safe to do it more often than that. Doing it at the beginning of each VBlank is ideal. This method is demonstrated in "AASExample2". The code used in this case is shown below:

void VBlankInterruptHandler()
{
  AAS_DoWork();
  
  // Insert your own VBlank code here
}

void UnusedInterruptHandler()
{
};

void (*AAS_IntrTable[13])(void) =
{
  VBlankInterruptHandler,      // VBlank Interrupt
  UnusedInterruptHandler,      // HBlank Interrupt
  UnusedInterruptHandler,      // V Counter Interrupt
  UnusedInterruptHandler,      // Timer 0 Interrupt
  UnusedInterruptHandler,      // Timer 2 Interrupt
  UnusedInterruptHandler,      // Timer 3 Interrupt
  UnusedInterruptHandler,      // Serial Communication Interrupt
  UnusedInterruptHandler,      // DMA0 Interrupt
  UnusedInterruptHandler,      // DMA1 Interrupt
  UnusedInterruptHandler,      // DMA2 Interrupt
  UnusedInterruptHandler,      // DMA3 Interrupt
  UnusedInterruptHandler,      // Key Interrupt
  UnusedInterruptHandler       // Cart Interrupt
};

Notice that there is not a "Timer 1 Interrupt" entry in the definition of AAS_IntrTable[] as this interrupt always results in a call to AAS_FastTimer1InterruptHandler. Also note that it is necessary to initialise the VBlank interrupt so that AAS_DoWork() will be called at least 50 times per second. To achieve this, the code below needs to be included somewhere in your setup routines:

// Define registers
#define REG_IE (*(volatile AAS_u16 *)0x4000200)
#define	REG_DISPSTAT (*(volatile AAS_u16*)0x04000004)

// Enable VBlank interrupt
REG_DISPSTAT |= 0x8;
REG_IE |= 0x1;

:: Using AAS

Once all the tricky stuff described above is working, actually using AAS is remarkably simple. The code from "AASExample" is shown below. The code for "AASExample2" is identical apart from the fact that it plays a different MOD and includes the VBlank initialisation code mentioned above. The code for "AASExample_c++" is also very similar except AgbMain() is declared with "extern "C"" and again a different MOD is used.

// Registers for GBA keys
#define	REG_KEY (*(volatile AAS_u16 *)0x04000130)
#define REG_KEY_A 0x0001
#define REG_KEY_B 0x0002

void AgbMain() 
{
  int keys, keys_changed;
  int prev_keys = 0;
	
  // Initialise AAS
  AAS_SetConfig( AAS_CONFIG_MIX_32KHZ, AAS_CONFIG_CHANS_8, AAS_CONFIG_SPATIAL_STEREO, AAS_CONFIG_DYNAMIC_OFF );
	
  // Start playing MOD
  AAS_MOD_Play( AAS_DATA_MOD_FlatOutLies );
	
  // Show AAS Logo (required for non-commercial projects)
  AAS_ShowLogo();
	
  // Main loop
  do
  {
    // Work out which keys have just been pressed
    keys = ~REG_KEY;
    keys_changed = keys ^ prev_keys;
    prev_keys = keys;
		
    // Play looping ambulance sound effect out of left speaker if A button is pressed, stop when released
    if ( keys_changed & REG_KEY_A )
    {
      if ( keys & REG_KEY_A )
        AAS_SFX_Play( 0, 64, 16000, AAS_DATA_SFX_START_Ambulance, AAS_DATA_SFX_END_Ambulance, AAS_DATA_SFX_START_Ambulance );
      else
        AAS_SFX_Stop( 0 );
    }
		
    // Play explosion sound effect out of right speaker if B button is pressed
    if ( keys_changed & keys & REG_KEY_B )
      AAS_SFX_Play( 1, 64, 8000, AAS_DATA_SFX_START_Boom, AAS_DATA_SFX_END_Boom, AAS_NULL );
  }
  while( 1 );
}

This code initialises AAS, starts playing FlatOutLies.mod, shows the AAS logo and then goes into an infinite loop whilst the MOD continues to play. Pressing A or B starts the Ambulance.wav or Boom.raw samples. It is important that AAS_SetConfig is called before AAS_MOD_Play, although AAS_ShowLogo can be safely called at any time. For more information about how these functions work, please read the API section of this documentation.