-
-
Save skovhus/3927298 to your computer and use it in GitHub Desktop.
Adding instrument metadata to a caf file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Adding instrument metadata (node value) to a given caf file. | |
* | |
* Original author: https://gist.github.com/2640513 | |
* Skovhus encountered a bug using gcc 4.2.1. This is the fixed version. | |
* | |
* 2012-10-21 | |
*/ | |
#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[strlen(input_filename) + 6]; // bug fixed | |
//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