An AIFF or AIFC file contains the sample data for a sampled sound along with other important auxiliary information such as the sample size (8 or 16 bits), recording sampling rate, looping points, and whether the sample is in stereo or mono. When the sample is loaded, it becomes a sample item. The sample data goes into the task's own memory, while the auxiliary sample information is stored as part of the item in system memory.
Item LoadSample( char *Name )
*Name
) to a string containing the filename of the AIFF or AIFC file containing the sampled sound. When executed, LoadSample()
finds the file, allocates task memory for the sample data, and loads the sample sound data there. It reads the auxiliary sample data, and creates a sample item that contains settings to match the auxiliary data. If successful, the call returns the item number of the loaded sample. If unsuccessful, it returns a negative value (an error code).
Item ScanSample( char *Name, int32 BufferSize )
*Name
) to a string containing the filename of the AIFF or AIFC file containing the sampled sound. It also accepts a buffer size (BufferSize
) in bytes.
When it executes, ScanSample()
allocates a buffer of the specified size in the task's memory. It then starts at the beginning of the sample data and loads as many of the samples as will fit into the buffer. It truncates the rest of the sample and creates a sample item. If successful, the call returns the item number of the loaded sample. If unsuccessful, it returns a negative value (an error code).
Note: In the Opera Portfolio, ScanSample()
will reject samples with markers outside the portion that is loaded into memory.
LoadSample()
and ScanSample()
automatically allocate RAM in the calling task's memory and then load the sample data into the allocated RAM. To load sample data into memory allocated by the task itself (which provides more control over where the sample data resides), use this call:
Item LoadSampleHere( char *Name , void * (*CustomAllocMem)(), void (*CustomFreeMem)() )
*Name
, is a pointer to a string containing the filename of the AIFF or AIFC file containing the sampled sound. The second, *(*CustomAllocMem)()
, is the name of a custom memory allocation function that the task must provide. The third, (*CustomFreeMem)()
, is the name of a custom memory-freeing function that the task must also provide.
When executed, LoadSampleHere()
calls the custom memory allocation function, and passes the parameters int32 NumBytes
and uint32 MemType
, which provide the size and type of memory to be allocated. The custom function allocates the memory and returns a DataPtr to the allocated memory if successful, or a NULL if unsuccessful. If allocation is successful, LoadSampleHere()
loads the sample data into the allocated memory, creates a sample item, and returns the item number of the sample. If unsuccessful, the call returns a negative value (an error code).
The custom memory-freeing function is not called until the loaded sample is unloaded later with the UnloadSample()
call. At that time, UnloadSample()
calls the memory-freeing function and passes the parameters (
void *DataPtr, int32 NumBytes
)
, which provide a pointer to the sample memory block and the size of the block. The custom call then frees the memory.
CreateSample()
can also create an empty sample. You must allocate memory for the sample and then fill it using any technique you choose. This is useful if you want to buffer parts of a large AIFF sample file in memory, synthesize a sample from scratch, or use a custom decompression technique to load a sample from disc. Once created, the sample can be used like any other sample. An example of using CreateSample()
is shown in Example 1.Example 1: Creating an empty sample.
The tags AF_TAG_ADDRESS and either AF_TAG_FRAMES or AF_TAG_NUMBYTES are mandatory.
/*Allocate data from memory accessible to audio DMA. * /
SampleData = (int16 *) AllocMem( NumBytes, MEMTYPE_AUDIO);
If (SampleData == NULL)
{
ERR(("Could not allocated sample data.\n"));
goto cleanup;
}
/* Use tags to define sample format and location. */
Tags[0].ta_Tag = AF_TAG_ADDRESS;
Tags[0].ta_Arg = (int32 *) Data;
Tags[1].ta_Tag = AF_TAG_NUMBYTES;
Tags[1].ta_Arg = (int32 *) NumBytes;
Tags[2].ta_Tag = AF_TAG_CHANNELS;
Tags[2].ta_Arg = (int32 *) 2; /* stereo, optional */
Tags[3].ta_Tag = TAG_END;
SampleItem = CreateSample (Tags);
CHECKRESULT(SampleItem, "CreateSample");
Note: CreateSample( ) replaces MakeSample(). Do not use MakeSample() in your applications as the API was such that it is open to misuse.
To use CreateSample()
, you create a list of tag arguments.
The possible tags are shown in Table 1.
Table 1: Tag arguments that define the characteristics of a sampled sound. ------------------------------------------------------------------- Tag Name |Description ------------------------------------------------------------------- AF_TAG_ADDRESS |Pointer to sample data. If this tag is not |specified on creation, memory is allocated |based on AF_TAG_NUMBYTES or AF_TAG_WIDTH. ------------------------------------------------------------------- AF_TAG_ALLOC_FUNCTION |Sets custom memory allocation function to |be called during sample creation. Mutually |exclusive with AF_TAG_DELAY_LINE. ------------------------------------------------------------------- AF_TAG_BASEFREQ |The frequency of the sample (in 16.16 Hz) |when played at the original sample rate. |This is derived from the AF_TAG_BASENOTE |value. ------------------------------------------------------------------- AF_TAG_BASENOTE |MIDI note number for this sample when |played at the original sample rate. This |defines the frequency conversion reference |note for the StartInstrument() |AF_TAG_PITCH tag. Default is 60. ------------------------------------------------------------------- AF_TAG_CHANNELS |Number of channels (or samples per sample |frame). For stereo, this would be 2. ------------------------------------------------------------------- AF_TAG_COMPRESSIONRATIO |Compression ratio of sample data. |Uncompressed data has a value of 1. |Default is 1. ------------------------------------------------------------------- AF_TAG_COMPRESSIONTYPE |32-bit ID representing AIFC compression |type of sample data (e.g. ID_SDX2). 0 for |no compression. ------------------------------------------------------------------- AF_TAG_DATA_OFFSET |Byte offset in sample file of the |beginning of the sample data. ------------------------------------------------------------------- AF_TAG_DATA_SIZE |Size of sample data in sample file in |bytes. Note that this may differ from the |length of the sample as loaded into |memory. ------------------------------------------------------------------- AF_TAG_DELAY_LINE |Creates a delay line consisting of ta_Arg |bytes (not frames). Mutually exclusive |with AF_TAG_IMAGE_ADDRESS and AF_TAG_NAME. | ------------------------------------------------------------------- AF_TAG_DETUNE |Amount to detune the MIDI base note in |cents to reach the original pitch. Must be |in the range of -100 to 100. Default is 0. ------------------------------------------------------------------- AF_TAG_FRAMES |Length of sample expressed in frames. For |a stereo sample, this is the number of |stereo pairs in the sample. When setting, |this tag is mutually exclusive with |AF_TAG_NUMBYTES. ------------------------------------------------------------------- AF_TAG_FREE_FUNCTION |Sets custom memory free function to be |called during sample deletion. Defaults to |FreeMem(). Mutually exclusive with |AF_TAG_DELAY_LINE. ------------------------------------------------------------------- AF_TAG_HIGHNOTE |Highest MIDI note number at which to play |this sample when part of a multisample. |Range is 0 to 127. Default is 127. ------------------------------------------------------------------- AF_TAG_HIGHVELOCITY |Highest MIDI note on velocity at which to |play this sample when part of a |multisample. Range is 0 to 127. Default is |127. ------------------------------------------------------------------- AF_TAG_IMAGE_ADDRESS |Specifies a memory location containing an |AIFF file image from which to read sample. |Must use in conjunction with |AF_TAG_IMAGE_LENGTH. Mutually exclusive |with AF_TAG_DELAY_LINE and AF_TAG_NAME. ------------------------------------------------------------------- AF_TAG_IMAGE_LENGTH |Specifies number of bytes in AIFF file |image pointed to by AF_TAG_IMAGE_ADDRESS. ------------------------------------------------------------------- AF_TAG_LOWNOTE |Lowest MIDI note number at which to play |this sample when part of a multisample. |Range is 0 to 127. Default is 0. ------------------------------------------------------------------- AF_TAG_LOWVELOCITY |Lowest MIDI note on velocity at which to |play this sample when part of a |multisample. Range is 0 to 127. Default is |0. ------------------------------------------------------------------- AF_TAG_NAME |Name of AIFF file to load. Mutually |exclusive with AF_TAG_DELAY_LINE and |AF_TAG_ADDRESS. ------------------------------------------------------------------- AF_TAG_NUMBITS |Number of bits per sample (uncompressed). |On creation, defaults to value from file |or 16. When setting, is mutually exclusive |with AF_TAG_WIDTH. Default is 16. ------------------------------------------------------------------- AF_TAG_NUMBYTES |Length of sample expressed in bytes. This |tag is supported for all operations for |non-delay line samples. When setting, this |tag is mutually exclusive with |AF_TAG_FRAMES. ------------------------------------------------------------------- AF_TAG_RELEASEBEGIN |Frame index of the first frame of the |release loop (0..NumFrames-1), or -1 if no |release loop. Must be used in conjunction |with AF_TAG_RELEASEEND. Default is -1. ------------------------------------------------------------------- AF_TAG_RELEASEEND |Frame index of the first frame after the |last frame in the release loop when |(1..NumFrames), or -1 if no release loop. |Must be used in conjunction with |AF_TAG_RELEASEBEGIN. Default is -1. ------------------------------------------------------------------- AF_TAG_SAMPLE |Internal use only. ------------------------------------------------------------------- AF_TAG_SAMPLE_RATE |Original sample rate for sample expressed |in 16.16 fractional Hz. Default is |44100.000 ------------------------------------------------------------------- AF_TAG_SCAN |Selects scan mode and specifies number of |sample data bytes to load from AIFF file |(see ScanSample()). ------------------------------------------------------------------- AF_TAG_SUSTAINBEGIN |Frame index of the first frame of the |sustain loop (0..NumFrames-1), or -1 if no |sustain loop. Must be used in conjunction |with AF_TAG_SUSTAINEND. Default is -1. ------------------------------------------------------------------- AF_TAG_SUSTAINEND |Frame index of the first frame after the |last frame in the sustain loop |(1..NumFrames), or -1 if no sustain loop. |Must be used in conjunction with |AF_TAG_SUSTAINBEGIN. Default is -1. ------------------------------------------------------------------- AF_TAG_WIDTH |Number of bytes per sample (uncompressed). |When setting, is mutually exclusive with |AF_TAG_NUMBITS. Default is 2. -------------------------------------------------------------------
The tag argument list is terminated with TAG_END or NULL. Any of the tags listed above that you do not supply are filled in with default values. Note that a sample frame is the set of data sent to the DSP for one DSP frame. In a stereo sample, that's two samples. A frame index is an offset from the first frame of the sample data. Thus, the first frame has a frame index of 0.
MakeSample()
, you can simply provide a NumBytes
value of 0, which asks the call not to allocate any sample buffer. You then provide a pointer to your sample data with the tag argument AF_TAG_ADDRESS and define the size of the data using the tag argument AF_TAG_NUMBYTES. MakeSample()
then creates a sample item from your existing sample data.
Item DefineSampleHere( uint8 *AIFFImage, int32 NumBytes, void *(*CustomAllocMem)(), void (*CustomFreeMem)() )
*AIFFImage
, which is a pointer to the beginning of the sample file image; NumBytes
, which is the size of the sample file image in bytes; *(*CustomAllocMem)()
, which is the name of a custom memory allocation function the task must provide; and (*CustomFreeMem)()
, which is the name of a custom memory-freeing function the task must also provide.
When it executes, DefineSampleHere()
calls the custom memory-allocation function and passes the parameters int32 NumBytes
and uint32 MemType
, which provide the size and the type of memory to be allocated. The custom function allocates the memory and returns a DataPtr to the allocated memory if successful or NULL if unsuccessful. If allocation is successful, DefineSampleHere()
loads the sample data from the file image into the allocated memory, creates a sample item, and returns the item number of the sample. If unsuccessful, the call returns a negative value (an error code).
The custom memory-freeing function is not called until the loaded sample is unloaded later (as described previously in the section on LoadSampleHere()
).
The minimum loop size is 8 bytes because of DMA restrictions. Your loop points should fall on word boundaries if you want precise loop definitions because Opera DMA works on 32-bit (4 byte) quantities.
The release loop, like the sustain loop, is defined with two sample tag arguments: AF_TAG_RELEASEBEGIN and AF_TAG_RELEASEEND. These tag arguments serve the same purpose for the release loop as the SUSTAIN tag arguments do for the sustain loop.
Item AttachSample( Item InstrumentItem, Item SampleItem, char *FIFOName )
InstrumentItem
, which is the item number of an instrument; SampleItem
, which is the item number of a sample; and *FIFOName
, which is a pointer to a string containing the name of the DSP input FIFO through which you want to play the sample.The FIFO name argument is provided for instruments such as delay instruments with more than one FIFO. If you pass NULL for this argument, you specify the first FIFO of the instrument.
When AttachSample()
executes, it attaches the specified sample to the specified FIFO of the specified instrument. It creates an attachment item that defines the sample's attachment to the instrument. If successful, the call returns the item number of the attachment. If unsuccessful, it returns a negative value (an error code).
AttachSample()
is important because it defines the attachment between a sample and an instrument, both of which remain independent entities. The attachment lists the sample and the instrument and ties the two together.Because an attachment item maintains the sample and the instrument as independent entities (it does not incorporate the sample into the instrument), one sample can be attached to more than one instrument. And more than one sample can be attached to a single FIFO of an instrument.
You can alter attachment items with attachment calls; to do so, you need the attachment item number returned by AttachSample()
and the attachment item number to detach the sample from the instrument.
The solution is multisampling: creating many different samples for a single instrument, one sample for each reasonable range of pitches. For example, a middle-C piano sample might be used for an instrument's A to D range, while an F above middle C on the piano may be used for the E-flat to A-flat range above, and so on, with a new sample for every half-octave of the instrument's range.
To create a multisample sound with a sampled sound instrument, you simply use AttachSample()
to attach as many samples as you need to a single FIFO of the instrument. Each of the samples should be defined to have an exclusive pitch or velocity range in which it plays. Some audio editors allow you to set the pitch range for the sample file. If you cannot set the pitch range there, you can set it using the sample tag arguments AF_TAG_LOWNOTE and AF_TAG_HIGHNOTE. You can set the velocity range using the tag arguments AF_TAG_LOWVELOCITY and AF_TAG_HIGHVELOCITY. The call SetAudioItemInfo()
, described in Setting Audio Item Characteristics, allows you to set these tag argument values for any existing sample item.
When you play the instrument later, specifying a pitch or a velocity, the Audio folio plays only the sample that is set to play in that pitch and velocity range. If more than one sample fits the pitch and velocity criteria, the Audio folio plays the first of the appropriate samples connected to the instrument. If no sample fits the pitch and velocity criteria, no sample plays.
Note that all samples in a multisample configuration must have the same format: the same number of bytes per sample, the same number of channels (stereo or mono), and the same compression type.
You can use multisample with nonvariable rate instruments such as fixedmonosample.dsp
. The pitch will not affect the frequency but it can be used to select a sample. This can be used to put a drum kit on a single instrument.
Err DetachSample( Item Attachment )
DetachSample()
returns 0 if successful, or a negative value (an error code) if unsuccessful.
Err UnloadSample( Item SampleItem )
UnloadSample()
removes the sample data from memory, frees the sample data memory (using the custom memory-freeing function if the sample was created with LoadSampleHere()
or DefineSampleHere()
), and deletes the sample item. It returns 0 if successful, or a negative value (an error code) if unsuccessful.
Note that if you use DeleteItem()
to delete a sample, the sample memory will not be freed because it is in the task's user memory.
Err DebugSample( Item SampleItem )
Please note that this call is for debugging only. It will not be available in the production version of Portfolio, so do not use it to get information for your task to act on. Use GetAudioItemInfo()
instead.