اشکال‌زدایی صوتی

این مقاله چند نکته و ترفند برای اشکال‌زدایی صدای Android را شرح می‌دهد.

روشویی تی

«سینک تی» یک ویژگی اشکال‌زدایی AudioFlinger است که فقط در ساخت‌های سفارشی دردسترس است و برای حفظ یک قطعه کوتاه از صدای اخیر برای تجزیه‌وتحلیل بعدی استفاده می‌شود. این کار امکان مقایسه بین آنچه واقعاً پخش یا ضبط شده است با آنچه انتظار می‌رفت را فراهم می‌کند.

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

دستورالعمل‌های این بخش برای Android نسخه ۷.x و بالاتر است. برای Android 5.x و 6.x،‏ /data/misc/audioserver را با /data/misc/media جایگزین کنید. علاوه‌براین، باید از ساخت کاربر اشکال‌زدایی یا مهندسی استفاده کنید. اگر از ساخت userdebug استفاده می‌کنید، با این دستور verity را غیرفعال کنید:

adb root && adb disable-verity && adb reboot

راه‌اندازی زمان ترجمه

  1. cd frameworks/av/services/audioflinger
  2. ویرایش Configuration.h.
  3. لغو نظر #define TEE_SINK.
  4. libaudioflinger.so را بازسازی کنید.
  5. adb root
  6. adb remount
  7. libaudioflinger.so جدید را به /system/lib دستگاه ارسال یا همگام‌سازی کنید.

راه‌اندازی زمان اجرا

  1. adb shell getprop | grep ro.debuggable
    تأیید کنید که برونداد این است: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    تأیید کنید که برونداد این است:

    drwx------ media media ... media
    

    اگر دایرکتوری وجود ندارد، آن را به‌صورت زیر ایجاد کنید:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    که در آن مقدار af.tee عددی است که در زیر توضیح داده شده است.
  5. chmod 644 /data/local.prop
  6. reboot

مقادیر برای دارایی af.tee

مقدار af.tee عددی بین ۰ و ۷ است که مجموع چند بیت را نشان می‌دهد، یک بیت برای هر ویژگی. برای توضیح هر بخش، کد را در AudioFlinger::AudioFlinger() در AudioFlinger.cpp ببینید اما به‌طور خلاصه:

  • ‫۱ = ورودی
  • ‫۲ = برونداد FastMixer
  • ‫۴ = AudioRecord و AudioTrack برای هر قطعه

هنوز بیتی برای بافر عمیق یا میکسر معمولی وجود ندارد، اما می‌توانید بااستفاده از «۴.» نتایج مشابهی دریافت کنید.

