【问题标题】:Piping AudioKit Microphone to Google Speech-to-Text将 AudioKit 麦克风连接到 Google Speech-to-Text
【发布时间】:2017-12-01 15:21:54
【问题描述】:

我正在尝试让 AudioKit 将麦克风通过管道传输到 Google 的 Speech-to-Text API,如 here 所示,但我不完全确定如何去做。

要为 Speech-to-Text 引擎准备音频,您需要设置编码并将其作为块传递。在 Google 使用的示例中,他们使用 Apple 的 AVFoundation,但我想使用 AudioKit,以便我可以进行一些预处理,例如切割低振幅等。

我相信正确的做法是使用Tap

首先,我应该通过以下方式匹配格式:

var asbd = AudioStreamBasicDescription()
asbd.mSampleRate = 16000.0
asbd.mFormatID = kAudioFormatLinearPCM
asbd.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked
asbd.mBytesPerPacket = 2
asbd.mFramesPerPacket = 1
asbd.mBytesPerFrame = 2
asbd.mChannelsPerFrame = 1
asbd.mBitsPerChannel = 16

AudioKit.format = AVAudioFormat(streamDescription: &asbd)!

然后创建一个水龙头,例如:

open class TestTap {
    internal let bufferSize: UInt32 = 1_024

    @objc public init(_ input: AKNode?) {
        input?.avAudioNode.installTap(onBus: 0, bufferSize: bufferSize, format: AudioKit.format) { buffer, _ in

         // do work here

        }
    }
}

但我无法确定处理这些数据的正确方法,这些数据通过streamAudioDataAudioKit 方法实时发送到 Google Speech-to-Text API,但也许我要去这是错误的方式吗?

更新:

我已经创建了一个Tap

open class TestTap {

    internal var audioData =  NSMutableData()
    internal let bufferSize: UInt32 = 1_024

    func toData(buffer: AVAudioPCMBuffer) -> NSData {
        let channelCount = 2  // given PCMBuffer channel count is
        let channels = UnsafeBufferPointer(start: buffer.floatChannelData, count: channelCount)
        return NSData(bytes: channels[0], length:Int(buffer.frameCapacity * buffer.format.streamDescription.pointee.mBytesPerFrame))
    }

    @objc public init(_ input: AKNode?) {

        input?.avAudioNode.installTap(onBus: 0, bufferSize: bufferSize, format: AudioKit.format) { buffer, _ in
            self.audioData.append(self.toData(buffer: buffer) as Data)

            // We recommend sending samples in 100ms chunks (from Google)
            let chunkSize: Int /* bytes/chunk */ = Int(0.1 /* seconds/chunk */
                * AudioKit.format.sampleRate /* samples/second */
                * 2 /* bytes/sample */ )

            if self.audioData.length > chunkSize {
                SpeechRecognitionService
                    .sharedInstance
                    .streamAudioData(self.audioData,
                                     completion: { response, error in
                                        if let error = error {
                                            print("ERROR: \(error.localizedDescription)")
                                            SpeechRecognitionService.sharedInstance.stopStreaming()
                                        } else if let response = response {
                                            print(response)
                                        }
                    })
                self.audioData = NSMutableData()
            }

        }
    }
}

viewDidLoad: 中,我正在设置 AudioKit:

AKSettings.sampleRate = 16_000
AKSettings.bufferLength = .shortest

但是,Google 抱怨:

ERROR: Audio data is being streamed too fast. Please stream audio data approximately at real time.

我尝试更改多个参数,例如块大小,但无济于事。

【问题讨论】:

    标签: ios google-cloud-platform google-speech-api audiokit


    【解决方案1】:

    我找到了解决方案here

    我的Tap 的最终代码是:

    open class GoogleSpeechToTextStreamingTap {
    
    internal var converter: AVAudioConverter!
    
    @objc public init(_ input: AKNode?, sampleRate: Double = 16000.0) {
    
        let format = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: sampleRate, channels: 1, interleaved: false)!
    
        self.converter = AVAudioConverter(from: AudioKit.format, to: format)
        self.converter?.sampleRateConverterAlgorithm = AVSampleRateConverterAlgorithm_Normal
        self.converter?.sampleRateConverterQuality = .max
    
        let sampleRateRatio = AKSettings.sampleRate / sampleRate
        let inputBufferSize = 4410 //  100ms of 44.1K = 4410 samples.
    
        input?.avAudioNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount(inputBufferSize), format: nil) { buffer, time in
    
            let capacity = Int(Double(buffer.frameCapacity) / sampleRateRatio)
            let bufferPCM16 = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(capacity))!
    
            var error: NSError? = nil
            self.converter?.convert(to: bufferPCM16, error: &error) { inNumPackets, outStatus in
                outStatus.pointee = AVAudioConverterInputStatus.haveData
                return buffer
            }
    
            let channel = UnsafeBufferPointer(start: bufferPCM16.int16ChannelData!, count: 1)
            let data = Data(bytes: channel[0], count: capacity * 2)
    
            SpeechRecognitionService
                .sharedInstance
                .streamAudioData(data,
                                 completion: { response, error in
                                    if let error = error {
                                        print("ERROR: \(error.localizedDescription)")
                                        SpeechRecognitionService.sharedInstance.stopStreaming()
                                    } else if let response = response {
                                        print(response)
                                    }
                })
        }
    }
    

    【讨论】:

      【解决方案2】:

      您可以使用 AKNodeRecorder 进行录制,并将缓冲区从生成的 AKAudioFile 传递给 API。如果你想要更多的实时性,你可以尝试在你想要记录的 AKNode 的 avAudioNode 属性上安装一个 Tap,并将缓冲区连续传递给 API。

      但是,我很好奇为什么您认为需要进行预处理 - 我确信 Google API 已针对您提到的示例代码生成的记录进行了充分优化。

      我在 iOS Speech API 上获得了很多成功/乐趣。不确定您是否有理由使用 Google API,但我会考虑检查一下,看看它是否可以更好地满足您的需求(如果您还没有的话)。

      希望这会有所帮助!

      【讨论】:

      • 嗨@cgmaier - 感谢您的回复。我想进行预处理,因为尽管使用了附加的心形麦克风,但我正在接听用户旁边的人。语音转文本系统似乎非常敏感,我想切断较低的信号。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多