เสียง

AAudio คือ Android C API ใหม่ที่เปิดตัวใน Android O โดยออกแบบมาสำหรับแอปพลิเคชันเสียงประสิทธิภาพสูงที่ต้องใช้เวลาในการตอบสนองต่ำ แอปสื่อสารกับ AAudio โดยการอ่านและเขียนข้อมูลไปยังสตรีม

AAudio API ออกแบบมาให้เรียบง่าย จึงไม่ได้ใช้ฟังก์ชันเหล่านี้

  • การแจงนับอุปกรณ์เสียง
  • การกำหนดเส้นทางอัตโนมัติระหว่างปลายทางเสียง
  • ไฟล์ I/O
  • การถอดรหัสเสียงที่บีบอัด
  • การนำเสนออินพุต/สตรีมทั้งหมดโดยอัตโนมัติใน Callback เดียว

เริ่มต้นใช้งาน

คุณสามารถเรียก AAudio จากโค้ด C++ หากต้องการเพิ่มชุดฟีเจอร์ AAudio ลงในแอปของคุณ ให้ใส่ไฟล์ส่วนหัว AAudio.h ดังนี้

#include <aaudio/AAudio.h>

สตรีมเสียง

AAudio จะย้ายข้อมูลเสียงระหว่างแอปของคุณกับอินพุตและเอาต์พุตเสียงในอุปกรณ์ Android แอปของคุณส่งข้อมูลเข้าและออกโดยการอ่านและเขียนจากสตรีมเสียง ซึ่งแสดงด้วยโครงสร้าง AAudioStream การเรียกอ่าน/เขียนอาจบล็อกหรือไม่บล็อกก็ได้

สตรีมมีคำจำกัดความดังนี้

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

อุปกรณ์เสียง

แต่ละสตรีมจะแนบอยู่กับอุปกรณ์เสียง 1 เครื่อง

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

คุณสามารถใช้ AudioManager เมธอด getDevices() เพื่อค้นหาอุปกรณ์เสียงที่พร้อมใช้งานในอุปกรณ์ Android ของคุณ เมธอดจะแสดงข้อมูลเกี่ยวกับ type ของอุปกรณ์แต่ละเครื่อง

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

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

โหมดการแชร์

สตรีมจะมีโหมดการแชร์ดังนี้

  • AAUDIO_SHARING_MODE_EXCLUSIVE หมายความว่าสตรีมมีสิทธิพิเศษในการเข้าถึงอุปกรณ์เสียง ไม่สามารถใช้อุปกรณ์นี้กับสตรีมเสียงอื่น หากมีการใช้อุปกรณ์เสียงอยู่แล้ว สตรีมอาจไม่มีสิทธิ์ในการเข้าถึงแบบพิเศษ สตรีมสุดพิเศษมีแนวโน้มที่จะมีเวลาในการตอบสนองต่ำกว่า แต่ก็มีแนวโน้มที่จะถูกตัดการเชื่อมต่อมากกว่าด้วย คุณควรปิดสตรีมสุดพิเศษทันทีที่ไม่จำเป็นต้องใช้แล้ว เพื่อให้แอปอื่นๆ เข้าถึงอุปกรณ์ได้ สตรีมสุดพิเศษใช้เวลาในการตอบสนองต่ำที่สุด
  • AAUDIO_SHARING_MODE_SHARED อนุญาตให้ AAudio มิกซ์เสียงได้ เสียงจะมิกซ์สตรีมที่แชร์ทั้งหมดที่มอบหมายให้กับอุปกรณ์เดียวกัน

คุณสามารถตั้งค่าโหมดการแชร์อย่างชัดแจ้งเมื่อสร้างสตรีม โดยค่าเริ่มต้น โหมดการแชร์คือ SHARED

รูปแบบเสียง

ข้อมูลที่ส่งผ่านสตรีมจะมีแอตทริบิวต์เสียงดิจิทัลตามปกติ ดังนี้

  • รูปแบบข้อมูลตัวอย่าง
  • จำนวนช่อง (ตัวอย่างต่อเฟรม)
  • อัตราตัวอย่าง

