โฟกัสเสียง

ก่อนเริ่มสตรีมแบบลอจิคัล แอปควรขอโฟกัสเสียงโดยใช้แอตทริบิวต์เสียงเดียวกับที่ใช้สำหรับสตรีมแบบลอจิคัล แม้ว่าเราจะแนะนำให้ส่งคำขอโฟกัสดังกล่าว แต่ระบบก็ไม่ได้บังคับ แอปบางแอปอาจข้ามการส่งคำขออย่างชัดเจนเพื่อให้ได้ลักษณะการทำงานที่เฉพาะเจาะจง (เช่น เพื่อเล่นเสียงในระหว่างการโทร)

ด้วยเหตุนี้ คุณจึงควรพิจารณาโฟกัสเป็นวิธีควบคุมและแก้ไขการเล่นที่ขัดแย้งกันโดยอ้อม ไม่ใช่เป็นกลไกการควบคุมเสียงหลัก เนื่องจากยานพาหนะไม่ควรขึ้นอยู่กับระบบโฟกัสสำหรับการทำงานของระบบย่อยเสียง

การโต้ตอบกับโฟกัส

ระบบจะจัดการคำขอโฟกัสเสียงตามการโต้ตอบที่กําหนดไว้ล่วงหน้าระหว่าง CarAudioContext ของคําขอกับ CarAudioContext ของผู้ที่มีโฟกัสอยู่ในปัจจุบัน เพื่อรองรับความต้องการของ AAOS การโต้ตอบมี 3 ประเภท ได้แก่ ยกเว้น ปฏิเสธ และพร้อมกัน

การโต้ตอบแบบพิเศษ

ในการโต้ตอบแบบพิเศษ ระบบจะอนุญาตให้แอปพลิเคชันหนึ่งๆ ยึดโฟกัสได้ครั้งละ 1 แอปเท่านั้น ดังนั้น คำขอโฟกัสที่เข้ามาจะได้รับโฟกัส ส่วนโฟกัสของผู้ที่มีโฟกัสอยู่แล้วจะหายไป ตัวอย่างเช่น เมื่อผู้ใช้เริ่มแอปพลิเคชันเพลงใหม่ขณะที่เพลงเล่นอยู่ในแอปพลิเคชันที่มีอยู่ เนื่องจากทั้ง 2 แอปเล่นสื่ออยู่ ระบบจึงอนุญาตให้แอปพลิเคชันใดแอปพลิเคชันหนึ่งโฟกัสได้ครั้งละ 1 แอปเท่านั้น ด้วยเหตุนี้ คำขอโฟกัสของแอปพลิเคชันที่เพิ่งเริ่มต้นจึงแสดงผลเป็น AUDIOFOCUS_REQUEST_GRANTED และแอปพลิเคชันที่เล่นเพลงอยู่จะได้รับเหตุการณ์การเปลี่ยนแปลงโฟกัสที่มีสถานะ "สูญเสีย" ซึ่งสอดคล้องกับประเภทคำขอที่ส่ง รูปแบบการโต้ตอบนี้พบได้บ่อยที่สุดกับ Android

ปฏิเสธการโต้ตอบ

เมื่อใช้การปฏิเสธการโต้ตอบ ระบบจะปฏิเสธคําขอขาเข้าเสมอ การพยายามเล่นเพลงขณะโทรอยู่เป็นตัวอย่างของการโต้ตอบที่ถูกปฏิเสธ ในกรณีนี้ หากเครื่องมือโทรกำลังโฟกัสเสียงสำหรับการโทรอยู่และแอปพลิเคชันที่สองขอโฟกัสเพื่อเล่นเพลง แอปพลิเคชันเพลงจะได้รับ AUDIOFOCUS_REQUEST_FAILED เพื่อตอบสนองต่อคำขอ เนื่องจากคำขอโฟกัสถูกปฏิเสธ ระบบจะไม่ส่งการสูญเสียโฟกัสประเภทใดๆ ไปยังผู้ถือโฟกัสปัจจุบัน

