AAaudio

AAudio یک API جدید اندروید C است که در نسخه اندروید O معرفی شده است. این برای برنامه های صوتی با کارایی بالا که نیاز به تاخیر کم دارند طراحی شده است. برنامه ها با خواندن و نوشتن داده ها در جریان ها با AAaudio ارتباط برقرار می کنند.

AAudio API از نظر طراحی بسیار کم است، این عملکردها را انجام نمی دهد:

  • شمارش دستگاه صوتی
  • مسیریابی خودکار بین نقاط پایانی صوتی
  • ورودی/خروجی فایل
  • رمزگشایی صدای فشرده
  • نمایش خودکار تمام ورودی/جریان ها در یک تماس پاسخ.

شروع کردن

می توانید با کد C++ با AAaudio تماس بگیرید. برای افزودن مجموعه ویژگی AAudio به برنامه خود، فایل هدر AAudio.h را اضافه کنید:

#include <aaudio/AAudio.h>

جریان های صوتی

AAudio داده های صوتی را بین برنامه شما و ورودی ها و خروجی های صوتی دستگاه Android شما منتقل می کند. برنامه شما با خواندن و نوشتن در جریان‌های صوتی ، که با ساختار AAudioStream نشان داده می‌شود، داده‌ها را به داخل و خارج می‌فرستد. تماس‌های خواندن/نوشتن می‌توانند مسدود یا غیرمسدود باشند.

یک جریان با موارد زیر تعریف می شود:

  • دستگاه صوتی که منبع یا مخزن داده های موجود در جریان است.
  • حالت اشتراک‌گذاری که تعیین می‌کند آیا یک جریان به یک دستگاه صوتی دسترسی انحصاری دارد که در غیر این صورت ممکن است بین چندین جریان به اشتراک گذاشته شود.
  • فرمت داده های صوتی در جریان.

دستگاه صوتی

هر جریان به یک دستگاه صوتی متصل است.

دستگاه صوتی یک رابط سخت افزاری یا نقطه پایانی مجازی است که به عنوان منبع یا سینک برای جریان مداوم داده های صوتی دیجیتال عمل می کند. یک دستگاه صوتی (یک میکروفون داخلی یا هدست بلوتوث) را با دستگاه Android (تلفن یا ساعت) که برنامه شما را اجرا می کند، اشتباه نگیرید.

می‌توانید از روش AudioManager getDevices() برای کشف دستگاه‌های صوتی موجود در دستگاه Android خود استفاده کنید. این روش اطلاعات مربوط به type هر دستگاه را برمی گرداند.

هر دستگاه صوتی دارای یک شناسه منحصر به فرد در دستگاه Android است. می توانید از شناسه برای اتصال یک جریان صوتی به یک دستگاه صوتی خاص استفاده کنید. با این حال، در بیشتر موارد می‌توانید به AAudio اجازه دهید دستگاه اصلی پیش‌فرض را انتخاب کند نه اینکه خودتان یکی را مشخص کنید.

دستگاه صوتی متصل به یک جریان تعیین می کند که جریان برای ورودی یا خروجی باشد. یک جریان فقط می تواند داده ها را در یک جهت حرکت دهد. هنگامی که یک جریان را تعریف می کنید، جهت آن را نیز تعیین می کنید. وقتی پخش جریانی را باز می‌کنید، آندروید بررسی می‌کند که دستگاه صوتی و جهت پخش همخوانی دارند.

حالت اشتراک گذاری

یک جریان یک حالت اشتراک‌گذاری دارد:

  • AAUDIO_SHARING_MODE_EXCLUSIVE به این معنی است که جریان دسترسی انحصاری به دستگاه صوتی خود دارد. دستگاه نمی تواند توسط هیچ جریان صوتی دیگری استفاده شود. اگر دستگاه صوتی از قبل در حال استفاده است، ممکن است امکان دسترسی انحصاری پخش جریانی وجود نداشته باشد. جریان‌های انحصاری احتمالاً تأخیر کمتری دارند، اما احتمال قطع شدن آنها نیز بیشتر است. باید به محض اینکه دیگر نیازی به جریان‌های انحصاری ندارید، آن‌ها را ببندید تا سایر برنامه‌ها بتوانند به دستگاه دسترسی داشته باشند. استریم های انحصاری کمترین تأخیر ممکن را ارائه می دهند.
  • AAUDIO_SHARING_MODE_SHARED به AAaudio اجازه می‌دهد صدا را ترکیب کند. 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);
    

    توجه داشته باشید که این روش ها خطاهایی مانند ثابت تعریف نشده یا مقدار خارج از محدوده را گزارش نمی کنند.

    اگر deviceId را مشخص نکنید، پیش فرض دستگاه خروجی اصلی است. اگر جهت جریان را مشخص نکنید، پیش فرض یک جریان خروجی است. برای همه پارامترهای دیگر، می‌توانید صریحاً یک مقدار را تنظیم کنید یا به سیستم اجازه دهید با عدم تعیین پارامتر یا تنظیم آن بر روی 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);
    

