1

How can I get the only the PCM data from AVAudioRecorder file? these are the settings I use to record the file:

        let settings : [String : Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM),
            AVSampleRateKey: Int(stethoscopeSampleRateDefault),
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.medium.rawValue,
        ]

the outcome of this is strange wav file with strange header. How can I extract only the PCM data out of it?

2 Answers 2

1

The actual sound data in a wav file is in the "data" subchunk of that file - this format description might help you visualize the structure you'll have to navigate. But maybe what's tripping you up is that Apple includes an extra subchunk called "fllr" which precedes the sound data, so you have to seek past that too. Fortunately every subchunk is given an id and size, so finding the data subchunk is still relatively straightforward.

  1. Open the file using FileHandle
  2. Seek to byte 12, which gets you past the header and puts you at the beginning of the first subchunk (should be fmt).
  3. Read 4 bytes and convert to a string, then read 4 more bytes and convert to an integer. The string is the subchunk name, and the integer is the size of that subchunk. If the string is not "data" then seek forward "size" number of bytes and repeat step 3.
  4. Read the rest of the file - this is your PCM data.
Sign up to request clarification or add additional context in comments.

Comments

0

With Jamie's guidance I managed to solve this. Here is my code:

    func extractSubchunks(data:Data) -> RiffFile?{
        var data = data
        var chunks = [SubChunk]()
        let position = data.subdata(in: 8..<12)
        let filelength = Int(data.subdata(in: 4..<8).uint32)
        let wave = String(bytes: position, encoding: .utf8) ?? "NoName"
        guard wave == "WAVE" else {
            print("File is \(wave) not WAVE")
            return nil
        }
        data.removeSubrange(0..<12)
        print("Found chunks")
        while data.count != 0{
            let position = data.subdata(in: 0..<4)
            let length = Int(data.subdata(in: 4..<8).uint32)
            guard let current = String(bytes: position, encoding: .utf8) else{
                return nil
            }
            data.removeSubrange(0..<8)
            let chunkData = data.subdata(in: 0..<length)
            data.removeSubrange(0..<length)
            let subchunk = SubChunk(name: current, size: length, data: chunkData)
            chunks.append(subchunk)
            print(subchunk.debugDescription)
        }
        let riff = RiffFile(size: filelength, subChunks: chunks)
        return riff
    }

Here's the definition for RiffFile and SubChunk structs:

struct RiffFile {
    var size : Int
    var subChunks : [SubChunk]
}

struct SubChunk {
    var debugDescription: String {
        return "name : \(name) size : \(size) dataAssignedsize : \(data.count)"
    }
    var name : String
    var size : Int
    var data : Data
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.