Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70f806ef80 | |||
| 0773d9496d | |||
| 1a4857ed62 | |||
| 962d814318 | |||
| 9276a92c83 | |||
| d16896c4b4 | |||
| 20050d4077 | |||
| 79760d1b2e |
@@ -79,8 +79,8 @@ android {
|
||||
applicationId "com.ariacockpit"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 807
|
||||
versionName "0.0.8.7"
|
||||
versionCode 901
|
||||
versionName "0.0.9.1"
|
||||
// Fallback fuer Libraries mit Product Flavors
|
||||
missingDimensionStrategy 'react-native-camera', 'general'
|
||||
}
|
||||
|
||||
@@ -78,9 +78,12 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
|
||||
val encoding = AudioFormat.ENCODING_PCM_16BIT
|
||||
val minBuf = AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding)
|
||||
val bytesPerSecond = sampleRate * channels * 2 // 16-bit = 2 bytes
|
||||
// Buffer muss mindestens PREROLL + etwas Spielraum fassen.
|
||||
val prerollTarget = (bytesPerSecond * prerollSec).toInt()
|
||||
val bufferSize = (minBuf * 32).coerceAtLeast(prerollTarget * 2)
|
||||
// Buffer entkoppelt von Preroll — fester ~4s-Buffer. OnePlus A12
|
||||
// mit USAGE_ASSISTANT laeuft AudioTrack erst ab ~3s gepufferter
|
||||
// Daten an. Wir padden Kurztexte vor play() auf 3s (siehe Block
|
||||
// nach mainLoop), Buffer braucht ~1s Headroom weil write() blockt.
|
||||
val bufferSize = (bytesPerSecond * 4).coerceAtLeast(minBuf * 8)
|
||||
prerollBytes = prerollTarget
|
||||
bytesBuffered = 0
|
||||
playbackStarted = false
|
||||
@@ -89,7 +92,12 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
|
||||
val newTrack = AudioTrack.Builder()
|
||||
.setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANT)
|
||||
// USAGE_MEDIA statt USAGE_ASSISTANT — auf OnePlus A12 stallt
|
||||
// AudioTrack mit USAGE_ASSISTANT wenn play() nach komplettem
|
||||
// Buffer-Fuellen called wird (pos bleibt 0). USAGE_MEDIA ist
|
||||
// robust. AudioFocus wird eh separat ueber AudioFocusModule
|
||||
// gehandhabt, nicht ueber dieses USAGE-Tag.
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build(),
|
||||
)
|
||||
@@ -156,16 +164,11 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
|
||||
val data = queue.poll(50, java.util.concurrent.TimeUnit.MILLISECONDS)
|
||||
if (data == null) {
|
||||
if (endRequested) {
|
||||
// Falls wir vor Pre-Roll enden (kurzer Text): trotzdem abspielen
|
||||
if (!playbackStarted) {
|
||||
try {
|
||||
t.play()
|
||||
playbackStarted = true
|
||||
Log.i(TAG, "Playback gestartet VOR Pre-Roll (kurzer Text, ${bytesBuffered}B gepuffert)")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "play() fallback failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
// Bei kurzem Text NICHT hier play() callen — erst nach
|
||||
// Trailing-Silence + Padding (siehe Block nach mainLoop),
|
||||
// damit AudioTrack mit komplett gefuelltem Buffer startet.
|
||||
// OnePlus A12: AudioTrack startet nicht zuverlaessig wenn
|
||||
// play() bei dünnem Buffer gerufen wird.
|
||||
break@mainLoop
|
||||
}
|
||||
// Underrun-Schutz: Stille reinfuettern wenn der AudioTrack-
|
||||
@@ -228,6 +231,31 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
|
||||
}
|
||||
bytesBuffered += silence.size
|
||||
}
|
||||
// Bei kurzem Text (play() noch nicht gestartet): Buffer auf min.
|
||||
// 3s padden + DANN play(). Auf OnePlus A12 startet AudioTrack
|
||||
// bei < 3s Buffer-Inhalt nicht — pos bleibt auf 0 stehen.
|
||||
// (2s war zu wenig, 8 Worte ~2.5s gingen, 3 Worte ~1s nicht.)
|
||||
if (!playbackStarted && !writerShouldStop) {
|
||||
val minStartBytes = bytesPerSecond * 3
|
||||
if (bytesBuffered < minStartBytes) {
|
||||
val padBytes = (minStartBytes - bytesBuffered.toInt()) and 0x7FFFFFFE
|
||||
val pad = ByteArray(padBytes)
|
||||
var padOff = 0
|
||||
while (padOff < pad.size && !writerShouldStop) {
|
||||
val w = t.write(pad, padOff, pad.size - padOff)
|
||||
if (w <= 0) break
|
||||
padOff += w
|
||||
}
|
||||
bytesBuffered += pad.size
|
||||
}
|
||||
try {
|
||||
t.play()
|
||||
playbackStarted = true
|
||||
Log.i(TAG, "Playback gestartet (kurzer Text, ${bytesBuffered}B komplett gepuffert)")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "play() short-text failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Writer-Thread Fehler: ${e.message}")
|
||||
} finally {
|
||||
@@ -237,12 +265,21 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
|
||||
val totalFrames = (bytesBuffered / streamBytesPerFrame).toInt()
|
||||
var lastPos = -1
|
||||
var stalledCount = 0
|
||||
var retried = false
|
||||
while (!writerShouldStop) {
|
||||
val pos = t.playbackHeadPosition
|
||||
if (pos >= totalFrames) break
|
||||
// Safety: wenn Position 2s nicht mehr vorwaerts → AudioTrack hing
|
||||
if (pos == lastPos) {
|
||||
stalledCount++
|
||||
// Nach 500ms Stillstand: AudioTrack-Quirk auf manchen
|
||||
// Geraeten (OnePlus A12) — play() nochmal anstossen.
|
||||
if (stalledCount == 10 && pos == 0 && !retried) {
|
||||
retried = true
|
||||
Log.w(TAG, "playback nicht angefahren — retry play()")
|
||||
try { t.play() } catch (e: Exception) {
|
||||
Log.w(TAG, "retry play() failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
if (stalledCount > 40) {
|
||||
Log.w(TAG, "playback stalled at $pos/$totalFrames — give up")
|
||||
break
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "aria-cockpit",
|
||||
"version": "0.0.8.7",
|
||||
"version": "0.0.9.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"android": "react-native run-android",
|
||||
|
||||
Reference in New Issue
Block a user