เสียง

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

AAudio API ได้รับการออกแบบมาให้มีฟังก์ชันการทำงานน้อยที่สุด จึงไม่สามารถดำเนินการต่อไปนี้

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

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

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

#include <aaudio/AAudio.h>

สตรีมเสียง

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

สตรีมจะกำหนดโดยสิ่งต่อไปนี้

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

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

สตรีมแต่ละรายการจะเชื่อมโยงกับอุปกรณ์เสียงเครื่องเดียว

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

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

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

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

โหมดการแชร์

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

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

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

รูปแบบเสียง

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

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

AAudio อนุญาตให้ใช้รูปแบบตัวอย่างต่อไปนี้

aaudio_format_t ประเภทข้อมูล C หมายเหตุ
AAUDIO_FORMAT_PCM_I16 int16_t ตัวอย่าง 16 บิตทั่วไป รูปแบบ Q0.15
AAUDIO_FORMAT_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 จะอยู่ในสถานะที่เสถียร 1 ใน 5 สถานะต่อไปนี้ (สถานะข้อผิดพลาด "ยกเลิกการเชื่อมต่อ" จะอธิบายไว้ที่ส่วนท้ายของส่วนนี้)

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

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

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

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

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

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

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

วงจร AAudio

AAudio ไม่ได้ให้คอลแบ็กเพื่อแจ้งให้คุณทราบถึงการเปลี่ยนแปลงสถานะ คุณสามารถรอการเปลี่ยนแปลงสถานะโดยใช้ฟังก์ชันพิเศษ 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 จะแสดงสถานะปัจจุบันของสตรีม

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

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

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

สําหรับการอ่านหรือเขียนแบบบล็อกที่โอนเฟรมตามจํานวนที่กำหนด ให้ตั้งค่า timeoutNanos มากกว่า 0 สําหรับการเรียกที่ไม่บล็อก ให้ตั้งค่า timeoutNanos เป็น 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);
}

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

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

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

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

AAudioStream_close(stream);

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

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

สตรีมเสียงตัดการเชื่อมต่อ

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

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

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

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

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

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

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() เพื่อยืนยันความจุจริงของบัฟเฟอร์

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

เมื่อแอปพลิเคชันเล่นเสียงออก ระบบจะเขียนลงในบัฟเฟอร์และบล็อกจนกว่าการเขียนจะเสร็จสมบูรณ์ AAudio จะอ่านจากบัฟเฟอร์เป็นชุดๆ แบบไม่ต่อเนื่อง แต่ละพัลส์ประกอบด้วยเฟรมเสียงหลายเฟรม และมักจะมีขนาดเล็กกว่าขนาดของบัฟเฟอร์ที่อ่าน ระบบจะควบคุมขนาดและอัตราของพัลส์ ซึ่งโดยทั่วไปแล้วจะกำหนดโดยวงจรของอุปกรณ์เสียง แม้ว่าคุณจะเปลี่ยนขนาดของภาพต่อเนื่องหรืออัตราภาพต่อเนื่องไม่ได้ แต่ก็สามารถตั้งค่าขนาดของบัฟเฟอร์ภายในตามจำนวนภาพต่อเนื่องที่มีได้ โดยทั่วไปแล้ว คุณจะได้รับเวลาในการตอบสนองต่ำสุดหากขนาดบัฟเฟอร์ของ 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);
        }
    }
}

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

การใช้การติดต่อกลับที่มีลําดับความสําคัญสูง

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

ฟังก์ชัน Callback มีโปรโตไทป์ดังนี้

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

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

AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);

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

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

เช่น คุณอาจใช้การเรียกกลับเพื่อสร้างเอาต์พุตคลื่นไซน์อย่างต่อเนื่อง ดังนี้

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

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

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 พร้อมกับการเรียกกลับที่มีลำดับความสำคัญสูง ทำตามตัวอย่างนี้

// 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 บางรายการพร้อมกันจากมากกว่า 1 เธรดไม่ได้ เนื่องจาก AAudio หลีกเลี่ยงการใช้ Mutex ซึ่งอาจทำให้เกิดการแย่งสิทธิ์ใช้เธรดและข้อบกพร่อง

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

การเรียกที่แสดงผลการตั้งค่าสตรีม เช่น AAudioStream_getSampleRate() และ AAudioStream_getChannelCount() จะเป็นแบบปลอดภัยสำหรับเธรด

การเรียกเหล่านี้ยังปลอดภัยสำหรับเธรดด้วย

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

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

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

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

ดูข้อมูลเพิ่มเติมได้จากแหล่งข้อมูลต่อไปนี้

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

Codelabs

วิดีโอ