การโต้ตอบพร้อมกัน

สิ่งสำคัญที่สุดที่ AAOS มีคือการทำงานพร้อมกัน ซึ่งจะช่วยให้แอปพลิเคชันที่ขอโฟกัสเสียงในรถสามารถโฟกัสได้พร้อมกันกับแอปพลิเคชันอื่นๆ เงื่อนไขต่อไปนี้ต้องได้รับการปฏิบัติตามเพื่อให้เกิดการโต้ตอบพร้อมกัน ข้อมูลต่อไปนี้

หากเป็นไปตามเกณฑ์เหล่านี้ คำขอโฟกัสจะแสดงผลพร้อม AUDIOFOCUS_REQUEST_GRANTED ขณะที่โฟกัสของโฮลเดอร์โฟกัสปัจจุบันจะไม่มีการเปลี่ยนแปลง อย่างไรก็ตาม หากโฟกัสที่ใช้งานอยู่เลือกรับเหตุการณ์การซ่อนหรือหยุดชั่วคราวเมื่อมีการซ่อน โฟกัสที่ใช้งานอยู่จะเสียโฟกัสเช่นเดียวกับการโต้ตอบแบบผูกขาด

การจัดการสตรีมพร้อมกัน

แม้ว่าการโต้ตอบพร้อมกันจะมีแอปพลิเคชันมีประโยชน์มากมาย แต่ OEM จะต้องจัดการการผสมและการตัดเสียงที่ระดับฮาร์ดแวร์ในอุปกรณ์เอาต์พุตต่างๆ ด้วยเหตุนี้ เราจึงขอแนะนำอย่างยิ่งให้กําหนดเส้นทาง CarAudioContext ไปยังอุปกรณ์เอาต์พุตเดียวกับ CarAudioContext ที่เล่นพร้อมกันไม่ได้เท่านั้น การมีอุปกรณ์เอาต์พุตแยกต่างหากสำหรับสตรีมพร้อมกันจะช่วยให้ HAL สามารถลดระดับเสียงสตรีมใดสตรีมหนึ่งก่อนที่จะผสมเข้าด้วยกัน หรือกำหนดเส้นทางสตรีมจริงไปยังลำโพงต่างๆ ในยานพาหนะ หากสตรีมเชิงตรรกะผสมกันใน Android ระบบจะไม่เปลี่ยนแปลงอัตราขยายและส่งสตรีมเป็นส่วนหนึ่งของสตรีมจริงเดียวกัน

ตัวอย่างเช่น เมื่อนำทางและสื่อไปพร้อมกัน ระบบอาจลดระดับสัญญาณของสตรีมสื่อชั่วคราว (ลดเสียง) เพื่อให้ได้ยินคำแนะนำในการไปยังส่วนต่างๆ อย่างชัดเจนยิ่งขึ้น หรือจะส่งสตรีมการนำทางไปยังลำโพงด้านคนขับขณะที่สื่อเล่นต่อไปในห้องโดยสารส่วนที่เหลือก็ได้

เมทริกซ์การโต้ตอบ

ตารางด้านล่างแสดงเมทริกซ์การโต้ตอบตามที่ CarAudioService กำหนด แถวแสดง CarAudioContext ของผู้ถือโฟกัสปัจจุบันและคอลัมน์แสดง CarAudioContext ของคําขอขาเข้า

มาดูตัวอย่างกัน เมื่อแอปสื่อเพลงมีโฟกัสอยู่และแอปการนําทางขอโฟกัส ตารางแสดงว่าการโต้ตอบ 2 รายการสามารถเล่นพร้อมกันได้ โดยสมมติว่าเป็นไปตามเกณฑ์อื่นๆ สําหรับการโต้ตอบพร้อมกัน

