简体   繁体   English

在NSMutableData中操作字节的问题 - Wave Header信息

[英]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. 我已将文件加载到NSMutableData中,并尝试更改wav文件头信息。 My problem is that on the line: 我的问题是在线:

[maindata replaceBytesInRange:range withBytes:((UInt32 *)tfSC1Length)];

I am receiving a EXC_BAD_ACCESS error. 我收到了EXC_BAD_ACCESS错误。 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';

// subchunk1 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? 另外,对于16位和32位整数,wav文件格式的字节顺序是多少? Is it the same as the byte order on the iPhone and on the iPhone simulator? 它与iPhone和iPhone模拟器上的字节顺序相同吗? You can use the CFByteOrder utilities to convert to the correct byte order. 您可以使用CFByteOrder实用程序转换为正确的字节顺序。

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. 另外,在声明长度以确保正确复制长度时,我会使用计数的确切类型(UInt32,UInt16)而不是int。 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). 现在你实现的方式是假设UInt32与int(可能是真的)二进制相同,并且int的前2个字节与具有相同值的UInt16相同(不一定是真的)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM