Problems with manipulating bytes in a NSMutableData - Wave Header info
I am trying to manipulate a wave file header. I have loaded the file into a NSMutableData and am trying to change the wav file header info. My problem is that on the line:
[maindata replaceBytesInRange:range withBytes:((UInt32 *)tfSC1Length)];
I am receiving a EXC_BAD_ACCESS error. Can't understand why. Any ideas?
The code I am using is as follows:
// calculate total file length (minus 8 for the header), chunk size, number of channels, sample rate and bits per sample
int tfLength = ([maindata length] - 8);
int tfChannels = 1; // mono
int tfSampleRate = 44100;
int tfDataLength = ([maindata length] - 44);
int tfBitsPerSample = 16;
int tfSC1Length = 16;
int tfAudioFormat = 1;
//we are rebuilding the header for the wave files...
// chunk identifier
((char *)[maindata mutableBytes])[0] = 'R';
((char *)[maindata mutableBytes])[1] = 'I';
((char *)[maindata mutableBytes])[2] = 'F';
((char *)[maindata mutableBytes])[3] = 'F';
// size (less 8 bytes)
NSRange range;
range.location = 4;
range.length = 4;
[maindata replaceBytesInRange:range withBytes:(UInt32 *)tfLength];
// the file format
((char *)[maindata mutableBytes])[8] = 'W';
((char *)[maindata mutableBytes])[9] = 'A';
((char *)[maindata mutableBytes])[10] = 'V';
((char *)[maindata mutableBytes])[11] = 'E';
// subchunk开发者_如何学编程1 ID
((char *)[maindata mutableBytes])[12] = 'f';
((char *)[maindata mutableBytes])[13] = 'm';
((char *)[maindata mutableBytes])[14] = 't';
((char *)[maindata mutableBytes])[15] = ' ';
// subchunk length
range.location = 16;
range.length = 4;
[maindata replaceBytesInRange:range withBytes:((UInt32 *)tfSC1Length)];
// audio format
range.location = 20;
range.length = 2;
[maindata replaceBytesInRange:range withBytes:((UInt16 *)tfAudioFormat)];
// number of channels
range.location = 22;
range.length = 2;
[maindata replaceBytesInRange:range withBytes:((UInt16 *)tfChannels)];
// sample rate
range.location = 24;
range.length = 4;
[maindata replaceBytesInRange:range withBytes:((UInt32 *)tfSampleRate)];
// byte rate
range.location = 28;
range.length = 4;
[maindata replaceBytesInRange:range withBytes:((UInt32 *)(tfSampleRate * ((tfBitsPerSample * tfChannels) / 8)))];
// block align
range.location = 32;
range.length = 2;
[maindata replaceBytesInRange:range withBytes:((UInt16 *)((tfBitsPerSample * tfChannels) / 8))];
// bits per sample
range.location = 34;
range.length = 2;
[maindata replaceBytesInRange:range withBytes:((UInt16 *)tfBitsPerSample)];
// adjust the length field of the wave file...
((char *)[maindata mutableBytes])[36] = 'd';
((char *)[maindata mutableBytes])[37] = 'a';
((char *)[maindata mutableBytes])[38] = 't';
((char *)[maindata mutableBytes])[39] = 'a';
// length of the audio data
range.location = 40;
range.length = 4;
[maindata replaceBytesInRange:range withBytes:(UInt32 *)tfDataLength];
I'd suggest using a struct to reference the header:
typedef struct {
UInt32 riffChunkID; // Always RIFF, in big endian. Integer fields are little-ending.
UInt32 fileLength;
UInt32 waveFileID; // 'WAVE' for Wave files.
UInt32 formatChunkID; // 'fmt '
UInt32 formatChunkSize;
SInt16 formatTag; // Wave Format ID: see constants
SInt16 channels; // Number of Channels: 1=mono, 2=stereo
SInt32 sampleRate; // Sample Rate: samples per second
SInt32 bytesPerSec; // sampleRate * blockAlign
SInt16 blockAlign; // sample frame size = channels * sampleSize / 8
SInt16 bitsPerSample; // sampleSize (8 or 16), also two's-complement for 16-bit, offset for 8-bit
UInt32 dataChunkID; // 'data'
UInt32 dataChunkSize;
} WaveHeader;
int tfChannels = 1; // mono
int tfSampleRate = 44100;
int tfBitsPerSample = 16;
WaveHeader *header = [maindata mutableBytes];
header->riffChunkID = CFSwapInt32HostToBig ('RIFF');
header->fileLength = CFSwapInt32HostToLittle ([maindata length] - 8);
header->waveFileID = CFSwapInt32HostToBig ('WAVE');
header->formatChunkID = CFSwapInt32HostToBig ('fmt ');
header->formatChunkSize = CFSwapInt32HostToLittle (16);
header->formatTag = CFSwapInt16HostToLittle (1);
header->channels = CFSwapInt16HostToLittle (tfChannels);
header->sampleRate = CFSwapInt32HostToLittle (tfSampleRate);
header->bytesPerSec = CFSwapInt32HostToLittle (tfSampleRate * tfBitsPerSample / 8 * tfChannels);
header->blockAlign = CFSwapInt16HostToLittle (tfBitsPerSample / 8 * tfChannels);
header->bitsPerSample = CFSwapInt16HostToLittle (tfBitsPerSample);
header->dataChunkID = CFSwapInt32HostToBig ('data');
header->dataChunkSize = CFSwapInt32HostToLittle ([maindata length] - 44);
Much shorter. Hope this helps.
You need to use the "&" operator to get the address of the integer values if you want to copy their values to the data buffer:
So instead of:
[maindata replaceBytesInRange:range withBytes:(UInt32 *)tfLength];
use:
[maindata replaceBytesInRange:range withBytes: &tfLength];
This is causing your crash since it's treating your lengths as pointers right now.
Also, what is the byte order expected by the wav file format for 16 bit and 32 bit integers? Is it the same as the byte order on the iPhone and on the iPhone simulator? You can use the CFByteOrder utilities to convert to the correct byte order.
Also, I'd use the exact types for the counts (UInt32, UInt16) instead of int when declaring the lengths to guarantee that you are copying the lengths correctly. The way it's implemented now you are assuming that UInt32 is binary identical to int (probably true) and the first 2 bytes of an int are the same as a UInt16 with the same value (not necessarily true).
精彩评论