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
- สร้าง AAudioStreamBuilder
AAudioStreamBuilder *builder; aaudio_result_t result = AAudio_createStreamBuilder(&builder);
- ตั้งค่าการกำหนดค่าสตรีมเสียงในเครื่องมือสร้างโดยใช้ฟังก์ชันเครื่องมือสร้างที่สอดคล้องกับพารามิเตอร์สตรีม ฟังก์ชันชุดที่ไม่บังคับมีดังนี้
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 ด้านล่าง เพื่อความปลอดภัย
- เมื่อกําหนดค่า AAudioStreamBuilder แล้ว ให้ใช้เพื่อสร้างสตรีม ดังนี้
AAudioStream *stream; result = AAudioStreamBuilder_openStream(builder, &stream);
- หลังจากสร้างสตรีมแล้ว ให้ยืนยันการกําหนดค่า หากคุณระบุรูปแบบตัวอย่าง อัตราตัวอย่าง หรือจำนวนตัวอย่างต่อเฟรมไว้ ระบบจะไม่เปลี่ยนแปลงค่าดังกล่าว หากคุณระบุโหมดการแชร์หรือความจุของบัฟเฟอร์ไว้ โหมดหรือความจุดังกล่าวอาจเปลี่ยนแปลงไปโดยขึ้นอยู่กับความสามารถของอุปกรณ์เสียงของสตรีมและอุปกรณ์ Android ที่ใช้งานอยู่ คุณควรตรวจสอบการกําหนดค่าของสตรีมก่อนใช้งานเพื่อความปลอดภัย ฟังก์ชันในการดึงข้อมูลการตั้งค่าสตรีมที่สอดคล้องกับการตั้งค่าบิลเดอร์แต่ละรายการมีดังนี้
- คุณสามารถบันทึกเครื่องมือสร้างและนำกลับมาใช้ใหม่ในอนาคตเพื่อสร้างสตรีมเพิ่มเติมได้ แต่หากไม่ได้วางแผนที่จะใช้อีกต่อไป คุณควรลบ
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 ไม่ได้ให้คอลแบ็กเพื่อแจ้งให้คุณทราบถึงการเปลี่ยนแปลงสถานะ คุณสามารถรอการเปลี่ยนแปลงสถานะโดยใช้ฟังก์ชันพิเศษ 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 วิธีดังนี้
- ใช้การติดต่อกลับที่มีลําดับความสําคัญสูง
- ใช้ฟังก์ชัน
AAudioStream_read(stream, buffer, numFrames, timeoutNanos)
และAAudioStream_write(stream, buffer, numFrames, timeoutNanos)
เพื่ออ่านหรือเขียนสตรีม
สําหรับการอ่านหรือเขียนแบบบล็อกที่โอนเฟรมตามจํานวนที่กำหนด ให้ตั้งค่า 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 เป็นแบบหลายเท่าของขนาดการระเบิดที่รายงาน
วิธีหนึ่งในการเพิ่มประสิทธิภาพขนาดบัฟเฟอร์คือการเริ่มต้นด้วยบัฟเฟอร์ขนาดใหญ่ แล้วค่อยๆ ลดขนาดลงจนกว่าจะเริ่มเกิดปัญหาการขาดแคลนบัฟเฟอร์ แล้วค่อยๆ เพิ่มขนาดกลับขึ้น หรือจะเริ่มต้นด้วยบัฟเฟอร์ขนาดเล็กก็ได้ หากเกิดปัญหาการประมวลผลไม่ทัน ให้เพิ่มขนาดบัฟเฟอร์จนกว่าเอาต์พุตจะทำงานได้อย่างราบรื่นอีกครั้ง
กระบวนการนี้อาจเกิดขึ้นอย่างรวดเร็ว ก่อนที่ผู้ใช้จะเล่นเสียงแรก คุณอาจต้องปรับขนาดบัฟเฟอร์เริ่มต้นก่อนโดยใช้เสียงเงียบ เพื่อให้ผู้ใช้ไม่ได้ยินเสียงที่กระตุก ประสิทธิภาพของระบบอาจเปลี่ยนแปลงเมื่อเวลาผ่านไป (เช่น ผู้ใช้อาจปิดโหมดบนเครื่องบิน) เนื่องจากการปรับบัฟเฟอร์จะเพิ่มค่าใช้จ่ายเพียงเล็กน้อย แอปของคุณจึงสามารถปรับได้อย่างต่อเนื่องขณะที่อ่านหรือเขียนข้อมูลลงในสตรีม
ต่อไปนี้คือตัวอย่างของลูปการเพิ่มประสิทธิภาพบัฟเฟอร์
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 ใช้การเรียกกลับเพื่อให้มีค่าความหน่วงต่ำลง
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมได้จากแหล่งข้อมูลต่อไปนี้