Compare commits

..

4 Commits

Author SHA1 Message Date
duffyduck 1a4857ed62 release: bump version to 0.0.9.0 2026-05-10 14:26:41 +02:00
duffyduck 962d814318 fix(audio): kurze TTS — Padding auf 3s erhoeht (OnePlus A12 Hard-Threshold)
Test mit 96000B (2s) Padding zeigte: AudioTrack stallt immer noch mit
pos=0/48000. Ab 8 Worten (~2.5s) geht's — der Hard-Threshold liegt also
zwischen 2s und 3s. Padding auf 3s, Buffer auf 4s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:25:43 +02:00
duffyduck 9276a92c83 release: bump version to 0.0.8.9 2026-05-10 14:20:43 +02:00
duffyduck d16896c4b4 fix(audio): kurze TTS-Texte — play() erst NACH Buffer-Fuellung mit Padding
Auf OnePlus A12 startet AudioTrack nicht zuverlaessig wenn play() bei
duennem Buffer gerufen wird (pos blieb 0/34112 trotz 71KB Daten + Retry).

Neue Reihenfolge bei kurzem Stream:
1. Daten in Buffer schreiben (mainLoop)
2. Trailing-Silence (0.3s)
3. Padding bis min. 2s gepuffert
4. DANN erst play()

Buffer auf 3s erhoeht damit blockingem write() noch Headroom bleibt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:19:45 +02:00
3 changed files with 38 additions and 18 deletions
+2 -2
View File
@@ -79,8 +79,8 @@ android {
applicationId "com.ariacockpit" applicationId "com.ariacockpit"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 808 versionCode 900
versionName "0.0.8.8" versionName "0.0.9.0"
// Fallback fuer Libraries mit Product Flavors // Fallback fuer Libraries mit Product Flavors
missingDimensionStrategy 'react-native-camera', 'general' missingDimensionStrategy 'react-native-camera', 'general'
} }
@@ -79,11 +79,11 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
val minBuf = AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding) val minBuf = AudioTrack.getMinBufferSize(sampleRate, channelConfig, encoding)
val bytesPerSecond = sampleRate * channels * 2 // 16-bit = 2 bytes val bytesPerSecond = sampleRate * channels * 2 // 16-bit = 2 bytes
val prerollTarget = (bytesPerSecond * prerollSec).toInt() val prerollTarget = (bytesPerSecond * prerollSec).toInt()
// Buffer entkoppelt von Preroll — fester ~2s-Buffer reicht. Wenn er // Buffer entkoppelt von Preroll — fester ~4s-Buffer. OnePlus A12
// an Preroll gekoppelt ist (z.B. 7s bei preroll=3.5s) und nur kurz // mit USAGE_ASSISTANT laeuft AudioTrack erst ab ~3s gepufferter
// gefuettert wird, stallt AudioTrack auf manchen Geraeten (OnePlus // Daten an. Wir padden Kurztexte vor play() auf 3s (siehe Block
// Android 12: pos bleibt 0 obwohl play() lief). // nach mainLoop), Buffer braucht ~1s Headroom weil write() blockt.
val bufferSize = (bytesPerSecond * 2).coerceAtLeast(minBuf * 8) val bufferSize = (bytesPerSecond * 4).coerceAtLeast(minBuf * 8)
prerollBytes = prerollTarget prerollBytes = prerollTarget
bytesBuffered = 0 bytesBuffered = 0
playbackStarted = false playbackStarted = false
@@ -159,16 +159,11 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
val data = queue.poll(50, java.util.concurrent.TimeUnit.MILLISECONDS) val data = queue.poll(50, java.util.concurrent.TimeUnit.MILLISECONDS)
if (data == null) { if (data == null) {
if (endRequested) { if (endRequested) {
// Falls wir vor Pre-Roll enden (kurzer Text): trotzdem abspielen // Bei kurzem Text NICHT hier play() callen — erst nach
if (!playbackStarted) { // Trailing-Silence + Padding (siehe Block nach mainLoop),
try { // damit AudioTrack mit komplett gefuelltem Buffer startet.
t.play() // OnePlus A12: AudioTrack startet nicht zuverlaessig wenn
playbackStarted = true // play() bei dünnem Buffer gerufen wird.
Log.i(TAG, "Playback gestartet VOR Pre-Roll (kurzer Text, ${bytesBuffered}B gepuffert)")
} catch (e: Exception) {
Log.w(TAG, "play() fallback failed: ${e.message}")
}
}
break@mainLoop break@mainLoop
} }
// Underrun-Schutz: Stille reinfuettern wenn der AudioTrack- // Underrun-Schutz: Stille reinfuettern wenn der AudioTrack-
@@ -231,6 +226,31 @@ class PcmStreamPlayerModule(reactContext: ReactApplicationContext) : ReactContex
} }
bytesBuffered += silence.size 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) { } catch (e: Exception) {
Log.w(TAG, "Writer-Thread Fehler: ${e.message}") Log.w(TAG, "Writer-Thread Fehler: ${e.message}")
} finally { } finally {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "aria-cockpit", "name": "aria-cockpit",
"version": "0.0.8.8", "version": "0.0.9.0",
"private": true, "private": true,
"scripts": { "scripts": {
"android": "react-native run-android", "android": "react-native run-android",