เนื่องจากการโต้ตอบเกิดขึ้นพร้อมกัน จึงอาจมีโฟกัสโฮลเดอร์มากกว่า 1 ราย ในกรณีนี้ ระบบจะเปรียบเทียบคำขอโฟกัสขาเข้ากับผู้ถือโฟกัสปัจจุบันแต่ละรายก่อนตัดสินใจว่าจะใช้การโต้ตอบประเภทใด ในกรณีนี้ การโต้ตอบแบบอนุรักษ์นิยมที่สุดจะเป็นผู้ชนะ (ปฏิเสธ ตามด้วยเฉพาะเจาะจง และสุดท้ายคือพร้อมกัน)

ในตารางต่อไปนี้ มีการระบุการโต้ตอบโฟกัสระหว่าง CarAudioContext สําหรับคําขอโฟกัสขาเข้า (คอลัมน์) กับบริบทของผู้ถือโฟกัสที่มีอยู่ (แถว) แต่ละเซลล์แสดงประเภทการโต้ตอบที่คาดไว้สําหรับบริบท 2 รายการ

การโต้ตอบกับโฟกัสเสียง

รูปที่ 1 การโต้ตอบกับโฟกัสเสียง

ใน Android 11 มีการเปิดตัวการตั้งค่าผู้ใช้ใหม่เพื่อให้ผู้ใช้เปลี่ยนแปลงลักษณะการโต้ตอบระหว่างการไปยังส่วนต่างๆ และการโทรได้ เมื่อตั้งค่าแล้ว android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL จะเปลี่ยนการโต้ตอบระหว่างคำขอโฟกัส NAVIGATION ขาเข้ากับผู้ถือโฟกัส CALL ปัจจุบันจากพร้อมกันเป็นปฏิเสธ ดังนั้นหากผู้ใช้ไม่ต้องการให้คําแนะนําการนําทางขัดจังหวะการโทร ผู้ใช้ก็สามารถเปิดใช้การตั้งค่านี้ได้ ค่านี้จะคงอยู่สำหรับผู้ใช้และสามารถตั้งค่าแบบไดนามิกได้เพื่อให้คำขอโฟกัสที่ตามมาใช้ค่าการตั้งค่าใหม่

โฟกัสเสียงที่เลื่อนเวลาได้

ใน Android 11 AAOS ได้เพิ่มการรองรับการขอโฟกัสเสียงที่เลื่อนเวลาได้ ซึ่งจะช่วยให้คำขอโฟกัสแบบถาวรล่าช้าได้เมื่อการโต้ตอบกับโฟกัสที่ถือครองอยู่ในปัจจุบันตามปกติแล้วจะทำให้คำขอถูกปฏิเสธ เมื่อการเปลี่ยนแปลงโฟกัสส่งผลให้มีสถานะที่คำขอที่เลื่อนเวลาไว้ได้รับโฟกัส คำขอดังกล่าวจะได้รับอนุมัติ

กฎสำหรับคำขอโฟกัสเสียงที่ล่าช้า

  • คำขอแบบถาวรเท่านั้น - ดังที่ได้กล่าวไว้ก่อนหน้านี้ คำขอที่เลื่อนเวลาไว้จะใช้ได้กับแหล่งที่มาแบบถาวรเท่านั้น วิธีนี้ช่วยหลีกเลี่ยงไม่ให้เสียงชั่วคราวเล่นนานหลังจากที่ไม่เกี่ยวข้องแล้ว
  • คุณเลื่อนเวลาคำขอได้เพียงครั้งละ 1 รายการ - หากมีคำขอที่เลื่อนเวลาได้เกิดขึ้นขณะที่มีคำขอที่เลื่อนเวลาอยู่แล้ว คำขอที่เลื่อนเวลาไว้ก่อนหน้านี้จะได้รับเหตุการณ์การเปลี่ยนแปลง AUDIOFOCUS_LOSS และคำขอใหม่จะได้รับการตอบกลับแบบซิงค์ของ AUDIOFOCUS_REQUEST_DELAYED
  • คำขอที่เลื่อนเวลาได้ต้องมี OnAudioFocusChangeListener เมื่อคำขอถูกเลื่อนออกไป จะใช้ Listener เพื่อแจ้งให้ผู้ขอทราบเมื่อคำขอได้รับอนุมัติในที่สุด (AUDIOFOCUS_GAIN) หรือหากถูกปฏิเสธในภายหลัง (AUDIOFOCUS_LOSS)

