Skip to content

Instantly share code, notes, and snippets.

@wader
Created January 3, 2012 17:19
Show Gist options
  • Save wader/1555889 to your computer and use it in GitHub Desktop.
Save wader/1555889 to your computer and use it in GitHub Desktop.
Wav merge
// a bit hackish way of merging two wav files, assumes raw samples.
#import <Foundation/Foundation.h>
#define RIFF_ID 0x52494646 // "RIFF"
#define RIFF_FMT_ID 0x666d7420 // "fmt "
#define RIFF_DATA_ID 0x64617461 // "data"
typedef struct riffChunkHeader {
UInt32 rsc_id; // big endian
UInt32 rsc_size; // little endian
} riffChunkHeader_t;
NSData *extractRiffChunk(NSData *riffData, UInt32 extractId, BOOL extractOnlyData);
NSData *extractRiffChunkAll(NSData *riffData, UInt32 extractId);
NSData *extractRiffChunkData(NSData *riffData, UInt32 extractId);
NSData *extractRiffChunk(NSData *riffData, UInt32 extractId, BOOL extractOnlyData) {
NSUInteger offset = 0;
while (([riffData length] - offset) >= sizeof(riffChunkHeader_t)) {
NSData *riffChunkRange = [riffData subdataWithRange:
NSMakeRange(offset, sizeof(riffChunkHeader_t))];
const riffChunkHeader_t *riffChunk = [riffChunkRange bytes];
UInt32 riffId = NSSwapBigIntToHost(riffChunk->rsc_id);
NSUInteger riffSize = NSSwapLittleIntToHost(riffChunk->rsc_size);
// treat RIFF chunk as a subchunk containing format
if (riffId == RIFF_ID) {
riffSize = 4;
}
if (riffId != extractId) {
offset += sizeof(riffChunkHeader_t) + riffSize;
continue;
}
NSUInteger len;
if (extractOnlyData) {
offset += sizeof(riffChunkHeader_t);
len = riffSize;
} else {
len = sizeof(riffChunkHeader_t) + riffSize;
}
if (([riffData length] - offset) < riffSize) {
break;
}
return [riffData subdataWithRange:NSMakeRange(offset, len)];
}
return nil;
}
NSData *extractRiffChunkAll(NSData *riffData, UInt32 extractId) {
return extractRiffChunk(riffData, extractId, NO);
}
NSData *extractRiffChunkData(NSData *riffData, UInt32 extractId) {
return extractRiffChunk(riffData, extractId, YES);
}
NSData *mergeWavFiles(NSData *wavData1, NSData *wavData2);
NSData *mergeWavFiles(NSData *wavData1, NSData *wavData2) {
NSMutableData *riffAll = [[extractRiffChunkAll(wavData1, RIFF_ID)
mutableCopy]
autorelease];
NSData *fmtAll = extractRiffChunkAll(wavData1, RIFF_FMT_ID);
NSData *file1Data = extractRiffChunkData(wavData1, RIFF_DATA_ID);
NSData *file2Data = extractRiffChunkData(wavData2, RIFF_DATA_ID);
riffChunkHeader_t riffDataSubChunk;
riffDataSubChunk.rsc_id = NSSwapHostIntToBig(RIFF_DATA_ID);
riffDataSubChunk.rsc_size = NSSwapHostIntToLittle((UInt32)[file1Data length] +
(UInt32)[file2Data length]);
NSData *dataHeader = [NSData dataWithBytes:&riffDataSubChunk
length:sizeof(riffDataSubChunk)];
riffChunkHeader_t *riffChunk = [riffAll mutableBytes];
riffChunk->rsc_size = NSSwapHostIntToLittle(sizeof(riffChunkHeader_t) +
(UInt32)[fmtAll length] +
(UInt32)[dataHeader length] +
(UInt32)[file1Data length] +
(UInt32)[file2Data length]);
NSMutableData *mergedWav = [NSMutableData data];
[mergedWav appendData:riffAll];
[mergedWav appendData:fmtAll];
[mergedWav appendData:dataHeader];
[mergedWav appendData:file1Data];
[mergedWav appendData:file2Data];
return mergedWav;
}
int main (int argc, const char * argv[])
{
@autoreleasepool {
[mergeWavFiles([NSData dataWithContentsOfURL:
[NSURL URLWithString:@"file:///path/to/test1.wav"]],
[NSData dataWithContentsOfURL:
[NSURL URLWithString:@"file:///path/to/test2.wav"]])
writeToURL:[NSURL URLWithString:@"file:///path/to/merged.wav"]
atomically:YES];
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment