Programming with Sound Pipeline

Overview

The final piece in the AX Sound Pipeline is the SP runtime library. This library provides the following:

The SP runtime library assumes the following:

Relationship between various libraries and your application

Data Abstraction

The SP runtime library defines the SPSoundTable data structure to describe each sound effect:

typedef struct
{
    u32             type;

#define SP_TYPE_ADPCM_ONESHOT   0
#define SP_TYPE_ADPCM_LOOPED    1
#define SP_TYPE_PCM16_ONESHOT   2
#define SP_TYPE_PCM16_LOOPED    3
#define SP_TYPE_PCM8_ONESHOT    4
#define SP_TYPE_PCM8_LOOPED     5

    u32             sampleRate;
    u32             loopAddr;
    u32             loopEndAddr;
    u32             endAddr;
    u32             currentAddr;
    SPAdpcmEntry    *adpcm;

} SPSoundEntry;

Each entry describes a sound effect in the associated SPD construct.

Upon execution, the SPInitSoundTable function will:

For more information about the SPInitSoundTable function, see SP API Functions.

typedef struct
{
    AXPBADPCM       adpcm;
    AXPBADPCMLOOP   adpcmloop;

} SPAdpcmEntry;

Each ADPCM-encoded sound effect has a corresponding entry of type SPAdpcmEntry. The constituent data structures, AXPBADPCM and AXPBADPCMLOOP are defined by the AX library.

The application must load the SPT file into memory verbatim. A pointer of type SPSoundTable must be assigned to the beginning of this aggregate of data.

typedef struct
{

    u32             entries;
    SPSoundEntry    sound[ ];

} SPSoundTable;
NOTE:
The first member is an integer specifying the total number of sound effects recorded within the SPT table. Also, the start of the SPAdpcmEntry data is implicit; the SPInitSoundTable function will assign the start pointer to the end of the SPSoundEntry structures.

The SPInitSoundTable function will initialize each SPSoundEntry structure sequentially; as it encounters ADPCM-encoded sound effects, it will assign the SPAdpcmEntry *adpcm pointer to subsequent SPAdpcmEntry entries.

Functions

The Sound Pipeline runtime module exports the following functions:

Using SP

Source Code

The source code for the SP library can be found under the Cafe SDK installation directory under the following path:

$CAFE_ROOT/system/src/demo/snd_user_rpl/sp

The header file can be found here:

$CAFE_ROOT/system/include/cafe/sp.h
NOTE:
The SP runtime library is dependent on the AX header file (ax.h) as well.

There is a demo provided at $CAFE_ROOT/src/demo/ax/spdemo.c which shows the use of this library.

Loading the SP Sound Table

The SP sound table must reside in main memory. Refer to the samplLoading the file from disc is straightforward:

#include <cafe.h>
#include <cafe/sp.h>
#include <cafe/mem.h>

#define mROUNDUP32(x)   (((u32)(x) + 32 - 1) & ~(32 - 1))
MEMHeapHandle  hExpHeap;
void          *arenaMem2Lo;
void          *arenaMem2Hi;
static SPSoundTable *sp_table;

static void *load_file(char *path, u32 *length)
{
    DVDFileInfo dvdFileInfo;
    u32         roundLength;
    void        *buffer;

	// open file
	DVDOpen(path, &dvdFileInfo));
 
	// get length, round up to next 32 Bytes
	roundLength= mROUNDUP32(DVDGetLength(&dvdFileInfo));

	// allocate memory
	buffer = MEMAllocFromExpHeapEx(hExpHeap, roundLength, 32);

	// read data; assume DVD auto-invalidate is ON!
	DVDRead(&dvdFileInfo, buffer, (s32)(roundLength), 0);

	*length = roundLength;

	return(buffer);

} // end load_file()

.
.
.
{
  u32 length;

    // initialize Exp Heap on MEM2
    arenaMem2Lo = OSGetMEM2ArenaLo();
    arenaMem2Hi = OSGetMEM2ArenaHi();
    hExpHeap    = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32)arenaMem2Lo);

    sp_table = (SPSoundTable *)load_file("test.spt",  &length);

}  
NOTE:
This example allocates memory for the SPT data at runtime using the MEM library. If you wish to statically allocate memory, ensure that the buffer is 32-byte aligned:
static u8 *buffer[SPT_SIZE] ATTRIBUTE_ALIGN(32);
Also ensure that your buffer is rounded up to the next multiple of 32 bytes.