รูปแบบเสียงที่ AAudio อนุญาตมีดังนี้

รูปแบบเสียง ประเภทข้อมูล C หมายเหตุ
เสียง_รูปแบบ PCM_I16 int16_t ตัวอย่าง 16 บิตทั่วไป รูปแบบ Q0.15
เสียง_รูปแบบ_PCM_FLOAT float -1.0 ถึง +1.0
AAUDIO_FORMAT_PCM_I24_PACKED uint8_t ในกลุ่ม 3 คน ตัวอย่าง 24 บิตที่แพ็ค รูปแบบ Q0.23
AAUDIO_FORMAT_PCM_I32 int32_t ตัวอย่าง 32 บิตทั่วไป รูปแบบ Q0.31
AAUDIO_FORMAT_IEC61937 Uint8_t เสียงที่บีบอัดใน IEC61937 สำหรับการส่งผ่านสัญญาณ HDMI หรือ S/PDIF

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

aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
//... later
if (dataFormat == AAUDIO_FORMAT_PCM_I16) {
     convertFloatToPcm16(...)
}

การสร้างสตรีมเสียง

ไลบรารี AAudio เป็นไปตามรูปแบบการออกแบบของเครื่องมือสร้างและมี AAudioStreamBuilder

  1. สร้าง AAudioStreamBuilder

    AAudioStreamBuilder *builder;
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
    

  2. ตั้งการกำหนดค่าสตรีมเสียงในเครื่องมือสร้าง โดยใช้ฟังก์ชันของเครื่องมือสร้างที่สอดคล้องกับพารามิเตอร์สตรีม ฟังก์ชันชุดตัวเลือกที่พร้อมใช้งานต่อไปนี้

    AAudioStreamBuilder_setDeviceId(builder, deviceId);
    AAudioStreamBuilder_setDirection(builder, direction);
    AAudioStreamBuilder_setSharingMode(builder, mode);
    AAudioStreamBuilder_setSampleRate(builder, sampleRate);
    AAudioStreamBuilder_setChannelCount(builder, channelCount);
    AAudioStreamBuilder_setFormat(builder, format);
    AAudioStreamBuilder_setBufferCapacityInFrames(builder, frames);
    

    โปรดทราบว่าวิธีการเหล่านี้จะไม่รายงานข้อผิดพลาด เช่น ค่าคงที่ที่ไม่ได้กำหนด หรือค่าอยู่นอกช่วง

    หากคุณไม่ระบุ deviceId ค่าเริ่มต้นจะเป็นอุปกรณ์เอาต์พุตหลัก หากคุณไม่ระบุทิศทางสตรีม ค่าเริ่มต้นจะเป็นสตรีมเอาต์พุต สําหรับพารามิเตอร์อื่นๆ ทั้งหมด คุณสามารถกำหนดค่าอย่างชัดแจ้งหรือให้ระบบ กำหนดค่าที่เหมาะสมที่สุดโดยการไม่ระบุพารามิเตอร์เลยหรือการตั้งค่า ไปยัง AAUDIO_UNSPECIFIED

    เพื่อความปลอดภัย ให้ตรวจสอบสถานะของสตรีมเสียงหลังจากที่คุณสร้างสตรีมแล้ว ตามที่อธิบายไว้ในขั้นตอนที่ 4 ด้านล่าง

  3. เมื่อกำหนดค่า AAudioStreamBuilder แล้ว ให้ใช้เพื่อสร้างสตรีมโดยทำดังนี้

    AAudioStream *stream;
    result = AAudioStreamBuilder_openStream(builder, &stream);
    

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

    AAudioStreamBuilder_setDeviceId() AAudioStream_getDeviceId()
    AAudioStreamBuilder_setDirection() AAudioStream_getDirection()
    AAudioStreamBuilder_setSharingMode() AAudioStream_getSharingMode()
    AAudioStreamBuilder_setSampleRate() AAudioStream_getSampleRate()
    AAudioStreamBuilder_setChannelCount() AAudioStream_getChannelCount()
    AAudioStreamBuilder_setFormat() AAudioStream_getFormat()
    AAudioStreamBuilder_setBufferCapacityInFrames() AAudioStream_getBufferCapacityInFrames()

  5. คุณสามารถบันทึกเครื่องมือสร้างและใช้ซ้ำในอนาคตเพื่อสร้างสตรีมเพิ่มเติม แต่หากไม่มีแผนจะใช้บัตรแล้ว คุณควรลบออก

    AAudioStreamBuilder_delete(builder);
    

