Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/audio_streamer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 4.3.0

* Fix issue where stream function made a new thread each time a recording starts - Fixes [#1242](https://github.com/cph-cachet/flutter-plugins/issues/1242)
* Updated Gradle and Kotlin
* Updated `permission_handler: ^12.0.0`
* Updated `sdk >=3.8.1`

## 4.2.2

* Reverts [#1226](https://github.com/cph-cachet/flutter-plugins/pull/1226)
Expand Down
4 changes: 2 additions & 2 deletions packages/audio_streamer/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ group 'plugins.cachet.audio_streamer'
version '1.0-SNAPSHOT'

buildscript {
ext.kotlin_version = '1.9.22'
ext.kotlin_version = '2.2.10'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:8.2.1'
classpath 'com.android.tools.build:gradle:8.12.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
1 change: 0 additions & 1 deletion packages/audio_streamer/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
4 changes: 2 additions & 2 deletions packages/audio_streamer/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.2"
id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.20" apply false
id "com.android.application" version "8.12.1" apply false
id "org.jetbrains.kotlin.android" version "2.2.10" apply false
}

include ":app"
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve

// / Variables (i.e. will change value)
private var eventSink: EventSink? = null
private var recording = false
@Volatile private var recording = false
private var recordingThread: Thread? = null


private var currentActivity: Activity? = null

Expand All @@ -48,16 +50,19 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
methodChannel.setMethodCallHandler {
call, result ->
if (call.method == "getSampleRate") {
// Sample rate never changes, so return the given sample rate.
result.success(audioRecord?.getSampleRate())
if (::audioRecord.isInitialized) {
result.success(audioRecord.sampleRate)
} else {
result.error("UNAVAILABLE", "AudioRecord not initialized.", null)
}
} else {
result.notImplemented()
}
}
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
recording = false
stopRecording()
}

override fun onDetachedFromActivity() {
Expand All @@ -83,20 +88,19 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
*/
override fun onListen(arguments: Any?, events: EventSink?) {
this.eventSink = events
recording = true
sampleRate = (arguments as Map<*, *>)["sampleRate"] as Int
if (sampleRate < 4000 || sampleRate > 48000) {
events!!.error("SampleRateError", "A sample rate of " + sampleRate + "Hz is not supported by Android.", null)
return
}
streamMicData()
startRecording()
}

/**
* Called from Flutter, which cancels the stream.
*/
override fun onCancel(arguments: Any?) {
recording = false
stopRecording()
}

/**
Expand All @@ -105,11 +109,36 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray): Boolean {
val requestAudioPermissionCode = 200
when (requestCode) {
requestAudioPermissionCode -> if (grantResults[0] == PackageManager.PERMISSION_GRANTED) return true
requestAudioPermissionCode -> if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) return true
}
return false
}

private fun startRecording() {
if (recording) {
Log.w(logTag, "Recording is already in progress")
return
}
recording = true
recordingThread = Thread { streamMicData() }
recordingThread?.start()
}

private fun stopRecording() {
if (!recording) {
Log.w(logTag, "Recording is not in progress")
return
}
recording = false
try {
recordingThread?.join()
} catch (e: InterruptedException) {
e.printStackTrace()
}
recordingThread = null
}


/**
* Starts recording and streaming audio data from the mic.
* Uses a buffer array of size 512. Whenever buffer is full, the content is sent to Flutter.
Expand All @@ -119,39 +148,51 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
* https://www.newventuresoftware.com/blog/record-play-and-visualize-raw-audio-data-in-android
*/
private fun streamMicData() {
Thread(
Runnable {
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO)
val audioBuffer = ShortArray(bufferSize / 2)
audioRecord = AudioRecord(
MediaRecorder.AudioSource.DEFAULT,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
)
if (audioRecord.state != AudioRecord.STATE_INITIALIZED) {
Log.e(logTag, "Audio Record can't initialize!")
return@Runnable
}
/** Start recording loop */
audioRecord.startRecording()
while (recording) {
/** Read data into buffer */
audioRecord.read(audioBuffer, 0, audioBuffer.size)
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO)

val audioBuffer = ShortArray(bufferSize / 2)
try {
audioRecord = AudioRecord(
MediaRecorder.AudioSource.DEFAULT,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
)

if (audioRecord.state != AudioRecord.STATE_INITIALIZED) {
Log.e(logTag, "Audio Record can't initialize!")
eventSink?.error("MIC_ERROR", "Audio Record can't initialize!", null)
recording = false
return
}

audioRecord.startRecording()

while (recording) {
val readSize = audioRecord.read(audioBuffer, 0, audioBuffer.size)
if(readSize > 0) {
val audioBufferList = ArrayList<Double>()
for (i in 0 until readSize) {
val normalizedImpulse = audioBuffer[i].toDouble() / maxAmplitude.toDouble()
audioBufferList.add(normalizedImpulse)
}
Handler(Looper.getMainLooper()).post {
// / Convert to list in order to send via EventChannel.
val audioBufferList = ArrayList<Double>()
for (impulse in audioBuffer) {
val normalizedImpulse = impulse.toDouble() / maxAmplitude.toDouble()
audioBufferList.add(normalizedImpulse)
}
eventSink!!.success(audioBufferList)
eventSink?.success(audioBufferList)
}
}
audioRecord.stop()
}
} catch (e: Exception) {
Log.e(logTag, "Error while recording audio", e)
eventSink?.error("MIC_ERROR", "Error while recording audio", e.message)
} finally {
if (::audioRecord.isInitialized) {
if (audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
audioRecord.stop()
}
audioRecord.release()
},
).start()
}
recording = false
}
}
}
}
2 changes: 0 additions & 2 deletions packages/audio_streamer/example/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Sun Mar 12 17:19:03 CST 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
4 changes: 2 additions & 2 deletions packages/audio_streamer/example/android/settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ pluginManagement {

plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.2"
id "com.android.application" version "8.1.0" apply false
id "org.jetbrains.kotlin.android" version "1.9.20" apply false
id "com.android.application" version '8.12.1' apply false
id "org.jetbrains.kotlin.android" version "2.2.10" apply false
}

include ":app"
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>12.0</string>
<string>13.0</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion packages/audio_streamer/example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'
# platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,12 @@
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand All @@ -282,13 +281,12 @@
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/audio_streamer/audio_streamer.framework",
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/audio_streamer.framework",
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -370,7 +368,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand All @@ -393,7 +391,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -457,7 +455,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -506,7 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand All @@ -530,7 +528,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -562,7 +560,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
Expand All @@ -45,6 +46,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
Loading