Loading the SPD file into Main Memory

#include <cafe.h>
#include <cafe/sp.h>
#include <cafe/mem.h>

#define ZEROBUFFER_BYTES       256           // 256 bytes for zero buffer

MEMHeapHandle  hExpHeap;
void          *arenaMem2Lo;
void          *arenaMem2Hi;
void          *spd_buffer;
u8            *zero_buffer;
  .
  .
  .
{
  u32   length;
    .
    .
    .
    // initialize Exp Heap on MEM2
    arenaMem2Lo = OSGetMEM2ArenaLo();
    arenaMem2Hi = OSGetMEM2ArenaHi();
    hExpHeap    = MEMCreateExpHeap(arenaMem2Lo, (u32)arenaMem2Hi - (u32)arenaMem2Lo);

    // use load_file() function from previous example
    spd_buffer = load_file("test_sfx.spd", &length);
   
    // setup zero_buffer
    zero_buffer = MEMAllocFromExpHeapEx(hExpHeap, ZEROBUFFER_BYTES, 8);
    memset(zero_buffer, 0, ZEROBUFFER_BYTES);
    DCFlushRange(zeroBuffer, ZEROBUFFER_BYTES);

}

In this example, we allocate memory for the sound data (spd_buffer) and the zero buffer (zero_buffer). We load the sound effects from the SPD file using the load_file function shown in the previous example.

Initializing the SP Sound Table

Now that the sound table and data have been loaded, we can initialize the SP library:

#include <cafe.h>
#include <cafe/sp.h>
#include <cafe/mem.h>

static SPSoundTable *sp_table;
static u8           *sp_data;
static u8           *zeroBuffer;
.
.
{
  .
  .
  .
  // load SPT data (see examples above)
  .
  .
  .
  // load SPD data (see examples above)
  .
  .
  .

  // Here we go! 
  SPInitSoundTable(sp_table, sp_data, zeroBuffer);

  // that's it!
} 

Preparing a Sound Effect for Playback

#include <cafe.h>
#include <cafe/sp.h>

#include "test_sfx.h"

static AXVPB *voice;

static SPSoundTable *sp_table;
static SPSoundEntry *sp_entry;

.
.
.
{
    // Initialize AX and Mixer (see AX and MIX documentation)

    // Load SPT file (see previous examples)

    // Load SPD data (see previous examples)

    // Initialize SPT table

    // Get sound table entry for given sound effect index
    // NOTE: The index is defined in the test_sfx.h header file
    sp_entry = SPGetSoundEntry(sp_table, SFX_BLAMMO);

    // Now acquire a voice!
    voice = AXAcquireVoice(15, NULL, 0);

    if (voice)
    {
	// play at default sampling frequency
	SPPrepareSound(sp_entry, voice, sp_entry->sampleRate);

	// setup a mixer channel for this voice
	MIXInitChannel(voice, 0, 0, -960, -960, 64, 127, 0);

	// Set voice state to run! 
	AXSetVoiceState(voice, AX_PB_STATE_RUN);
     } 
}
NOTE:
The sound effect enumeration, SFX_BLAMMO, is defined in the header file associated with the SPT and SPD data (test_sfx.h).

A voice must be acquired before a sound effect is prepared for playback. Note also that playback must be started manually with the AXSetVoiceState call.

Preparing a Looped Effect for Termination

#include <cafe.h>
#include <cafe/sp.h>

#include "test_sfx.h"

static AXVPB *voice;

static SPSoundTable *sp_table;
static SPSoundEntry *sp_entry;

.
.
.
{
    // Initialize AX and Mixer (see AX and MIX documentation)

    // Load SPT file (see previous examples)

    // Load SPD data (see previous examples)

    // Initialize SPT table

    // Start playing a looped sound effect (see previous examples)

    .
    .
    .      

    if (TRUE == please_stop_this_sound)
    {
	// Refresh voice parameter data from SP sound entry
	SPPrepareEnd(sp_entry, voice);
    } 
}

After a looped sound effect is playing, terminate the sound using the SPPrepareEnd call. This function reinitializes the voice‘s loop state information with values from the sound effect‘s table entry, causing the voice to stop. The AX user application can then free the voice at will.

NOTE:
We recommend applying a volume envelope or a fade-out before stopping a voice, to prevent discontinuity artifacts.

Revision History

2013/05/08 Automated cleanup pass.
2012/08/02 Cleanup Pass.
2011/02/21 Initial version.


CONFIDENTIAL