Skip to content

Commit

Permalink
AudioRecorder improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pantstamp committed May 30, 2024
1 parent 84df633 commit 5ede5bc
Showing 1 changed file with 67 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.os.Build
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -53,31 +54,40 @@ class AudioRecorder(
private val _isPaused = MutableStateFlow(false)
val isPaused: StateFlow<Boolean> = _isPaused

@Suppress("DEPRECATION")
override fun startRecording(onRecordingFinished: (String) -> Unit) {
this.onRecordingFinished = onRecordingFinished
if (applicationContext.checkSelfPermission(Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_GRANTED) {
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setOutputFile(filePath)
try {
recorder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
MediaRecorder(applicationContext)
} else {
MediaRecorder()
}.apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
setOutputFile(filePath)

try {
prepare()
start()
startRecordingUpdates()
_isRecording.value = true
_isPaused.value = false
} catch (e: IOException) {
// Use a logging framework like Timber
Log.e("AudioRecorder", "Error starting recording")
}
} catch (e: IOException) {
Log.e("AudioRecorder", "Error starting recording: ${e.message}")
onRecordingFinished("")
} catch (e: IllegalStateException) {
Log.e("AudioRecorder", "Illegal state when starting recording: ${e.message}")
onRecordingFinished("")
} catch (e: SecurityException) {
Log.e("AudioRecorder", "Security exception: ${e.message}")
onRecordingFinished("")
}
} else {
// Handle permission not granted case, e.g., throw an exception or show a message
Log.e("AudioRecorder","Permission to record audio not granted")
Log.e("AudioRecorder", "Permission to record audio not granted")
onRecordingFinished("")
}
}

Expand All @@ -88,7 +98,7 @@ class AudioRecorder(
release()
}
} catch (e: IllegalStateException) {
Log.e("AudioRecorder", "Error stopping recording")
Log.e("AudioRecorder", "Error stopping recording: ${e.message}")
} finally {
recorder = null
stopRecordingUpdates()
Expand All @@ -100,16 +110,14 @@ class AudioRecorder(
}

override fun pauseRecording() {
if (recorder != null) {
try {
recorder?.pause()
_isPaused.value = true
stopRecordingUpdates()
} catch (e: IllegalStateException) {
Log.e("AudioRecorder", "Error pausing recording")
}
} else {
Log.e("AudioRecorder","Pause not supported on this device")
try {
recorder?.pause()
_isPaused.value = true
stopRecordingUpdates()
} catch (e: IllegalStateException) {
Log.e("AudioRecorder", "Error pausing recording: ${e.message}")
} catch (e: UnsupportedOperationException) {
Log.e("AudioRecorder", "Pause not supported on this device: ${e.message}")
}
}

Expand All @@ -135,33 +143,62 @@ class AudioRecorder(
recordingParams = params
}

@Suppress("MagicNumber")
private fun startRecordingUpdates() {
recordingJob = coroutineScope.launch {
var elapsedTime = 0
var elapsedTimeInSeconds = 0
while (recorder != null) {
delay(RECORDING_UPDATE_INTERVAL)
elapsedTime++
elapsedTimeInSeconds += (RECORDING_UPDATE_INTERVAL / 1000).toInt()
val fileSize = File(filePath).length()
_recordingUpdates.value = RecordingUpdate(
elapsedTime = elapsedTime,
elapsedTime = elapsedTimeInSeconds,
fileSize = fileSize,
fileSizeLimitExceeded = fileSize >= recordingParams.maxFileSize,
)

if (fileSize >= recordingParams.maxFileSize
|| elapsedTime >= recordingParams.maxDuration) {
if ( maxFileSizeExceeded(fileSize) || maxDurationExceeded(elapsedTimeInSeconds) ) {
stopRecording()
break
}
}
}
}

/**
* Checks if the recorded file size has exceeded the specified maximum file size.
*
* @param fileSize The current size of the recorded file in bytes.
* @return `true` if the file size has exceeded the maximum file size minus the threshold, `false` otherwise.
* If `recordingParams.maxFileSize` is set to `-1L`, this function always returns `false` indicating
* no limit.
*/
private fun maxFileSizeExceeded(fileSize: Long): Boolean = when {
recordingParams.maxFileSize == -1L -> false
else -> fileSize >= recordingParams.maxFileSize - FILE_SIZE_THRESHOLD
}

/**
* Checks if the recording duration has exceeded the specified maximum duration.
*
* @param elapsedTimeInSeconds The elapsed recording time in seconds.
* @return `true` if the elapsed time has exceeded the maximum duration minus the threshold, `false` otherwise.
* If `recordingParams.maxDuration` is set to `-1`, this function always returns `false` indicating
* no limit.
*/
private fun maxDurationExceeded(elapsedTimeInSeconds: Int): Boolean = when {
recordingParams.maxDuration == -1 -> false
else -> elapsedTimeInSeconds >= recordingParams.maxDuration - DURATION_THRESHOLD
}

private fun stopRecordingUpdates() {
recordingJob?.cancel()
}

companion object {
private const val RECORDING_UPDATE_INTERVAL = 1000L
private const val RESUME_DELAY = 500L
private const val RECORDING_UPDATE_INTERVAL = 1000L // in milliseconds
private const val RESUME_DELAY = 500L // in milliseconds
private const val FILE_SIZE_THRESHOLD = 100000L
private const val DURATION_THRESHOLD = 1
}
}

0 comments on commit 5ede5bc

Please sign in to comment.