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
- สร้าง 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 มักจะอยู่ในสถานะเสถียรหนึ่งใน 5 สถานะ (สถานะข้อผิดพลาด "ยกเลิกการเชื่อมต่อแล้ว" จะอธิบายไว้ที่ตอนท้ายของส่วนนี้)
- เปิด
- เริ่มต้นแล้ว
- หยุดชั่วคราว
- หน้าแดงก่ำ
- หยุดแล้ว
ข้อมูลไหลผ่านสตรีมเมื่อสตรีมอยู่ในสถานะ "เริ่มต้น" เท่านั้น ถึง ย้ายสตรีมระหว่างรัฐ ใช้ฟังก์ชันใดฟังก์ชันหนึ่งที่ขอสถานะ การเปลี่ยน:
aaudio_result_t result;
result = AAudioStream_requestStart(stream);
result = AAudioStream_requestStop(stream);
result = AAudioStream_requestPause(stream);
result = AAudioStream_requestFlush(stream);
โปรดทราบว่าคุณขอหยุดชั่วคราวหรือล้างสตรีมเอาต์พุตได้เท่านั้นในกรณีต่อไปนี้
ฟังก์ชันเหล่านี้ทำงานไม่พร้อมกัน และการเปลี่ยนแปลงสถานะจะไม่เกิดขึ้น ทันที เมื่อคุณขอเปลี่ยนแปลงสถานะ สตรีมจะย้ายหนึ่งใน สถานะชั่วคราวที่เกี่ยวข้อง:
- กำลังเริ่มต้น
- กำลังหยุดชั่วคราว
- ผิวแดง
- กำลังหยุด
- เปิดจากขอบ
แผนภาพสถานะด้านล่างแสดงสถานะที่คงที่เป็นสี่เหลี่ยมผืนผ้ามุมมน และสถานะชั่วคราวเป็นรูปสี่เหลี่ยมจุดประ
แต่คุณสามารถโทรหา close()
จากทุกรัฐแม้ว่าหมายเลขนั้นจะไม่ได้แสดงอยู่ก็ตาม
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 วิธี ได้แก่
- ใช้ Callback ที่มีลําดับความสําคัญสูง
- ใช้ฟังก์ชัน
AAudioStream_read(stream, buffer, numFrames, timeoutNanos)
และAAudioStream_write(stream, buffer, numFrames, timeoutNanos)
เพื่ออ่านหรือเขียนสตรีม
สำหรับการอ่านหรือการเขียนการบล็อกที่โอนจำนวนเฟรมที่ระบุ ให้ตั้งค่าระยะหมดเวลา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 เป็นทวีคูณของขนาดภาพถ่ายอัจฉริยะที่รายงาน
วิธีหนึ่งในการเพิ่มประสิทธิภาพขนาดบัฟเฟอร์คือการเริ่มต้นด้วยบัฟเฟอร์ขนาดใหญ่ แล้วค่อยๆ ลดขนาดเล็กลงจนกว่าขนาดบัฟเฟอร์จะเริ่ม จากนั้นสะบัดกลับขึ้น หรือคุณอาจเริ่มจากขนาดบัฟเฟอร์เล็กๆ และถ้าอัตราดังกล่าวมีประสิทธิภาพต่ำกว่าที่ควรจะเป็น ให้เพิ่มขนาดบัฟเฟอร์จนกว่าเอาต์พุตจะไหลลื่นอีกครั้ง
กระบวนการนี้จะเกิดขึ้นอย่างรวดเร็ว โดยอาจเกิดขึ้นก่อนที่ผู้ใช้จะเล่นเสียงครั้งแรก คุณอาจต้องปรับขนาดบัฟเฟอร์เริ่มต้นก่อน โดยใช้ปิดเสียง เพื่อไม่ให้ผู้ใช้ได้ยินเสียงบกพร่องใดๆ ประสิทธิภาพของระบบอาจเปลี่ยนแปลงเมื่อเวลาผ่านไป (ตัวอย่างเช่น ผู้ใช้อาจปิดโหมดบนเครื่องบิน) เนื่องจากการปรับแต่งบัฟเฟอร์เพิ่มค่าใช้จ่ายในการดำเนินการน้อยมาก แอปของคุณ จะทำได้อย่างต่อเนื่องขณะที่แอปอ่านหรือเขียนข้อมูลลงในสตรีม
ต่อไปนี้คือตัวอย่างของลูปการเพิ่มประสิทธิภาพบัฟเฟอร์
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 เพื่อลดเวลาในการตอบสนอง
แหล่งข้อมูลเพิ่มเติม
หากต้องการดูข้อมูลเพิ่มเติม โปรดใช้ประโยชน์จากแหล่งข้อมูลต่อไปนี้