@@ -15,29 +15,45 @@ import io.flutter.embedding.engine.plugins.activity.ActivityAware
1515import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
1616import io.flutter.plugin.common.EventChannel
1717import io.flutter.plugin.common.EventChannel.EventSink
18+ import io.flutter.plugin.common.MethodChannel
1819import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
1920import java.util.*
2021
2122/* * AudioStreamerPlugin */
2223class AudioStreamerPlugin : FlutterPlugin , RequestPermissionsResultListener , EventChannel .StreamHandler , ActivityAware {
2324
24- // / Constants
25+ // / Constants
2526 private val eventChannelName = " audio_streamer.eventChannel"
26- private val sampleRate = 44100
27- private var bufferSize = 6400 * 2 ; // / Magical number!
27+
28+ // / Method channel for returning the sample rate.
29+ private val methodChannelName = " audio_streamer.methodChannel"
30+ private var sampleRate = 44100 // standard value to initialize
31+ private var bufferSize = 6400 * 2 ; // / Magical number!
2832 private val maxAmplitude = 32767 // same as 2^15
2933 private val logTag = " AudioStreamerPlugin"
3034
31- // / Variables (i.e. will change value)
35+ // / Variables (i.e. will change value)
3236 private var eventSink: EventSink ? = null
3337 private var recording = false
3438
3539 private var currentActivity: Activity ? = null
3640
41+ private lateinit var audioRecord: AudioRecord ;
42+
3743 override fun onAttachedToEngine (@NonNull flutterPluginBinding : FlutterPlugin .FlutterPluginBinding ) {
3844 val messenger = flutterPluginBinding.binaryMessenger
3945 val eventChannel = EventChannel (messenger, eventChannelName)
4046 eventChannel.setStreamHandler(this )
47+ val methodChannel = MethodChannel (messenger, methodChannelName)
48+ methodChannel.setMethodCallHandler {
49+ call, result ->
50+ if (call.method == " getSampleRate" ) {
51+ // Sample rate never changes, so return the given sample rate.
52+ result.success(audioRecord?.getSampleRate())
53+ } else {
54+ result.notImplemented()
55+ }
56+ }
4157 }
4258
4359 override fun onDetachedFromEngine (@NonNull binding : FlutterPlugin .FlutterPluginBinding ) {
@@ -63,11 +79,16 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
6379 }
6480
6581 /* *
66- * Called from Flutter, starts the stream.
82+ * Called from Flutter, starts the stream and updates the sample rate using the argument .
6783 */
6884 override fun onListen (arguments : Any? , events : EventSink ? ) {
6985 this .eventSink = events
7086 recording = true
87+ sampleRate = (arguments as Map <* , * >)[" sampleRate" ] as Int
88+ if (sampleRate < 4000 || sampleRate > 48000 ) {
89+ events!! .error(" SampleRateError" , " A sample rate of " + sampleRate + " Hz is not supported by Android." , null )
90+ return
91+ }
7192 streamMicData()
7293 }
7394
@@ -92,42 +113,45 @@ class AudioStreamerPlugin : FlutterPlugin, RequestPermissionsResultListener, Eve
92113 /* *
93114 * Starts recording and streaming audio data from the mic.
94115 * Uses a buffer array of size 512. Whenever buffer is full, the content is sent to Flutter.
95- *
116+ * Sets the sample rate as defined by [sampleRate].
96117 *
97118 * Source:
98119 * https://www.newventuresoftware.com/blog/record-play-and-visualize-raw-audio-data-in-android
99120 */
100121 private fun streamMicData () {
101- Thread (Runnable {
102- Process .setThreadPriority(Process .THREAD_PRIORITY_AUDIO )
103- val audioBuffer = ShortArray (bufferSize / 2 )
104- val record = AudioRecord (
122+ Thread (
123+ Runnable {
124+ Process .setThreadPriority(Process .THREAD_PRIORITY_AUDIO )
125+ val audioBuffer = ShortArray (bufferSize / 2 )
126+ audioRecord = AudioRecord (
105127 MediaRecorder .AudioSource .DEFAULT ,
106128 sampleRate,
107129 AudioFormat .CHANNEL_IN_MONO ,
108130 AudioFormat .ENCODING_PCM_16BIT ,
109- bufferSize)
110- if (record.state != AudioRecord .STATE_INITIALIZED ) {
111- Log .e(logTag, " Audio Record can't initialize!" )
112- return @Runnable
113- }
114- /* * Start recording loop */
115- record.startRecording()
116- while (recording) {
117- /* * Read data into buffer */
118- record.read(audioBuffer, 0 , audioBuffer.size)
119- Handler (Looper .getMainLooper()).post {
120- // / Convert to list in order to send via EventChannel.
121- val audioBufferList = ArrayList <Double >()
122- for (impulse in audioBuffer) {
123- val normalizedImpulse = impulse.toDouble() / maxAmplitude.toDouble()
124- audioBufferList.add(normalizedImpulse)
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)
142+ 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)
125150 }
126- eventSink!! .success(audioBufferList)
127151 }
128- }
129- record.stop ()
130- record.release()
131- } ).start()
152+ audioRecord.stop()
153+ audioRecord.release ()
154+ },
155+ ).start()
132156 }
133157}
0 commit comments