استفاده از جریان صوتی

انتقال دولت

یک جریان صوتی AA معمولاً در یکی از پنج حالت پایدار است (وضعیت خطا، Disconnected، در انتهای این بخش توضیح داده شده است):

  • باز کنید
  • شروع شد
  • مکث کرد
  • برافروخته شد
  • متوقف شد

داده‌ها تنها زمانی از طریق یک جریان جریان می‌یابند که جریان در حالت Started باشد. برای جابجایی یک جریان بین حالت ها، از یکی از توابع درخواست انتقال حالت استفاده کنید:

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

توجه داشته باشید که فقط می‌توانید در یک جریان خروجی درخواست توقف یا فلاش کنید:

این توابع ناهمزمان هستند و تغییر حالت بلافاصله اتفاق نمی افتد. هنگامی که درخواست تغییر حالت می دهید، جریان یکی از حالت های گذرای مربوطه را جابجا می کند:

  • شروع کردن
  • مکث
  • گرگرفتگی
  • توقف
  • بسته شدن

نمودار حالت زیر حالت های پایدار را به صورت مستطیل های گرد و حالت های گذرا را به صورت مستطیل های نقطه چین نشان می دهد. اگرچه نشان داده نمی‌شود، می‌توانید از هر وضعیتی close() فراخوانی کنید

چرخه حیات AAaudio

AAudio برای هشدار به شما در مورد تغییرات وضعیت، تماس‌های برگشتی ارائه نمی‌کند. یک تابع خاص، AAudioStream_waitForStateChange(stream, inputState, nextState, timeout) می توان برای منتظر ماندن برای تغییر حالت استفاده کرد.

تابع به خودی خود تغییر حالت را تشخیص نمی دهد و منتظر وضعیت خاصی نمی ماند. منتظر می ماند تا وضعیت فعلی با inputState که شما مشخص می کنید متفاوت باشد.

برای مثال، پس از درخواست توقف، یک جریان باید فوراً وارد حالت گذرا Pausing شود و مدتی بعد به حالت Paused برسد - اگرچه هیچ تضمینی وجود ندارد که انجام شود. از آنجایی که نمی توانید منتظر وضعیت Paused باشید، از waitForStateChange() استفاده کنید تا برای هر حالتی غیر از Pausing منتظر بمانید. در اینجا نحوه انجام این کار آمده است:

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 وضعیت فعلی جریان را نشان می دهد.

شما می توانید از همین تکنیک پس از فراخوانی شروع، توقف یا فلاش درخواست استفاده کنید، با استفاده از حالت گذرا مربوطه به عنوان حالت ورودی. پس از فراخوانی AAudioStream_close() waitForStateChange() را صدا نکنید زیرا جریان به محض بسته شدن حذف می شود. و در حالی که waitForStateChange() در رشته دیگری در حال اجرا است، AAudioStream_close() فراخوانی نکنید.

خواندن و نوشتن در یک جریان صوتی

دو روش برای پردازش داده ها در یک جریان پس از شروع آن وجود دارد:

برای مسدود کردن خواندن یا نوشتن که تعداد فریم های مشخص شده را منتقل می کند، 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);

پس از بستن یک جریان، نمی توانید از آن با هیچ عملکرد مبتنی بر جریان صوتی AA استفاده کنید.

جریان صوتی قطع شد

اگر یکی از این رویدادها اتفاق بیفتد، یک جریان صوتی می‌تواند در هر زمانی قطع شود:

  • دستگاه صوتی مرتبط دیگر متصل نیست (مثلاً وقتی هدفون از برق جدا می شود).
  • یک خطا در داخل رخ می دهد.
  • دستگاه صوتی دیگر دستگاه صوتی اصلی نیست.

هنگامی که یک جریان قطع می شود، حالت "Disconnected" را دارد و هر تلاشی برای اجرای 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() برای تأیید ظرفیت واقعی بافر استفاده کنید.

یک برنامه مجبور نیست از کل ظرفیت یک بافر استفاده کند. AAaudio یک بافر را تا اندازه ای که می توانید تنظیم کنید پر می کند. اندازه یک بافر نمی تواند بزرگتر از ظرفیت آن باشد و اغلب کوچکتر است. با کنترل اندازه بافر، تعداد انفجارهای مورد نیاز برای پر کردن آن را تعیین می کنید و بنابراین تأخیر را کنترل می کنید. برای کار با اندازه بافر از روش های AAudioStreamBuilder_setBufferSizeInFrames() و AAudioStreamBuilder_getBufferSizeInFrames() استفاده کنید.

