الصوت

A Audio هي واجهة برمجة تطبيقات جديدة من Android C تم طرحها في إصدار Android O. وقد صُمِّمت لتطبيقات الصوت العالية الأداء التي تتطلّب وقت استجابة سريعًا. تتواصل التطبيقات مع AAudio من خلال قراءة البيانات وكتابتها في ساحة المشاركات.

تتميّز AAudio API بتصميمها البسيط، لا يؤدي الدوال التالية:

  • تعداد الأجهزة السماعية
  • التوجيه التلقائي بين نقاط نهاية الصوت
  • إدخال/إخراج الملف
  • فك ترميز الصوت المضغوط
  • العرض التلقائي لجميع مصادر الإدخال/البث في معاودة اتصال واحدة

الخطوات الأولى

يمكنك الاتصال بـ 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 عائم من -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);
    

    لاحظ أن هذه الطرق لا تقوم بالإبلاغ عن الأخطاء، مثل وجود ثابت غير محدد أو قيمة خارج النطاق.

    في حال عدم تحديد رقم تعريف الجهاز، سيكون جهاز الإخراج الأساسي هو الجهاز التلقائي. في حال عدم تحديد اتجاه البث، سيكون الوضع التلقائي هو بث الناتج. وبالنسبة إلى كل المعلمات الأخرى، يمكنك تحديد قيمة بشكل صريح أو السماح للنظام عليك تعيين القيمة المثلى من خلال عدم تحديد المعلمة على الإطلاق أو من خلال إلى 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_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 الحالة الحالية دفق.

يمكنك استخدام هذا الأسلوب نفسه بعد بدء الطلب أو إيقافه أو مسحه ضوئيًا باستخدام الحالة المؤقتة المقابلة كـ enterState. عدم الاتصال waitForStateChange() بعد الاتصال بـ AAudioStream_close() منذ البث سيتم حذفها بمجرد إغلاقها. وعدم الاتصال بـ AAudioStream_close() بينما waitForStateChange() قيد التشغيل في سلسلة محادثات أخرى.

القراءة والكتابة في بث صوتي

تتوفّر طريقتان لمعالجة البيانات في مصدر البيانات بعد بدئه:

بالنسبة إلى قراءة أو كتابة حظر التي تنقل العدد المحدّد من الإطارات، يمكنك ضبط انتهاء المهلة "Nanos" أكبر من الصفر. بالنسبة إلى الاستدعاءات التي لا تؤدي إلى حظر، اضبط انتهاء المهلة على صفر. في هذه الحالة، تكون النتيجة هي العدد الفعلي للإطارات المنقولة.

وعند قراءة الإدخال، يجب عليك التحقق من العدد الصحيح تمت قراءة الإطارات. إذا لم يكن الأمر كذلك، فقد يحتوي المخزن المؤقت على بيانات غير معروفة قد تتسبب في تأثير خلل صوتي. يمكنك وضع المخزن المؤقت بأصفار لإنشاء توقُّف صامت:

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);
}

يمكنك توفير جزء رئيسي من المخزن المؤقت للبث قبل بدء البث من خلال كتابة البيانات أو كتم صوته. ويجب أن يتم ذلك في استدعاء لا يحظر الإعلانات مع ضبط انتهاء المهلة على صفر.

يجب أن تتطابق البيانات في المخزن المؤقت مع تنسيق البيانات الذي يعرضه AAudioStream_getDataFormat().

إغلاق بث صوتي

عند الانتهاء من استخدام ساحة مشاركات، أغلقها:

AAudioStream_close(stream);

بعد إغلاق البث، لا يمكنك استخدامه مع أي وظيفة مستندة إلى البث AAudio.

مجموعة بث صوتي غير متصلة

قد يتم قطع اتصال البث الصوتي في أي وقت في حال وقوع أحد الأحداث التالية:

  • الجهاز السماعي المرتبط لم يعُد متصلاً (مثلاً عند فصل سماعات الرأس)
  • يحدث الخطأ داخليًا.
  • لم يعُد الجهاز السماعي هو الجهاز السماعي الأساسي.

عندما يكون البث غير متصل، تكون حالته "غير متصل" وستؤدي أي محاولات لتنفيذ AAudioStream_ write() أو دوال أخرى إلى حدوث خطأ. يجب دائمًا إيقاف البث وإغلاقه بغض النظر عن رمز الخطأ.

إذا كنت تستخدم استدعاء البيانات (على عكس إحدى طرق القراءة/الكتابة المباشرة) فلن يصلك أي رمز إرجاع عندما يكون البث غير متصل. ليتم إعلامك عند حدوث ذلك، اكتب 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 البيانات داخل وخارج التخزين المؤقت الداخلي الذي يحتفظ به، بمعدل واحد لكل جهاز سماعي.

