Ses gecikmesi

Gecikme, bir sinyalin sistem içinde ilerlemesi için gereken süredir. Ses uygulamalarıyla ilgili yaygın gecikme türleri şunlardır:

  • Ses çıkışı gecikmesi, bir uygulama tarafından oluşturulan ses örneği ile kulaklık jakı veya yerleşik hoparlör üzerinden çalınan örnek arasındaki süredir.
  • Ses girişi gecikmesi, bir cihazın ses girişi (ör. mikrofon) tarafından alınan ses sinyali ile aynı ses verilerinin bir uygulama tarafından kullanılabilir hale gelmesi arasındaki süredir.
  • Gidiş dönüş gecikmesi; giriş gecikmesi, uygulama işleme süresi ve çıkış gecikmesinin toplamıdır.

  • Dokunma gecikmesi, kullanıcının ekrana dokunması ile bir uygulama tarafından alınan dokunma etkinliği arasında geçen süredir.
  • Isıma gecikmesi, veriler bir arabellekte ilk kez sıraya alındığında ses ardışık düzeninin başlatılması için gereken süredir.

Bu sayfada, düşük gecikmeli giriş ve çıkışla ses uygulamanızı nasıl geliştireceğiniz ve ısınma gecikmesinin nasıl önleneceği açıklanmaktadır.

Gecikmeyi ölç

İlk örneğin ses yoluna tam olarak ne zaman gönderileceğini bilmek gerektirdiği için ses giriş ve çıkış gecikmesinin yalıtımını ölçmek zordur (ancak bu, bir ışık test devresi ve osiloskop kullanılarak yapılabilir). Gidiş dönüş ses gecikmesini biliyorsanız genel kuralı kullanabilirsiniz: ses giriş (ve çıkış) gecikmesi, sinyal işlemeyen yollardaki gidiş dönüş ses gecikmesinin yarısıdır.

Gidiş dönüş ses gecikmesi cihaz modeline ve Android derlemesine bağlı olarak büyük ölçüde farklılık gösterir. Yayınlanan ölçümleri okuyarak Nexus cihazlar için gidiş dönüş gecikmesi hakkında genel bir fikir edinebilirsiniz.

Ses sinyali oluşturan, bu sinyali dinleyen ve gönderme ile alma arasındaki süreyi ölçen bir uygulama oluşturarak gidiş dönüş ses gecikmesini ölçebilirsiniz.

En düşük gecikme, minimum sinyal işleme ile ses yollarında elde edildiğinden testin mikrofonlu kulaklık konnektörü üzerinde çalıştırılmasını sağlayan bir Ses Geri Dönme Donanımı kullanmak isteyebilirsiniz.

Gecikmeyi en aza indirmek için en iyi uygulamalar

Ses performansını doğrulama

Android Uyumluluk Tanımlama Belgesi (CDD), uyumlu bir Android cihazın donanım ve yazılım gereksinimlerini sıralar. Genel uyumluluk programı hakkında daha fazla bilgi için Android Uyumluluğu sayfasına ve gerçek CDD dokümanı için CDD'ye göz atın.

CDD'de gidiş dönüş gecikmesi 20 ms veya daha düşük olarak belirtilir (müzisyenler genellikle 10 ms gerektirse de). Bunun nedeni, 20 ms. içinde etkinleştirilen önemli kullanım alanlarının olmasıdır.

Şu anda Android cihazlarda çalışma zamanında herhangi bir yoldaki ses gecikmesini belirleyen bir API yoktur. Bununla birlikte, cihazın gecikme garantisi verip vermediğini öğrenmek için aşağıdaki donanım özelliği işaretlerini kullanabilirsiniz:

Bu işaretleri bildirme ölçütleri, CDD'nin 5.6 Ses Gecikmesi ve 5.10 Profesyonel Ses bölümlerinde tanımlanmıştır.

Bu özellikleri Java'da şu şekilde kontrol edebilirsiniz:

Kotlin

val hasLowLatencyFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

val hasProFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)

Java

boolean hasLowLatencyFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);

boolean hasProFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);

Ses özelliklerinin ilişkisiyle ilgili olarak, android.hardware.audio.low_latency özelliği android.hardware.audio.pro için bir ön koşuldur. Bir cihazda android.hardware.audio.pro değil android.hardware.audio.low_latency uygulanabilir, ancak tersi mümkün değildir.

Ses performansı hakkında varsayımda bulunmayın.