การใช้สตรีมเสียง

การเปลี่ยนผ่านรัฐ

สตรีม AAudio มักจะอยู่ในสถานะเสถียรหนึ่งใน 5 สถานะ (สถานะข้อผิดพลาด "ยกเลิกการเชื่อมต่อแล้ว" จะอธิบายไว้ที่ตอนท้ายของส่วนนี้)

  • เปิด
  • เริ่มต้นแล้ว
  • หยุดชั่วคราว
  • หน้าแดงก่ำ
  • หยุดแล้ว

ข้อมูลไหลผ่านสตรีมเมื่อสตรีมอยู่ในสถานะ "เริ่มต้น" เท่านั้น ถึง ย้ายสตรีมระหว่างรัฐ ใช้ฟังก์ชันใดฟังก์ชันหนึ่งที่ขอสถานะ การเปลี่ยน:

aaudio_result_t result;
result = AAudioStream_requestStart(stream);
result = AAudioStream_requestStop(stream);
result = AAudioStream_requestPause(stream);
result = AAudioStream_requestFlush(stream);

โปรดทราบว่าคุณขอหยุดชั่วคราวหรือล้างสตรีมเอาต์พุตได้เท่านั้นในกรณีต่อไปนี้

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

  • กำลังเริ่มต้น
  • กำลังหยุดชั่วคราว
  • ผิวแดง
  • กำลังหยุด
  • เปิดจากขอบ

แผนภาพสถานะด้านล่างแสดงสถานะที่คงที่เป็นสี่เหลี่ยมผืนผ้ามุมมน และสถานะชั่วคราวเป็นรูปสี่เหลี่ยมจุดประ แต่คุณสามารถโทรหา close() จากทุกรัฐแม้ว่าหมายเลขนั้นจะไม่ได้แสดงอยู่ก็ตาม

วงจรของ AAudio

AAudio จะไม่มี Callback เพื่อแจ้งเตือนการเปลี่ยนแปลงสถานะ พิเศษ 1 รายการ ฟังก์ชัน คุณใช้ AAudioStream_waitForStateChange(stream, inputState, nextState, timeout) เพื่อรอการเปลี่ยนแปลงสถานะได้

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

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

aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
aaudio_stream_state_t nextState = AAUDIO_STREAM_STATE_UNINITIALIZED;
int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND;
result = AAudioStream_requestPause(stream);
result = AAudioStream_waitForStateChange(stream, inputState, &nextState, timeoutNanos);

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

คุณสามารถใช้เทคนิคเดียวกันนี้หลังจากเรียกใช้คำขอเริ่ม หยุด ล้าง หรือล้างออก โดยใช้สถานะชั่วคราวที่สอดคล้องกันเป็น InputState ไม่ต้องโทร waitForStateChange() หลังจากเรียก AAudioStream_close() ตั้งแต่สตรีม จะถูกลบทันทีที่ปิด และอย่าโทรหา AAudioStream_close() ในขณะที่ waitForStateChange() กำลังทำงานในชุดข้อความอื่น

การอ่านและการเขียนไปยังสตรีมเสียง

การประมวลผลข้อมูลในสตรีมหลังจากเริ่มมี 2 วิธี ได้แก่

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

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

aaudio_result_t result =
    AAudioStream_read(stream, audioData, numFrames, timeout);
if (result < 0) {
  // Error!
}
if (result != numFrames) {
  // pad the buffer with zeros
  memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
      sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
}

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

