万歩計ならぬ、ことばの歩数計アプリ開発案をモジモジと語るのみ。誰か一緒にやってくれないかなー?
import AVFoundation
import Foundation
// MARK: - 発話時間計測クラス (Local-only VAD Engine)
class SpeechActivityTracker: NSObject, ObservableObject {
private let audioEngine = AVAudioEngine()
@Published var totalSpeechSeconds: Double = 0.0
@Published var isSpeaking: Bool = false
// 音量しきい値 (環境に合わせて調整)
private let speechThreshold: Float = -30.0
func startMonitoring() {
let inputNode = audioEngine.inputNode
let recordingFormat = inputNode.outputFormat(forBus: 0)
// 端末ローカルでマイク入力をキャプチャ (録音データは保存・送信せず即時破棄)
inputNode.installTap(onBus: 0, bufferSize: 1024, format: recordingFormat) { [weak self] buffer, _ in
guard let self = self else { return }
// 音量(RMS)の計算
guard let channelData = buffer.floatChannelData?[0] else { return }
let channelDataValue = Array(UnsafeBufferPointer(start: channelData, count: Int(buffer.frameLength)))
var sum: Float = 0.0
for sample in channelDataValue {
sum += sample * sample
}
let rms = sqrt(sum / Float(buffer.frameLength))
let avgPower = 20 * log10(rms) // dB単位
// 判定処理
DispatchQueue.main.async {
if avgPower > self.speechThreshold {
self.isSpeaking = true
// バッファの長さ分(約0.02秒)を発話時間に加算
let duration = Double(buffer.frameLength) / recordingFormat.sampleRate
self.totalSpeechSeconds += duration
} else {
self.isSpeaking = false
}
}
}
do {
try audioEngine.start()
} catch {
print("AudioEngine起動エラー: \(error.localizedDescription)")
}
}
func stopMonitoring() {
audioEngine.inputNode.removeTap(onBus: 0)
audioEngine.stop()
}
}