Gecikme sorunları yaşamamak için aşağıdaki varsayımlara dikkat edin:

  • Mobil cihazlarda kullanılan hoparlörlerin ve mikrofonların genel olarak iyi akustiği olduğunu varsaymayın. Boyutları küçük olduğundan akustiği genellikle zayıftır. Bu nedenle, ses kalitesini iyileştirmek için sinyal işleme özelliği eklenir. Bu sinyal işleme, gecikmeye neden olur.
  • Giriş ve çıkış geri çağırmalarınızın senkronize edildiğini varsaymayın. Eşzamanlı giriş ve çıkış için her iki taraf için ayrı arabellek sırası tamamlama işleyicileri kullanılır. Her iki taraf da aynı örnek hızını kullansa bile bu geri çağırmaların göreli sırasına veya ses saatlerinin senkronizasyonuna dair herhangi bir garanti verilmez. Uygulamanız, verileri doğru arabellek senkronizasyonuyla arabelleğe almalıdır.
  • Gerçek örnek hızının nominal örnek hızıyla tam olarak eşleştiğini varsaymayın. Örneğin, nominal örnek hızı 48.000 Hz ise ses saatinin CLOCK_MONOTONIC işletim sistemininkinden biraz farklı bir hızda ilerlemesi normaldir. Bunun nedeni, ses ve sistem saatlerinin farklı kristallerden türetilmiş olabilmesidir.
  • Gerçek oynatma örnek hızının, özellikle uç noktalar ayrı yollarda olduğu durumlarda gerçek yakalama örnek hızıyla tam olarak eşleştiğini varsaymayın. Örneğin, cihaz üzerindeki mikrofondan 48.000 Hz nominal örnek hızında görüntü yakalıyorsanız ve USB seste 48.000 Hz nominal örnek hızında oynatıyorsanız, gerçek örnek hızları büyük olasılıkla birbirinden biraz farklı olacaktır.

Potansiyel olarak bağımsız ses saatlerinin sonucu, eşzamansız örnek hızı dönüşümüne ihtiyaç duyar. Eşzamansız örnek hızı dönüşümü için basit (ses kalitesi için ideal değildir) bir teknik, örnekleri gerektiğinde bir sıfır geçiş noktasının yakınında kopyalamak veya bırakmaktır. Daha gelişmiş dönüşümler mümkündür.

Giriş gecikmesini en aza indirme

Bu bölümde, yerleşik mikrofon veya harici mikrofonlu kulaklık mikrofonuyla kayıt yaparken ses girişi gecikmesini azaltmanıza yardımcı olacak öneriler sunulmaktadır.

  • Uygulamanız girişi izliyorsa kullanıcılarınıza bir mikrofonlu kulaklık kullanmalarını önerin (örneğin, ilk çalıştırmada Kulaklıklarla en iyi ekranı görüntüleyerek). Yalnızca mikrofonlu kulaklığı kullanmanın olası en düşük gecikmeyi garanti etmediğini unutmayın. Ses yolundan istenmeyen sinyal işlemelerini kaldırmak için kayıt sırasında VOICE_RECOGNITION hazır ayarını kullanmak gibi başka adımlar uygulamanız gerekebilir.
  • PROPERTY_OUTPUT_ÖRNEK_RATE için getMülk(String) tarafından bildirildiği üzere 44.100 ve 48.000 Hz nominal örnek hızlarını kullanmaya hazır olun. Başka örnek hızları da mümkündür, ancak nadirdir.
  • PROPERTY_OUTPUT_FRAMES_PER_BUFFER için getMülk(String) tarafından bildirilen arabellek boyutunu işlemeye hazır olun. Tipik arabellek boyutları 96, 128, 160, 192, 240, 256 veya 512 kareyi içerir ancak diğer değerler de olabilir.

Çıkış gecikmesini en aza indirme

Ses çalarınızı oluştururken en uygun örnek hızını kullanın

En düşük gecikmeyi elde etmek için cihazın optimum örnek hızı ve arabellek boyutuyla eşleşen ses verileri sağlamanız gerekir. Daha fazla bilgi için Azaltılmış Gecikme İçin Tasarlama bölümüne bakın.

Java'da, aşağıdaki kod örneğinde gösterildiği gibi AudioManager'dan en uygun örnek hızını alabilirsiniz:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sampleRateStr: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
var sampleRate: Int = sampleRateStr?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 44100 // Use a default value if property not found

Java

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found