ขอโฟกัสที่เลื่อนเวลาได้

หากต้องการสร้างคำขอที่เลื่อนเวลาได้ ให้ใช้ตัวเลือกต่อไปนี้ AudioFocusRequest.Builder#setAcceptsDelayedFocusGain

mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener();

mDelayedFocusRequest = new AudioFocusRequest
     .Builder(AudioManager.AUDIOFOCUS_GAIN)
     .setAudioAttributes(mMusicAudioAttrib)
     .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener)
     .setForceDucking(false)
     .setWillPauseWhenDucked(false)
     .setAcceptsDelayedFocusGain(true)
     .build();

จากนั้นเมื่อส่งคำขอ ให้จัดการกับคําตอบ AUDIOFOCUS_REQUEST_DELAYED ดังนี้

int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest);
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// start audio playback
return;
}
if (delayedFocusRequestResults == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
     // audio playback delayed to audio focus listener
     return;
}

เมื่อคำขอล่าช้า ผู้ฟังโฟกัสมีหน้าที่รับผิดชอบในการจัดการการเปลี่ยนแปลงโฟกัส ดังนี้

private final class MediaWithDelayedFocusListener implements
OnAudioFocusChangeListener {
       @Override
       public void onAudioFocusChange(int focusChange) {
           synchronized (mLock) {
               switch (focusChange) {
                   case AudioManager.AUDIOFOCUS_GAIN:
                        // Start focus playback
                   case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        // Pause media transiently
                   case AudioManager.AUDIOFOCUS_LOSS:
                        // Stop media

การจัดการโฟกัสแบบหลายโซน

สำหรับยานพาหนะที่มีหลายโซนเสียง ระบบจะจัดการโฟกัสเสียงสำหรับแต่ละโซนแยกกัน ดังนั้น คำขอไปยังโซนหนึ่งจะไม่คำนึงถึงสิ่งที่โฟกัสอยู่ในโซนอื่น และไม่ทำให้โฟกัสในโซนอื่นเสียโฟกัส ฟีเจอร์นี้ช่วยให้คุณจัดการโฟกัสของห้องโดยสารหลักแยกจากระบบความบันเทิงสำหรับเบาะหลังได้ จึงหลีกเลี่ยงการหยุดเล่นเสียงในโซนหนึ่งเนื่องจากการเปลี่ยนแปลงโฟกัสในอีกโซนหนึ่ง

สำหรับแอปพลิเคชันทั้งหมด CarAudioService จะจัดการโฟกัสโดยอัตโนมัติ ระบบจะกำหนดโซนเสียงของคำขอโฟกัสตาม UserId หรือ UID ที่เชื่อมโยง โปรดดูรายละเอียดที่หัวข้อการกำหนดเส้นทางเสียง

การขอเสียงจากหลายโซนพร้อมกัน

หากแอปต้องการเล่นเสียงในหลายโซนพร้อมกัน จะต้องขอโฟกัสสำหรับแต่ละโซนโดยใส่ AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID ไว้ในแพ็กเกจ

// Create attribute with bundle and AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID
Bundle bundle = new Bundle();
bundle.putInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID,
               zoneId);

AudioAttributes attributesWithZone = new AudioAttributes.Builder()
     .setUsage(AudioAttributes.USAGE_MEDIA)
     .addBundle(bundle)
     .build();

// Create focus request using built attributesWithZone

พารามิเตอร์กลุ่มนี้ช่วยให้ผู้ขอลบล้างการแมปโซนเสียงอัตโนมัติเพื่อใช้รหัสโซนที่ระบุแทน ดังนั้น แอปจึงสามารถส่งคำขอแยกต่างหากสำหรับโซนเสียงแต่ละโซนได้

โฟกัสเสียง HAL

ตั้งแต่ Android 11 เป็นต้นไป HAL จะเปิดใช้เพื่อขอโฟกัสในนามของสตรีมภายนอก แม้ว่าจะเป็น API ที่ไม่บังคับ แต่เราขอแนะนำให้ใช้ API เหล่านี้เพื่อให้เสียงภายนอกมีส่วนร่วมในระบบนิเวศ Android ได้ดียิ่งขึ้นและมอบประสบการณ์การใช้งานที่ราบรื่นยิ่งขึ้นแก่ผู้ใช้

โปรดทราบว่า HAL ยังคงต้องรับผิดชอบในการตัดสินใจขั้นสุดท้ายว่าเสียงใดควรมีลําดับความสําคัญมากกว่า ด้วยเหตุนี้ ระบบจึงควรเล่นเสียงฉุกเฉินและเสียงที่สำคัญต่อความปลอดภัย ไม่ว่า HAL จะได้รับโฟกัสเสียงหรือไม่ก็ตาม และควรเล่นเสียงต่อไปตามความเหมาะสมแม้ว่า HAL จะเสียโฟกัสเสียงก็ตาม เสียงใดๆ ก็ตามที่จําเป็นตามกฎระเบียบก็เช่นเดียวกัน

ในทำนองเดียวกัน HAL ควรปิดเสียงสตรีม Android อย่างสม่ำเสมอตามความเหมาะสมเมื่อเล่นเสียงฉุกเฉินหรือเสียงที่สำคัญต่อความปลอดภัยเพื่อให้ได้ยินเสียงดังกล่าวอย่างชัดเจน

AudioControl@2.0

AudioControl HAL เวอร์ชัน 2.0 เปิดตัว API ใหม่หลายรายการ ดังนี้

API วัตถุประสงค์
IAudioControl#registerFocusListener ลงทะเบียนอินสแตนซ์ของ IFocusListener กับ HAL ของ AudioControl โปรแกรมรับฟังนี้ช่วยให้ HAL สามารถขอและยกเลิกโฟกัสเสียงได้ โดย HAl ควรจะระบุอินสแตนซ์ ICloseHandle เพื่อให้ Android ใช้ยกเลิกการลงทะเบียนตัวรับฟัง
IAudioControl#onAudioFocusChange แจ้ง HAL เกี่ยวกับการเปลี่ยนแปลงสถานะเพื่อโฟกัสคำขอที่ HAL ส่งผ่านIFocusListener ซึ่งรวมถึงการตอบกลับคำขอโฟกัสครั้งแรก
IFocusListener#requestAudioFocus ส่งคําขอโฟกัสในนามของ HAL สําหรับการใช้งาน รหัสโซน และประเภทการเพิ่มความคมชัดที่ระบุ
IFocusListener#abandonAudioFocus ยกเลิกคำขอโฟกัส HAL ที่มีอยู่สำหรับการใช้งานและรหัสโซนที่ระบุ

HAL อาจมีคำขอโฟกัสหลายรายการพร้อมกัน แต่จำกัดไว้ที่ 1 คำขอต่อการใช้งานและการจับคู่รหัสโซน โปรดทราบว่า Android จะถือว่า HAL เริ่มเล่นเสียงสำหรับการใช้งานทันทีเมื่อมีคำขอ และเล่นต่อไปจนกว่าจะยกเลิกโฟกัส

นอกเหนือจาก registerFocusListener แล้ว คำขอเหล่านี้ทั้งหมดจะเป็น oneway เพื่อให้ Android ไม่เลื่อนเวลา HAL ขณะประมวลผลคำขอโฟกัส HALไม่ควรรอรับโฟกัสก่อนเล่นเสียงที่สำคัญต่อความปลอดภัย HAL จะฟังและตอบสนองต่อการเปลี่ยนแปลงโฟกัสเสียงผ่าน IAudioControl#onAudioFocusChange หรือไม่ก็ได้ แต่เราขอแนะนำให้ทำเมื่อเหมาะสม