Ses gecikmesi

Gecikme, bir sinyalin sistemde ilerlemesi için gereken süredir. Bunlar yaygın olarak kullanılan Ses uygulamalarıyla ilgili gecikme türleri:

  • Ses çıkışı gecikmesi, bir ses örneğinin bir ses örneğinin oluşturması arasında geçen süredir. kulaklık jakı veya yerleşik hoparlörden çalınan örneği içerir.
  • Ses girişi gecikmesi, bir ses sinyalinin cihazın ses girişi (ör. mikrofon) ve aynı ses verisinin başka bir kullanıcı tarafından uygulamasını indirin.
  • Gidiş dönüş gecikmesi; giriş gecikmesi, uygulama işleme süresi ve Çıkış gecikmesi.

  • Dokunma gecikmesi, kullanıcının ekrana dokunması ile uygulamanın Bir uygulama tarafından alınan dokunma etkinliği.
  • ısınma gecikmesi, ses ardışık düzeninin başlatılması için geçen süredir. bir tamponda sıraya alındığından emin olmalısınız.

Bu sayfada, düşük gecikmeli giriş ve çıkışlarla ses uygulamanızı nasıl geliştireceğiniz ve bunu nasıl önleyebileceğiniz açıklanmaktadır. ısınma gecikmesi.

Gecikmeyi ölç

Ses girişi ve çıkış gecikmesinin tam olarak bilinmesi gerektiğinden izole olarak ölçmek zordur. ses yoluna ilk örnek gönderildiğinde (ancak bu işlem ışık testi devresi ve osiloskop). Gidiş-dönüş ses gecikmesini biliyorsanız şunları yapabilirsiniz: şu genel kuralı kullanın: ses girişi (ve çıkışı) gecikmesi, gidiş dönüş ses gecikmesinin yarısıdır yollarına kıyasla daha fazla avantaj sağlar.

Gidiş dönüş ses gecikmesi cihaz modeline ve Android sürümü. Gidiş dönüş hakkında kabaca bir fikir edinebilirsiniz aşağıdaki kodu okuyarak Nexus cihazlar için gecikme yayınlanmış ölçümler gösterilir.

Ses sinyali üreten bir uygulama oluşturarak gidiş dönüş sesteki gecikmeyi ölçebilirsiniz. o sinyali dinler ve iletinin gönderilmesi ile alınması arasındaki süreyi ölçer.

En düşük gecikme, minimum sinyal işlemeyle ses yollarında ulaşıldığı için aynı zamanda bir Ses Döngüsü Dongle: Testin mikrofonlu kulaklık konnektörü üzerinde yürütülmesine olanak tanır.

Gecikmeyi en aza indirmek için en iyi uygulamalar

Ses performansını doğrulayın

Android Uyumluluk Tanımlama Belgesi (CDD), donanım ve yazılımları numaralandırır. gereksinimlerine uygun olmalıdır. Bkz. Genel uyumluluk programı hakkında daha fazla bilgi için Android Uyumluluğu ve CDD'yi gönderin.

CDD'de gidiş dönüş gecikmesi 20 ms veya daha düşük olarak belirtilir (müzisyenler genellikle 10 ms gerektirir). Bunun nedeni, Google Analytics 4'te Google Ads'in 20 ms.

Şu anda aşağıdaki Android cihazlarda herhangi bir yoldaki ses gecikmesini belirleyecek bir API yoktur: belirler. Bununla birlikte, gecikmeyle ilgili garanti verir:

Bu işaretlerin raporlanmasına ilişkin ölçütler, CDD'nin 5.6 Ses Gecikmesi ve 5.10 Profesyonel Ses.

Java'da bu özellikleri nasıl kontrol edeceğiniz aşağıda açıklanmıştır:

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şkisi konusunda, android.hardware.audio.low_latency özelliği, android.hardware.audio.pro için bir ön koşuldur. Bir cihaz, android.hardware.audio.pro değil, android.hardware.audio.low_latency ancak şu değil: ve bu böyle devam eder.

Ses performansı hakkında varsayımlarda bulunmayın

Gecikme sorunlarının önlenmesine yardımcı olması için aşağıdaki varsayımlara dikkat edin:

  • Mobil cihazlarda kullanılan hoparlörlerin ve mikrofonların genellikle iyi kalitede olduğunu varsaymayın. akustik. Boyutları küçük olduğu için akustik genelde kötüdür ve dolayısıyla sinyal işleme ses kalitesini iyileştirmek için eklendi. Bu sinyal işleme, gecikmeye yol açar.
  • Giriş ve çıkış geri aramalarınızın senkronize edildiğini varsaymayın. Eş zamanlı giriş için her taraf için ayrı arabellek sırası tamamlama işleyicileri kullanılır. Hayır veya ses saatlerinin senkronizasyonu ile ilgili güvence, her iki taraf da aynı örnek hızını kullansa bile çalışır. Uygulamanız, verileri arabellek senkronizasyonunda sorun yok.
  • Gerçek örnek hızının nominal örnek hızıyla tam olarak eşleştiğini varsaymayın. Örneğin, örneğin, nominal örnek hızı 48.000 Hz ise ses saatinin ilerlemesi normaldir. CLOCK_MONOTONIC işletim sistemine göre biraz daha farklı bir hızda çalışıyor. Çünkü ses ve sistem saatleri farklı kristallerden türetilebilir.
  • Gerçek oynatma örnek hızının, gerçek yakalama örneğiyle tam olarak eşleştiğini varsaymayın oranını artırmanız gerekir. Örneğin, cihazın mikrofonunu 48.000 Hz nominal örnek hızında göstermek ve USB ses ile çalmak Hz nominal örnek hızında gösterdiğinde gerçek örnek hızları büyük olasılıkla biraz farklı olacaktır bir şeyler öğrenme fırsatı sunar.

Bağımsız olabilecek ses saatleri, eşzamansız örnek hızına duyulan ihtiyacın bir sonucudur. gösterir. Eşzamansız örnek hızına yönelik basit (ancak ses kalitesi için ideal değildir) bir teknik bir sıfır kesişim noktasına yakın bir noktada örnekleri gerektiği gibi kopyalamak veya bırakmaktır. Daha fazla gelişmiş dönüşümlerin nasıl mümkün olduğunu gösterir.

Giriş gecikmesini en aza indir

Bu bölümde, dahili mikrofon veya harici kulaklık mikrofonu.

  • Uygulamanız girişi izliyorsa kullanıcılarınıza mikrofonlu kulaklık kullanmalarını önerin (örneğin, ilk çalıştırmada Kulaklıklarla En İyiler ekranını göstererek). Not gecikmeyi garantilemeyeceğine dikkat edin. Şunları yapmanız gerekebilir: istenmeyen sinyal işlemeyi ses yolundan kaldırmak için diğer adımları uygulayın: Kayıt sırasında VOICE_RECOGNITION hazır ayarı.
  • Şu kaynakta belirtildiği gibi 44.100 ve 48.000 Hz nominal örnek hızlarını getProperty(String) değerini PROPERTY_OUTPUT_sample_RATE. Başka örnek hızları da mümkündür, ancak nadirdir.
  • tarafından bildirilen tampon boyutunu işlemeye getProperty(String) değerini PROPERTY_OUTPUT_FRAMES_PER_BUFFER. Tipik tampon boyutları şunlardır: 96, 128, 160, 192, 240, 256, ancak başka değerler kullanılabilir.

Çıkış gecikmesini en aza indirme

Optimum örnek hızını kullanma ses çalarınızı oluşturduğunuzda

En düşük gecikmeyi elde etmek için cihazın optimum özelliğiyle eşleşen ses verileri sağlamanız gerekir örnek hızı ve tampon boyutu gibi. Daha fazla bilgi için bkz. Düşük Gecikme İçin Tasarlayın.

Java'da, aşağıda gösterildiği şekilde AudioManager'dan optimum örnek hızını elde edebilirsiniz kod örneği:

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 bunu 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, Google'daki kanal başına örnek ücretini gösterir. millihertz (1 Hz = 1000 mHz).

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

AudioManager API'sı:

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

İlgili içeriği oluşturmak için kullanılan PROPERTY_OUTPUT_FRAMES_PER_BUFFER özelliği, ses karelerinin sayısını gösterir soyutlama katmanının barındırabileceği bir pakettir. Sesinizi oluşturmanız gerekir tamponları oluştururlar. Doğru numarayı kullanıyorsanız geri çağırmalarınız düzenli aralıklarla gerçekleşir, bu da titremeyi azaltır.

Tampon boyutunu belirlemek için sabit kodlu bir değer kullanmak yerine API'nin kullanılması önemlidir. Çünkü HAL arabelleği boyutları cihazlar ve Android derlemelerinde farklılık gösterir.

Çıkış arayüzleri ekleme sinyal işlemeyi içeren

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

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

Bu arayüzler, sinyal işlemeyi içerdiklerinden ve hızlı kanalın reddedilmesiyle ilgili isteğiniz:

  • SL_IID_BASSBOOST
  • SL_IID_EFSEND
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROIDTABLE
  • SL_IID_ANDROIDEFSEND

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

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

Düşük gecikmeli bir kanal kullandığınızı doğrulayın

Düşük gecikmeli bir kanalı başarıyla aldığınızı doğrulamak için şu adımları uygulayı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 ses çalın. Denemeyi çalıştırmak için yaklaşık üç saniyeniz var. aşağıdaki komutu çalıştırın:
  5. adb shell dumpsys media.audio_flinger
    
  6. İşlem kimliğinizi tarayın. Ad sütununda F harfi görüyorsanız bu bir düşük gecikmeli kanal (F'nin açılımı hızlı çekim'dir).

Isınma gecikmesini en aza indirme

Ses verilerini ilk kez sıraya oluşturduğunuzda, bu işlem küçük ama önemli olsa da geçen süreyi ifade eder. Bu ısınma gecikmesini önlemek için şunları yapabilirsiniz: aşağıdaki kod örneğinde gösterildiği gibi sessizlik içeren ses verilerinin arabelleklerini sıraya koyun:

#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 gerektiğinde verileri de sunar.

Not: Ses çıkışının sürekli olması önemli miktarda güç tüketimine neden olur. çıktısını onPause() yöntemini kullanabilirsiniz. Ayrıca, kullanıcı bir süre işlem yapılmadığında sessiz çıkışı duraklatma seçeneğini de kullanabilirsiniz.

Ek örnek kod

Ses gecikmesini gösteren örnek bir uygulama indirmek için şuraya bakın: NDK Örnekleri.

Daha fazla bilgi için

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