AAudio, Android O रिलीज़ में पेश किया गया एक नया Android C API है. इसे बेहतर परफ़ॉर्मेंस वाले उन ऑडियो ऐप्लिकेशन के लिए डिज़ाइन किया गया है जिन्हें कम इंतज़ार का समय चाहिए. ऐप्लिकेशन, स्ट्रीम में डेटा पढ़कर और उसमें डेटा लिखकर, AAudio के साथ कम्यूनिकेट करते हैं.
AAudio API को डिज़ाइन के हिसाब से कम से कम बनाया गया है. इसलिए, यह ये काम नहीं करता:
- ऑडियो डिवाइस की जानकारी
- ऑडियो एंडपॉइंट के बीच अपने-आप रूटिंग होना
- फ़ाइल का इनपुट/आउटपुट
- कंप्रेस किए गए ऑडियो को डिकोड करना
- एक ही कॉलबैक में सभी इनपुट/स्ट्रीम अपने-आप प्रज़ेंट हो जाती हैं.
शुरू करना
C++ कोड से AAudio को कॉल किया जा सकता है. अपने ऐप्लिकेशन में AAudio सुविधा सेट जोड़ने के लिए, AAudio.h हेडर फ़ाइल शामिल करें:
#include <aaudio/AAudio.h>
ऑडियो स्ट्रीम
AAudio, आपके ऐप्लिकेशन और Android डिवाइस के ऑडियो इनपुट और आउटपुट के बीच ऑडियो डेटा को ट्रांसफ़र करता है. आपका ऐप्लिकेशन, ऑडियो स्ट्रीम में डेटा को पढ़कर और उसमें डेटा लिखकर, डेटा को इन और आउट करता है. इन स्ट्रीम को AAudioStream स्ट्रक्चर से दिखाया जाता है. रीड/राइट कॉल, ब्लॉकिंग या नॉन-ब्लॉकिंग हो सकते हैं.
स्ट्रीम की परिभाषा इनके आधार पर तय की जाती है:
- स्ट्रीम में डेटा का सोर्स या सिंक होने वाला ऑडियो डिवाइस.
- शेयर करने का मोड, यह तय करता है कि किसी स्ट्रीम के पास ऑडियो डिवाइस का खास ऐक्सेस है या नहीं. ऐसा हो सकता है कि ऑडियो डिवाइस को कई स्ट्रीम के बीच शेयर किया जाए.
- स्ट्रीम में मौजूद ऑडियो डेटा का फ़ॉर्मैट.
ऑडियो डिवाइस
हर स्ट्रीम, किसी एक ऑडियो डिवाइस से जुड़ी होती है.
ऑडियो डिवाइस, एक हार्डवेयर इंटरफ़ेस या वर्चुअल एंडपॉइंट होता है. यह डिजिटल ऑडियो डेटा की लगातार स्ट्रीम के लिए, सोर्स या सिंक के तौर पर काम करता है. ऑडियो डिवाइस (डिवाइस में पहले से मौजूद माइक या ब्लूटूथ हेडसेट) को, Android डिवाइस (फ़ोन या स्मार्टवॉच) से न जोड़ें. यह डिवाइस, आपका ऐप्लिकेशन चला रहा है.
अपने Android डिवाइस पर उपलब्ध ऑडियो डिवाइसों को ढूंढने के लिए, AudioManager
तरीके getDevices()
का इस्तेमाल किया जा सकता है. यह तरीका, हर डिवाइस के 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 | फ़्लोट | -1.0 से +1.0 |
AAUDIO_FORMAT_PCM_I24_PACKED | uint8_t, तीन के ग्रुप में | पैक किए गए 24-बिट सैंपल, Q0.23 फ़ॉर्मैट |
AAUDIO_FORMAT_PCM_I32 | int32_t | सामान्य 32-बिट सैंपल, Q0.31 फ़ॉर्मैट |
AAUDIO_FORMAT_IEC61937 | uint8_t | एचडीएमआई या S/PDIF पासथ्रू के लिए, IEC61937 में रैप किया गया कंप्रेस किया गया ऑडियो |
अगर किसी खास सैंपल फ़ॉर्मैट का अनुरोध किया जाता है, तो स्ट्रीम उस फ़ॉर्मैट का इस्तेमाल करेगी. भले ही, वह फ़ॉर्मैट डिवाइस के लिए सही न हो. अगर आपने सैंपल फ़ॉर्मैट नहीं बताया है, तो 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
पर सेट करके, सिस्टम को सबसे सही वैल्यू असाइन करने दिया जा सकता है.सुरक्षित रहने के लिए, ऑडियो स्ट्रीम बनाने के बाद उसकी स्थिति देखें. इसके बारे में नीचे चौथे चरण में बताया गया है.
- AAudioStreamBuilder कॉन्फ़िगर होने के बाद, स्ट्रीम बनाने के लिए इसका इस्तेमाल करें:
AAudioStream *stream; result = AAudioStreamBuilder_openStream(builder, &stream);
- स्ट्रीम बनाने के बाद, उसके कॉन्फ़िगरेशन की पुष्टि करें. अगर आपने सैंपल फ़ॉर्मैट, सैंपल रेट या हर फ़्रेम के सैंपल की जानकारी दी है, तो इनमें कोई बदलाव नहीं होगा. अगर आपने शेयर करने का मोड या बफ़र की क्षमता तय की है, तो ये स्ट्रीम के ऑडियो डिवाइस और उस Android डिवाइस की क्षमताओं के आधार पर बदल सकते हैं जिस पर स्ट्रीम चल रही है. सुरक्षा के लिहाज़ से, किसी स्ट्रीम का इस्तेमाल करने से पहले, आपको उसका कॉन्फ़िगरेशन देखना चाहिए. हर बिल्डर सेटिंग से जुड़ी स्ट्रीम सेटिंग को वापस पाने के लिए, ये फ़ंक्शन उपलब्ध हैं:
- बिल्डर को सेव करके, आने वाले समय में ज़्यादा स्ट्रीम बनाने के लिए उसका फिर से इस्तेमाल किया जा सकता है. हालांकि, अगर आपको अब इसका इस्तेमाल नहीं करना है, तो इसे मिटा दें.
AAudioStreamBuilder_delete(builder);
ऑडियो स्ट्रीम का इस्तेमाल करना
स्टेट ट्रांज़िशन
आम तौर पर, AAudio स्ट्रीम इनमें से किसी एक स्टेटस में होती है. गड़बड़ी की स्थिति, 'डिसकनेक्टेड' के बारे में इस सेक्शन के आखिर में बताया गया है:
- खोलें
- शुरू किया गया
- रोकी गई
- शर्म से लाल चेहरा
- बंद की गई
डेटा सिर्फ़ तब स्ट्रीम में फ़्लो होता है, जब स्ट्रीम 'शुरू हो गई' स्थिति में हो. किसी स्ट्रीम को एक स्टेटस से दूसरे स्टेटस पर ले जाने के लिए, स्टेटस ट्रांज़िशन का अनुरोध करने वाले इनमें से किसी फ़ंक्शन का इस्तेमाल करें:
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 को कॉल करने के बाद, इसी तकनीक का इस्तेमाल किया जा सकता है. इसके लिए, इनपुट स्टेटस के तौर पर ट्रांज़िशन स्टेटस का इस्तेमाल करें. AAudioStream_close()
को कॉल करने के बाद, waitForStateChange()
को कॉल न करें. ऐसा इसलिए, क्योंकि स्ट्रीम बंद होने के तुरंत बाद उसे मिटा दिया जाएगा. साथ ही, waitForStateChange()
किसी दूसरी थ्रेड में चलने के दौरान, AAudioStream_close()
को कॉल न करें.
ऑडियो स्ट्रीम को पढ़ना और उसमें बदलाव करना
स्ट्रीम शुरू होने के बाद, डेटा को प्रोसेस करने के दो तरीके हैं:
- ज़्यादा प्राथमिकता वाले कॉलबैक का इस्तेमाल करें.
AAudioStream_read(stream, buffer, numFrames, timeoutNanos)
औरAAudioStream_write(stream, buffer, numFrames, timeoutNanos)
फ़ंक्शन का इस्तेमाल करें. स्ट्रीम को पढ़ने या उसमें बदलाव करने के लिए.
तय संख्या में फ़्रेम ट्रांसफ़र करने वाली ब्लॉकिंग रीड या राइट के लिए, timeoutNanos को शून्य से ज़्यादा पर सेट करें. बिना ब्लॉक किए जाने वाले कॉल के लिए, timeoutNanos को शून्य पर सेट करें. इस मामले में, नतीजा ट्रांसफ़र किए गए फ़्रेम की असल संख्या होती है.
इनपुट पढ़ते समय, आपको यह पुष्टि करनी चाहिए कि सही संख्या में फ़्रेम पढ़े गए हैं. ऐसा न करने पर, बफ़र में अनजान डेटा हो सकता है. इससे ऑडियो में गड़बड़ी हो सकती है. साइलेंट ड्रॉपआउट बनाने के लिए, बफ़र में शून्य जोड़े जा सकते हैं:
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 को शून्य पर सेट करके, नॉन-ब्लॉकिंग कॉल में किया जाना चाहिए.
बफ़र में मौजूद डेटा, 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, अपने बनाए गए इंटरनल बफ़र में डेटा डालता है और उससे डेटा निकालता है. हर ऑडियो डिवाइस के लिए एक इंटरनल बफ़र होता है.
बफ़र की क्षमता से पता चलता है कि बफ़र में कितना डेटा सेव किया जा सकता है. सीटों की संख्या सेट करने के लिए,
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, कॉलबैक को ज़्यादा प्राथमिकता वाली थ्रेड में लागू करता है, जिसकी परफ़ॉर्मेंस बेहतर होती है.
कॉलबैक फ़ंक्शन का प्रोटोटाइप यह है:
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames);
कॉलबैक रजिस्टर करने के लिए, स्ट्रीम बिल्डिंग का इस्तेमाल करें:
AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);
सबसे आसान मामले में, स्ट्रीम अपने अगले बर्स्ट के लिए डेटा हासिल करने के लिए, समय-समय पर कॉलबैक फ़ंक्शन को लागू करती है.
कॉलबैक फ़ंक्शन को उस स्ट्रीम पर पढ़ने या लिखने की अनुमति नहीं होनी चाहिए जिसने उसे ट्रिगर किया है. अगर कॉलबैक किसी इनपुट स्ट्रीम से जुड़ा है, तो आपके कोड को audioData बफ़र में दिए गए डेटा को प्रोसेस करना चाहिए. इसे तीसरे आर्ग्युमेंट के तौर पर बताया गया है. अगर कॉलबैक किसी आउटपुट स्ट्रीम से जुड़ा है, तो आपके कोड को डेटा को बफ़र में डालना चाहिए.
उदाहरण के लिए, कॉलबैक का इस्तेमाल करके, इस तरह लगातार साइन वेव आउटपुट जनरेट किया जा सकता है:
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;
}
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 में एक परफ़ॉर्मेंस मोड होता है, जिसका आपके ऐप्लिकेशन के व्यवहार पर काफ़ी असर पड़ता है. तीन मोड होते हैं:
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
का इस्तेमाल करें.
यह सुविधा, पहले से जनरेट किए गए संगीत को चलाने वाले ऐप्लिकेशन के लिए आम तौर पर इस्तेमाल की जाती है. जैसे, स्ट्रीमिंग ऑडियो या एमआईडीआई फ़ाइल प्लेयर.
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 के कुछ फ़ंक्शन को एक से ज़्यादा थ्रेड से एक साथ कॉल नहीं किया जा सकता. ऐसा इसलिए है, क्योंकि AAudio, म्यूटेक्स का इस्तेमाल नहीं करता. इससे थ्रेड को पहले से ही रोका जा सकता है और गड़बड़ियां हो सकती हैं.
सुरक्षित रहने के लिए, AAudioStream_waitForStateChange()
को कॉल न करें या एक ही स्ट्रीम को दो अलग-अलग थ्रेड से न पढ़ें या उसमें न लिखें. इसी तरह, किसी स्ट्रीम को पढ़ते या उसमें लिखते समय, किसी दूसरी थ्रेड में उस स्ट्रीम को बंद न करें.
AAudioStream_getSampleRate()
और AAudioStream_getChannelCount()
जैसी स्ट्रीम सेटिंग दिखाने वाले कॉल, थ्रेड के हिसाब से सुरक्षित होते हैं.
ये कॉल थ्रेड सेफ़ भी होते हैं:
AAudio_convert*ToText()
AAudio_createStreamBuilder()
AAudioStream_getTimestamp()
को छोड़कर,AAudioStream_get*()
पहले से मालूम समस्याएं
- write() को ब्लॉक करने पर ऑडियो में लगने वाला समय ज़्यादा होता है, क्योंकि Android O DP2 रिलीज़ में फ़ास्ट ट्रैक का इस्तेमाल नहीं किया जाता. कम लेटेंसी पाने के लिए, कॉलबैक का इस्तेमाल करें.
अन्य संसाधन
ज़्यादा जानने के लिए, इन संसाधनों का इस्तेमाल करें: