AudioConverter challenges
AudioConverter challenges
- Subject: AudioConverter challenges
- From: Tom Mitchell <email@hidden>
- Date: Wed, 28 Oct 2009 08:48:49 -0400
Ultimately I need to write compressed audio to a QuickTime file using
AddMediaSample2. I can't seem to get that to work, so I'm starting
from the ConvertFile sample and trying to migrate towards the
QuickTime solution. I have attached below my attempt to write a short
sine wave to a Core Audio File using an AudioConverter to compress
the data using the Apple Lossless codec. It generates a file that is
suspiciously short in length (4096 bytes), and fails to play any audio
in QuickTime Player. I'm building and running on Snow Leopard (10.6.1).
Can anyone see what I'm doing wrong, or offer any suggestions? I've
tried to compare with the code in ConvertFile, but I must be missing
something.
Thanks,
-tom
--- ConvertAudio.m ---
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
void check_error(OSStatus err, char *errFn)
{
if (err != noErr) {
fprintf(stderr, "Error: %s ==> %d", errFn, err);
exit(1);
}
}
/* Populate an ASBD for Linear PCM. */
void fill_asbd_for_lpcm(AudioStreamBasicDescription *asbd)
{
bzero(asbd, sizeof(AudioStreamBasicDescription));
asbd->mSampleRate = 16000;
asbd->mFormatID = kAudioFormatLinearPCM;
asbd->mFormatFlags = (kAudioFormatFlagIsSignedInteger |
kAudioFormatFlagIsPacked);
asbd->mBytesPerPacket = 2;
asbd->mFramesPerPacket = 1;
asbd->mBytesPerFrame = 2;
asbd->mChannelsPerFrame = 1;
asbd->mBitsPerChannel = 16;
}
/* Populate an ASBD for Apple Lossless Compression. */
static void fill_asbd_for_alac(AudioStreamBasicDescription *asbd)
{
bzero(asbd, sizeof(AudioStreamBasicDescription));
asbd->mFormatID = kAudioFormatAppleLossless;
/* asbd->mFormatFlags = kAppleLosslessFormatFlag_16BitSourceData; */
asbd->mFormatFlags = 0;
asbd->mSampleRate = 16000;
asbd->mBytesPerPacket = 0;
asbd->mFramesPerPacket = 4096; // Is there a constant for this?
asbd->mBytesPerFrame = 0;
asbd->mChannelsPerFrame = 1;
asbd->mBitsPerChannel = 0;
}
/* A struct to hold the audio data and associated info. */
struct MyAudioData {
UInt32 size;
UInt32 ptr;
SInt16 *data;
};
typedef struct MyAudioData MyAudioData;
static MyAudioData *makeSineWave(UInt32 durationSeconds)
{
MyAudioData *result = (MyAudioData *)malloc(sizeof(MyAudioData));
UInt32 bytesPerSample = sizeof(SInt16);
UInt32 sampleRate = 16000;
UInt32 sampleCount = sampleRate * durationSeconds;
result->size = bytesPerSample * sampleCount;
result->ptr = 0;
result->data = (SInt16 *)calloc(sampleCount, sizeof(SInt16));
double factor = pow(2.0, 14.0);
double pi12 = M_PI / 12.0;
UInt32 i;
for (i = 0; i < sampleCount; i++) {
double val = (sin (i * pi12)) * factor;
result->data[i] = (SInt16) val;
}
return result;
}
static OSStatus sineWaveInputProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData)
{
MyAudioData *inputData = (MyAudioData *)inUserData;
UInt32 bytesLeft = inputData->size - inputData->ptr;
if (bytesLeft == 0) {
// signal end of input data.
*ioNumberDataPackets = 0;
NSLog(@"Input Proc End Of Data");
return noErr;
}
UInt32 byteCount = *ioNumberDataPackets * sizeof(SInt16);
if (byteCount > bytesLeft) {
byteCount = bytesLeft;
*ioNumberDataPackets = byteCount / sizeof(SInt16);
}
ioData->mBuffers[0].mData = inputData->data + inputData->ptr;
ioData->mBuffers[0].mDataByteSize = byteCount;
ioData->mBuffers[0].mNumberChannels = 1;
*outDataPacketDescription = NULL;
inputData->ptr += byteCount;
NSLog(@"Input Proc Yielded %d bytes", byteCount);
return noErr;
}
// Cribbed from ConvertFile sample code
void WriteCookie (AudioConverterRef converter, AudioFileID outfile)
{
// grab the cookie from the converter and write it to the file
UInt32 cookieSize = 0;
OSStatus err = AudioConverterGetPropertyInfo(converter,
kAudioConverterCompressionMagicCookie, &cookieSize, NULL);
// if there is an error here, then the format doesn't have a cookie,
so on we go
if (!err && cookieSize) {
char* cookie = (char *)calloc(cookieSize, sizeof(char));
err = AudioConverterGetProperty(converter,
kAudioConverterCompressionMagicCookie, &cookieSize, cookie);
check_error(err, "Get Cookie From AudioConverter");
err = AudioFileSetProperty (outfile,
kAudioFilePropertyMagicCookieData, cookieSize, cookie);
// even though some formats have cookies, some files don't take them
check_error(err, "Set Cookie Property On AudioFile");
free(cookie);
}
}
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
OSStatus err;
// Create the output file
NSURL *outputFileURL = [NSURL fileURLWithPath:@"sample.caf"];
AudioFileTypeID outputFileType = kAudioFileCAFType;
AudioStreamBasicDescription outputFormat;
fill_asbd_for_alac(&outputFormat);
UInt32 outputFlags = kAudioFileFlags_EraseFile;
AudioFileID outputFile;
err = AudioFileCreateWithURL((CFURLRef) outputFileURL,
outputFileType, &outputFormat, outputFlags, &outputFile);
check_error(err, "AudioFileCreateWithURL");
// Create the AudioConverter
AudioStreamBasicDescription inputFormat;
fill_asbd_for_lpcm(&inputFormat);
AudioConverterRef converter;
err = AudioConverterNew(&inputFormat, &outputFormat, &converter);
check_error(err, "AudioConverterNew");
// Set up the buffer
AudioStreamPacketDescription* outputPktDescs = NULL;
UInt32 theOutputBufSize = 32768;
char* outputBuffer = (char *)calloc(theOutputBufSize, sizeof(char));
int outputSizePerPacket = 0;
UInt32 size = sizeof(outputSizePerPacket);
err = AudioConverterGetProperty(converter,
kAudioConverterPropertyMaximumOutputPacketSize,
&size,
&outputSizePerPacket);
check_error(err, "Get Max Packet Size");
NSLog(@"outputSizePerPacket = %d", outputSizePerPacket);
outputPktDescs = (AudioStreamPacketDescription *)calloc
(theOutputBufSize / outputSizePerPacket,
sizeof(AudioStreamPacketDescription));
UInt32 numOutputPackets = theOutputBufSize / outputSizePerPacket;
WriteCookie(converter, outputFile);
// Construct the sample data
MyAudioData *inputData = makeSineWave(3);
// Convert data and write to file
UInt64 totalOutputFrames = 0;
SInt64 outputPos = 0;
while (1) {
AudioBufferList fillBufList;
fillBufList.mNumberBuffers = 1;
fillBufList.mBuffers[0].mNumberChannels =
inputFormat.mChannelsPerFrame;
fillBufList.mBuffers[0].mDataByteSize = theOutputBufSize;
fillBufList.mBuffers[0].mData = outputBuffer;
UInt32 ioOutputDataPackets = numOutputPackets;
err = AudioConverterFillComplexBuffer(converter,
sineWaveInputProc,
(void *)inputData,
&ioOutputDataPackets,
&fillBufList,
NULL);
check_error(err, "AudioConverterFillComplexBuffer");
if (ioOutputDataPackets == 0) {
// this is the EOF conditon
break;
}
// Write the resulting data to file.
UInt32 inNumBytes = fillBufList.mBuffers[0].mDataByteSize;
err = AudioFileWritePackets(outputFile, false, inNumBytes,
outputPktDescs, outputPos, &ioOutputDataPackets, outputBuffer);
check_error(err, "AudioConverterFillComplexBuffer");
// advance output file packet position
outputPos += ioOutputDataPackets;
totalOutputFrames += (ioOutputDataPackets *
outputFormat.mFramesPerPacket);
}
AudioConverterPrimeInfo primeInfo;
UInt32 primeSize = sizeof(primeInfo);
err = AudioConverterGetProperty(converter,
kAudioConverterPrimeInfo, &primeSize, &primeInfo);
check_error(err, "Get Property Converter Prime Info");
AudioFilePacketTableInfo pti;
pti.mPrimingFrames = primeInfo.leadingFrames;
pti.mRemainderFrames = primeInfo.trailingFrames;
pti.mNumberValidFrames = totalOutputFrames - pti.mPrimingFrames -
pti.mRemainderFrames;
err = AudioFileSetProperty(outputFile,
kAudioFilePropertyPacketTableInfo, sizeof(pti), &pti);
check_error(err, "Set Property File Packet Table Info");
WriteCookie (converter, outputFile);
// Finish up
AudioConverterDispose(converter);
AudioFileClose(outputFile);
[pool drain];
return 0;
}
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden