@@ -206,28 +206,35 @@ public extension Conversation {
206
206
@MainActor func startHandlingVoice( ) throws {
207
207
guard !handlingVoice else { return }
208
208
209
- guard let converter = AVAudioConverter ( from: audioEngine. inputNode. outputFormat ( forBus: 0 ) , to: desiredFormat) else {
209
+ #if os(iOS)
210
+ // 1️⃣ Configure and activate the session first
211
+ let audioSession = AVAudioSession . sharedInstance ( )
212
+ try audioSession. setCategory ( . playAndRecord,
213
+ mode: . voiceChat,
214
+ options: [ . defaultToSpeaker, . allowBluetooth] )
215
+ try audioSession. setPreferredSampleRate ( 48_000 ) // optional but typical
216
+ try audioSession. setActive ( true )
217
+ #endif
218
+
219
+ // 2️⃣ Now the format has a real sample-rate
220
+ let hwFormat = audioEngine. inputNode. outputFormat ( forBus: 0 )
221
+ guard let converter = AVAudioConverter ( from: hwFormat, to: desiredFormat) else {
210
222
throw ConversationError . converterInitializationFailed
211
223
}
212
224
userConverter. set ( converter)
213
225
214
226
audioEngine. attach ( playerNode)
215
- audioEngine. connect ( playerNode, to: audioEngine. mainMixerNode, format: converter. inputFormat)
227
+ // letting the engine pick a format avoids future mismatches
228
+ audioEngine. connect ( playerNode, to: audioEngine. mainMixerNode, format: nil )
216
229
217
- #if os(iOS)
230
+ #if os(iOS)
218
231
try audioEngine. inputNode. setVoiceProcessingEnabled ( true )
219
- #endif
232
+ #endif
220
233
221
234
audioEngine. prepare ( )
222
235
do {
223
236
try audioEngine. start ( )
224
237
225
- #if os(iOS)
226
- let audioSession = AVAudioSession . sharedInstance ( )
227
- try audioSession. setCategory ( . playAndRecord, mode: . voiceChat, options: [ . defaultToSpeaker, . allowBluetooth] )
228
- try audioSession. setActive ( true )
229
- #endif
230
-
231
238
handlingVoice = true
232
239
} catch {
233
240
print ( " Failed to enable audio engine: \( error) " )
0 commit comments