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.
LibAiff supports both linear encodings and logarithmic encodings when reading AIFF or AIFF-C files. However, the only format supported for writing is Linear PCM, the standard audio signal encoding.
Linear encodings divide the given voltage range in equidistant steps or quants. As a result, loud signals (multiplicative) are encoded with more precision than weak signals (which have more rounding error in the quantization to steps of the same difference than the multiplicative signals).
LibAiff supports the following linear encodings:
Logarithmic encodings or floating point encodings have more quantization points in the low level range than in the high level range. As a result, loud signals (multiplicative) and weak signals are given the same resolution. When compared to linear encodings, logarithmic encodings provide better resolution than linear ones for low signals but worse resolution for loud signals.
LibAiff supports the following logarithmic encodings:
A 8-bit µ-Law encoding is equivalent to a 14-bit LPCM encoding for low signals, and equivalent to a 6-bit LPCM encoding for loud signals. Thus, it's a lossy encoding when compared to a full 14-bit LPCM, but provides a 2:1 size ratio (since 14-bit LPCM is packed into 2-byte segments) while retaining the same resolution for low signals, which can be used for compression purposes. It can efficiently compress signals which are statistically more likely to be near a low level than a high signal level.
As detailed before, LibAiff can do any conversions automatically, so any logarithmic encoded data will be transparently handled and converted by the library, so the application only needs to handle Linear PCM data (or floating-point PCM data if the application prefers that). µ-Law encodings are converted to full 14-bit LPCM data (lossless conversion), and A-Law encodings are converted to 13-bit LPCM.
32-bit IEEE-754 floating point can be converted to 32-bit integer LPCM. Note that this conversion loses some extra precision present on the original floating point values, due to the differences between linear and logarithmic encodings detailed above. Reading these samples in floating-point mode is a lossless operation.
LPCM 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):
#define LIBAIFF_NOCOMPAT 1 // do not use LibAiff 2 API compatibility
#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 type to refer to Audio IFF files:
AIFF_Ref file;
/*
* 'name' is a relative or absolute path
* to a file.
*/
AIFF_Ref AIFF_OpenFile(const char* name, int flags) ;
name is the name (or path) of the file to open. flags are a combination of ORed binary flags which can be:
You use the above function 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 function uses dynamic memory, you can open more than one Audio IFF file at the same time.
When you have finished working with an Audio IFF file you should close it:
int AIFF_CloseFile(AIFF_Ref r) ;
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.
Sample code:
AIFF_Ref ref ; ref = AIFF_OpenFile("../Music/track05.aiff", F_RDONLY) ; 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).
The available attribute identifiers are defined on the LibAiff header file.
The following attributes can be used on Audio IFF files:
To extract an attribute from a reading Audio IFF file:
char* AIFF_GetAttribute(AIFF_Ref r,IFFType typ) ;
IFFType should be a valid identifier of an attribute (see the list above).
The function will return a valid nul-terminated C-string if the attribute is present on the file, or NULL if it isn`t.
You should free the resulting pointer when you have finished using it.
Sample code:
char* attrib ; attrib = AIFF_GetAttribute( ref, AIFF_COPY ) ; 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_Ref 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.
If you want to edit an existing Audio IFF file, it's often useful to clone all the attributes present on the file to the new edited copy.
You can use the AIFF_CloneAttributes function to accomplish this.
int AIFF_CloneAttributes(AIFF_Ref w, AIFF_Ref r, int cloneMarkers);
The r parameter is a reference to an opening-for-reading Audio IFF file from which the attributes will be cloned.
If the cloneMarkers parameter is 1, markers (see section 13) will be cloned too (this is not desirable if you have modified the audio content).
This function will return 1 on success, < 1 on error.
int AIFF_GetAudioFormat(AIFF_Ref r,uint64_t* nSamples,int* channels,double* samplingRate, int* bitsPerSample,int* segmentSize) ; int AIFF_SetAudioFormat(AIFF_Ref w,int channels,double samplingRate, int bitsPerSample) ;
You use the AIFF_GetAudioFormat 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:
uint64_t seconds, nSamples ;
int channels ;
double samplingRate ;
int bitsPerSample ;
int segmentSize ;
if( AIFF_GetAudioFormat( 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", (unsigned long) seconds/3600,
(unsigned long) (seconds/60)%60, (unsigned long) seconds%60 ) ;
The AIFF_SetAudioFormat 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_Ref 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_GetAudioFormat 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_Ref r,int32_t* samples,int nsamples) ; int AIFF_ReadSamplesFloat(AIFF_Ref r,float* samples,int nsamples) ;
Use this function to avoid dealing with segments, multiple sample sizes or byte order: it will provide samples into 32-bit (4-byte) signed values or floating-point values (respectively). If the samples are not stored in the requested format in the file, LibAiff will do the necessary conversions.
nsamples is the number of sample points you want to read. samples is an array of 32-bit signed values or floating-point values (respectively).
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_GetAudioFormat function.
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_Ref r,uint64_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 calling this function is valid both when using AIFF_ReadSamples and AIFF_ReadSamples32Bit/AIFF_ReadSamplesFloat methods.
This function will return as usual.
int AIFF_StartWritingSamples(AIFF_Ref w) ; int AIFF_WriteSamples(AIFF_Ref w,void* buffer,size_t len) ; int AIFF_WriteSamples32Bit(AIFF_Ref w,int32_t* samples,int nsamples) ; int AIFF_EndWritingSamples(AIFF_Ref 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_SetAudioFormat 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_Ref r,int* id,uint32_t* position,char** name) ; int AIFF_StartWritingMarkers(AIFF_Ref w) ; int AIFF_WriteMarker(AIFF_Ref w,uint32_t position,char* name) ; int AIFF_EndWritingMarkers(AIFF_Ref 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_Ref 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.