25
25
package com.github.squti.androidwaverecordersample
26
26
27
27
import android.Manifest
28
+ import android.content.ContentUris
28
29
import android.content.ContentValues
29
30
import android.content.Intent
30
31
import android.content.pm.PackageManager
31
32
import android.media.AudioFormat
32
33
import android.net.Uri
33
34
import android.os.Build
34
35
import android.os.Bundle
36
+ import android.os.Environment
35
37
import android.provider.MediaStore
36
38
import android.provider.Settings
37
39
import android.util.Log
@@ -44,12 +46,13 @@ import androidx.core.content.ContextCompat
44
46
import com.github.squti.androidwaverecorder.RecorderState
45
47
import com.github.squti.androidwaverecorder.WaveRecorder
46
48
import com.github.squti.androidwaverecordersample.databinding.ActivityMainBinding
47
- import java.io.IOException
49
+ import java.io.File
48
50
import java.util.Locale
49
51
import java.util.concurrent.TimeUnit
50
52
51
53
class MainActivity : AppCompatActivity () {
52
54
private val PERMISSIONS_REQUEST_RECORD_AUDIO = 77
55
+ private val PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 88
53
56
54
57
private lateinit var waveRecorder: WaveRecorder
55
58
private lateinit var filePath: String
@@ -65,39 +68,70 @@ class MainActivity : AppCompatActivity() {
65
68
initRecorder(isSaveToExternalStorage = false )
66
69
67
70
binding.saveToExternalStorageSwitch.setOnCheckedChangeListener { _, isChecked ->
68
- if (isChecked) {
71
+ if (! isChecked) {
72
+ resetSwitches()
73
+ initRecorder(isSaveToExternalStorage = false )
74
+ return @setOnCheckedChangeListener
75
+ }
76
+
77
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
78
+ resetSwitches()
79
+ initRecorder(isSaveToExternalStorage = true )
80
+ return @setOnCheckedChangeListener
81
+ }
82
+
83
+ if (ContextCompat .checkSelfPermission(
84
+ this ,
85
+ Manifest .permission.WRITE_EXTERNAL_STORAGE
86
+ ) == PackageManager .PERMISSION_GRANTED
87
+ ) {
88
+ resetSwitches()
69
89
initRecorder(isSaveToExternalStorage = true )
90
+ return @setOnCheckedChangeListener
91
+ }
92
+
93
+ if (ActivityCompat .shouldShowRequestPermissionRationale(
94
+ this ,
95
+ Manifest .permission.WRITE_EXTERNAL_STORAGE
96
+ )
97
+ ) {
98
+ showPermissionSettingsDialog()
70
99
} else {
71
- initRecorder(isSaveToExternalStorage = false )
100
+ ActivityCompat .requestPermissions(
101
+ this ,
102
+ arrayOf(Manifest .permission.WRITE_EXTERNAL_STORAGE ),
103
+ PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE
104
+ )
72
105
}
73
106
}
74
107
75
108
binding.startStopRecordingButton.setOnClickListener {
109
+ if (isRecording) {
110
+ waveRecorder.stopRecording()
111
+ return @setOnClickListener
112
+ }
76
113
77
- if (! isRecording) {
78
- if (ContextCompat .checkSelfPermission(
79
- this ,
80
- Manifest .permission.RECORD_AUDIO
81
- ) != PackageManager .PERMISSION_GRANTED
82
- ) {
83
- if (ActivityCompat .shouldShowRequestPermissionRationale(
84
- this ,
85
- Manifest .permission.RECORD_AUDIO
86
- )
87
- ) {
88
- ActivityCompat .requestPermissions(
89
- this ,
90
- arrayOf(Manifest .permission.RECORD_AUDIO ),
91
- PERMISSIONS_REQUEST_RECORD_AUDIO
92
- )
93
- } else {
94
- showPermissionSettingsDialog()
95
- }
96
- } else {
97
- waveRecorder.startRecording()
98
- }
114
+ if (ContextCompat .checkSelfPermission(
115
+ this ,
116
+ Manifest .permission.RECORD_AUDIO
117
+ ) == PackageManager .PERMISSION_GRANTED
118
+ ) {
119
+ waveRecorder.startRecording()
120
+ return @setOnClickListener
121
+ }
122
+
123
+ if (ActivityCompat .shouldShowRequestPermissionRationale(
124
+ this ,
125
+ Manifest .permission.RECORD_AUDIO
126
+ )
127
+ ) {
128
+ showPermissionSettingsDialog()
99
129
} else {
100
- waveRecorder.stopRecording()
130
+ ActivityCompat .requestPermissions(
131
+ this ,
132
+ arrayOf(Manifest .permission.RECORD_AUDIO ),
133
+ PERMISSIONS_REQUEST_RECORD_AUDIO
134
+ )
101
135
}
102
136
}
103
137
@@ -109,34 +143,47 @@ class MainActivity : AppCompatActivity() {
109
143
}
110
144
}
111
145
binding.showAmplitudeSwitch.setOnCheckedChangeListener { _, isChecked ->
112
- if (isChecked) {
113
- binding.amplitudeTextView.text = " Amplitude : 0"
114
- binding.amplitudeTextView.visibility = View .VISIBLE
115
- waveRecorder.onAmplitudeListener = {
116
- binding.amplitudeTextView.text = " Amplitude : $it "
117
- }
146
+ if (this ::waveRecorder.isInitialized) {
147
+ if (isChecked) {
148
+ binding.amplitudeTextView.text = " Amplitude : 0"
149
+ binding.amplitudeTextView.visibility = View .VISIBLE
150
+ waveRecorder.onAmplitudeListener = {
151
+ binding.amplitudeTextView.text = " Amplitude : $it "
152
+ }
118
153
119
- } else {
120
- waveRecorder.onAmplitudeListener = null
121
- binding.amplitudeTextView.visibility = View .GONE
154
+ } else {
155
+ waveRecorder.onAmplitudeListener = null
156
+ binding.amplitudeTextView.visibility = View .GONE
157
+ }
122
158
}
123
159
}
124
160
125
161
binding.silenceDetectionSwitch.setOnCheckedChangeListener { _, isChecked ->
126
- waveRecorder.silenceDetection = isChecked
127
- if (isChecked)
128
- Toast .makeText(this , " Noise Suppressor Activated" , Toast .LENGTH_SHORT ).show()
129
-
162
+ if (this ::waveRecorder.isInitialized) {
163
+ waveRecorder.silenceDetection = isChecked
164
+ if (isChecked)
165
+ Toast .makeText(this , " Silence Detection Activated" , Toast .LENGTH_SHORT ).show()
166
+ }
130
167
}
131
168
132
169
binding.noiseSuppressorSwitch.setOnCheckedChangeListener { _, isChecked ->
133
- waveRecorder.noiseSuppressorActive = isChecked
134
- if (isChecked)
135
- Toast .makeText(this , " Noise Suppressor Activated" , Toast .LENGTH_SHORT ).show()
170
+ if (this ::waveRecorder.isInitialized) {
171
+ waveRecorder.noiseSuppressorActive = isChecked
172
+
173
+ if (isChecked) {
174
+ Toast .makeText(this , " Noise Suppressor Activated" , Toast .LENGTH_SHORT ).show()
175
+ }
176
+ }
136
177
137
178
}
138
179
}
139
180
181
+ private fun resetSwitches () {
182
+ binding.showAmplitudeSwitch.isChecked = false
183
+ binding.silenceDetectionSwitch.isChecked = false
184
+ binding.noiseSuppressorSwitch.isChecked = false
185
+ }
186
+
140
187
private fun initRecorder (isSaveToExternalStorage : Boolean ) {
141
188
if (isSaveToExternalStorage)
142
189
initWithExternalStorage(" audioFile" )
@@ -191,9 +238,9 @@ class MainActivity : AppCompatActivity() {
191
238
binding.recordingTextView.visibility = View .GONE
192
239
binding.messageTextView.visibility = View .VISIBLE
193
240
binding.pauseResumeRecordingButton.visibility = View .GONE
194
- binding.showAmplitudeSwitch.isChecked = false
195
241
binding.startStopRecordingButton.text = " START"
196
242
binding.noiseSuppressorSwitch.isEnabled = true
243
+ resetSwitches()
197
244
Toast .makeText(this , " File saved at : $filePath " , Toast .LENGTH_LONG ).show()
198
245
}
199
246
@@ -213,6 +260,10 @@ class MainActivity : AppCompatActivity() {
213
260
if (requestCode == PERMISSIONS_REQUEST_RECORD_AUDIO && grantResults.isNotEmpty() && grantResults[0 ] == PackageManager .PERMISSION_GRANTED ) {
214
261
waveRecorder.startRecording()
215
262
}
263
+ if (requestCode == PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE && grantResults.isNotEmpty() && grantResults[0 ] == PackageManager .PERMISSION_GRANTED ) {
264
+ resetSwitches()
265
+ initRecorder(isSaveToExternalStorage = true )
266
+ }
216
267
}
217
268
218
269
companion object {
@@ -235,35 +286,62 @@ class MainActivity : AppCompatActivity() {
235
286
}
236
287
}
237
288
238
- private fun initWithExternalStorage (fileName : String ): Boolean {
289
+ private fun initWithExternalStorage (fileName : String ) {
239
290
val folderName = " Android-Wave-Recorder"
240
291
val audioUri = if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
241
292
MediaStore .Audio .Media .getContentUri(MediaStore .VOLUME_EXTERNAL_PRIMARY )
242
293
} else MediaStore .Audio .Media .EXTERNAL_CONTENT_URI
243
-
294
+ val path =
295
+ Environment .getExternalStoragePublicDirectory(Environment .DIRECTORY_MUSIC ).absolutePath + " /$folderName /$fileName .wav"
244
296
val contentValues = ContentValues ().apply {
245
297
put(MediaStore .Audio .Media .DISPLAY_NAME , " $fileName .wav" )
246
298
put(MediaStore .Audio .Media .MIME_TYPE , " audio/x-wav" )
247
- put(MediaStore .Audio .Media .RELATIVE_PATH , " Music/$folderName " )
299
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ) {
300
+ put(MediaStore .Audio .Media .RELATIVE_PATH , " Music/$folderName " )
301
+ } else {
302
+ val file = File (path)
303
+ val parentFile = file.parentFile
304
+ if (parentFile != null && ! parentFile.exists()) {
305
+ parentFile.mkdirs()
306
+ }
307
+ put(MediaStore .Audio .AudioColumns .DATA , path)
308
+ }
248
309
}
249
- return try {
250
- contentResolver.insert(audioUri, contentValues)?.also { uri ->
251
- waveRecorder = WaveRecorder (uri, context = this )
310
+
311
+ try {
312
+ val existingUri = contentResolver.query(
313
+ audioUri,
314
+ arrayOf(MediaStore .Audio .Media ._ID ),
315
+ " ${MediaStore .Audio .AudioColumns .DATA } =?" ,
316
+ arrayOf(path),
317
+ null
318
+ )?.use { cursor ->
319
+ if (cursor.moveToFirst()) {
320
+ ContentUris .withAppendedId(
321
+ audioUri,
322
+ cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore .Audio .Media ._ID ))
323
+ )
324
+ } else {
325
+ null
326
+ }
327
+ }
328
+
329
+ val uri = existingUri ? : contentResolver.insert(audioUri, contentValues)
330
+ uri?.let {
331
+ waveRecorder = WaveRecorder (it, context = this )
252
332
.configureWaveSettings {
253
333
sampleRate = 44100
254
- channels = AudioFormat .CHANNEL_IN_STEREO
255
- audioEncoding = AudioFormat .ENCODING_PCM_32BIT
334
+ channels = AudioFormat .CHANNEL_IN_MONO
335
+ audioEncoding = AudioFormat .ENCODING_PCM_FLOAT
256
336
}.configureSilenceDetection {
257
- minAmplitudeThreshold = 80
337
+ minAmplitudeThreshold = 2000
258
338
bufferDurationInMillis = 1500
259
339
preSilenceDurationInMillis = 1500
260
340
}
261
341
filePath = " /Music/$folderName /$fileName .wav"
262
- } ? : throw IOException (" Couldn't create MediaStore entry" )
263
- true
264
- } catch (e: IOException ) {
342
+ }
343
+ } catch (e: Exception ) {
265
344
e.printStackTrace()
266
- false
267
345
}
268
346
}
269
347
@@ -274,10 +352,10 @@ class MainActivity : AppCompatActivity() {
274
352
waveRecorder = WaveRecorder (filePath)
275
353
.configureWaveSettings {
276
354
sampleRate = 44100
277
- channels = AudioFormat .CHANNEL_IN_STEREO
278
- audioEncoding = AudioFormat .ENCODING_PCM_32BIT
355
+ channels = AudioFormat .CHANNEL_IN_MONO
356
+ audioEncoding = AudioFormat .ENCODING_PCM_FLOAT
279
357
}.configureSilenceDetection {
280
- minAmplitudeThreshold = 80
358
+ minAmplitudeThreshold = 2000
281
359
bufferDurationInMillis = 1500
282
360
preSilenceDurationInMillis = 1500
283
361
}
0 commit comments