LibAiff is a library for C applications, providing transparent read & write operations for Audio Interchange File Format files.
With LibAiff your application can easily use the Audio IFF format to interchange digital audio.
LibAiff wants to implement all the features of the AIFF 1.3 standard, including markers, comments, etc.
This version of LibAiff supports the following features:
Audio Interchange File Format (Audio IFF or simply AIFF) is a flexible file standard for storing and interchanging digital audio, used by Apple and Silicon Graphics, and designed to facilitate the sharing of sound data between applications.
According to the official specification, Audio IFF is the result of several meetings held with music developers over a period of ten months in 1987-88.
Digital audio is stored as sample points, which are samples taken of the sound wave. Each sample point is a signed value quantized to a fixed number of bits (the bitwidth or sample size). Of course, increasing the sample size will increase the resolution or definition of the sound wave, resulting in a greater sound quality.
Audio IFF supports sample sizes from 8 to 32 bits per sample.
Sample points are aligned into byte-oriented segments. The size of the segments in bytes is the segment size. Audio IFF supports segment sizes from 1 to 4 bytes.
Segments are used to make dealing with sample points easier, since most CPU's are byte-oriented. This means, in example, that 13-bit sample points are aligned into 2-byte segments.
Sample size | Segment size |
---|---|
8 bits | 1 byte |
9 to 16 bits | 2 bytes |
17 to 24 bits | 3 bytes |
25 to 32 bits | 4 bytes |
Sample points are left-justified inside a segment:
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | | | | | | | | | | | | | | | | | | 1 0 1 0 0 0 0 1 0 1 1 0 1 0 0 0 | |___|___|___|___|___|___|___|___|___|___|___|___|___|___|___|___| <-------------------------------------------------> <---------> 13-bit sample point Padding <-------------------------------------------------------------> 2-byte segment
An audio file may contain more than one channel of sound. This is used, in example, in stereo, surround and quadraphonic speaker-arrangements.
Each channel has its own independent sample points and segments.
Channel segments that are meant to be played simultaneously are packed together into a sample frame. In example, in a 2-channel stereo file, each sample frame contains 2 segments, one for each channel.
___________ ___________ ___________ ___________ | | | | | | Channel 1 | Channel 2 | Channel 1 | Channel 2 | |___________|___________|___________|___________| <---------> <---------> <---------> <---------> Segment Segment Segment Segment <---------------------> <---------------------> Sample frame 1 Sample frame 2
All of the segments packed in a sample frame correspond to the same time on the sound.
The number of sample frames per second taken of a sound wave is called the sampling rate. Audio IFF supports virtually any sampling rate, being this limited by your playing/recording software or hardware.
The most used sampling rates are: 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200 and 96000 sample frames per second.
Increasing the sampling rate will result in a greater sound quality.
Also, modifying the sampling rate without modifying the samples will alter the sound speed and pitch.
The first step to use the LibAiff in your C application, is to include the LibAiff header file (you need to have LibAiff installed or accessible from your development environment):
#include <libaiff/libaiff.h>
The <libaiff.h> file has the type definitions and function prototypes of LibAiff.
When compiling your application, you need to link it with LibAiff. In example, using the command-line C compiler on a UNIX operating system:
% cc obj1.o obj2.o obj3.o -lm -laiff -o binary
(The -lm modifier is to link with the math library, used by LibAiff and perhaps by your application too).
References are an abstract form of representing Audio IFF files used for reading & writing operations inside LibAiff.
You pass these references as an argument to the LibAiff functions.
The library uses the following C data types to refer to Audio IFF files:
AIFF_ReadRef r ; AIFF_WriteRef w ;
The AIFF_ReadRef refers to an Audio IFF which is being reading, and the AIFF_WriteRef refers to an Audio IFF which is being writing.
/*
* 'name' is a relative or absolute path
* to a file.
*/
AIFF_ReadRef AIFF_Open(const char* name) ;
AIFF_WriteRef AIFF_WriteOpen(const char* name) ;
You use the above functions to open an Audio IFF file for reading or writing.
The function will return a reference (see section 5) or NULL if the file cannot be opened (i.e, the file does not exist or it cannot be created).
Since the functions use dinamic memory, you can open more than one Audio IFF file at the same time.
When you have finished working with an Audio IFF file (i.e, the writing process has been completed) you should close it:
int AIFF_Close(AIFF_ReadRef r) ; int AIFF_WriteClose(AIFF_WriteRef w) ;
In case of writing, you need to close it when finished to construct a valid Audio IFF. If your program finish without closing the file, it will be incomplete.
If you close the file without finishing the write process, the library will complain to stderr that the file is unfinished.
Sample code:
AIFF_ReadRef ref ; ref = AIFF_Open( "../Music/track05.aiff" ) ; if( ref ) { puts( "File opened successfully." ) ; AIFF_Close( ref ) ; }
Attributes are metadata stored into an Audio IFF file, which helps to identify the content of the file (like the name of the musical piece, its author or composer, its copyright and licensing, and some annotations).
Attributes are identified by an IFFType data type. An IFFType is a four-byte identifier into an IFF file.
IFFType typ ;
The available identifiers are defined on the LibAiff header file. You can use memcpy to copy one to your IFFType. In example, to use the Name attribute:
memcpy( &typ, NameID, 4 ) ;
The following attributes can be used on Audio IFF files:
To extract an attribute from a reading Audio IFF file:
char* AIFF_GetAttribute(AIFF_ReadRef r,IFFType typ) ;
IFFType should contain a valid identifier of an attribute (see the list above).
The function will return a valid C-string if these attribute is present on the file, or NULL if it isn`t.
You should free the resulting pointer when you have done using it.
Sample code:
IFFType typ ; char* attrib ; memcpy( &typ, CopyID, 4 ) ; attrib = AIFF_GetAttribute( ref,typ ) ; if( attrib ) { printf( "Copyright (C) %s\n", attrib ) ; free( attrib ) ; }
To store an attribute into a writing Audio IFF file, you use:
int AIFF_SetAttribute(AIFF_WriteRef w,IFFType typ,char* attribute) ;
typ should be a valid identifier, and attribute a valid C-string with the content of the attribute.
This function will return 1 on success, < 1 on error.
int AIFF_GetSoundFormat(AIFF_ReadRef r,uint32_t* nSamples,int* channels,int* samplingRate, int* bitsPerSample,int* segmentSize) ; int AIFF_SetSoundFormat(AIFF_WriteRef w,int channels,int samplingRate, int bitsPerSample) ;
You use the AIFF_GetSoundFormat function to extract format information about the sound in the Audio IFF file, like number of sample frames, n. of channels, sampling rate, the sample size or the segment size (you have an explanation of these concepts on section 3).
The number of sample frames, nSamples, can be used to calculate the length in seconds of the sound:
uint32_t seconds ;
uint32_t nSamples ;
int channels ;
int samplingRate ;
int bitsPerSample ;
int segmentSize ;
if( AIFF_GetSoundFormat( ref,&nSamples,&channels,
&samplingRate,&bitsPerSample,
&segmentSize ) < 1 )
{
puts( "Error" ) ;
AIFF_Close( ref ) ;
}
seconds = nSamples / samplingRate ;
/*
* Print out the seconds in H:MM:SS format
*/
printf("Length: %lu:%02lu:%02lu \n", seconds/3600,
(seconds/60)%60, seconds%60 ) ;
The AIFF_SetSoundFormat is used to set the sound format before writing the sound to the Audio IFF file.
You should call this function before calling the sound writing functions (described in the next sections).
Both of these functions will return 1 for success, 0 for errors, -1 for critical errors.
size_t AIFF_ReadSamples(AIFF_WriteRef r,void* buffer,size_t len) ;
The above function is used to extract raw sound data from the Audio IFF file. This function is not recommended if you simply want to get the samples or play back the sound, use instead the function described on the next section.
You should pass the reference to the Audio IFF file, a point to a buffer which will be filled with sound data, and the len in bytes of the buffer which you want filled with sound data.
The function will return the n. of bytes read from the file and stored in buffer, 0 when no more sound is present on the file, and -1 for errors.
The sound data will be presented according to the format provided with the AIFF_GetSoundFormat function, as described on section 3.
Note that LibAiff will do the endian conversion automatically, so the samples will be in native endian form.
Each call of this function will advance the current reading position, so you can extract all of the sound on an Audio IFF file by simply doing repeated calls in a loop.
int AIFF_ReadSamples32Bit(AIFF_ReadRef r,sint32* samples,int nsamples) ;
Use this function to avoid dealing with segments, multiple sample sizes or byte order: it will convert and provide samples into 32-bit (4-byte) signed values.
nsamples is the number of sample points you want to read. samples is an array of 32-bit signed values.
The function will return the n. of sample points read from the file. If the returned value is 0, no more samples are present on the file. If it`s -1, an error occurred.
The sampling rate of the samples is provided using the AIFF_GetSoundFormat function. You have to call that function before using this one.
Each call of this function will advance the current reading position, so you can extract all of the sound on an Audio IFF file by simply doing repeated calls in a loop.
int AIFF_Seek(AIFF_ReadRef r,uint32_t sampleFrame) ;
Use this function if you want to seek to a given sample frame into the file.
If you simply want to rewind, pass 0 as sampleFrame.
If you want to specify seek positions on seconds, do as in the sample code below:
uint32_t seconds = 67 ; /* 01:07 */
uint32_t sampleFrame ;
sampleFrame = seconds * samplingRate ;
if( AIFF_Seek( ref,sampleFrame ) < 1 )
{
puts( "Error while seeking!" ) ;
AIFF_Close( ref ) ;
}
Of course this function is valid both from AIFF_ReadSamples and AIFF_ReadSamples32Bit methods.
This function will return as usual.
int AIFF_StartWritingSamples(AIFF_WriteRef w) ; int AIFF_WriteSamples(AIFF_WriteRef w,void* buffer,size_t len) ; int AIFF_WriteSamples32Bit(AIFF_WriteRef w,sint32* samples,int nsamples) ; int AIFF_EndWritingSamples(AIFF_WriteRef w) ;
To start writing sound data, use the AIFF_StartWritingSamples function which will return as usual.
The AIFF_WriteSamples function is not recommended for general sound writing, since it will not do any conversion (except endian-swap) of the sound data (use the normal method instead). Use only this function to do very specific operations.
With this function you should provide a buffer filled with len bytes of sampled sound data. The sound data should be presented as described in section 3.
Note that you don't have to endian-convert the samples, as LibAiff will do that for you. Your samples will be left untouched; LibAiff will use its internal buffers to do the byte-swapping (if necessary).
It will return as usual.
This is the preferred method to write samples to an Audio IFF file. It will convert automatically the samples to the values specified to the AIFF_SetSoundFormat function (which should be called before this one) and will automatically align the samples on segments and deal with the byte-order.
nsamples is the number of samples you want to write.
samples is an array of nsamples signed 32-bit samples.
It will return as usual.
Simply use the AIFF_EndWritingSamples function when you have done delivering the samples. You need to call this function before closing the Audio IFF file or terminating your program.
If this function is not called, the resulting Audio IFF will be incomplete and could not be played back.
This function will return as usual.
Audio IFF allows the storage of markers into the file. Markers are references to some position on the sound, including a text describing what`s on that marker (marker name).
In example, you can store a marker to the position 0:28:13 with a text Begin of Part II.
These are the functions used to read & write markers:
int AIFF_ReadMarker(AIFF_ReadRef r,int* id,uint32_t* position,char** name) ; int AIFF_StartWritingMarkers(AIFF_WriteRef w) ; int AIFF_WriteMarker(AIFF_WriteRef w,uint32_t position,char* name) ; int AIFF_EndWritingMarkers(AIFF_WriteRef w) ;
You use the AIFF_ReadMarker function to read the markers stored on the file. It will return 1 if a marker is read, 0 when no more markers are stored on file, and -1 on error. This function will automatically advance the reading position, so to read all the markers on a file just call the function repeatedly until it returns 0 (or -1 if an error has occurred).
name will point to a valid C-string containing the marker name, or to NULL if that marker has not a name. It should be freed when you have done using it.
position is the position where the marker points inside the sound, measured on sample frames.
Sample code:
int id ; uint32_t position ; uint32_t seconds ; char* name ; while( 1 ) { if( AIFF_ReadMarker( ref, &id, &position, &name ) < 1 ) break ; seconds = position / samplingRate ; printf("Marker %d --> [%lu:%02lu:%02lu]\n",id, seconds/3600, (seconds/60)%60, seconds%60 ) ; if( name ) { printf( "(%s)\n", name ) ; free( name ) ; } }
To start the markers` writing procedure, just call the AIFF_StartWritingMarkers (it will return as usual).
Then do one call for each marker to the AIFF_WriteMarker function. The name argument can be NULL if a marker has not a name.
When you have finished writing markers, call the AIFF_EndWritingMarkers function. You need to call that function before closing the Audio IFF file or terminating your program, or the resulting Audio IFF file will be damaged and incomplete (LibAiff will complain of that to stderr).
Audio IFF allows the storage of instrument data into the file.
Instrument data is used to store samples in AIFF files and describe them.
LibAiff provides the Instrument data type. The Instrument data type is defined as follows:
struct s_Loop { int16_t playMode ; uint32_t beginLoop ; uint32_t endLoop ; } ; struct s_Instrument { int8_t baseNote ; int8_t detune ; int8_t lowNote ; int8_t highNote ; int8_t lowVelocity ; int8_t highVelocity ; int16_t gain ; struct s_Loop sustainLoop ; struct s_Loop releaseLoop ; } ; typedef struct s_Instrument Instrument ;
struct s_Loop defines the instrument loop points (sustainLoop and releaseLoop).
The sustainLoop is the loop to be played when the instrument sustains a sound. The releaseLoop is the loop to be played when the instrument is in the release phase of playing back a sound. The release phase usually occurs after a key on an instrument is released.
The field playMode in each loop describes its repetition mode:
#define kModeNoLooping 0 #define kModeForwardLooping 1 #define kModeForwardBackwardLooping 2
Note that if the mode kModeNoLooping is used, the remaining fields are to be ignored.
beginLoop and endLoop reference to positions in the song, measured in sample frames (so they can be used as arguments to the AIFF_Seek function).
The remaining fields are used according to the official AIFF specification:
The detune field determines how much the instrument should alter the pitch of the sound when it is played back. Units are in cents (1/100 of a semitone) and range from -50 to +50. Negative numbers mean that the pitch of the sound should be lowered, while positive numbers mean that it should be raised.
The lowNote and highNote fields specify the suggested note range on a keyboard for playback of the waveform data. The waveform data should be played if the instrument is requested to play a note between the low and high note numbers, inclusive. The base note does not have to be within this range. Units for lowNote and highNote are MIDI note values.
The lowVelocity and highVelocity fields specify the suggested range of velocities for playback of the waveform data. The waveform data should be played if the note-on velocity is between low and high velocity, inclusive. Units are MIDI velocity values, 1 (lowest velocity) through 127 (highest velocity).
The gain is the amount by which to change the gain of the sound when it is played. Units are decibels. For example, 0db means no change, 6db means double the value of each sample point (ie, every additional 6db doubles the gain), while -6db means halve the value of each sample point.
To extract the data from a AIFF file you can use the following function:
int AIFF_GetInstrumentData(AIFF_ReadRef r,Instrument* ins) ;
The function returns as usual, and the data will be stored in the structure pointed to by `ins'.
Copyright © 2005, 2006 by Marco Trillo <marcotrillo@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.