เวลาในการตอบสนองคือระยะเวลาที่สัญญาณใช้ในการเดินทางผ่านระบบ ซึ่งมักเป็น ประเภทของเวลาในการตอบสนองที่เกี่ยวข้องกับแอปเสียง
- เวลาในการตอบสนองของเอาต์พุตเสียงคือช่วงเวลาระหว่างที่ตัวอย่างเสียงสร้างขึ้นโดย แอปและตัวอย่างที่เล่นผ่านช่องเสียบหูฟังหรือลำโพงในตัว
- เวลาในการตอบสนองของอินพุตเสียง คือช่วงเวลาระหว่างที่สัญญาณเสียงได้รับ อินพุตเสียงของอุปกรณ์ เช่น ไมโครโฟน และข้อมูลเสียงเดียวกันนั้น ก่อนที่จะพร้อมใช้งาน แอป
เวลาในการตอบสนองไป-กลับคือผลรวมของเวลาในการตอบสนองของอินพุต เวลาในการประมวลผลแอป และ เวลาในการตอบสนองเอาต์พุต
- เวลาในการตอบสนองของการแตะ คือช่วงเวลาระหว่างที่ผู้ใช้แตะหน้าจอ แอปได้รับเหตุการณ์การแตะ
- เวลาในการตอบสนองอุ่นเครื่องคือเวลาที่ใช้ในการเริ่มต้นไปป์ไลน์เสียงในช่วงแรก ข้อมูลเวลาจะอยู่ในคิวบัฟเฟอร์
หน้านี้อธิบายวิธีพัฒนาแอปเสียงด้วยอินพุตและเอาต์พุตที่มีเวลาในการตอบสนองต่ำ รวมถึงวิธีหลีกเลี่ยง เวลาในการตอบสนองของการอุ่นเครื่อง
วัดเวลาในการตอบสนอง
การวัดเวลาในการตอบสนองของอินพุตและเอาต์พุตเสียงด้วยตัวเองทำได้ยาก เนื่องจากจะต้องทราบข้อมูลที่แน่นอน เมื่อมีการส่งตัวอย่างแรกไปยังเส้นทางเสียง (แม้ว่าจะสามารถทำได้โดยใช้ วงจรทดสอบด้วยแสงและออสซิลโลสโคป) หากคุณทราบเวลาในการตอบสนองของเสียงไป-กลับ คุณสามารถ หลักทั่วไปก็คือ เวลาในการตอบสนองของอินพุตเสียง (และเอาต์พุต) คิดเป็นครึ่งหนึ่งของเวลาในการตอบสนองของเสียงไป-กลับ ในเส้นทางที่ไม่มีการประมวลผลสัญญาณ
เวลาในการตอบสนองของเสียงไป-กลับจะแตกต่างกันไป อย่างมากโดยขึ้นอยู่กับรุ่นอุปกรณ์และ บิลด์ของ Android คุณสามารถดูภาพรวมคร่าวๆ เกี่ยวกับการเดินทางไป-กลับ เวลาในการตอบสนองสำหรับอุปกรณ์ Nexus โดยการอ่านค่า การวัดที่เผยแพร่
คุณสามารถวัดเวลาในการตอบสนองของเสียงไป-กลับได้โดยการสร้างแอปที่สร้างสัญญาณเสียง คอยฟังสัญญาณนั้น และวัดเวลาตั้งแต่การส่งและรับสัญญาณ
และเนื่องจากเส้นทางเสียงจะมีเวลาในการตอบสนองต่ำที่สุด โดยมีการประมวลผลสัญญาณน้อยที่สุด คุณจึงอาจ และต้องการใช้ ดองเกิล Loopback ของเสียง ซึ่งช่วยให้สามารถทดสอบผ่านขั้วต่อชุดหูฟังได้
แนวทางปฏิบัติแนะนำในการลดเวลาในการตอบสนอง
ตรวจสอบประสิทธิภาพเสียง
เอกสารข้อกำหนดความเข้ากันได้ของ Android (CDD) จะแจกแจงฮาร์ดแวร์และซอฟต์แวร์ ข้อกำหนดของอุปกรณ์ Android ที่ใช้งานร่วมกันได้ โปรดดู ความเข้ากันได้กับ Android สำหรับข้อมูลเพิ่มเติมเกี่ยวกับโปรแกรมความเข้ากันได้โดยรวม และ CDD สำหรับเอกสาร CDD จริง
ใน CDD เวลาในการตอบสนองไป-กลับจะระบุเป็น 20 มิลลิวินาทีหรือน้อยกว่า (แม้ว่านักดนตรี โดยทั่วไปจะใช้เวลา 10 มิลลิวินาที) เนื่องจากมี Use Case สําคัญที่เปิดใช้โดย 20 มิลลิวินาที
ขณะนี้ยังไม่มี API ที่ใช้กำหนดเวลาในการตอบสนองของเสียงบนเส้นทางทั้งหมดในอุปกรณ์ Android รันไทม์ อย่างไรก็ตาม คุณสามารถใช้แฟล็กฟีเจอร์ฮาร์ดแวร์ต่อไปนี้เพื่อค้นหาว่า ให้การรับประกันเวลาในการตอบสนองดังนี้
-
android.hardware.audio.low_latency
ระบุเวลาในการตอบสนองของเอาต์พุตอย่างต่อเนื่อง 45 มิลลิวินาที หรือ น้อยลง -
android.hardware.audio.pro
แสดงเวลาในการตอบสนองไป-กลับอย่างต่อเนื่องเท่ากับ 20 มิลลิวินาที หรือ น้อยลง
เกณฑ์ในการรายงานการแจ้งว่าไม่เหมาะสมเหล่านี้มีระบุไว้ใน CDD ในส่วนต่างๆ 5.6 เวลาในการตอบสนองของเสียง และ เสียงระดับมืออาชีพ 5.10
วิธีตรวจสอบฟีเจอร์เหล่านี้ใน Java
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);
เกี่ยวกับความสัมพันธ์ของฟีเจอร์เสียง android.hardware.audio.low_latency
เป็นข้อกำหนดเบื้องต้นสำหรับ android.hardware.audio.pro
อุปกรณ์สามารถนำไปใช้
android.hardware.audio.low_latency
ไม่ใช่ android.hardware.audio.pro
หรือในทางกลับกันก็ได้
ไม่ต้องคาดเดาเกี่ยวกับประสิทธิภาพเสียง
โปรดระวังสมมติฐานต่อไปนี้เพื่อหลีกเลี่ยงปัญหาเกี่ยวกับเวลาในการตอบสนอง
- อย่าทึกทักเอาว่าลำโพงและไมโครโฟนที่ใช้ในอุปกรณ์เคลื่อนที่นั้น อะคูสติก เนื่องจากมีขนาดเล็ก คุณภาพเสียงโดยทั่วไปจะไม่ดีนัก การประมวลผลสัญญาณจึง เพิ่มเข้ามาเพื่อปรับปรุงคุณภาพเสียง การประมวลผลสัญญาณนี้จะทำให้เกิดเวลาในการตอบสนอง
- อย่าคิดเอาเองว่าการเรียกกลับอินพุตและเอาต์พุตของคุณซิงค์กัน สําหรับการป้อนข้อมูลพร้อมกัน และเอาต์พุต ตัวแฮนเดิลคิวบัฟเฟอร์ ที่แยกต่างหากจะใช้สำหรับแต่ละฝั่ง ไม่มี รับประกันลำดับที่เกี่ยวข้องของ Callback เหล่านี้หรือการซิงค์นาฬิกาเสียง แม้ว่าทั้ง 2 ฝั่งจะใช้อัตราการสุ่มตัวอย่างเดียวกันก็ตาม แอปพลิเคชันควรบัฟเฟอร์ข้อมูลกับ การซิงค์บัฟเฟอร์ที่เหมาะสม
- อย่าคิดเอาเองว่าอัตราตัวอย่างจริงตรงกับอัตราตัวอย่างค่ากลางทุกประการ สำหรับ
ตัวอย่างเช่น หากอัตราการสุ่มตัวอย่างค่ากลางคือ 48,000 Hz ก็เป็นเรื่องปกติที่นาฬิกาเสียงจะเลื่อน
ในอัตราที่แตกต่างจากระบบปฏิบัติการ
CLOCK_MONOTONIC
เล็กน้อย นั่นเป็นเพราะ เสียงและนาฬิกาของระบบอาจมาจากคริสตัลที่ต่างกัน - อย่าคาดเดาว่าอัตราการสุ่มตัวอย่างการเล่นจริงตรงกับตัวอย่างการบันทึกจริง โดยเฉพาะอย่างยิ่งหากปลายทางอยู่บนเส้นทางแยกกัน ตัวอย่างเช่น หากคุณกำลังจับภาพจาก ไมโครโฟนบนอุปกรณ์ที่มีอัตราการสุ่มตัวอย่างค่ากลาง 48,000 Hz และเล่นโดยใช้เสียง USB ที่อัตราการสุ่มตัวอย่างค่าปกติ 48,000 Hz อัตราการสุ่มตัวอย่างจริงจะมีแนวโน้มที่จะแตกต่างออกไปเล็กน้อย ได้
ผลของนาฬิกาเสียงที่อาจเป็นอิสระคือความต้องการอัตราการสุ่มตัวอย่างแบบไม่พร้อมกัน Conversion เทคนิคง่ายๆ (แต่ไม่เหมาะสำหรับคุณภาพเสียง) สำหรับอัตราการสุ่มตัวอย่างแบบไม่พร้อมกัน Conversion คือ การทำซ้ำหรือปล่อยตัวอย่างตามความจำเป็นใกล้กับจุดตัดขวางที่เป็นศูนย์ เพิ่มเติม จึงมีโอกาสทำ Conversion ที่ซับซ้อนได้
ลดเวลาในการตอบสนองของอินพุต
ส่วนนี้จะมีคำแนะนำที่จะช่วยลดเวลาในการตอบสนองของอินพุตเสียงเมื่อบันทึกด้วย ไมโครโฟนในตัวหรือไมโครโฟนของชุดหูฟังภายนอก
- หากแอปกำลังตรวจสอบอินพุต โปรดแจ้งให้ผู้ใช้ใช้ชุดหูฟัง
(เช่น โดยการแสดงหน้าจอดีที่สุดเมื่อใช้หูฟังเมื่อเรียกใช้ครั้งแรก) หมายเหตุ
การใช้เพียงชุดหูฟังไม่ได้รับประกันว่า
จะมีเวลาในการตอบสนองต่ำที่สุด คุณอาจต้องทำดังนี้
ดำเนินขั้นตอนอื่นๆ เพื่อลบการประมวลผลสัญญาณที่ไม่ต้องการออกจากเส้นทางเสียง เช่น
โดยใช้
VOICE_RECOGNITION
ที่กำหนดล่วงหน้าเมื่อบันทึก - เตรียมตัวรับมือกับอัตราการสุ่มตัวอย่างขั้นต่ำ 44,100 และ 48,000 Hz ตามที่รายงาน getProperty(String) สำหรับ PROPERTY_OUTPUT_SAMPLE_RATE อาจมีอัตราตัวอย่างอื่นๆ ด้วย แต่หายาก
- เตรียมรับมือกับขนาดบัฟเฟอร์ที่รายงานโดย getProperty(String) สำหรับ PROPERTY_OUTPUT_FRAMES_PER_BUFFER ขนาดบัฟเฟอร์ทั่วไป ได้แก่ 96, 128, 160, 192, 240, 256, หรือ 512 เฟรมแต่มีค่าอื่นๆ ได้
ลดเวลาในการตอบสนองของเอาต์พุต
ใช้อัตราการสุ่มตัวอย่างที่เหมาะสม เมื่อคุณสร้างโปรแกรมเล่นเสียง
หากต้องการเวลาในการตอบสนองที่ต่ำที่สุด คุณต้องให้ข้อมูลเสียงที่ตรงกับสัญญาณที่เหมาะสมที่สุดของอุปกรณ์ อัตราการสุ่มตัวอย่างและขนาดบัฟเฟอร์ สำหรับข้อมูลเพิ่มเติม โปรดดู ออกแบบเวลาในการตอบสนองที่ลดลง
ใน Java คุณจะได้รับอัตราการสุ่มตัวอย่างที่ดีที่สุดจาก AudioManager ดังที่แสดงใน ตัวอย่างโค้ด:
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
เมื่อคุณทราบอัตราการสุ่มตัวอย่างที่เหมาะสมแล้ว คุณก็สามารถระบุอัตราดังกล่าวเมื่อสร้างโปรแกรมเล่นได้ ตัวอย่างนี้ใช้ OpenSL ES
// 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; ... }
หมายเหตุ: samplesPerSec
หมายถึงอัตราการสุ่มตัวอย่างต่อช่องใน
มิลลิเฮิรตซ์ (1 Hz = 1000 mHz)
ใช้ขนาดบัฟเฟอร์ที่เหมาะสมที่สุดเพื่อกำหนดคิวข้อมูลเสียง
คุณสามารถหาขนาดบัฟเฟอร์ที่เหมาะสมที่สุดด้วยวิธีเดียวกันกับอัตราการสุ่มตัวอย่างที่เหมาะสมที่สุดโดยใช้ API ของ AudioManager:
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
ระบุจำนวนเฟรมเสียง
ที่บัฟเฟอร์ HAL (ฮาร์ดแวร์ Abstraction Layer) เก็บได้ คุณควรสร้างเสียง
บัฟเฟอร์ เพื่อให้มีการคูณตัวเลขนี้ที่ตรงกัน หากคุณใช้หมายเลขที่ถูกต้อง
ของเฟรมเสียง การเรียกกลับจะเกิดขึ้นในช่วงเวลาที่สม่ำเสมอ ซึ่งลดเสียงรบกวน
คุณจะต้องใช้ API เพื่อระบุขนาดบัฟเฟอร์แทนการใช้ค่าฮาร์ดโค้ด เนื่องจากขนาดบัฟเฟอร์ HAL แตกต่างกันไปตามอุปกรณ์และแต่ละบิลด์ของ Android
ไม่ต้องเพิ่มอินเทอร์เฟซเอาต์พุต ที่เกี่ยวข้องกับการประมวลผลสัญญาณ
โปรแกรมผสมแบบรวดเร็วรองรับเฉพาะอินเทอร์เฟซเหล่านี้เท่านั้น
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE
- SL_IID_VOLUME
- SL_IID_MUTESOLO
อินเทอร์เฟซเหล่านี้ไม่ได้รับอนุญาต เนื่องจากเกี่ยวข้องกับการประมวลผลสัญญาณและจะทำให้ คำขอสำหรับช่องทางด่วนที่จะถูกปฏิเสธ:
- SL_IID_BASSBOOST
- SL_IID_EFFECTSEND
- SL_IID_สภาพแวดล้อม
- SL_IID_EQUALIZER
- SL_IID_PLAYBACKRATE
- SL_IID_PRESETREVERB
- SL_IID_VIRTUALIZER
- SL_IID_ANDROIDEFFECT
- SL_IID_ANDROIDEFFECTSEND
เมื่อคุณสร้างโปรแกรมเล่น ให้ตรวจสอบว่าคุณได้เพิ่มอินเทอร์เฟซที่เร็วเท่านั้น ดังที่แสดงใน ตัวอย่างต่อไปนี้
const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
ตรวจสอบว่าคุณกำลังใช้แทร็กที่มีเวลาในการตอบสนองต่ำ
ทำตามขั้นตอนเหล่านี้เพื่อยืนยันว่าคุณได้รับแทร็กที่มีเวลาในการตอบสนองต่ำสำเร็จแล้ว
- เปิดแอปแล้วเรียกใช้คำสั่งต่อไปนี้
- จดบันทึกรหัสกระบวนการของแอป
- ตอนนี้ให้เล่นเสียงจากแอป คุณมีเวลาประมาณ 3 วินาทีในการเรียกใช้ คำสั่งต่อไปนี้จากเทอร์มินัล:
- สแกนหารหัสกระบวนการของคุณ หากคุณเห็น F ในคอลัมน์ชื่อ แสดงว่าอยู่บน การติดตามเวลาในการตอบสนองต่ำ (F ย่อมาจาก fast Track)
adb shell ps | grep your_app_name
adb shell dumpsys media.audio_flinger
ลดเวลาในการตอบสนองของการอุ่นเครื่อง
เมื่อคุณสร้างการจัดคิวข้อมูลเสียงเป็นครั้งแรก จะกินเวลาเพียงเล็กน้อยแต่ก็มีความหมาย ระยะเวลาที่วงจรเสียงของอุปกรณ์จะร้อนขึ้น เพื่อหลีกเลี่ยงเวลาในการตอบสนองของการอุ่นเครื่อง คุณสามารถ บัฟเฟอร์ย่อยของข้อมูลเสียงที่มีช่วงที่ไม่มีเสียง ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้
#define CHANNELS 1 static short* silenceBuffer; int numSamples = frames * CHANNELS; silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples); for (i = 0; i<numSamples; i++) { silenceBuffer[i] = 0; }
เมื่อถึงจุดที่ควรสร้างเสียง คุณสามารถเปลี่ยนไปใช้การเข้ารหัสแบบจัดคิวที่มี ข้อมูลเสียงจริง
หมายเหตุ: การเล่นเสียงอย่างต่อเนื่องมีการใช้พลังงานอย่างมาก โปรดตรวจสอบว่าคุณหยุด ในส่วน onPause() ใหม่ นอกจากนี้ ให้พิจารณาหยุดเอาต์พุตแบบไม่มีเสียงไว้ชั่วคราวหลังจากที่ผู้ใช้ไม่มีการใช้งานเป็นระยะเวลาหนึ่ง
โค้ดตัวอย่างเพิ่มเติม
หากต้องการดาวน์โหลดแอปตัวอย่างที่แสดงเวลาในการตอบสนองของเสียง โปรดดู ตัวอย่าง NDK
สำหรับข้อมูลเพิ่มเติม
- เวลาในการตอบสนองของเสียงสำหรับนักพัฒนาแอป
- ปัจจัยที่ส่งผลต่อเวลาในการตอบสนองของเสียง
- การวัดเวลาในการตอบสนองของเสียง
- วอร์มอัพเสียง
- เวลาในการตอบสนอง (เสียง)
- ระยะหน่วงเวลาไป-กลับ