سعة المخزن المؤقت هي إجمالي مقدار البيانات التي يمكن أن يحتفظ بها المخزن المؤقت. يمكنك الاتصال AAudioStreamBuilder_setBufferCapacityInFrames() لضبط القدرة. تحدد الطريقة السعة التي يمكنك تخصيصها إلى أقصى قيمة يسمح بها الجهاز. استخدام AAudioStream_getBufferCapacityInFrames() للتحقق من السعة الفعلية للمخزن المؤقت.

لا يلزم أن يستخدم التطبيق السعة الكاملة للمخزن المؤقت. يملأ الصوت المخزن المؤقت بحجم يمكنك ضبطه. لا يمكن أن يكون حجم المخزن المؤقت أكبر من سعته، وغالبًا ما يكون أصغر. ومن خلال التحكم في حجم المخزن المؤقت، يمكنك تحديد عدد الصور المتسلسلة اللازمة لملئه، وبالتالي التحكم في وقت الاستجابة. استخدام الطريقتَين 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);
        }
    }
}

ليست هناك فائدة من استخدام هذا الأسلوب لتحسين حجم المخزن المؤقت لتدفق البيانات. يتم تشغيل مجموعات بث الإدخال بأسرع ما يمكن، مع محاولة الاحتفاظ بكم من البيانات المخزنة مؤقتًا إلى الحد الأدنى، ثم تمتلئ عندما يكون التطبيق استباقيًا.

استخدام معاودة اتصال ذات أولوية عالية

إذا كان تطبيقك يقرأ البيانات الصوتية أو يكتبها من سلسلة محادثات عادية، قد يتم اتخاذ إجراء استباقي أو قد يواجه عدم استقرار في التوقيت. قد يتسبب هذا الإجراء في حدوث خلل في الصوت. وقد يوفر استخدام مخازن مؤقتة أكبر حماية من مثل هذه الأخطاء، إلا أن المخزن المؤقت الكبير يزيد أيضًا من وقت استجابة الصوت. بالنسبة إلى التطبيقات التي تتطلب وقت استجابة سريعًا، يمكن للبث الصوتي استخدام وظيفة استدعاء غير متزامنة لنقل البيانات من تطبيقك وإليه. تنفّذ 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. يمكنك استخدام مجموعة بث واحدة كعرض رئيسي تمرير المؤشرات إلى مصادر بيانات أخرى في بيانات المستخدمين سجِّل معاودة الاتصال للبث الرئيسي. بعد ذلك، يمكنك استخدام وحدات الإدخال والإخراج التي لا تحظر البث في أحداث البث الأخرى. في ما يلي مثال على عملية معاودة الاتصال ذهابًا وإيابًا تنقل بثًا من مصدر إدخال إلى بث إخراج. مصدر الاتصال الرئيسي هو مصدر البيانات. ويتم تضمين مصدر الإدخال في بيانات المستخدمين.

تُجري عملية الاستدعاء قراءة من مصدر الإدخال بدون حظر، ما يؤدي إلى وضع البيانات في المخزن المؤقت لمصدر بيانات الإخراج:

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()، واكتشاف الوضع الحالي من خلال استدعاء getالأداءMode().

إذا كان وقت الاستجابة البطيء أكثر أهمية من توفير الطاقة في تطبيقك، استخدِم 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 بشكل متزامن من أكثر من سلسلة محادثات واحدة في الوقت نفسه. والسبب في ذلك هو أنّ AAudio يتجنّب استخدام محوِّلات الصوت المتبادلة، ما قد يؤدي إلى إيقاف سلسلة المحادثات وحدوث أعطال فنية.

لضمان أمانك، لا تتصل برقم AAudioStream_waitForStateChange() أو تقرأ أو تكتب في ساحة المشاركات نفسها من سلسلتَي محادثات مختلفتَين. وبالمثل، لا تغلق ساحة مشاركات في سلسلة محادثات أثناء القراءة أو الكتابة فيها في سلسلة محادثات أخرى.

تكون المكالمات التي تعرض إعدادات البث المباشر، مثل AAudioStream_getSampleRate() وAAudioStream_getChannelCount()، آمنة في سلاسل المحادثات.

تكون هذه المكالمات أيضًا آمنة في سلاسل المحادثات:

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*() باستثناء AAudioStream_getTimestamp()

المشاكل المعروفة

  • وقت استجابة الصوت مرتفع لحظر write() لأنّ إصدار Android O DP2 لا يستخدم المسار FAST. استخدِم معاودة الاتصال لتقليل وقت الاستجابة.

مصادر إضافية

لمزيد من المعلومات، يمكنك الاستفادة من المراجع التالية:

مرجع حول API

الدروس التطبيقية حول الترميز

الفيديوهات