En uygun örnek hızını öğrendikten sonra, oynatıcınızı oluştururken bu oranı sağlayabilirsiniz. Bu örnekte OpenSL ES kullanılmaktadır:

// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
        (JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
   ...
   // specify the audio source format
   SLDataFormat_PCM format_pcm;
   format_pcm.numChannels = 2;
   format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
   ...
}

Not: samplesPerSec, milihertz cinsinden kanal başına örnek hızı (1 Hz = 1000 mHz) anlamına gelir.

Ses verilerini sıraya almak için optimum arabellek boyutunu kullanın

AudioManager API'yi kullanarak, optimum arabellek boyutunu optimum örnek hızına benzer bir şekilde elde edebilirsiniz:

Kotlin

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val framesPerBuffer: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
var framesPerBufferInt: Int = framesPerBuffer?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 256 // Use default

Java

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default

PROPERTY_OUTPUT_FRAMES_PER_BUFFER özelliği, HAL (Donanım Soyutlama Katmanı) arabelleğinin barındırabileceği ses karelerinin sayısını belirtir. Ses arabelleklerinizi, bu sayının tam katını içerecek şekilde oluşturmanız gerekir. Doğru sayıda ses karesi kullanırsanız, geri çağırmalarınız düzenli aralıklarla gerçekleşir. Bu da ses dalgalanmasını azaltır.

HAL arabellek boyutları cihazlar ve Android derlemeleri arasında farklılık gösterdiğinden arabellek boyutunu belirlemek için sabit kodlu bir değer kullanmak yerine API'yi kullanmak önemlidir.

Sinyal işleme içeren çıkış arayüzleri eklemeyin

Hızlı karıştırıcı yalnızca şu arayüzleri destekler:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

Bu arayüzlere, sinyal işleme içerdikleri ve hızlı izleme isteğinizin reddedilmesine neden oldukları için izin verilmez:

  • SL_IID_BASSBOOST
  • SL_IID_EFFECTS SONU
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • [SL_ID_VIRTUALIZER]
  • SL_IID_ANDROID altında
  • SL_IID_ANDROIDETKİLERİEND

Oynatıcınızı oluştururken aşağıdaki örnekte gösterildiği gibi yalnızca hızlı arayüzler eklediğinizden emin olun:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

Düşük gecikmeli bir parça kullandığınızı doğrulayın

Düşük gecikmeli bir parçayı başarıyla tamamladığınızı doğrulamak için aşağıdaki adımları tamamlayın:

  1. Uygulamanızı başlatın ve ardından aşağıdaki komutu çalıştırın:
  2. adb shell ps | grep your_app_name
    
  3. Uygulamanızın işlem kimliğini not edin.
  4. Şimdi, uygulamanızdan biraz ses çalın. Aşağıdaki komutu terminalden çalıştırmak için yaklaşık üç saniyeniz vardır:
  5. adb shell dumpsys media.audio_flinger
    
  6. İşlem kimliğinizi tarayın. Ad sütununda F işaretini görüyorsanız bu, düşük gecikmeli bir yoldadır (F, hızlı geçiş anlamına gelir).

Isınma gecikmesini en aza indirme

Ses verilerini ilk kez sıraya aldığınızda cihaz ses devresinin ısınması kısa ama yine de önemli bir zaman alır. Bu ısınma gecikmesini önlemek için aşağıdaki kod örneğinde gösterildiği gibi sessizlik içeren ses verileri arabelleklerini kuyruğa alabilirsiniz:

#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
    for (i = 0; i<numSamples; i++) {
        silenceBuffer[i] = 0;
    }

Sesin üretilmesi gereken noktada, gerçek ses verileri içeren arabellekleri sıraya ekleyebilirsiniz.

Not: Sürekli ses çıkışı, önemli miktarda güç tüketimine neden olur. onPause() yönteminde çıkışı durdurduğunuzdan emin olun. Ayrıca, kullanıcı bir süre işlem yapmadığında sessiz çıkışı duraklatmayı da düşünebilirsiniz.

Ek örnek kod

Ses gecikmesini gösteren örnek bir uygulama indirmek için NDK Örnekleri'ne bakın.

Daha fazla bilgi için

  1. Uygulama Geliştiriciler için Ses Gecikmesi
  2. Ses Gecikmesine Katkıda Bulunan
  3. Ses Gecikmesini Ölçme
  4. Ses Isınma
  5. Gecikme (ses)
  6. Gidiş dönüş gecikme süresi