Skip to content

Instantly share code, notes, and snippets.

@sajjadyousefnia
Created October 31, 2024 16:50
Show Gist options
  • Save sajjadyousefnia/5dfd60c372c655f6a1327e18fd517f40 to your computer and use it in GitHub Desktop.
Save sajjadyousefnia/5dfd60c372c655f6a1327e18fd517f40 to your computer and use it in GitHub Desktop.
package com.sajjady.recorder.Socket
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaCodec
import android.media.MediaFormat
import android.media.MediaRecorder
import io.socket.client.Socket
class AudioStreamer(
private val context: Context,
private val onLiveTextChangeListener: OnLiveTextChangeListener
/*, private val captureUUID: String*/
) {
private var audioRecord: AudioRecord? = null
private var audioEncoder: MediaCodec? = null
private var socket: Socket? = null
// private val serverUrl = AppConstants.apiBaseURL
private val sampleRate = 16000 //44100
private val channelConfig = AudioFormat.CHANNEL_IN_MONO
private val audioFormat = AudioFormat.ENCODING_PCM_16BIT
private val bufferSize =
1024//AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
var lastSendTime = System.currentTimeMillis()
val accumulatedData = mutableListOf<Byte>() // Temporary storage for 500ms of audio data
// private val deviceName = "android"
private lateinit var socketManager: SocketManager
@Volatile
private var isStreaming = false
init {
initializeSocket()
}
// initialize and start socket and
private fun initializeSocket() {
socketManager = SocketManager()
socketManager.setSocketLiveListener(object : SocketLiveListener {
override fun onStartMicrophone() {
startStreaming()
}
override fun onMicrophoneBlob() {
}
override fun onReceived(data: String) {
}
override fun onStopMicrophone() {
}
override fun onReceiveFault() {
}
override fun onFinish() {
}
})
socketManager.connect()
socketManager.startLiveStreaming()
}
@android.annotation.SuppressLint("MissingPermission")
fun startStreaming() {
setupAudioRecord()
setupAudioEncoder()
audioRecord?.startRecording()
audioEncoder?.start()
isStreaming = true
// thread(start = true) {
captureAndEncodeLoop()
// }
}
@SuppressLint("MissingPermission")
private fun setupAudioRecord() {
audioRecord = AudioRecord.Builder()
.setAudioSource(MediaRecorder.AudioSource.VOICE_RECOGNITION)
.setAudioFormat(
AudioFormat.Builder()
.setEncoding(audioFormat)
.setSampleRate(sampleRate)
.setChannelMask(channelConfig)
.build()
)
.setBufferSizeInBytes(bufferSize)
.build()
}
private fun setupAudioEncoder() {
val format = MediaFormat.createAudioFormat(
MediaFormat.MIMETYPE_AUDIO_OPUS/*MIMETYPE_AUDIO_AAC*/,
sampleRate,
1
)
// format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)
format.setInteger(MediaFormat.KEY_BIT_RATE, 16000 /*64000*/)
audioEncoder =
MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_OPUS/*MIMETYPE_AUDIO_AAC*/)
audioEncoder?.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
}
private fun captureAndEncodeLoop() {
// FFmpeg command setup
val inputBuffer = ByteArray(bufferSize)
val bufferInfo = MediaCodec.BufferInfo()
while (isStreaming) {
val readResult = audioRecord?.read(inputBuffer, 0, inputBuffer.size) ?: 0
if (readResult > 0) {
// Add read data to accumulatedData
accumulatedData.addAll(inputBuffer.take(readResult))
// Check if 500ms has passed
val currentTime = System.currentTimeMillis()
if (currentTime - lastSendTime >= 500) {
// Encode and send the data
val dataToSend = accumulatedData.toByteArray()
/* encode(
dataToSend,
dataToSend.size,
bufferInfo
)*/ // Encodes before sending, if needed
// Clear accumulatedData and reset the timer
accumulatedData.clear()
lastSendTime = currentTime
// encode(inputBuffer, readResult, bufferInfo)
socketManager.emitAudioData(dataToSend!!)
}
}
}
audioEncoder?.signalEndOfInputStream()
releaseResources()
}
private fun releaseResources() {
audioRecord?.stop()
audioRecord?.release()
audioRecord = null
audioEncoder?.stop()
audioEncoder?.release()
audioEncoder = null
socket?.disconnect()
}
fun stopStreaming() {
isStreaming = false
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment