How to add playable(such as wav,wmv) header with PCM data/buffer in iOS?

后端 未结 2 1163
清歌不尽
清歌不尽 2020-12-03 09:09

I am trying to add a wav header on top of raw PCM data to make it playable via AVAudioPlayer. But i couldn\'t find any solution or source code to do that on iOS using Object

相关标签:
2条回答
  • 2020-12-03 10:03

    OK, I am answering my own question if it helps someone else. After few days of tireless trying, at last i have got it working. Below is a complete Function written with Objective-C and C. It takes a file path as a parameter which contains RAW PCM data directly captured from microphone and returns a file path which contains PCM data followed by appropriate wav header info. Then you can play that file with AVAudioPlayer or AVPlayer. Here is the code...

    - (NSURL *) getAndCreatePlayableFileFromPcmData:(NSString *)filePath
    {
        NSString *wavFileName = [[filePath lastPathComponent] stringByDeletingPathExtension];
        NSString *wavFileFullName = [NSString stringWithFormat:@"%@.wav",wavFileName];
    
        [self createFileWithName:wavFileFullName];
        NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *docsDir = [dirPaths objectAtIndex:0];
        NSString *wavFilePath = [docsDir stringByAppendingPathComponent:wavFileFullName];
    
        NSLog(@"PCM file path : %@",filePath);
    
        FILE *fout;
    
        short NumChannels = AUDIO_CHANNELS_PER_FRAME;
        short BitsPerSample = AUDIO_BITS_PER_CHANNEL;
        int SamplingRate = AUDIO_SAMPLE_RATE;
        int numOfSamples = [[NSData dataWithContentsOfFile:filePath] length];
    
        int ByteRate = NumChannels*BitsPerSample*SamplingRate/8;
        short BlockAlign = NumChannels*BitsPerSample/8;
        int DataSize = NumChannels*numOfSamples*BitsPerSample/8;
        int chunkSize = 16;
        int totalSize = 46 + DataSize;
        short audioFormat = 1;
    
        if((fout = fopen([wavFilePath cStringUsingEncoding:1], "w")) == NULL)
        {
            printf("Error opening out file ");
        }
    
        fwrite("RIFF", sizeof(char), 4,fout);
        fwrite(&totalSize, sizeof(int), 1, fout);
        fwrite("WAVE", sizeof(char), 4, fout);
        fwrite("fmt ", sizeof(char), 4, fout);
        fwrite(&chunkSize, sizeof(int),1,fout);
        fwrite(&audioFormat, sizeof(short), 1, fout);
        fwrite(&NumChannels, sizeof(short),1,fout);
        fwrite(&SamplingRate, sizeof(int), 1, fout);
        fwrite(&ByteRate, sizeof(int), 1, fout);
        fwrite(&BlockAlign, sizeof(short), 1, fout);
        fwrite(&BitsPerSample, sizeof(short), 1, fout);
        fwrite("data", sizeof(char), 4, fout);
        fwrite(&DataSize, sizeof(int), 1, fout);
    
        fclose(fout);
    
        NSMutableData *pamdata = [NSMutableData dataWithContentsOfFile:filePath];
        NSFileHandle *handle;
        handle = [NSFileHandle fileHandleForUpdatingAtPath:wavFilePath];
        [handle seekToEndOfFile];
        [handle writeData:pamdata];
        [handle closeFile];
    
        return [NSURL URLWithString:wavFilePath];
    }
    

    But that function only works with the following audio settings.

    // Audio settings.
    #define AUDIO_SAMPLE_RATE 8000
    #define AUDIO_FRAMES_PER_PACKET 1
    #define AUDIO_CHANNELS_PER_FRAME 1
    #define AUDIO_BITS_PER_CHANNEL 16
    #define AUDIO_BYTES_PER_PACKET 2
    #define AUDIO_BYTES_PER_FRAME 2
    
    0 讨论(0)
  • 2020-12-03 10:09

    Very helpful question and answer, thank you very much.

    This swift version is for those in need:

    static func createWAV(from pcmFilePath: String, to wavFilePath: String) -> Bool {
    
        // Make sure that the path does not contain non-ascii characters
        guard let fout = fopen(wavFilePath.cString(using: .ascii), "w") else { return false }
    
        guard let pcmData = try? Data(contentsOf: URL(fileURLWithPath: pcmFilePath)) else { return false }
    
        var numChannels: CShort = 1
        let numChannelsInt: CInt = 1
        var bitsPerSample: CShort = 16
        let bitsPerSampleInt: CInt = 16
        var samplingRate: CInt = 16000
        let numOfSamples = CInt(pcmData.count)
        var byteRate = numChannelsInt * bitsPerSampleInt * samplingRate / 8
        var blockAlign = numChannelsInt * bitsPerSampleInt / 8
        var dataSize = numChannelsInt * numOfSamples * bitsPerSampleInt / 8
        var chunkSize: CInt = 16
        var totalSize = 46 + dataSize
        var audioFormat: CShort = 1
    
        fwrite("RIFF".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout)
        fwrite(&totalSize, MemoryLayout<CInt>.size, 1, fout)
        fwrite("WAVE".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
        fwrite("fmt ".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
        fwrite(&chunkSize, MemoryLayout<CInt>.size,1,fout);
        fwrite(&audioFormat, MemoryLayout<CShort>.size, 1, fout);
        fwrite(&numChannels, MemoryLayout<CShort>.size,1,fout);
        fwrite(&samplingRate, MemoryLayout<CInt>.size, 1, fout);
        fwrite(&byteRate, MemoryLayout<CInt>.size, 1, fout);
        fwrite(&blockAlign, MemoryLayout<CShort>.size, 1, fout);
        fwrite(&bitsPerSample, MemoryLayout<CShort>.size, 1, fout);
        fwrite("data".cString(using: .ascii), MemoryLayout<CChar>.size, 4, fout);
        fwrite(&dataSize, MemoryLayout<CInt>.size, 1, fout);
        fclose(fout);
    
        guard let handle = FileHandle(forUpdatingAtPath: wavFilePath) else { return false }
    
        handle.seekToEndOfFile()
        handle.write(pcmData)
        handle.closeFile()
    
        return true
    }
    
    0 讨论(0)
提交回复
热议问题