Skip to content

Instantly share code, notes, and snippets.

@brennanMKE
Last active November 2, 2023 09:25
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brennanMKE/dfe246bb06a4973ae40380bb915676bb to your computer and use it in GitHub Desktop.
Save brennanMKE/dfe246bb06a4973ae40380bb915676bb to your computer and use it in GitHub Desktop.
Process a CMSampleBuffer to modify the audio using an AVAssetReader and AVAssetWriter in Swift

Processing Audio with AVAssetReader and AVAssetWriter

Modifying an audio stream can be done with an AVAssetReader and AVAssetWriter. While reading a CMSampleBuffer can be used to get CMBlockBuffer which is a pointer to a buffer within CMSampleBuffer. Those bytes can be copied into a mutable buffer with CMBlockBufferCopyDataBytes to modify the values and then use CMBlockBufferReplaceDataBytes to put the modified values back into the CMSampleBuffer which also modifies CMSampleBuffer before appending it to the AVAssetWriterInput instance used by AVAssetWriter.

func processSampleBuffer(scale: Float, sampleBuffer: CMSampleBuffer, writerInput: AVAssetWriterInput) -> Bool {
guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
return false
}
let length = CMBlockBufferGetDataLength(blockBuffer)
var sampleBytes = UnsafeMutablePointer<Int16>.allocate(capacity: length)
defer { sampleBytes.deallocate(capacity: length) }
guard checkStatus(CMBlockBufferCopyDataBytes(blockBuffer, 0, length, sampleBytes), message: "Copying block buffer") else {
return false
}
(0..<length).forEach { index in
let ptr = sampleBytes + index
let scaledValue = Float(ptr.pointee) * scale
let processedValue = Int16(max(min(scaledValue, Float(Int16.max)), Float(Int16.min)))
ptr.pointee = processedValue
}
guard checkStatus(CMBlockBufferReplaceDataBytes(sampleBytes, blockBuffer, 0, length), message: "Replacing data bytes in block buffer") else { return false }
assert(CMSampleBufferIsValid(sampleBuffer))
return writerInput.append(sampleBuffer)
}
func checkStatus(_ status: OSStatus, message: String) -> Bool {
// See: https://www.osstatus.com/
assert(kCMBlockBufferNoErr == noErr)
if status != noErr {
debugPrint("Error: \(message) [\(status)]")
}
return status == noErr
}
@krossford
Copy link

this code helped me, but it spent about 5-7ms

@daobao777
Copy link

what is the value of 'scale', I can't find it

@krossford
Copy link

what is the value of 'scale', I can't find it

@daobao777 scale could be specified by yourself. it could be 0.0-1.0

@daobao777
Copy link

what is the value of 'scale', I can't find it

@daobao777 scale could be specified by yourself. it could be 0.0-1.0

line 15: let scaledValue = Float(ptr.pointee) * scale

@daobao777
Copy link

what is the value of 'scale', I can't find it

@daobao777 scale could be specified by yourself. it could be 0.0-1.0

I would get the wrong answer when scale is 1.0
So, how can i get accurate data

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment