Skip to content

Instantly share code, notes, and snippets.

@zonble
Last active November 3, 2017 03:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zonble/f7b2c52f724de196f0cc86fdbfd90378 to your computer and use it in GitHub Desktop.
Save zonble/f7b2c52f724de196f0cc86fdbfd90378 to your computer and use it in GitHub Desktop.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioEngine = AVAudioEngine()
var audioData = Data()
override func viewDidLoad() {
super.viewDidLoad()
func toData(PCMBuffer: AVAudioPCMBuffer) -> Data {
let channelCount = 1 // given PCMBuffer channel count is 1
let channels = UnsafeBufferPointer(start: PCMBuffer.int16ChannelData, count: channelCount)
let ch0Data = NSData(bytes: channels[0], length:Int(PCMBuffer.frameCapacity * PCMBuffer.format.streamDescription.pointee.mBytesPerFrame)) as Data
return ch0Data
}
guard let outputFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: 16_000, channels: AVAudioChannelCount(1), interleaved: false) else {
return
}
let inputFormat = self.audioEngine.inputNode.outputFormat(forBus: 0)
let converter = AVAudioConverter(from: inputFormat, to: outputFormat)
self.audioEngine.inputNode.installTap(onBus: AVAudioNodeBus(0), bufferSize: AVAudioFrameCount(32000), format: self.audioEngine.inputNode.outputFormat(forBus: 0)) { (pcmBuffer, time) in
guard let outputBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity:pcmBuffer.frameCapacity) else {
return
}
outputBuffer.frameLength = pcmBuffer.frameLength
let inputBlock: AVAudioConverterInputBlock = { inNumPackets, outStatus in
outStatus.pointee = AVAudioConverterInputStatus.haveData
return pcmBuffer
}
var error:NSError? = nil
converter?.convert(to: outputBuffer, error: &error, withInputFrom: inputBlock)
self.audioData.append(toData(PCMBuffer: outputBuffer))
}
self.audioEngine.prepare()
do {
try self.audioEngine.start()
} catch {
print("\(error)")
}
}
}
@pofat
Copy link

pofat commented Nov 3, 2017

因為 resample 後 buffer 有意義的長度也會改變,converter 好像會把 frameCapacity 的長度塞滿(不確定),outputBuffer 若給一樣長,在down-sampling 時就會出現重複的音,比如說「今天」,聽到結果會變成「今天今天今天」(48_000 -> 16_000 時),所以下面這段 buffer declaration

guard let outputBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity:pcmBuffer.frameCapacity) else {
    return
}

可以改成如下以避免此問題

let sampleRateRatio = 16_000/inputFormat.sampleRate

// ...

let outputFrameCapacity = AVAudioFrameCount(Double(pcmBuffer.frameCapacity) * sampleRateRatio)
guard let outputBuffer = AVAudioPCMBuffer(pcmFormat: outputFormat, frameCapacity: outputFrameCapacity) else {
    return
}

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