Skip to content

Commit 485e521

Browse files
committed
address comments
1 parent 84e656a commit 485e521

File tree

2 files changed

+42
-52
lines changed

2 files changed

+42
-52
lines changed

firebase-vertexai/src/main/kotlin/com/google/firebase/vertexai/common/APIController.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import io.ktor.http.contentType
5252
import io.ktor.http.withCharset
5353
import io.ktor.serialization.kotlinx.json.json
5454
import io.ktor.utils.io.charsets.Charset
55-
import io.ktor.websocket.WebSocketSession
5655
import kotlin.math.max
5756
import kotlin.time.Duration
5857
import kotlin.time.Duration.Companion.seconds

firebase-vertexai/src/main/kotlin/com/google/firebase/vertexai/type/LiveSession.kt

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ import kotlinx.serialization.encodeToString
3838
import kotlinx.serialization.json.Json
3939
import 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. */
4442
public class LiveSession
4543
internal 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

Comments
 (0)