آزمایش و کسب داده‌ها

  1. آزمایش صدا را اجرا کنید.
  2. adb shell dumpsys media.audio_flinger
  3. در برونداد dumpsys به‌دنبال خطی مانند این بگردید:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    این فایل PCM با پسوند wav است.
  4. سپس adb pull هر /data/misc/audioserver/*.wav فایل موردعلاقه را اضافه کنید؛ توجه داشته باشید که نام فایل‌های تخلیه مخصوص قطعه در برونداد dumpsys نشان داده نمی‌شود، اما همچنان پس‌از بسته شدن قطعه در /data/misc/audioserver ذخیره می‌شوند.
  5. قبل‌از هم‌رسانی کردن فایل‌های تخلیه با دیگران، آن‌ها را ازنظر نگرانی‌های حریم خصوصی بررسی کنید.

پیشنهادها

برای نتایج مفیدتر، این ایده‌ها را امتحان کنید:

  • برای کاهش وقفه‌ها در برونداد آزمایش، صداهای لمس و کلیک‌های کلید را غیرفعال کنید.
  • همه صداها را به حداکثر برسانید.
  • برنامه‌هایی را که صدا تولید می‌کنند یا از میکروفون ضبط می‌کنند غیرفعال کنید، اگر برای آزمایش شما جالب نیستند.
  • داده‌های انبوهشی خاص قطعه فقط زمانی ذخیره می‌شود که قطعه بسته باشد؛ ممکن است لازم باشد برنامه‌ای را اجباراً ببندید تا داده‌های انبوهشی خاص قطعه آن را دریافت کنید
  • dumpsys را بلافاصله پس‌از آزمایش انجام دهید؛ فضای ضبط محدود است.
  • برای اینکه فایل‌های تخلیه را ازدست ندهید، آن‌ها را به‌طور دوره‌ای در میزبان خود بارگذاری کنید. فقط تعداد محدودی از فایل‌های تخلیه حفظ می‌شوند؛ تخلیه‌های قدیمی‌تر پس‌از رسیدن به این محدودیت برداشته می‌شوند.

بازگرداندن

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

  1. تغییرات کد منبع را به Configuration.h برگردانید.
  2. libaudioflinger.so را بازسازی کنید.
  3. libaudioflinger.so بازیابی‌شده را به /system/lib دستگاه ارسال یا همگام‌سازی کنید.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

ماکروهای ALOGx

میانای برنامه‌سازی کاربردی استاندارد ثبت گزارش زبان Java در «کیت توسعه نرم‌افزار Android» android.util.Log است.

میانای برنامه‌سازی کاربردی زبان C مربوطه در Android NDK __android_log_print در <android/log.h> تعریف شده است.

در بخش بومی چارچوب Android، ما ترجیح می‌دهیم از کلان‌دستورهایی با نام‌های ALOGE، ALOGW، ALOGI، ALOGV، و غیره استفاده کنیم. این کلان‌دستورها در <utils/Log.h> تعریف شده‌اند، و برای اهداف این مقاله به‌طور کلی از آن‌ها به‌عنوان ALOGx یاد می‌کنیم.

همه این میاناهای برنامه‌سازی کاربردی آسان و قابل‌فهم هستند، بنابراین در سراسر پلاتفرم Android فراگیر هستند. به‌طور خاص، mediaserver فرایند، که شامل سرور صدای AudioFlinger می‌شود، به‌طور گسترده از ALOGx استفاده می‌کند.

بااین‌حال، محدودیت‌هایی برای ALOGx و دوستان وجود دارد:

  • این گزارش‌ها مستعد «هرزنامه گزارش» هستند: بافر گزارش یک منبع مشترک است بنابراین به‌راحتی می‌تواند به‌دلیل ورودی‌های گزارش نامربوط سرریز شود و درنتیجه اطلاعات ازدست برود. متغیر ALOGV به‌طور پیش‌فرض در زمان کامپایل غیرفعال است. اما البته حتی این هم می‌تواند منجر به هرزنامه گزارش شود اگر فعال باشد.
  • فراخوانی‌های سیستم هسته اصلی ممکن است مسدود شود، که احتمالاً منجر به وارونگی اولویت و درنتیجه اختلالات و عدم دقت در اندازه‌گیری می‌شود. این موضوع به‌ویژه برای رشته‌های حساس به زمان مثل FastMixer و FastCapture اهمیت دارد.
  • اگر گزارش خاصی برای کاهش هرزنامه گزارش غیرفعال شده باشد، هرگونه اطلاعاتی که توسط آن گزارش ثبت می‌شد ازدست می‌رود. فعال کردن گزارش خاصی به‌صورت پس‌رونده، پس‌از مشخص شدن اینکه گزارش جالب بوده است، امکان‌پذیر نیست.

NBLOG،‏ media.log، و MediaLogService

NBLOG APIs و media.log فرایند و MediaLogService سرویس مرتبط با آن‌ها با هم سیستم گزارش‌گیری جدیدتری برای رسانه‌ها تشکیل می‌دهند و به‌طور خاص برای رفع مشکلات فوق طراحی شده‌اند. ما به‌طور کلی از اصطلاح «media.log» برای اشاره به هر سه استفاده می‌کنیم، اما به‌طور دقیق NBLOG میانای برنامه‌سازی کاربردی ثبت گزارش C++‎ است، media.log نام فرایند Linux است، و MediaLogService سرویس binder در Android برای بررسی گزارش‌ها است.

media.log «خط زمان» مجموعه‌ای از ورودی‌های گزارش است که ترتیب نسبی آن‌ها حفظ می‌شود. طبق قرارداد، هر رشته باید از خط زمان خودش استفاده کند.

مزايا

مزایای سیستم media.log این است که:

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

معماری

نمودار زیر رابطه فرایند mediaserver و فرایند init را قبل‌از معرفی media.log نشان می‌دهد:

معماری قبل‌از media.log

شکل ۱. معماری قبل‌از media.log

نکات قابل‌توجه:

  • ‫init انشعاب و mediaserver اجرا.
  • ‫init مرگ mediaserver را تشخیص می‌دهد و درصورت نیاز دوباره انشعاب ایجاد می‌کند.
  • گزارش‌گیری ALOGx نشان داده نمی‌شود.

نمودار زیر رابطه جدید عناصر را پس‌از اضافه شدن media.log به معماری نشان می‌دهد:

معماری پس‌از media.log

شکل ۲. معماری بعداز media.log

تغییرات مهم:

  • کارخواه‌ها از NBLOG API برای ساختن ورودی‌های گزارش و پیوست کردن آن‌ها به بافر حلقوی در حافظه مشترک استفاده می‌کنند.
  • MediaLogService می‌تواند محتوای میان‌گیر دایره‌ای را در هر زمانی تخلیه کند.
  • میان‌گیر دایره‌ای به‌گونه‌ای طراحی شده است که هرگونه خرابی در حافظه مشترک باعث ازکارافتادن MediaLogService نمی‌شود و همچنان می‌تواند بخش‌هایی از میان‌گیر را که تحت تأثیر خرابی قرار نگرفته‌اند تخلیه کند.
  • بافر دایره‌ای برای نوشتن ورودی‌های جدید و خواندن ورودی‌های موجود غیرمسدودکننده و بدون قفل است.
  • برای نوشتن در بافر حلقوی یا خواندن از آن، به هیچ فراخوان سیستم هسته نیاز نیست (به‌جز مُهرهای زمان اختیاری).

کجا می‌تواند استفاده شود

از Android 4.4، فقط چند نقطه گزارش در AudioFlinger وجود دارد که از سیستم media.log استفاده می‌کند. اگرچه استفاده از میاناهای برنامه‌سازی کاربردی جدید به آسانی ALOGx نیست، اما بسیار دشوار هم نیست. توصیه می‌کنیم سیستم گزارش‌گیری جدید را برای مواقعی که ضروری است یاد بگیرید. به‌طور خاص، برای رشته‌های AudioFlinger که باید به‌طور مکرر، دوره‌ای، و بدون مسدود کردن اجرا شوند، مانند رشته‌های FastMixer و FastCapture توصیه می‌شود.

طریقه استفاده

افزودن گزارش‌ها

ابتدا باید گزارش‌های ثبت رویداد را به کدتان اضافه کنید.

در FastMixer و FastCapture رشته، از کدی مانند این استفاده کنید:

logWriter->log("string");
logWriter->logf("format", parameters);
logWriter->logTimestamp();

ازآنجایی‌که این خط زمان NBLog فقط توسط FastMixer و FastCapture رشته‌ها استفاده می‌شود، نیازی به حذف متقابل نیست.

در رشته‌های دیگر AudioFlinger، از mNBLogWriter استفاده کنید:

mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();

برای رشته‌هایی غیراز FastMixer و FastCapture، خط زمان NBLog رشته می‌تواند هم توسط خود رشته و هم توسط عملیات binder استفاده شود. NBLog::Writer هیچ‌گونه انحصار متقابل ضمنی برای هر خط زمان ارائه نمی‌دهد، بنابراین مطمئن شوید که همه گزارش‌ها در زمینه‌ای رخ می‌دهند که انحصار متقابل رشته mLock حفظ می‌شود.

پس‌از افزودن گزارش‌ها، AudioFlinger را دوباره بسازید.

احتیاط: برای هر رشته، NBLog::Writer خط زمان جداگانه‌ای لازم است تا ایمنی رشته تضمین شود، زیرا خطوط زمان به‌طور طراحی‌شده از متقابل‌ها صرف‌نظر می‌کنند. اگر می‌خواهید بیش‌از یک رشته از یک خط زمان استفاده کند، می‌توانید با یک متقابل انحصاری موجود (همان‌طور که در بالا برای mLock توضیح داده شد) محافظت کنید. یا می‌توانید به‌جای NBLog::Writer از بسته‌بندی NBLog::LockedWriter استفاده کنید. بااین‌حال، این کار یکی از مزایای اصلی این API را ازبین می‌برد: عملکرد غیرمسدودکننده آن.

میانای برنامه‌سازی کاربردی کامل NBLog در frameworks/av/include/media/nbaio/NBLog.h است.

فعال کردن media.log

‫media.log به‌طور پیش‌فرض غیرفعال است. فقط وقتی دارایی ro.test_harness 1 باشد فعال است. می‌توانید آن را ازطریق زیر فعال کنید:

adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot

اتصال درطول بازراه‌اندازی قطع می‌شود، بنابراین:

adb shell
فرمان ps media اکنون دو فرایند را نشان می‌دهد:
  • media.log
  • کارساز رسانه

شناسه پردازش mediaserver را برای بعد یادداشت کنید.

نمایش خط زمان‌ها

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

dumpsys media.log

توجه داشته باشید که خطوط زمان به‌طور مستقل طراحی شده‌اند، و هیچ امکانی برای ادغام خطوط زمان وجود ندارد.

بازیابی گزارش‌ها پس‌از خرابی mediaserver

اکنون سعی کنید فرایند mediaserver را متوقف کنید: kill -9 #، که در آن # شناسه فرایندی است که قبلاً یادداشت کرده‌اید. باید یک تخلیه از media.log در logcat اصلی ببینید که همه گزارش‌های منتهی به خرابی را نشان می‌دهد.

dumpsys media.log