@@ -12,34 +12,20 @@ import androidx.appcompat.app.AppCompatActivity
1212import androidx.core.app.ActivityCompat
1313import kotlinx.android.synthetic.main.activity_main.*
1414import org.mozilla.deepspeech.libdeepspeech.DeepSpeechModel
15- import org.mozilla.deepspeech.libdeepspeech.DeepSpeechStreamingState
1615import java.io.File
17-
16+ import java.util.concurrent.atomic.AtomicBoolean
1817
1918class MainActivity : AppCompatActivity () {
2019 private var model: DeepSpeechModel ? = null
21- private var streamContext: DeepSpeechStreamingState ? = null
22-
23- // Change the following parameters regarding
24- // what works best for your use case or your language.
25- private val BEAM_WIDTH = 500L
26- private val LM_ALPHA = 0.931289039105002f
27- private val LM_BETA = 1.1834137581510284f
28-
29- private val RECORDER_CHANNELS : Int = AudioFormat .CHANNEL_IN_MONO
30- private val RECORDER_AUDIO_ENCODING : Int = AudioFormat .ENCODING_PCM_16BIT
31- private var recorder: AudioRecord ? = null
32- private var recordingThread: Thread ? = null
33- private var isRecording: Boolean = false
3420
35- private val NUM_BUFFER_ELEMENTS = 1024
36- private val BYTES_PER_ELEMENT = 2 // 2 bytes (short) because of 16 bit format
21+ private var transcriptionThread : Thread ? = null
22+ private var isRecording : AtomicBoolean = AtomicBoolean ( false )
3723
3824 private val TFLITE_MODEL_FILENAME = " deepspeech-0.8.0-models.tflite"
3925 private val SCORER_FILENAME = " deepspeech-0.8.0-models.scorer"
4026
4127 private fun checkAudioPermission () {
42- // permission is automatically granted on sdk < 23 upon installation
28+ // Permission is automatically granted on SDK < 23 upon installation.
4329 if (Build .VERSION .SDK_INT >= 23 ) {
4430 val permission = Manifest .permission.RECORD_AUDIO
4531
@@ -50,21 +36,42 @@ class MainActivity : AppCompatActivity() {
5036 }
5137
5238 private fun transcribe () {
53- val audioData = ShortArray (NUM_BUFFER_ELEMENTS )
39+ // We read from the recorder in chunks of 2048 shorts. With a model that expects its input
40+ // at 16000Hz, this corresponds to 2048/16000 = 0.128s or 128ms.
41+ val audioBufferSize = 2048
42+ val audioData = ShortArray (audioBufferSize)
5443
55- while (isRecording) {
56- recorder?.read(
57- audioData,
58- 0 ,
59- NUM_BUFFER_ELEMENTS
44+ runOnUiThread { btnStartInference.text = " Stop Recording" }
45+
46+ model?.let { model ->
47+ val streamContext = model.createStream()
48+
49+ val recorder = AudioRecord (
50+ MediaRecorder .AudioSource .VOICE_RECOGNITION ,
51+ model.sampleRate(),
52+ AudioFormat .CHANNEL_IN_MONO ,
53+ AudioFormat .ENCODING_PCM_16BIT ,
54+ audioBufferSize
6055 )
61- model?.feedAudioContent(streamContext, audioData, audioData.size)
62- val decoded = model?.intermediateDecode(streamContext)
63- runOnUiThread { transcription.text = decoded }
56+ recorder.startRecording()
57+
58+ while (isRecording.get()) {
59+ recorder.read(audioData, 0 , audioBufferSize)
60+ model.feedAudioContent(streamContext, audioData, audioData.size)
61+ val decoded = model.intermediateDecode(streamContext)
62+ runOnUiThread { transcription.text = decoded }
63+ }
64+
65+ val decoded = model.finishStream(streamContext)
66+
67+ runOnUiThread {
68+ btnStartInference.text = " Start Recording"
69+ transcription.text = decoded
70+ }
71+
72+ recorder.stop()
73+ recorder.release()
6474 }
65- val decoded = model?.finishStream(streamContext)
66- runOnUiThread { transcription.text = decoded }
67- recorder?.stop()
6875 }
6976
7077 private fun createModel (): Boolean {
@@ -73,73 +80,49 @@ class MainActivity : AppCompatActivity() {
7380 val scorerPath = " $modelsPath /$SCORER_FILENAME "
7481
7582 for (path in listOf (tfliteModelPath, scorerPath)) {
76- if (! ( File (path).exists() )) {
77- status.text = " Model creation failed: $path does not exist."
83+ if (! File (path).exists()) {
84+ status.append( " Model creation failed: $path does not exist.\n " )
7885 return false
7986 }
8087 }
8188
8289 model = DeepSpeechModel (tfliteModelPath)
83- model?.setBeamWidth(BEAM_WIDTH )
8490 model?.enableExternalScorer(scorerPath)
85- model?.setScorerAlphaBeta( LM_ALPHA , LM_BETA )
91+
8692 return true
8793 }
8894
8995 private fun startListening () {
90- status.text = " Creating model...\n "
91-
92- if (model == null ) {
93- if (! createModel()) {
94- return
95- }
96- status.append(" Created model.\n " )
97- } else {
98- status.append(" Model already created.\n " )
99- }
100-
101- model?.let { model ->
102- btnStartInference.text = " Stop Recording"
103- streamContext = model.createStream()
104-
105- if (recorder == null ) {
106- recorder = AudioRecord (
107- MediaRecorder .AudioSource .VOICE_RECOGNITION ,
108- model.sampleRate(),
109- RECORDER_CHANNELS ,
110- RECORDER_AUDIO_ENCODING ,
111- NUM_BUFFER_ELEMENTS * BYTES_PER_ELEMENT )
112- }
113-
114- recorder?.startRecording()
115- isRecording = true
116-
117- if (recordingThread == null ) {
118- recordingThread = Thread (Runnable { transcribe() }, " AudioRecorder Thread" )
119- recordingThread?.start()
120- }
96+ if (isRecording.compareAndSet(false , true )) {
97+ transcriptionThread = Thread (Runnable { transcribe() }, " Transcription Thread" )
98+ transcriptionThread?.start()
12199 }
122100 }
123101
124102 override fun onCreate (savedInstanceState : Bundle ? ) {
125103 super .onCreate(savedInstanceState)
126- getExternalFilesDir(null )
127104 setContentView(R .layout.activity_main)
128105 checkAudioPermission()
129106
130- // create application data directory on the device
131- getExternalFilesDir(null )
107+ // Create application data directory on the device
108+ val modelsPath = getExternalFilesDir(null ).toString( )
132109
133- status.text = " Ready, waiting ... "
110+ status.text = " Ready. Copy model files to \" $modelsPath \" if running for the first time. \n "
134111 }
135112
136113 private fun stopListening () {
137- isRecording = false
138- btnStartInference.text = " Start Recording"
114+ isRecording.set(false )
139115 }
140116
141117 fun onRecordClick (v : View ? ) {
142- if (isRecording) {
118+ if (model == null ) {
119+ if (! createModel()) {
120+ return
121+ }
122+ status.append(" Created model.\n " )
123+ }
124+
125+ if (isRecording.get()) {
143126 stopListening()
144127 } else {
145128 startListening()
0 commit comments