الصوت

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

واجهة برمجة تطبيقات AAudio بسيطة من حيث التصميم، وهي لا تؤدي الوظائف التالية:

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

البدء

يمكنك استدعاء Aالصوت من خلال كود C++. لإضافة ميزة AAudio التي تم ضبطها إلى تطبيقك، عليك تضمين ملف العنوان AAudio.h:

#include <aaudio/AAudio.h>

أجهزة البث الصوتي

ينقل Aالصوت البيانات الصوتية بين تطبيقك والإدخالات الصوتية وإخراجها على جهاز Android. ينقل تطبيقك البيانات من خلال القراءة إلى عمليات البث الصوتي والكتابة إليها، والتي تمثّلها بنية AAudioStream. قد تكون استدعاءات القراءة/الكتابة محظورة أو غير محظورة.

ويتم تعريف ساحة المشاركات من خلال ما يلي:

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

جهاز سماعي

يتم إرفاق كل بث بجهاز صوتي واحد.

الجهاز الصوتي هو واجهة أجهزة أو نقطة نهاية افتراضية تعمل كمصدر أو مصدر للبث المستمر للبيانات الصوتية الرقمية. لا تخلط بين جهاز صوتي (سماعة رأس بلوتوث أو ميكروفون مدمج) وجهاز Android (الهاتف أو الساعة) الذي يشغّل تطبيقك.

يمكنك استخدام طريقة AudioManager getDevices() لاكتشاف الأجهزة الصوتية المتوفرة على جهاز Android. تعرض الطريقة معلومات حول type لكل جهاز.

لكل جهاز سماعي معرّف فريد على جهاز Android. يمكنك استخدام المعرّف لربط بث صوتي بجهاز صوتي معيّن. ومع ذلك، يمكنك في معظم الحالات السماح لتطبيق AAudio باختيار الجهاز الأساسي التلقائي بدلاً من تحديد الجهاز بنفسك.

يحدّد جهاز الصوت المرفق بالبث ما إذا كان البث مخصصًا للإدخال أو الإخراج. يمكن بمصدر البيانات نقل البيانات في اتجاه واحد فقط. عند تحديد مصدر بيانات، يمكنك أيضًا ضبط اتجاهه. عند فتح مجموعة بث، يتحقّق Android من توافق الجهاز السماعي مع اتجاه البث.

وضع المشاركة

تشتمل ساحة المشاركات على وضع مشاركة:

  • يشير AAUDIO_SHARING_MODE_EXCLUSIVE إلى أنّ البث المباشر يوفّر إمكانية وصول حصرية إلى جهازه الصوتي، ولا يمكن استخدام الجهاز في أي بث صوتي آخر. إذا كان الجهاز الصوتي مستخدَمًا حاليًا، قد لا تتمكّن من الحصول على إمكانية وصول حصرية إلى البث. من المرجّح أن يكون وقت استجابة أحداث البث الحصرية أقصر، ولكن من المرجّح أكثر أن يتم إلغاء ربطها. عليك إغلاق أحداث البث الحصرية فور الاستغناء عنها، وذلك لكي تتمكّن التطبيقات الأخرى من الوصول إلى الجهاز. تقدّم مجموعات البث الحصرية أقل وقت استجابة ممكن.
  • يسمح AAUDIO_SHARING_MODE_SHARED لخدمة AAudio بمزج الصوت. يمزج الصوت كل أحداث البث التي تمت مشاركتها والمخصصة للجهاز نفسه.

يمكنك ضبط وضع المشاركة بوضوح عند إنشاء بث. يكون وضع المشاركة تلقائيًا هو SHARED.

صيغة الصوت

وتحتوي البيانات التي يتم تمريرها من خلال البث على سمات الصوت الرقمية المعتادة. وفيما يلي:

  • نموذج تنسيق البيانات
  • عدد القنوات (عيّنات لكل إطار)
  • معدل العينة

تتيح AAudio نماذج التنسيقات التالية:

تنسيق_تنسيق_الصوت نوع البيانات C Notes
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);
    

استخدام بث صوتي

انتقالات الولاية

عادةً ما يكون البث الصوتي Aالصوتي في حالة واحدة من خمس حالات مستقرة (يتم توضيح حالة الخطأ "غير متصل" في نهاية هذا القسم):

  • فتح
  • تم بدء التنفيذ.
  • متوقف مؤقتًا
  • وجه محمّر الخدود
  • الجهاز متوقّف

لا تتدفق البيانات من مجموعة بث إلا عندما يكون البث في حالة "تم البدء". لتحريك تدفق بين الحالات، استخدم إحدى الدوال التي تطلب انتقال الحالة:

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

تجدر الإشارة إلى أنّه لا يمكنك طلب إيقاف مؤقت أو مسح على البث المباشر إلا في الحدث المباشر التالي:

هذه الدوال غير متزامنة، ولا يحدث تغيير الحالة على الفور. عند طلب تغيير الحالة، يحرّك البث إحدى الحالات المؤقتة المقابلة:

  • جارٍ البدء
  • الإيقاف المؤقت
  • مع تدفق مائي
  • جارٍ الإيقاف
  • الخاتمة

يوضح مخطط الحالة أدناه الحالات المستقرة كمستطيلات مستديرة، والحالات المؤقتة كمستطيلات منقطة. يمكنك الاتصال بـ "close()" من أي ولاية على الرغم من عدم عرضه.

دورة حياة المحتوى الصوتي

لا يوفر Aالصوت استدعاءات لتنبيهك بحدوث تغييرات في الحالة. دالة خاصة واحدة، يمكن استخدام 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 الحالة الحالية للبث.

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

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

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

بالنسبة إلى عملية حظر القراءة أو الكتابة التي تؤدي إلى نقل العدد المحدّد من الإطارات، يمكنك ضبط ضبط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);

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

البث الصوتي غير متصل

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

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

عندما يتم فصل البث، تكون حالته "غير متصل" وأي محاولات لتنفيذ 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.
}

تحسين الأداء

يمكنك تحسين أداء تطبيق صوتي عن طريق ضبط المخازن المؤقتة الداخلية واستخدام سلاسل محادثات خاصة ذات أولوية عالية.

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

ويمرر الصوت البيانات من المخازن الداخلية الداخلية التي يحتفظ بها وخارجها، بمعدل جهاز واحد لكل جهاز سماعي.

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

إذا كان وقت الاستجابة البطيء أكثر أهمية من توفير الطاقة في تطبيقك، استخدِم AAUDIO_PERFORMANCE_MODE_LOW_LATENCY. ويكون هذا مفيدًا مع التطبيقات التفاعلية للغاية، مثل الألعاب أو أدوات المزج باستخدام لوحة المفاتيح.

إذا كان توفير الطاقة أكثر أهمية من وقت الاستجابة المنخفض في تطبيقك، استخدِم AAUDIO_PERFORMANCE_MODE_POWER_SAVING. وينطبق ذلك عادةً على التطبيقات التي تشغّل الموسيقى التي تم إنشاؤها سابقًا، مثل بث الصوت أو مشغّلات ملفات MIDI.

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

حفاظًا على أمانك، يُرجى عدم الاتصال بـ "AAudioStream_waitForStateChange()" أو القراءة أو الكتابة في ساحة المشاركات نفسها من سلسلتَي محادثات مختلفتَين. وبالمثل، لا تغلق ساحة المشاركات في سلسلة محادثات أثناء القراءة أو الكتابة إليها في سلسلة محادثات أخرى.

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

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

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

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

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

مراجع إضافية

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

مرجع حول API

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

الفيديوهات الطويلة