Skip to content

Instantly share code, notes, and snippets.

@merf
Created May 8, 2012 23:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save merf/2640513 to your computer and use it in GitHub Desktop.
Save merf/2640513 to your computer and use it in GitHub Desktop.
Adding instrument metadata to a caf file
#include <stdio.h>
#include <AudioToolbox/AudioToolbox.h>
#include <Foundation/NSData.h>
#include <Foundation/NSAutoreleasePool.h>
// generic error handler - if result is nonzero, prints error message and exits program.
static void CheckResult(OSStatus result, const char *operation)
{
if (result == noErr) return;
char errorString[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);
if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) {
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
} else
// no, format it as an integer
sprintf(errorString, "%d", (int)result);
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
exit(1);
}
int main(int argc, const char *argv[])
{
UInt8 note = 0;
const char* input_filename;
if(argc < 3)
{
printf("Not enough arguments provided - need an input file and a note for the instrument\n");
return 0;
}
else
{
input_filename = argv[1];
note = atoi(argv[2]);
}
CFStringRef inputFileLocation = CFStringCreateWithCString(kCFAllocatorDefault, input_filename, kCFStringEncodingASCII);
CFURLRef inputFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, inputFileLocation, kCFURLPOSIXPathStyle, false);
// create output file
char* output_filename = new char[strlen(input_filename) + 6];
//copy minus extension
strncpy(output_filename, input_filename, strlen(input_filename) - 4);
sprintf(&output_filename[strlen(input_filename) - 4], "_%d.caf", note);
CFStringRef outputFileLocation = CFStringCreateWithCString(kCFAllocatorDefault, output_filename, kCFStringEncodingASCII);
CFURLRef outputFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, outputFileLocation, kCFURLPOSIXPathStyle, false);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSData *data = [NSData dataWithContentsOfURL:(NSURL*)inputFileURL];
UInt32 curr_byte_index = 0;
UInt8* p_bytes = (UInt8*)[data bytes];
UInt32* p_word = (UInt32*)(&p_bytes[curr_byte_index]);
UInt32 fourcc = CFSwapInt32BigToHost(*p_word);
if(fourcc == 'caff')
{
printf("Searching caff file for free chunk...\n");
//step over fourcc and version number
curr_byte_index += 8;
//walk through the chunks in the file looking for the 'free' chunk.
while(1)
{
fourcc = CFSwapInt32BigToHost(*((UInt32*)(&p_bytes[curr_byte_index])));
UInt64 num_bytes = CFSwapInt64BigToHost(*((UInt64*)(&p_bytes[curr_byte_index + 4])));
printf("Found \'%c%c%c%c\' chunk - %qi bytes\n", p_bytes[curr_byte_index], p_bytes[curr_byte_index+1], p_bytes[curr_byte_index+2], p_bytes[curr_byte_index+3], num_bytes);
if(fourcc == 'free')
{
UInt64 num_bytes_in_free_chunk = num_bytes;
num_bytes_in_free_chunk -= sizeof(CAFChunkHeader);
num_bytes_in_free_chunk -= sizeof(CAFInstrumentChunk);
//write instrument chunk header
CAFChunkHeader instrument_header;
instrument_header.mChunkType = CFSwapInt32HostToBig('inst');
instrument_header.mChunkSize = CFSwapInt64HostToBig(sizeof(CAFInstrumentChunk));
*(CAFChunkHeader*)(&p_bytes[curr_byte_index]) = instrument_header;
curr_byte_index += sizeof(CAFChunkHeader);
//write instrument chunk
CAFInstrumentChunk instrument_chunk = {0};
instrument_chunk.mMIDILowVelocity = 0;
instrument_chunk.mMIDIHighVelocity = 127;
instrument_chunk.mMIDILowNote = note;
instrument_chunk.mMIDIHighNote = note;
*(CAFInstrumentChunk*)(&p_bytes[curr_byte_index]) = instrument_chunk;
//byte swapping floats is a pain in the arse...
CFSwappedFloat32 base_note = CFConvertFloatHostToSwapped((float)note);
*(CFSwappedFloat32*)(&p_bytes[curr_byte_index]) = base_note;
curr_byte_index += sizeof(CAFInstrumentChunk);
num_bytes = sizeof(CAFInstrumentChunk) + sizeof(CAFChunkHeader);
printf("Inserting %qi bytes of instrument data\n", num_bytes);
//Update the free header
CAFChunkHeader free_header;
free_header.mChunkType = CFSwapInt32HostToBig('free');
free_header.mChunkSize = CFSwapInt64HostToBig(num_bytes_in_free_chunk);
*(CAFChunkHeader*)(&p_bytes[curr_byte_index]) = free_header;
break;
}
else
{
curr_byte_index += num_bytes + sizeof(CAFChunkHeader);
}
}
}
else
{
printf("Not a caff file");
}
[data writeToURL:(NSURL*)outputFileURL atomically:TRUE];
[pool drain];
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment