开发者

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).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