2010-01-02 28 views
2

我想操縱波文件頭。我已經將文件加載到NSMutableData中,並試圖更改wav文件頭信息。我的問題是,在線上:處理NSMutableData中的字節的問題 - 波頭信息

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

我收到一個EXC_BAD_ACCESS錯誤。不明白爲什麼。有任何想法嗎?

我使用的代碼如下:

// 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]; 

回答

3

你需要使用「&」操作符來獲得整數值的地址,如果你想自己的值複製到數據緩衝區:

所以不是:

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

使用:

[maindata replaceBytesInRange:range withBytes: &tfLength]; 

這會導致你的崩潰,因爲它現在將你的長度視爲指針。

此外,16位和32位整數的wav文件格式所期望的字節順序是什麼?它是否與iPhone和iPhone模擬器上的字節順序相同?您可以使用CFByteOrder utilities轉換爲正確的字節順序。

另外,當聲明長度以保證正確複製長度時,我會使用計數(UInt32,UInt16)的確切類型而不是int。它現在的實現方式是假設UInt32與int的二進制相同(可能爲true),並且int的前2個字節與具有相同值(不一定爲true)的UInt16相同。

+0

感謝您的快速響應..我會嘗試整合CFByteOrder實用程序.. – 2010-01-02 20:22:11

5

我建議使用一個結構來引用的標題:

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

短得多。希望這可以幫助。

+0

感謝 - 比我更清潔的代碼! :) – 2010-01-04 12:41:48

+0

嗨Lucius,你的答案聽起來很有趣,很有幫助。我是iphone開發新手,我正在開發一個項目,我需要將兩個或更多音頻塊組合在一起。我正在提取所有標題和數據。我擁有所有聲音數據,需要根據數據重新寫入標題。但是我沒有在你的代碼中獲得很少的東西。 WaveHeader中的'maindata'是什麼* header = [maindata mutableBytes] ;.我該如何繼續?提前致謝。 – 2010-11-23 11:12:00

+0

'NSMutableData * maindata = [NSMutableData dataWithLength:sizeof(WaveHeader);'所以maindata是一個NSMutableData實例,波頭的長度,填充零。 – lucius 2010-11-23 14:55:58