From 26d6908aedfd839b25475834d9171c4144512901 Mon Sep 17 00:00:00 2001 From: REU8ER Date: Sun, 28 Dec 2025 18:13:42 -0300 Subject: [PATCH 1/4] fix(android): release audio focus on cancel/pause and use AUDIOFOCUS_GAIN_TRANSIENT - Add restoreAudioManagerSettings() call in cancel() method to properly release audio focus - Change from AUDIOFOCUS_GAIN to AUDIOFOCUS_GAIN_TRANSIENT for temporary audio focus - This allows other audio apps to automatically resume playback after recording stops Fixes: Audio from external apps (YouTube Music, Spotify, etc.) not resuming after cancel/pause --- record_android/CHANGELOG.md | 4 +++- .../com/llfbandit/record/record/recorder/AudioRecorder.kt | 5 +++-- record_android/pubspec.yaml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/record_android/CHANGELOG.md b/record_android/CHANGELOG.md index 14da5b02..54771f33 100644 --- a/record_android/CHANGELOG.md +++ b/record_android/CHANGELOG.md @@ -1,5 +1,7 @@ -## Upcoming +## 1.4.6 * feat: Add `request` parameter to `hasPermission()` method to check permission status without requesting. +* fix: Release audio focus when calling `cancel()` method. +* fix: Use `AUDIOFOCUS_GAIN_TRANSIENT` instead of `AUDIOFOCUS_GAIN` to allow other audio apps to resume automatically after recording stops. ## 1.4.5 * fix: WAVE header for files larger than 2GB. diff --git a/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt b/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt index 0e85e01e..f2bb721a 100644 --- a/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt +++ b/record_android/android/src/main/kotlin/com/llfbandit/record/record/recorder/AudioRecorder.kt @@ -96,6 +96,7 @@ class AudioRecorder( override fun cancel() { recorderThread?.cancelRecording() + restoreAudioManagerSettings() } override fun pause() { @@ -246,7 +247,7 @@ class AudioRecorder( } if (Build.VERSION.SDK_INT >= 26) { - afRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run { + afRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT).run { setAudioAttributes(AudioAttributes.Builder().run { setUsage(AudioAttributes.USAGE_MEDIA) setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) @@ -259,7 +260,7 @@ class AudioRecorder( audioManager.requestAudioFocus(afRequest!!) } else { audioManager.requestAudioFocus( - afChangeListener, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN + afChangeListener, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT ) } } diff --git a/record_android/pubspec.yaml b/record_android/pubspec.yaml index afc93dde..4c3c798f 100644 --- a/record_android/pubspec.yaml +++ b/record_android/pubspec.yaml @@ -1,6 +1,6 @@ name: record_android description: Android specific implementation for record package called by record_platform_interface. -version: 1.4.5 +version: 1.4.6 homepage: https://github.com/llfbandit/record/tree/master/record_android environment: @@ -13,7 +13,7 @@ dependencies: flutter: sdk: flutter - record_platform_interface: ^1.4.0 + record_platform_interface: ^1.4.1 flutter: plugin: From f553a413902ef93ed931313fdc8b4e2b1c51fb26 Mon Sep 17 00:00:00 2001 From: REU8ER Date: Sun, 28 Dec 2025 18:14:07 -0300 Subject: [PATCH 2/4] fix(ios): deactivate audio session on stop/pause/cancel - Add deactivateAudioSession() helper function - Call deactivateAudioSession() in stop() and pause() methods of both delegates - This properly releases the audio session and notifies other apps to resume Fixes: Audio from external apps (Apple Music, Spotify, etc.) not resuming after stop/pause/cancel --- record_ios/CHANGELOG.md | 3 +- .../delegate/RecorderFileDelegate.swift | 44 ++++---- .../delegate/RecorderStreamDelegate.swift | 100 ++++++++++-------- .../extension/RecorderSessionExtension.swift | 79 +++++++++----- record_ios/pubspec.yaml | 4 +- 5 files changed, 138 insertions(+), 92 deletions(-) diff --git a/record_ios/CHANGELOG.md b/record_ios/CHANGELOG.md index 266218f0..6324bb55 100644 --- a/record_ios/CHANGELOG.md +++ b/record_ios/CHANGELOG.md @@ -1,5 +1,6 @@ -## Upcoming +## 1.1.6 * feat: Add `request` parameter to `hasPermission()` method to check permission status without requesting. +* fix: Deactivate audio session when calling `stop()`, `pause()`, or `cancel()` to allow other audio apps to resume automatically. ## 1.1.5 * fix: Clamp to supported sample rates for Opus. diff --git a/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderFileDelegate.swift b/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderFileDelegate.swift index 73009036..8d5b9aae 100644 --- a/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderFileDelegate.swift +++ b/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderFileDelegate.swift @@ -3,14 +3,14 @@ import Foundation class RecorderFileDelegate: NSObject, AudioRecordingFileDelegate, AVAudioRecorderDelegate { var config: RecordConfig? - + private var audioRecorder: AVAudioRecorder? private var path: String? - private var onPause: () -> () - private var onStop: () -> () + private var onPause: () -> Void + private var onStop: () -> Void private let manageAudioSession: Bool - - init(manageAudioSession: Bool, onPause: @escaping () -> (), onStop: @escaping () -> ()) { + + init(manageAudioSession: Bool, onPause: @escaping () -> Void, onStop: @escaping () -> Void) { self.manageAudioSession = manageAudioSession self.onPause = onPause self.onStop = onStop @@ -28,68 +28,72 @@ class RecorderFileDelegate: NSObject, AudioRecordingFileDelegate, AVAudioRecorde recorder.delegate = self recorder.isMeteringEnabled = true recorder.prepareToRecord() - + recorder.record() - + audioRecorder = recorder self.path = path self.config = config } - func stop(completionHandler: @escaping (String?) -> ()) { + func stop(completionHandler: @escaping (String?) -> Void) { audioRecorder?.stop() audioRecorder = nil + deactivateAudioSession() + completionHandler(path) onStop() - + path = nil config = nil } - + func pause() { guard let recorder = audioRecorder, recorder.isRecording else { return } - + recorder.pause() + deactivateAudioSession() onPause() } - + func resume() { audioRecorder?.record() } func cancel() throws { guard let path = path else { return } - + stop { path in } - + try deleteFile(path: path) } - + func getAmplitude() -> Float { audioRecorder?.updateMeters() return audioRecorder?.averagePower(forChannel: 0) ?? -160 } - + func dispose() { stop { path in } } func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { - // Audio recording has stopped + // Audio recording has stopped } - + private func deleteFile(path: String) throws { do { let fileManager = FileManager.default - + if fileManager.fileExists(atPath: path) { try fileManager.removeItem(atPath: path) } } catch { - throw RecorderError.error(message: "Failed to delete previous recording", details: error.localizedDescription) + throw RecorderError.error( + message: "Failed to delete previous recording", details: error.localizedDescription) } } } diff --git a/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderStreamDelegate.swift b/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderStreamDelegate.swift index 8d877519..887145bf 100644 --- a/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderStreamDelegate.swift +++ b/record_ios/ios/record_ios/Sources/record_ios/delegate/RecorderStreamDelegate.swift @@ -1,18 +1,18 @@ import AVFoundation -import Foundation import Flutter +import Foundation class RecorderStreamDelegate: NSObject, AudioRecordingStreamDelegate { var config: RecordConfig? - + private var audioEngine: AVAudioEngine? private var amplitude: Float = -160.0 private let bus = 0 - private var onPause: () -> () - private var onStop: () -> () + private var onPause: () -> Void + private var onStop: () -> Void private let manageAudioSession: Bool - - init(manageAudioSession: Bool, onPause: @escaping () -> (), onStop: @escaping () -> ()) { + + init(manageAudioSession: Bool, onPause: @escaping () -> Void, onStop: @escaping () -> Void) { self.manageAudioSession = manageAudioSession self.onPause = onPause self.onStop = onStop @@ -22,10 +22,11 @@ class RecorderStreamDelegate: NSObject, AudioRecordingStreamDelegate { let audioEngine = AVAudioEngine() try initAVAudioSession(config: config, manageAudioSession: manageAudioSession) - try setVoiceProcessing(echoCancel: config.echoCancel, autoGain: config.autoGain, audioEngine: audioEngine) - + try setVoiceProcessing( + echoCancel: config.echoCancel, autoGain: config.autoGain, audioEngine: audioEngine) + let srcFormat = audioEngine.inputNode.inputFormat(forBus: 0) - + let dstFormat = AVAudioFormat( commonFormat: .pcmFormatInt16, sampleRate: Double(config.sampleRate), @@ -51,7 +52,8 @@ class RecorderStreamDelegate: NSObject, AudioRecordingStreamDelegate { audioEngine.inputNode.installTap( onBus: bus, bufferSize: AVAudioFrameCount(config.streamBufferSize ?? 1024), - format: srcFormat) { (buffer, _) -> Void in + format: srcFormat + ) { (buffer, _) -> Void in self.stream( buffer: buffer, @@ -60,112 +62,120 @@ class RecorderStreamDelegate: NSObject, AudioRecordingStreamDelegate { recordEventHandler: recordEventHandler ) } - + audioEngine.prepare() try audioEngine.start() - + self.audioEngine = audioEngine - + self.config = config } - - func stop(completionHandler: @escaping (String?) -> ()) { + + func stop(completionHandler: @escaping (String?) -> Void) { if let audioEngine = audioEngine { do { try setVoiceProcessing(echoCancel: false, autoGain: false, audioEngine: audioEngine) } catch {} } - + audioEngine?.inputNode.removeTap(onBus: bus) audioEngine?.stop() audioEngine = nil - + + deactivateAudioSession() + completionHandler(nil) onStop() - + config = nil } - + func pause() { audioEngine?.pause() + deactivateAudioSession() onPause() } - + func resume() throws { try audioEngine?.start() } - + func cancel() throws { stop { path in } } - + func getAmplitude() -> Float { return amplitude } - + private func updateAmplitude(_ samples: [Int16]) { - var maxSample:Float = -160.0 + var maxSample: Float = -160.0 for sample in samples { let curSample = abs(Float(sample)) - if (curSample > maxSample) { + if curSample > maxSample { maxSample = curSample } } - + amplitude = 20 * (log(maxSample / 32767.0) / log(10)) } - + func dispose() { stop { path in } } - + // Little endian private func convertInt16toUInt8(_ samples: [Int16]) -> [UInt8] { var bytes: [UInt8] = [] - + for sample in samples { bytes.append(UInt8(sample & 0x00ff)) bytes.append(UInt8(sample >> 8 & 0x00ff)) } - + return bytes } - + private func stream( buffer: AVAudioPCMBuffer, dstFormat: AVAudioFormat, converter: AVAudioConverter, recordEventHandler: RecordStreamHandler - ) -> Void { + ) { let inputCallback: AVAudioConverterInputBlock = { inNumPackets, outStatus in outStatus.pointee = .haveData return buffer } - + // Determine frame capacity - let capacity = (UInt32(dstFormat.sampleRate) * dstFormat.channelCount * buffer.frameLength) / (UInt32(buffer.format.sampleRate) * buffer.format.channelCount) - + let capacity = + (UInt32(dstFormat.sampleRate) * dstFormat.channelCount * buffer.frameLength) + / (UInt32(buffer.format.sampleRate) * buffer.format.channelCount) + // Destination buffer - guard let convertedBuffer = AVAudioPCMBuffer(pcmFormat: dstFormat, frameCapacity: capacity) else { + guard let convertedBuffer = AVAudioPCMBuffer(pcmFormat: dstFormat, frameCapacity: capacity) + else { print("Unable to create output buffer") stop { path in } return } - + // Convert input buffer (resample, num channels) var error: NSError? = nil converter.convert(to: convertedBuffer, error: &error, withInputFrom: inputCallback) if error != nil { return } - + if let channelData = convertedBuffer.int16ChannelData { // Fill samples let channelDataPointer = channelData.pointee - let samples = stride(from: 0, - to: Int(convertedBuffer.frameLength), - by: buffer.stride).map{ channelDataPointer[$0] } + let samples = stride( + from: 0, + to: Int(convertedBuffer.frameLength), + by: buffer.stride + ).map { channelDataPointer[$0] } // Update current amplitude updateAmplitude(samples) @@ -173,16 +183,18 @@ class RecorderStreamDelegate: NSObject, AudioRecordingStreamDelegate { // Send bytes if let eventSink = recordEventHandler.eventSink { let bytes = Data(_: convertInt16toUInt8(samples)) - + DispatchQueue.main.async { eventSink(FlutterStandardTypedData(bytes: bytes)) } } } } - + // Set up AGC & echo cancel - private func setVoiceProcessing(echoCancel: Bool, autoGain: Bool, audioEngine: AVAudioEngine) throws { + private func setVoiceProcessing(echoCancel: Bool, autoGain: Bool, audioEngine: AVAudioEngine) + throws + { if #available(iOS 13.0, *) { do { try audioEngine.inputNode.setVoiceProcessingEnabled(echoCancel) diff --git a/record_ios/ios/record_ios/Sources/record_ios/extension/RecorderSessionExtension.swift b/record_ios/ios/record_ios/Sources/record_ios/extension/RecorderSessionExtension.swift index 3801d34b..35126dff 100644 --- a/record_ios/ios/record_ios/Sources/record_ios/extension/RecorderSessionExtension.swift +++ b/record_ios/ios/record_ios/Sources/record_ios/extension/RecorderSessionExtension.swift @@ -5,51 +5,65 @@ extension AudioRecordingDelegate { let manage = manageAudioSession && config.iosConfig.manageAudioSession let audioSession = AVAudioSession.sharedInstance() - + do { - try audioSession.setPreferredSampleRate((config.sampleRate <= 48000) ? Double(config.sampleRate) : 48000.0) + try audioSession.setPreferredSampleRate( + (config.sampleRate <= 48000) ? Double(config.sampleRate) : 48000.0) } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setPreferredSampleRate: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", + details: "setPreferredSampleRate: \(error.localizedDescription)") } - + if #available(iOS 14.5, *) { do { - try audioSession.setPrefersNoInterruptionsFromSystemAlerts(config.audioInterruption == AudioInterruptionMode.none) + try audioSession.setPrefersNoInterruptionsFromSystemAlerts( + config.audioInterruption == AudioInterruptionMode.none) } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setPrefersNoInterruptionsFromSystemAlerts: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", + details: "setPrefersNoInterruptionsFromSystemAlerts: \(error.localizedDescription)") } } - + if manage { do { - try audioSession.setCategory(.playAndRecord, options: AVAudioSession.CategoryOptions(config.iosConfig.categoryOptions)) + try audioSession.setCategory( + .playAndRecord, options: AVAudioSession.CategoryOptions(config.iosConfig.categoryOptions)) } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setCategory: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", + details: "setCategory: \(error.localizedDescription)") } do { - try audioSession.setActive(true, options: .notifyOthersOnDeactivation) // Must be done before setting channels and others + try audioSession.setActive(true, options: .notifyOthersOnDeactivation) // Must be done before setting channels and others } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setActive: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", details: "setActive: \(error.localizedDescription)") } } - + do { - let newPreferredInputNumberOfChannels = min(config.numChannels, audioSession.maximumInputNumberOfChannels) + let newPreferredInputNumberOfChannels = min( + config.numChannels, audioSession.maximumInputNumberOfChannels) if newPreferredInputNumberOfChannels > 0 { try audioSession.setPreferredInputNumberOfChannels(newPreferredInputNumberOfChannels) } } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setPreferredInputNumberOfChannels: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", + details: "setPreferredInputNumberOfChannels: \(error.localizedDescription)") } - + do { try setInput(config) } catch { - throw RecorderError.error(message: "Failed to start recording", details: "setInput: \(error.localizedDescription)") + throw RecorderError.error( + message: "Failed to start recording", details: "setInput: \(error.localizedDescription)") } - + NotificationCenter.default.addObserver( forName: AVAudioSession.interruptionNotification, object: nil, @@ -57,17 +71,32 @@ extension AudioRecordingDelegate { using: onAudioSessionInterruption) } - private func onAudioSessionInterruption(notification: Notification) -> Void { + func deactivateAudioSession() { + guard let conf = config, conf.iosConfig.manageAudioSession else { + return + } + + let audioSession = AVAudioSession.sharedInstance() + + do { + try audioSession.setActive(false, options: .notifyOthersOnDeactivation) + } catch { + // Silently fail - audio session deactivation is best effort + } + } + + private func onAudioSessionInterruption(notification: Notification) { guard let userInfo = notification.userInfo, - let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, - let type = AVAudioSession.InterruptionType(rawValue: typeValue) else { + let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt, + let type = AVAudioSession.InterruptionType(rawValue: typeValue) + else { return } - + guard let config = self.config else { return } - + if type == AVAudioSession.InterruptionType.began { if config.audioInterruption != AudioInterruptionMode.none { pause() @@ -97,14 +126,14 @@ extension AudioRecordingDelegate { guard let device = config.device else { return } - + let inputs = try listInputDevices() guard let inputs = inputs else { return } - + let audioSession = AVAudioSession.sharedInstance() - + for input in inputs { if input.uid == device.id { try audioSession.setPreferredInput(input) diff --git a/record_ios/pubspec.yaml b/record_ios/pubspec.yaml index 9739f59a..f74c8079 100644 --- a/record_ios/pubspec.yaml +++ b/record_ios/pubspec.yaml @@ -1,6 +1,6 @@ name: record_ios description: iOS implementation for record package called by record_platform_interface. -version: 1.1.5 +version: 1.1.6 homepage: https://github.com/llfbandit/record/tree/master/record_ios environment: @@ -13,7 +13,7 @@ dependencies: flutter: sdk: flutter - record_platform_interface: ^1.4.0 + record_platform_interface: ^1.4.1 flutter: plugin: From bc66d1feb90da56a9bf766cde8708f07c14d48f8 Mon Sep 17 00:00:00 2001 From: REU8ER Date: Sun, 28 Dec 2025 18:14:23 -0300 Subject: [PATCH 3/4] chore: update package versions and dependencies for audio fixes - record_platform_interface: 1.4.0 -> 1.4.1 - record_android: 1.4.5 -> 1.4.6 - record_ios: 1.1.5 -> 1.1.7 - record_macos: 1.1.2 -> 1.1.3 - Update dependencies in all packages to use compatible versions --- record/pubspec.yaml | 8 ++++---- record_macos/CHANGELOG.md | 2 +- record_macos/pubspec.yaml | 4 ++-- record_platform_interface/CHANGELOG.md | 2 +- record_platform_interface/pubspec.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/record/pubspec.yaml b/record/pubspec.yaml index 1b17815b..51e4dab5 100644 --- a/record/pubspec.yaml +++ b/record/pubspec.yaml @@ -15,13 +15,13 @@ dependencies: uuid: ">=3.0.7 <5.0.0" - record_platform_interface: ^1.4.0 + record_platform_interface: ^1.4.1 record_web: ^1.2.0 record_windows: ^1.0.7 record_linux: ^1.2.1 - record_android: ^1.4.3 - record_ios: ^1.1.3 - record_macos: ^1.1.2 + record_android: ^1.4.6 + record_ios: ^1.1.6 + record_macos: ^1.1.3 dev_dependencies: flutter_lints: ^6.0.0 diff --git a/record_macos/CHANGELOG.md b/record_macos/CHANGELOG.md index 57691217..87df787d 100644 --- a/record_macos/CHANGELOG.md +++ b/record_macos/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 1.1.3 * feat: Add `request` parameter to `hasPermission()` method to check permission status without requesting. ## 1.1.2 diff --git a/record_macos/pubspec.yaml b/record_macos/pubspec.yaml index b49d62b5..f2eae8b7 100644 --- a/record_macos/pubspec.yaml +++ b/record_macos/pubspec.yaml @@ -1,6 +1,6 @@ name: record_macos description: macOS implementation for record package called by record_platform_interface. -version: 1.1.2 +version: 1.1.3 homepage: https://github.com/llfbandit/record/tree/master/record_macos environment: @@ -13,7 +13,7 @@ dependencies: flutter: sdk: flutter - record_platform_interface: ^1.4.0 + record_platform_interface: ^1.4.1 flutter: plugin: diff --git a/record_platform_interface/CHANGELOG.md b/record_platform_interface/CHANGELOG.md index 88bc41a8..785c4ee9 100644 --- a/record_platform_interface/CHANGELOG.md +++ b/record_platform_interface/CHANGELOG.md @@ -1,4 +1,4 @@ -## Upcoming +## 1.4.1 * feat: Add `request` parameter to `hasPermission()` method to check permission status without requesting. ## 1.4.0 diff --git a/record_platform_interface/pubspec.yaml b/record_platform_interface/pubspec.yaml index 408a9f1b..15396c79 100644 --- a/record_platform_interface/pubspec.yaml +++ b/record_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: record_platform_interface description: A common interface for the record package to call dedicated platforms with method channel. homepage: https://github.com/llfbandit/record/tree/master/record_platform_interface -version: 1.4.0 +version: 1.4.1 environment: sdk: ^3.5.0 From 61f11d1038e23a61603e96fd02c842824742af6c Mon Sep 17 00:00:00 2001 From: REU8ER Date: Sun, 28 Dec 2025 18:32:43 -0300 Subject: [PATCH 4/4] chore(record): bump version to 6.1.3 Update main package version to reflect audio focus/session fixes on Android and iOS platforms. --- record/CHANGELOG.md | 5 ++++- record/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/record/CHANGELOG.md b/record/CHANGELOG.md index ddc3a8da..cebdedae 100644 --- a/record/CHANGELOG.md +++ b/record/CHANGELOG.md @@ -1,5 +1,8 @@ -## Upcoming +## 6.1.3 * feat: Add `request` parameter to `hasPermission()` method to check permission status without requesting. +* fix(android): Release audio focus when calling `cancel()` method. +* fix(android): Use `AUDIOFOCUS_GAIN_TRANSIENT` instead of `AUDIOFOCUS_GAIN` to allow other audio apps to resume automatically. +* fix(ios): Deactivate audio session when calling `stop()`, `pause()` methods to allow other audio apps to resume automatically. ## 6.1.2 * chore: Updated transitive dependencies. diff --git a/record/pubspec.yaml b/record/pubspec.yaml index 51e4dab5..269a49d8 100644 --- a/record/pubspec.yaml +++ b/record/pubspec.yaml @@ -1,6 +1,6 @@ name: record description: Audio recorder from microphone to file or stream with multiple codecs, bit rate and sampling rate options. -version: 6.1.2 +version: 6.1.3 homepage: https://github.com/llfbandit/record/tree/master/record environment: