目标c中的音频文件格式问题

Posted

技术标签:

【中文标题】目标c中的音频文件格式问题【英文标题】:Audio file format issue in objective c 【发布时间】:2015-08-31 13:31:32 【问题描述】:

我使用 AVAudioRecorder 编写了一个音频 WAV(已录制我的声音)文件。最终的文件格式是 WAV 文件。文件已成功保存,我可以听到我的声音。我想将此文件发送到后端服务器(网络服务)。但我的服务器只接受 WAV 中的数据和 FMT 信息。它不接受我的 wav 文件,因为我的 wav 文件信息包含 FLLR、数据、FMT。我已经在 Riffpad 工具中检查了我的 WAV 文件信息。它显示 FLLR、数据、FMT。但我只想要数据和 fmt。因为我的服务器端只接受数据和 FMT。那么请建议如何以编程方式删除我的 wav 文件中的 FLLR?

我的源代码记录:

  NSError *error;

    // Recording settings
    NSMutableDictionary *settings = [NSMutableDictionary dictionary];
    [settings setValue: [NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
    [settings setValue: [NSNumber numberWithFloat:22050] forKey:AVSampleRateKey];
    [settings setValue: [NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey]; // mono
    [settings setValue: [NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    [settings setValue: [NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
    //[settings setValue: [NSNumber numberWithInt:16] forKey:AudiosampleType];




    // File URL
    NSURL *url = [NSURL fileURLWithPath:FILEPATH];

    //NSLog(@"Url file path ::: %@",url);

    // Create recorder
    recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
    if (!recorder)
    
//        NSLog(@"Error establishing recorder: %@", error.localizedFailureReason);
        return;
    

【问题讨论】:

你能否跳过 FLLR,如下所示:***.com/questions/6284651/… 感谢您的留言。你能解释一下WS的意思吗? 网络服务。正如链接答案中所建议的那样,您的服务器会更好更简单,只需跳过 FLLR 前面没有办法(目标c)结束? 根据链接的答案,这是Apple实现添加的一些块数据。因此,除非您确切地知道它的作用(也许网络上的一些研究可以告诉您究竟是什么),否则这可能会非常困难。除了 AVAudioRecorder 之外,可能还有另一个元素(在 GitHub 中)可以做到这一点。 【参考方案1】:

感谢上帝和您支持的朋友。是的,最后我解决了我的问题。我不知道方法是否正确。但是我的问题解决了。我使用上面的代码录制了语音并保存了我的音频。然后我将再次使用以下代码导出音频。我从https://developer.ibm.com/answers/questions/180732/seems-watson-text-to-speech-service-returns-a-wav.html得到这个代码

-(NSData*) stripAndAddWavHeader:(NSData*) wav 
    unsigned long wavDataSize = [wav length] - 44;

    NSData *WaveFile= [NSMutableData dataWithData:[wav subdataWithRange:NSMakeRange(44, wavDataSize)]];

    NSMutableData *newWavData;
    newWavData = [self addWavHeader:WaveFile];

    return newWavData;
   


 - (NSMutableData *)addWavHeader:(NSData *)wavNoheader 

       int headerSize = 44;
       long totalAudioLen = [wavNoheader length];
       long totalDataLen = [wavNoheader length] + headerSize-8;
       long longSampleRate = 22050.0;
       int channels = 1;
       long byteRate = 8 * 44100.0 * channels/8;



     Byte *header = (Byte*)malloc(44);
     header[0] = 'R';  // RIFF/WAVE header
     header[1] = 'I';
     header[2] = 'F';
     header[3] = 'F';
     header[4] = (Byte) (totalDataLen & 0xff);
     header[5] = (Byte) ((totalDataLen >> 8) & 0xff);
     header[6] = (Byte) ((totalDataLen >> 16) & 0xff);
     header[7] = (Byte) ((totalDataLen >> 24) & 0xff);
     header[8] = 'W';
     header[9] = 'A';
     header[10] = 'V';
     header[11] = 'E';
     header[12] = 'f';  // 'fmt ' chunk
     header[13] = 'm';
     header[14] = 't';
     header[15] = ' ';
     header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
     header[17] = 0;
     header[18] = 0;
     header[19] = 0;
     header[20] = 1;  // format = 1
     header[21] = 0;
     header[22] = (Byte) channels;
     header[23] = 0;
     header[24] = (Byte) (longSampleRate & 0xff);
     header[25] = (Byte) ((longSampleRate >> 8) & 0xff);
     header[26] = (Byte) ((longSampleRate >> 16) & 0xff);
     header[27] = (Byte) ((longSampleRate >> 24) & 0xff);
     header[28] = (Byte) (byteRate & 0xff);
     header[29] = (Byte) ((byteRate >> 8) & 0xff);
     header[30] = (Byte) ((byteRate >> 16) & 0xff);
     header[31] = (Byte) ((byteRate >> 24) & 0xff);
     header[32] = (Byte) (2 * 8 / 8);  // block align
     header[33] = 0;
     header[34] = 16;  // bits per sample
     header[35] = 0;
     header[36] = 'd';
     header[37] = 'a';
     header[38] = 't';
     header[39] = 'a';
     header[40] = (Byte) (totalAudioLen & 0xff);
     header[41] = (Byte) ((totalAudioLen >> 8) & 0xff);
     header[42] = (Byte) ((totalAudioLen >> 16) & 0xff);
     header[43] = (Byte) ((totalAudioLen >> 24) & 0xff);

     NSMutableData *newWavData = [NSMutableData dataWithBytes:header length:44];
     [newWavData appendBytes:[wavNoheader bytes] length:[wavNoheader length]];
     return newWavData;
 

【讨论】:

音频播放不正确。音频质量下降。【参考方案2】:

寻找数据边缘的 Swift 解决方案


class MediaUtil 
    
    private static func dataToUTF8String(data: NSData, offset: Int, length: Int) -> String? 
        let range = NSMakeRange(offset, length)
        let subdata = data.subdata(with: range)
        return String(data: subdata, encoding: String.Encoding.utf8)
    
    
    private static func dataToUInt32(data: NSData, offset: Int) -> Int 
        var num: UInt32 = 0
        let length = 4
        let range = NSMakeRange(offset, length)
        data.getBytes(&num, range: range)
        return Int(num)
    
    
    static func repairWAVHeader(data: NSMutableData)->Data 
        
        // resources for WAV header format:
        // [1] http://unusedino.de/ec64/technical/formats/wav.html
        // [2] http://soundfile.sapp.org/doc/WaveFormat/
        
        var newData = Data()
        
        // update RIFF chunk size
        let fileLength = data.length
        var riffChunkSize = UInt32(fileLength - 8)
        let riffChunkSizeRange = NSMakeRange(4, 4)
        data.replaceBytes(in: riffChunkSizeRange, withBytes: &riffChunkSize)
        
        // find data subchunk
        var subchunkID: String?
        var subchunkSize = 0
        var fieldOffset = 12
        let fieldSize = 4
        while true 
            // prevent running off the end of the byte buffer
            if fieldOffset + 2*fieldSize >= data.length 
                return newData
            
            
            // read subchunk ID
            subchunkID = dataToUTF8String(data: data, offset: fieldOffset, length: fieldSize)
            fieldOffset += fieldSize
            if subchunkID == "data" 
                break
            
            
            // read subchunk size
            subchunkSize = dataToUInt32(data: data, offset: fieldOffset)
            fieldOffset += fieldSize + subchunkSize
        
        
        let rllrRange = NSMakeRange(0, fieldOffset)

        data.replaceBytes(in: rllrRange, withBytes: nil, length: 0)
        newData = newWavHeader(pcmDataLength: data.length)
        newData.append(data as Data)
        return newData
    
    
    private static func newWavHeader(pcmDataLength: Int) -> Data 
        var header = Data()
        let headerSize = 44
        let bitsPerSample = Int32(16)
        let numChannels: Int32 = 1
        let sampleRate: Int32 = 16000
        
        // RIFF chunk descriptor
        let chunkID = [UInt8]("RIFF".utf8)
        header.append(chunkID, count: 4)
        
        var chunkSize = Int32(pcmDataLength + headerSize - 4).littleEndian
        let chunkSizePointer = UnsafeBufferPointer(start: &chunkSize, count: 1)
        header.append(chunkSizePointer)
        
        let format = [UInt8]("WAVE".utf8)
        header.append(format, count: 4)
        
        // "fmt" sub-chunk
        let subchunk1ID = [UInt8]("fmt ".utf8)
        header.append(subchunk1ID, count: 4)
        
        var subchunk1Size = Int32(16).littleEndian
        let subchunk1SizePointer = UnsafeBufferPointer(start: &subchunk1Size, count: 1)
        header.append(subchunk1SizePointer)
        
        var audioFormat = Int16(1).littleEndian
        let audioFormatPointer = UnsafeBufferPointer(start: &audioFormat, count: 1)
        header.append(audioFormatPointer)
        
        var headerNumChannels = Int16(numChannels).littleEndian
        let headerNumChannelsPointer = UnsafeBufferPointer(start: &headerNumChannels, count: 1)
        header.append(headerNumChannelsPointer)
        
        var headerSampleRate = Int32(sampleRate).littleEndian
        let headerSampleRatePointer = UnsafeBufferPointer(start: &headerSampleRate, count: 1)
        header.append(headerSampleRatePointer)
        
        var byteRate = Int32(sampleRate * numChannels * bitsPerSample / 8).littleEndian
        let byteRatePointer = UnsafeBufferPointer(start: &byteRate, count: 1)
        header.append(byteRatePointer)
        
        var blockAlign = Int16(numChannels * bitsPerSample / 8).littleEndian
        let blockAlignPointer = UnsafeBufferPointer(start: &blockAlign, count: 1)
        header.append(blockAlignPointer)
        
        var headerBitsPerSample = Int16(bitsPerSample).littleEndian
        let headerBitsPerSamplePointer = UnsafeBufferPointer(start: &headerBitsPerSample, count: 1)
        header.append(headerBitsPerSamplePointer)
        
        // "data" sub-chunk
        let subchunk2ID = [UInt8]("data".utf8)
        header.append(subchunk2ID, count: 4)
        
        var subchunk2Size = Int32(pcmDataLength).littleEndian
        let subchunk2SizePointer = UnsafeBufferPointer(start: &subchunk2Size, count: 1)
        
        header.append(subchunk2SizePointer)
        
        return header
    

【讨论】:

以上是关于目标c中的音频文件格式问题的主要内容,如果未能解决你的问题,请参考以下文章

怎样播放DFF格式的音频文件

iOS 中的avaudioplayer支持哪些音频格式

怎么把mp3转成aac格式?

如何在android中录制音频m4a格式的音频文件?

如何在Objective c中获取音频文件的某些部分

将音频样本转换为可听文件格式