@@ -38,9 +38,7 @@ import kotlinx.serialization.encodeToString
3838import kotlinx.serialization.json.Json
3939import kotlinx.serialization.json.JsonNull
4040
41- /* *
42- * Represents a live WebSocket session capable of streaming content to and from the server.
43- */
41+ /* * Represents a live WebSocket session capable of streaming content to and from the server. */
4442public class LiveSession
4543internal constructor (
4644 private val session: ClientWebSocketSession ? ,
@@ -54,6 +52,15 @@ internal constructor(
5452 private var receiveChannel: Channel <Frame > = Channel ()
5553 private var functionCallChannel: Channel <List <FunctionCallPart >> = Channel ()
5654
55+ private companion object {
56+ val MIN_BUFFER_SIZE =
57+ AudioTrack .getMinBufferSize(
58+ 24000 ,
59+ AudioFormat .CHANNEL_OUT_MONO ,
60+ AudioFormat .ENCODING_PCM_16BIT
61+ )
62+ }
63+
5764 internal class ClientContentSetup (val turns : List <Content .Internal >, val turnComplete : Boolean ) {
5865 @Serializable
5966 internal class Internal (@SerialName(" client_content" ) val clientContent : ClientContent ) {
@@ -136,32 +143,17 @@ internal constructor(
136143 }
137144 }
138145
139- private fun sendAudioDataToServer () {
140- CoroutineScope (Dispatchers .Default ).launch {
141- val minBufferSize =
142- AudioTrack .getMinBufferSize(
143- 24000 ,
144- AudioFormat .CHANNEL_OUT_MONO ,
145- AudioFormat .ENCODING_PCM_16BIT
146- )
147- var bytesRead = 0
148- var recordedData = ByteArray (minBufferSize * 2 )
149- while (true ) {
150- if (! isRecording) {
151- break
152- }
153- val byteArr = audioQueue.poll()
154- if (byteArr != null ) {
155- bytesRead + = byteArr.size
156- recordedData + = byteArr
157- if (bytesRead >= minBufferSize) {
158- sendMediaStream(listOf (MediaData (" audio/pcm" , recordedData)))
159- bytesRead = 0
160- recordedData = byteArrayOf()
161- }
162- } else {
163- continue
164- }
146+ private suspend fun sendAudioDataToServer () {
147+ var offset = 0
148+ val audioBuffer = ByteArray (MIN_BUFFER_SIZE * 2 )
149+ while (isRecording) {
150+ val receivedAudio = audioQueue.poll() ? : continue
151+ receivedAudio.copyInto(audioBuffer, offset)
152+ offset + = receivedAudio.size
153+ if (offset >= MIN_BUFFER_SIZE ) {
154+ sendMediaStream(listOf (MediaData (" audio/pcm" , audioBuffer)))
155+ audioBuffer.fill(0 )
156+ offset = 0
165157 }
166158 }
167159 }
@@ -172,29 +164,28 @@ internal constructor(
172164 if (! isRecording) {
173165 cancel()
174166 }
175- if (it.status == LiveContentResponse .Status .INTERRUPTED ) {
176- while (! playBackQueue.isEmpty()) playBackQueue.poll()
177- } else if (it.status == LiveContentResponse .Status .NORMAL ) {
178- if (! it.functionCalls.isNullOrEmpty()) {
179- functionCallChannel.send(it.functionCalls)
180- } else {
181- playBackQueue.add(it.data!! .parts[0 ].asInlineDataPartOrNull()!! .inlineData)
182- }
167+ when (it.status) {
168+ LiveContentResponse .Status .INTERRUPTED ->
169+ while (! playBackQueue.isEmpty()) playBackQueue.poll()
170+ LiveContentResponse .Status .NORMAL ->
171+ if (! it.functionCalls.isNullOrEmpty()) {
172+ functionCallChannel.send(it.functionCalls)
173+ } else {
174+ val audioData = it.data?.parts?.get(0 )?.asInlineDataPartOrNull()?.inlineData
175+ if (audioData != null ) {
176+ playBackQueue.add(audioData)
177+ }
178+ }
183179 }
184180 }
185181 }
186182 }
187183
188184 private fun playServerResponseAudio () {
189185 CoroutineScope (Dispatchers .IO ).launch {
190- while (true ) {
191- if (! isRecording) {
192- break
193- }
194- val x = playBackQueue.poll()
195- if (x != null ) {
196- audioHelper!! .playAudio(x)
197- }
186+ while (isRecording) {
187+ val x = playBackQueue.poll() ? : continue
188+ audioHelper?.playAudio(x)
198189 }
199190 }
200191 }
@@ -213,7 +204,7 @@ internal constructor(
213204 audioHelper!! .setupAudioTrack()
214205 val scope = CoroutineScope (Dispatchers .Default )
215206 fillRecordedAudioQueue()
216- sendAudioDataToServer()
207+ CoroutineScope ( Dispatchers . Default ).launch { sendAudioDataToServer() }
217208 fillServerResponseAudioQueue()
218209 playServerResponseAudio()
219210 delay(1000 )
@@ -223,12 +214,12 @@ internal constructor(
223214 public fun stopAudioConversation () {
224215 stopReceiving()
225216 isRecording = false
226- if (audioHelper != null ) {
227- while (! playBackQueue.isEmpty()) playBackQueue.poll()
228- while (! audioQueue.isEmpty()) audioQueue.poll()
229- audioHelper!! .release()
230- audioHelper = null
217+ audioHelper?.let {
218+ while (playBackQueue.isNotEmpty()) playBackQueue.poll()
219+ while (audioQueue.isNotEmpty()) audioQueue.poll()
220+ it.release()
231221 }
222+ audioHelper = null
232223 }
233224
234225 /* *
0 commit comments