ข้อมูลในบัฟเฟอร์ต้องตรงกับรูปแบบข้อมูลที่แสดงผลโดย AAudioStream_getDataFormat()

การปิดสตรีมเสียง

เมื่อคุณใช้สตรีมเสร็จแล้ว ให้ปิดสตรีมด้วยคำสั่งต่อไปนี้

AAudioStream_close(stream);

หลังจากปิดสตรีมแล้ว คุณจะใช้สตรีมดังกล่าวกับฟังก์ชันที่ใช้สตรีม AAudio ไม่ได้

ยกเลิกการเชื่อมต่อสตรีมเสียงแล้ว

สตรีมเสียงอาจถูกยกเลิกการเชื่อมต่อได้ทุกเมื่อหากมีเหตุการณ์ใดเหตุการณ์หนึ่งต่อไปนี้เกิดขึ้น

  • อุปกรณ์เสียงที่เกี่ยวข้องไม่ได้เชื่อมต่ออยู่อีกต่อไป (เช่น เมื่อถอดปลั๊กหูฟัง)
  • เกิดข้อผิดพลาดภายใน
  • อุปกรณ์เสียงไม่ได้เป็นอุปกรณ์เสียงหลักอีกต่อไป

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

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

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

โปรดทราบว่าหากคุณเปิดสตรีมใหม่ สตรีมอาจมีการตั้งค่าที่แตกต่างกัน จากสตรีมต้นฉบับ (ตัวอย่างเช่น framePerBurst):

void errorCallback(AAudioStream *stream,
                   void *userData,
                   aaudio_result_t error) {
    // Launch a new thread to handle the disconnect.
    std::thread myThread(my_error_thread_proc, stream, userData);
    myThread.detach(); // Don't wait for the thread to finish.
}

การเพิ่มประสิทธิภาพ

คุณสามารถเพิ่มประสิทธิภาพการทำงานของแอปพลิเคชันเสียงได้ด้วยการปรับบัฟเฟอร์ภายในและใช้ชุดข้อความที่มีลําดับความสําคัญสูงพิเศษ

ปรับบัฟเฟอร์เพื่อลดเวลาในการตอบสนอง

AAudio จะส่งข้อมูลเข้าและออกจากบัฟเฟอร์ภายในที่เก็บรักษาไว้ 1 ส่วนสำหรับอุปกรณ์เสียงแต่ละเครื่อง

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

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

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

      บัฟเฟอร์ AAudio

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

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

ต่อไปนี้คือตัวอย่างของลูปการเพิ่มประสิทธิภาพบัฟเฟอร์

int32_t previousUnderrunCount = 0;
int32_t framesPerBurst = AAudioStream_getFramesPerBurst(stream);
int32_t bufferSize = AAudioStream_getBufferSizeInFrames(stream);

int32_t bufferCapacity = AAudioStream_getBufferCapacityInFrames(stream);

while (go) {
    result = writeSomeData();
    if (result < 0) break;

    // Are we getting underruns?
    if (bufferSize < bufferCapacity) {
        int32_t underrunCount = AAudioStream_getXRunCount(stream);
        if (underrunCount > previousUnderrunCount) {
            previousUnderrunCount = underrunCount;
            // Try increasing the buffer size by one burst
            bufferSize += framesPerBurst;
            bufferSize = AAudioStream_setBufferSize(stream, bufferSize);
        }
    }
}

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

การใช้ Callback ที่มีลำดับความสำคัญสูง

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

ฟังก์ชัน Callback มีต้นแบบนี้

typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames);

ใช้การสร้างสตรีมเพื่อลงทะเบียนการติดต่อกลับ ดังนี้

AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

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

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

ตัวอย่างเช่น คุณสามารถใช้ Callback เพื่อสร้างเอาต์พุตคลื่นไซน์อย่างต่อเนื่อง ดังนี้

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    int64_t timeout = 0;

    // Write samples directly into the audioData array.
    generateSineWave(static_cast<float *>(audioData), numFrames);
    return AAUDIO_CALLABCK_RESULT_CONTINUE;
}

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

