mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-10 16:02:25 +01:00
audio: when the car runs out of sound, Gearslip runs a little further ahead of it
This commit is contained in:
1 parent
7d02ac8b75
commit
d1f67bb64d
1 file changed
+42
@@ -52,6 +52,10 @@ class AudioLink(
|
|||||||
private var bytes = 0L
|
private var bytes = 0L
|
||||||
private var dropped = 0
|
private var dropped = 0
|
||||||
@Volatile private var configTimer: Thread? = null
|
@Volatile private var configTimer: Thread? = null
|
||||||
|
/** Underflows the car reported this drive, and the silence still to slip in ahead of the next chunk. */
|
||||||
|
private var underflows = 0
|
||||||
|
@Volatile private var padMs = 0
|
||||||
|
@Volatile private var pendingPadMs = 0
|
||||||
private val ackLock = Object()
|
private val ackLock = Object()
|
||||||
/** Only for a sink that takes AAC and nothing else; PCM sinks get the capture as it is. */
|
/** Only for a sink that takes AAC and nothing else; PCM sinks get the capture as it is. */
|
||||||
@Volatile private var aac: AacEncoder? = null
|
@Volatile private var aac: AacEncoder? = null
|
||||||
@@ -180,6 +184,8 @@ class AudioLink(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MSG_UNDERFLOW -> onUnderflow()
|
||||||
|
|
||||||
MSG_ACK -> {
|
MSG_ACK -> {
|
||||||
val acked = Wire.varint(Wire.fields(body), 2)?.toInt() ?: 1
|
val acked = Wire.varint(Wire.fields(body), 2)?.toInt() ?: 1
|
||||||
synchronized(ackLock) {
|
synchronized(ackLock) {
|
||||||
@@ -221,6 +227,26 @@ class AudioLink(
|
|||||||
if (had && (state == FOCUS_STATE_LOSS || state == FOCUS_STATE_LOSS_TRANSIENT)) onTakenBack()
|
if (had && (state == FOCUS_STATE_LOSS || state == FOCUS_STATE_LOSS_TRANSIENT)) onTakenBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The car ran out of sound to play (AudioUnderflowNotification, no body): its buffer emptied
|
||||||
|
* before the next chunk arrived, which the driver hears as a gap. Capture runs in real time, so
|
||||||
|
* the car's buffer only ever holds what the window lets through. A little silence slipped in
|
||||||
|
* ahead of the next chunk puts it that far ahead, so the next slow patch is soaked up rather
|
||||||
|
* than heard. Capped, as each step adds a little delay to the music.
|
||||||
|
*/
|
||||||
|
private fun onUnderflow() {
|
||||||
|
underflows++
|
||||||
|
val step = if (started && padMs < MAX_PAD_MS) PAD_STEP_MS else 0
|
||||||
|
padMs += step
|
||||||
|
pendingPadMs += step
|
||||||
|
if (underflows <= 3 || underflows % 20 == 0) {
|
||||||
|
GearslipLog.w("$tag: <- AudioUnderflow ($underflows this drive) - running ${padMs}ms ahead now")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Silence for [ms], whole frames of this sink's format. */
|
||||||
|
private fun silence(ms: Int): ByteArray = ByteArray(sampleRate * ms / 1000 * channels * 2)
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
if (!ready || started) return
|
if (!ready || started) return
|
||||||
if (asksFocus && !focusRequested) {
|
if (asksFocus && !focusRequested) {
|
||||||
@@ -288,10 +314,23 @@ class AudioLink(
|
|||||||
GearslipLog.i("$tag: -> Stop, AudioFocusRequest(RELEASE) after $chunks chunks / $bytes bytes")
|
GearslipLog.i("$tag: -> Stop, AudioFocusRequest(RELEASE) after $chunks chunks / $bytes bytes")
|
||||||
chunks = 0
|
chunks = 0
|
||||||
bytes = 0
|
bytes = 0
|
||||||
|
// A new stream starts with the car's buffer empty again: earn the lead back if it's needed.
|
||||||
|
padMs = 0
|
||||||
|
pendingPadMs = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun write(pcm: ByteArray, size: Int) {
|
override fun write(pcm: ByteArray, size: Int) {
|
||||||
if (!started) return
|
if (!started) return
|
||||||
|
val pad = pendingPadMs
|
||||||
|
if (pad > 0) {
|
||||||
|
pendingPadMs = 0
|
||||||
|
val quiet = silence(pad)
|
||||||
|
writeNow(quiet, quiet.size)
|
||||||
|
}
|
||||||
|
writeNow(pcm, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeNow(pcm: ByteArray, size: Int) {
|
||||||
val encoder = aac
|
val encoder = aac
|
||||||
if (encoder == null) {
|
if (encoder == null) {
|
||||||
send(pcm.copyOf(size))
|
send(pcm.copyOf(size))
|
||||||
@@ -377,6 +416,9 @@ class AudioLink(
|
|||||||
private const val MSG_CONFIG = 32771
|
private const val MSG_CONFIG = 32771
|
||||||
private const val MSG_ACK = 32772
|
private const val MSG_ACK = 32772
|
||||||
private const val MSG_SETUP_ANSWER = 255
|
private const val MSG_SETUP_ANSWER = 255
|
||||||
|
private const val MSG_UNDERFLOW = 32779 // 0x800B, car to phone, no body
|
||||||
|
private const val PAD_STEP_MS = 60
|
||||||
|
private const val MAX_PAD_MS = 240
|
||||||
private const val STATUS_READY = 2
|
private const val STATUS_READY = 2
|
||||||
private const val FOCUS_GAIN = 1
|
private const val FOCUS_GAIN = 1
|
||||||
private const val FOCUS_GAIN_TRANSIENT_MAY_DUCK = 3
|
private const val FOCUS_GAIN_TRANSIENT_MAY_DUCK = 3
|
||||||
|
|||||||
Reference in new issue
Block a user