Gerçek Zamanlı PCM İşlemede Deterministik Zaman Ekseni
PCM medya zamanı, sistem saatından değil kanal başına işlenmiş gerçek örnek sayısından türetilmelidir. Bu yaklaşım stereo veri, kısmi okumalar, son çerçeve, yuvarlama ve segment sınırlarında tutarlı zaman damgaları sağlar.
t = n / fₛ
Burada n, kanal başına işlenmiş örnek sayısı; fₛ ise örnekleme hızıdır. Örneğin 16 kHz bir akışta 16.000 örnek tam olarak bir saniyedir:
t = 16000 / 16000 = 1 saniyeDeterministik bir PCM hattında aynı girdi, her yürütmede aynı çerçeveleri, segment sınırlarını ve zaman damgalarını üretmelidir. Bunun temel koşulu, ana zaman durumunun bir duvar saati değil, işlenmiş örnek sayacı olmasıdır.
Örnek, bayt ve kanal sayısını ayırmak
16 bit signed little-endian PCM'de kanal başına bir örnek iki bayttır:
bytesPerSample = 216 kHz'de 10 ms'lik mono çerçeve 160 örnek ve 320 bayttır:
samplesPerFrame = 16000 × 10 / 1000 = 160
monoFrameBytes = 160 × 2 = 320Stereo interleaved PCM'de aynı süre için veri miktarı 640 bayttır:
stereoFrameBytes = 160 × 2 kanal × 2 bayt = 640Buna rağmen zaman ekseni iki katına çıkmaz. Stereo çerçeve L₀, R₀, L₁, R₁, ... düzeninde toplam 320 adet 16 bit değer taşır; ancak her kanal yalnızca 160 zaman konumuna sahiptir. Bu nedenle sayaç yalnızca kanal başına geçerli örnek sayısıyla güncellenmelidir:
processedSamples += validSamplesPerChannel640 baytlık stereo çerçevede 640 / 2 = 320 adet 16 bit değer, dolayısıyla 320 / 2 = 160 zaman örneği vardır. processedSamples += 320 işlemi, 10 ms'lik veriyi yanlış biçimde 20 ms olarak kaydeder. Kanal ayrıştırılırken de sayaç her stereo çerçeve için yalnızca bir kez artırılmalıdır.
Medya zamanını örnek konumundan üretmek
Sistem saati işlem zamanını ölçer; medya zamanını değil. Dosyadan okunan bir saatlik ses birkaç dakikada işlenebilir; ağ veya kuyruk gecikmeleri ise bir saniyelik sesin daha geç işlenmesine neden olabilir. Bu nedenle System.nanoTime(), DateTime.UtcNow veya benzeri duvar saati ölçümleri segmentin ses içindeki konumunu belirlememelidir.
Çerçevenin örnek sınırları doğrudan sayaçtan türetilir:
frameStart = processedSamples
frameEnd = frameStart + validSamples
processedSamples = frameEndMilisaniye gösterimi yalnızca dışa aktarım sırasında hesaplanmalıdır:
startMs = frameStart × 1000 / sampleRate
endMs = frameEnd × 1000 / sampleRate16 kHz için timeMs = sampleIndex / 16 sadeleşmesi geçerlidir; genel kodda ise sampleRate açıkça kullanılmalıdır. Ana sayaç long olmalıdır. 32 bit sayaç 16 kHz'de yaklaşık 37,3 saat sonra sınırı aşabilir. Çarpma da long aritmetiğiyle yapılmalıdır:
final long timeMs = (long) sampleIndex * 1000L / sampleRate;Tamsayı bölme aşağı yuvarlar. Başlangıç ve bitiş zamanları aynı dönüşümle, mutlak örnek konumundan hesaplandığında ardışık segmentler arasında yapay boşluk veya çakışma oluşmaz. Çerçeve sürelerini ayrı ayrı yuvarlayıp toplamak yerine toplam örnek konumu dönüştürülmelidir.
Kısmi okumalar ve son çerçevenin geçerli uzunluğu
Bir read çağrısının istenen tamponu tamamen dolduracağı varsayılamaz. Örneğin 320 baytlık mono çerçeve, 120 ve 200 baytlık iki okumayla gelebilir. Tampon dolana veya gerçek dosya sonuna ulaşılana kadar okuma sürdürülmelidir:
offset = 0
while offset < frameBytes:
n = read(buffer, offset, frameBytes - offset)
if n < 0:
break
offset += nGenel stream soyutlamalarında read işleminin sıfır döndürmesi sonsuz döngü riski bakımından ayrıca değerlendirilmelidir. Geçerli kanal örneği sayısı şöyledir:
validSamples = bytesRead / (bytesPerSample × channelCount)Stereo 16 bit PCM için bu değer bytesRead / 4 olur. Ancak önce aşağıdaki koşul doğrulanmalıdır:
bytesRead mod (bytesPerSample × channelCount) = 0Stereo 16 bit akışta tek zaman örneği dört bayttır. Dosya sonunda kalan 2 veya 3 bayt, eksik kanal örneği anlamına gelir; bu veri sessizce tamamlanmamalı veya önceki tampon içeriğiyle birleştirilmemelidir.
Son çerçeve tam çerçeve uzunluğunda olmayabilir. Örneğin tampon kapasitesi 160 örnek iken dosya sonunda yalnızca 73 örnek geçerli olabilir. Algoritma yalnızca bu 73 örneği gerçek medya verisi kabul etmelidir. Güvenli seçenekler şunlardır:
- Algoritma destekliyorsa yalnızca geçerli örnekleri işlemek.
- Son çerçeveyi açıkça sıfırlarla tamamlamak.
- Yalnızca tam çerçeve kabul eden algoritmalarda kalan örnekleri belirlenmiş bir sonlandırma politikasıyla yönetmek.
Sıfırla tamamlama kullanılırsa medya ve model giriş uzunlukları ayrılmalıdır:
mediaSamples = 73
processingSamples = 160Zaman sayacı yalnızca mediaSamples kadar ilerler. Aksi halde her dosyanın sonuna en fazla bir çerçevelik yapay süre eklenir.
Yarı açık segment aralıkları ve PCM yerleşimi
Segmentler yarı açık örnek aralıklarıyla temsil edilmelidir:
[startSample, endSample)Ardışık aralıklar [A, B) ve [B, C) olduğunda ne mükerrer örnek ne de boşluk bulunur. Aynı sözleşme PCM bayt konumlarına doğrudan uygulanır:
byteStart = sampleStart × bytesPerSample × channelCount
byteEnd = sampleEnd × bytesPerSample × channelCountBu gösterim dosya arama işlemleri, HTTP aralıkları, NIO tampon sınırları ve dizi indeksleriyle uyumludur.
16 bit little-endian örnek yeniden oluşturulurken alt bayt maskelenmelidir:
final int sample = (buffer[index + 1] << 8) | (buffer[index] & 0xFF);Java'da byte işaretli olduğundan, alt baytın maskelenmemesi işaret genişletme hatasına yol açabilir. Stereo interleaved örnek dizisinde zaman sırası korunur:
left[n] = stereo[2n]
right[n] = stereo[2n + 1]Buradaki stereo, 16 bit örnek dizisidir; bayt dizisinde indeksler ayrıca bytesPerSample ile ölçeklenir.
Çerçeve süresi, durum eşikleri ve performans ölçümü
VAD eşikleri çerçeve sayısıyla tanımlandığında çerçeve süresine bağlıdır. Örneğin 10 ms çerçevede confirmFrames = 4 değeri 40 ms, silenceFrames = 30 değeri 300 ms anlamına gelir. Çerçeve süresi 20 ms'ye çıkarılıp sayaçlar değiştirilmezse bu eşikler sırasıyla 80 ms ve 600 ms olur.
Eşikler mümkünse zaman veya örnek cinsinden tanımlanarak çerçeve sayısına dönüştürülmelidir:
confirmFrames =
ceil(confirmMs × sampleRate /
(1000 × samplesPerFrame))Çerçeve süresi; VAD karar sıklığını, konuşmacı değişim gecikmesini, segment sonlandırma çözünürlüğünü, minimum algılanabilir konuşma süresini, tampon boyutunu ve çağrı başına işlem maliyetini etkiler.
Medya süresi ile işlem süresi ayrı ölçülmelidir:
mediaDuration = processedSamples / sampleRate
processingDuration = monotonicClockEnd - monotonicClockStart
RTF = processingDuration / mediaDuration100 saniyelik ses 12 saniyede işlenirse RTF = 0,12 olur. Medya süresi örnek sayacından, işlem süresi ise monotonik saatten türetilmelidir; duvar saati değişiklikleri performans ölçümünü etkilememelidir.
Ortak zaman sözleşmesi
VAD, konuşmacı seçimi, segment birleştirme, ASR, JSON çıktısı ve oynatıcı farklı zaman temsilleri kullanabilir. Güvenilir tasarımda bunların ortak ana birimi kanal başına örnek indeksidir. Milisaniye, PCM bayt konumu, çerçeve numarası ve oynatıcı aralığı bu birimden türetilir.
Örneğin interleaved bir akışta örnek ofsetinin bayt karşılığı şöyledir:
byteOffset = sampleOffset × channelCount × bytesPerSampleKanalı ayrılmış mono dosyada ise:
monoByteOffset = sampleOffset × bytesPerSampleZaman değişmez; yalnızca fiziksel veri yerleşimi değişir. Kısmi okumalar, kanal sayısı, son çerçeve padding'i ve işlem gecikmesi temel örnek sayacını değiştirmemelidir. Zaman damgası, işlenmiş gerçek örnek sayısının dışa sunulan gösterimidir.