@@ -34,7 +34,9 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
34
34
35
35
// / Variables (i.e. will change value)
36
36
private var eventSink: EventSink ? = null
37
- private var recording = false
37
+ @Volatile private var recording = false
38
+ private var recordingThread: Thread ? = null
39
+
38
40
39
41
private var currentActivity: Activity ? = null
40
42
@@ -48,16 +50,19 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
48
50
methodChannel.setMethodCallHandler {
49
51
call, result ->
50
52
if (call.method == " getSampleRate" ) {
51
- // Sample rate never changes, so return the given sample rate.
52
- result.success(audioRecord?.getSampleRate())
53
+ if (::audioRecord.isInitialized) {
54
+ result.success(audioRecord.sampleRate)
55
+ } else {
56
+ result.error(" UNAVAILABLE" , " AudioRecord not initialized." , null )
57
+ }
53
58
} else {
54
59
result.notImplemented()
55
60
}
56
61
}
57
62
}
58
63
59
64
override fun onDetachedFromEngine (@NonNull binding : FlutterPlugin .FlutterPluginBinding ) {
60
- recording = false
65
+ stopRecording()
61
66
}
62
67
63
68
override fun onDetachedFromActivity () {
@@ -83,20 +88,19 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
83
88
*/
84
89
override fun onListen (arguments : Any? , events : EventSink ? ) {
85
90
this .eventSink = events
86
- recording = true
87
91
sampleRate = (arguments as Map <* , * >)[" sampleRate" ] as Int
88
92
if (sampleRate < 4000 || sampleRate > 48000 ) {
89
93
events!! .error(" SampleRateError" , " A sample rate of " + sampleRate + " Hz is not supported by Android." , null )
90
94
return
91
95
}
92
- streamMicData ()
96
+ startRecording ()
93
97
}
94
98
95
99
/* *
96
100
* Called from Flutter, which cancels the stream.
97
101
*/
98
102
override fun onCancel (arguments : Any? ) {
99
- recording = false
103
+ stopRecording()
100
104
}
101
105
102
106
/* *
@@ -105,11 +109,36 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
105
109
override fun onRequestPermissionsResult (requestCode : Int , permissions : Array <String >, grantResults : IntArray ): Boolean {
106
110
val requestAudioPermissionCode = 200
107
111
when (requestCode) {
108
- requestAudioPermissionCode -> if (grantResults[0 ] == PackageManager .PERMISSION_GRANTED ) return true
112
+ requestAudioPermissionCode -> if (grantResults.isNotEmpty() && grantResults [0 ] == PackageManager .PERMISSION_GRANTED ) return true
109
113
}
110
114
return false
111
115
}
112
116
117
+ private fun startRecording () {
118
+ if (recording) {
119
+ Log .w(logTag, " Recording is already in progress" )
120
+ return
121
+ }
122
+ recording = true
123
+ recordingThread = Thread { streamMicData() }
124
+ recordingThread?.start()
125
+ }
126
+
127
+ private fun stopRecording () {
128
+ if (! recording) {
129
+ Log .w(logTag, " Recording is not in progress" )
130
+ return
131
+ }
132
+ recording = false
133
+ try {
134
+ recordingThread?.join()
135
+ } catch (e: InterruptedException ) {
136
+ e.printStackTrace()
137
+ }
138
+ recordingThread = null
139
+ }
140
+
141
+
113
142
/* *
114
143
* Starts recording and streaming audio data from the mic.
115
144
* Uses a buffer array of size 512. Whenever buffer is full, the content is sent to Flutter.
@@ -119,39 +148,51 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
119
148
* https://www.newventuresoftware.com/blog/record-play-and-visualize-raw-audio-data-in-android
120
149
*/
121
150
private fun streamMicData () {
122
- Thread (
123
- Runnable {
124
- Process .setThreadPriority(Process .THREAD_PRIORITY_AUDIO )
125
- val audioBuffer = ShortArray (bufferSize / 2 )
126
- audioRecord = AudioRecord (
127
- MediaRecorder .AudioSource .DEFAULT ,
128
- sampleRate,
129
- AudioFormat .CHANNEL_IN_MONO ,
130
- AudioFormat .ENCODING_PCM_16BIT ,
131
- bufferSize,
132
- )
133
- if (audioRecord.state != AudioRecord .STATE_INITIALIZED ) {
134
- Log .e(logTag, " Audio Record can't initialize!" )
135
- return @Runnable
136
- }
137
- /* * Start recording loop */
138
- audioRecord.startRecording()
139
- while (recording) {
140
- /* * Read data into buffer */
141
- audioRecord.read(audioBuffer, 0 , audioBuffer.size)
151
+ Process .setThreadPriority(Process .THREAD_PRIORITY_AUDIO )
152
+
153
+ val audioBuffer = ShortArray (bufferSize / 2 )
154
+ try {
155
+ audioRecord = AudioRecord (
156
+ MediaRecorder .AudioSource .DEFAULT ,
157
+ sampleRate,
158
+ AudioFormat .CHANNEL_IN_MONO ,
159
+ AudioFormat .ENCODING_PCM_16BIT ,
160
+ bufferSize,
161
+ )
162
+
163
+ if (audioRecord.state != AudioRecord .STATE_INITIALIZED ) {
164
+ Log .e(logTag, " Audio Record can't initialize!" )
165
+ eventSink?.error(" MIC_ERROR" , " Audio Record can't initialize!" , null )
166
+ recording = false
167
+ return
168
+ }
169
+
170
+ audioRecord.startRecording()
171
+
172
+ while (recording) {
173
+ val readSize = audioRecord.read(audioBuffer, 0 , audioBuffer.size)
174
+ if (readSize > 0 ) {
175
+ val audioBufferList = ArrayList <Double >()
176
+ for (i in 0 until readSize) {
177
+ val normalizedImpulse = audioBuffer[i].toDouble() / maxAmplitude.toDouble()
178
+ audioBufferList.add(normalizedImpulse)
179
+ }
142
180
Handler (Looper .getMainLooper()).post {
143
- // / Convert to list in order to send via EventChannel.
144
- val audioBufferList = ArrayList <Double >()
145
- for (impulse in audioBuffer) {
146
- val normalizedImpulse = impulse.toDouble() / maxAmplitude.toDouble()
147
- audioBufferList.add(normalizedImpulse)
148
- }
149
- eventSink!! .success(audioBufferList)
181
+ eventSink?.success(audioBufferList)
150
182
}
151
183
}
152
- audioRecord.stop()
184
+ }
185
+ } catch (e: Exception ) {
186
+ Log .e(logTag, " Error while recording audio" , e)
187
+ eventSink?.error(" MIC_ERROR" , " Error while recording audio" , e.message)
188
+ } finally {
189
+ if (::audioRecord.isInitialized) {
190
+ if (audioRecord.recordingState == AudioRecord .RECORDSTATE_RECORDING ) {
191
+ audioRecord.stop()
192
+ }
153
193
audioRecord.release()
154
- },
155
- ).start()
194
+ }
195
+ recording = false
196
+ }
156
197
}
157
- }
198
+ }
0 commit comments