هنگامی که یک برنامه صدا را پخش می کند، در یک بافر می نویسد و تا زمانی که نوشتن کامل شود مسدود می شود. AAaudio از بافر به صورت انفجاری مجزا خوانده می شود. هر انفجار حاوی چندین فریم صوتی است و معمولاً از اندازه بافر خوانده شده کوچکتر است. سیستم اندازه و سرعت انفجار را کنترل می‌کند، این ویژگی‌ها معمولاً توسط مدارهای دستگاه صوتی دیکته می‌شوند. اگرچه نمی‌توانید اندازه انفجار یا نرخ انفجار را تغییر دهید، می‌توانید اندازه بافر داخلی را با توجه به تعداد انفجارهای موجود در آن تنظیم کنید. به طور کلی، اگر اندازه بافر AAudioStream شما مضربی از اندازه انفجار گزارش شده باشد، کمترین تأخیر را دریافت می کنید.

بافر صوتی AA

یکی از راه‌های بهینه‌سازی اندازه بافر این است که با یک بافر بزرگ شروع کنید و به تدریج آن را پایین بیاورید تا زمانی که underrun شروع شود، سپس آن را به عقب برگردانید. از طرف دیگر، می‌توانید با یک اندازه بافر کوچک شروع کنید و اگر باعث ایجاد افت می‌شود، اندازه بافر را افزایش دهید تا خروجی دوباره تمیز شود.

این فرآیند می تواند خیلی سریع انجام شود، احتمالاً قبل از اینکه کاربر اولین صدا را پخش کند. ممکن است بخواهید ابتدا اندازه بافر اولیه را با استفاده از سکوت انجام دهید تا کاربر هیچ اشکال صوتی را نشنود. عملکرد سیستم ممکن است در طول زمان تغییر کند (به عنوان مثال، کاربر ممکن است حالت هواپیما را خاموش کند). از آنجایی که تنظیم بافر سربار بسیار کمی اضافه می کند، برنامه شما می تواند آن را به طور مداوم در حالی که برنامه می خواند یا می نویسد داده ها را در یک جریان انجام دهد.

در اینجا یک مثال از یک حلقه بهینه سازی بافر آورده شده است:

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 را اجرا می کند تا داده ها را برای انفجار بعدی خود بدست آورد.

تابع callback نباید خواندن یا نوشتن را در جریانی که آن را فراخوانی کرده است انجام دهد. اگر تماس برگشتی متعلق به جریان ورودی باشد، کد شما باید داده‌هایی را که در بافر audioData ارائه شده است (به عنوان آرگومان سوم مشخص شده) پردازش کند. اگر پاسخ تماس متعلق به یک جریان خروجی باشد، کد شما باید داده ها را در بافر قرار دهد.

به عنوان مثال، می توانید از یک 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;
}

پردازش بیش از یک جریان با استفاده از AAaudio امکان پذیر است. می توانید از یک جریان به عنوان اصلی استفاده کنید و نشانگرها را به جریان های دیگر در داده های کاربر منتقل کنید. برای جریان اصلی یک تماس برگشتی ثبت کنید. سپس از 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 استفاده کنید. این امر برای برنامه‌هایی که موسیقی قبلاً تولید شده را پخش می‌کنند، مانند پخش‌کننده‌های فایل صوتی یا 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 کاملاً ایمن نیست. شما نمی توانید برخی از توابع AAaudio را همزمان از بیش از یک رشته در یک زمان فراخوانی کنید. این به این دلیل است که AAudio از استفاده از mutexes اجتناب می‌کند، که می‌تواند باعث پیش‌پرداخت رشته و اشکال شود.

برای ایمن بودن، با AAudioStream_waitForStateChange() تماس نگیرید یا در یک جریان از دو رشته مختلف نخوانید یا بنویسید. به طور مشابه، هنگام خواندن یا نوشتن جریان در یک رشته، جریانی را در یک رشته دیگر نبندید.

تماس‌هایی که تنظیمات جریانی را برمی‌گردانند، مانند AAudioStream_getSampleRate() و AAudioStream_getChannelCount() ، در ارتباط با موضوع هستند.

این تماس‌ها در ارتباط با موضوع نیز امن هستند:

  • AAudio_convert*ToText()
  • AAudio_createStreamBuilder()
  • AAudioStream_get*() به جز AAudioStream_getTimestamp()

مسائل شناخته شده

  • تأخیر صوتی برای مسدود کردن write() زیاد است زیرا نسخه Android O DP2 از آهنگ FAST استفاده نمی‌کند. برای دریافت تأخیر کمتر از تماس برگشتی استفاده کنید.

منابع اضافی

برای کسب اطلاعات بیشتر، از منابع زیر استفاده کنید:

مرجع API

Codelabs

ویدیوها