Callback จะอ่านแบบไม่บล็อกจากสตรีมอินพุตโดยวางข้อมูลลงในบัฟเฟอร์ของสตรีมเอาต์พุต ดังนี้

aaudio_data_callback_result_t myCallback(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames) {
    AAudioStream *inputStream = (AAudioStream *) userData;
    int64_t timeout = 0;
    aaudio_result_t result =
        AAudioStream_read(inputStream, audioData, numFrames, timeout);

  if (result == numFrames)
      return AAUDIO_CALLABCK_RESULT_CONTINUE;
  if (result >= 0) {
      memset(static_cast<sample_type*>(audioData) + result * samplesPerFrame, 0,
          sizeof(sample_type) * (numFrames - result) * samplesPerFrame);
      return AAUDIO_CALLBACK_RESULT_CONTINUE;
  }
  return AAUDIO_CALLBACK_RESULT_STOP;
}

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

การตั้งค่าโหมดประสิทธิภาพ

AAudioStream จะมีโหมดประสิทธิภาพซึ่งมีผลกับลักษณะการทำงานของแอปอย่างมาก โดยมี 3 โหมดดังนี้

  • AAUDIO_PERFORMANCE_MODE_NONE คือโหมดเริ่มต้น โดยใช้สตรีมพื้นฐานที่รักษาสมดุลระหว่างเวลาในการตอบสนองและการประหยัดพลังงาน
  • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY ใช้บัฟเฟอร์ที่เล็กลงและเส้นทางข้อมูลที่มีการเพิ่มประสิทธิภาพเพื่อลดเวลาในการตอบสนอง
  • AAUDIO_PERFORMANCE_MODE_POWER_SAVING ใช้บัฟเฟอร์ภายในที่มีขนาดใหญ่กว่าและเส้นทางข้อมูลเพื่อลดเวลาในการตอบสนองเพื่อใช้พลังงานน้อยลง

คุณเลือกโหมดประสิทธิภาพได้โดยเรียกใช้ setPerformanceMode() และค้นหาโหมดปัจจุบันได้โดยเรียกใช้ getPerformanceMode()

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

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

ใน AAudio เวอร์ชันปัจจุบัน คุณต้องใช้โหมดประสิทธิภาพของ AAUDIO_PERFORMANCE_MODE_LOW_LATENCY ร่วมกับ Callback ที่มีลำดับความสำคัญสูงเพื่อให้ได้เวลาในการตอบสนองต่ำที่สุด ทำตามตัวอย่างนี้

// Create a stream builder
AAudioStreamBuilder *streamBuilder;
AAudio_createStreamBuilder(&streamBuilder);
AAudioStreamBuilder_setDataCallback(streamBuilder, dataCallback, nullptr);
AAudioStreamBuilder_setPerformanceMode(streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

// Use it to create the stream
AAudioStream *stream;
AAudioStreamBuilder_openStream(streamBuilder, &stream);

ความปลอดภัยของชุดข้อความ

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

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

การโทรที่ส่งคืนการตั้งค่าสตรีม เช่น AAudioStream_getSampleRate() และ AAudioStream_getChannelCount() จะใช้ชุดข้อความได้อย่างปลอดภัย

การเรียกเหล่านี้ยังปลอดภัยในชุดข้อความด้วย

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*() ยกเว้น AAudioStream_getTimestamp()

ปัญหาที่ทราบ

  • เวลาในการตอบสนองของเสียงจะสูงสำหรับการบล็อก Write() เนื่องจากรุ่น Android O DP2 ไม่ได้ใช้แทร็ก FAST ใช้ Callback เพื่อลดเวลาในการตอบสนอง

แหล่งข้อมูลเพิ่มเติม

หากต้องการดูข้อมูลเพิ่มเติม โปรดใช้ประโยชน์จากแหล่งข้อมูลต่อไปนี้

ข้อมูลอ้างอิงของ API

Codelab

วิดีโอ