অডিও লেটেন্সি

লেটেন্সি হল একটি সিস্টেমের মধ্য দিয়ে একটি সিগন্যাল যাতায়াতের জন্য যে সময় লাগে। এইগুলি হল অডিও অ্যাপগুলির সাথে সম্পর্কিত লেটেন্সির সাধারণ প্রকার:

  • অডিও আউটপুট লেটেন্সি হল একটি অডিও নমুনা একটি অ্যাপ দ্বারা তৈরি হওয়া এবং নমুনা হেডফোন জ্যাক বা অন্তর্নির্মিত স্পিকারের মাধ্যমে চালানোর মধ্যবর্তী সময়।
  • অডিও ইনপুট লেটেন্সি হল একটি ডিভাইসের অডিও ইনপুট, যেমন মাইক্রোফোন দ্বারা একটি অডিও সিগন্যাল প্রাপ্ত হওয়ার এবং সেই একই অডিও ডেটা একটি অ্যাপে উপলব্ধ হওয়ার মধ্যবর্তী সময়।
  • রাউন্ড-ট্রিপ লেটেন্সি হল ইনপুট লেটেন্সি, অ্যাপ প্রসেসিং টাইম এবং আউটপুট লেটেন্সির সমষ্টি।

  • টাচ লেটেন্সি হল একজন ব্যবহারকারীর স্ক্রীন স্পর্শ করা এবং সেই স্পর্শ ইভেন্ট একটি অ্যাপ দ্বারা প্রাপ্ত হওয়ার মধ্যে সময়।
  • ওয়ার্মআপ লেটেন্সি হল অডিও পাইপলাইন শুরু করতে যে সময় লাগে প্রথমবার ডেটা বাফারে সারিবদ্ধ করা হয়৷

এই পৃষ্ঠাটি বর্ণনা করে যে কীভাবে কম লেটেন্সি ইনপুট এবং আউটপুট দিয়ে আপনার অডিও অ্যাপ ডেভেলপ করা যায় এবং কীভাবে ওয়ার্মআপ লেটেন্সি এড়ানো যায়।

বিলম্ব পরিমাপ করুন

বিচ্ছিন্নভাবে অডিও ইনপুট এবং আউটপুট লেটেন্সি পরিমাপ করা কঠিন কারণ প্রথম নমুনাটি কখন অডিও পাথে পাঠানো হয় তা সঠিকভাবে জানা প্রয়োজন (যদিও এটি একটি হালকা টেস্টিং সার্কিট এবং একটি অসিলোস্কোপ ব্যবহার করে করা যেতে পারে)। আপনি যদি রাউন্ড-ট্রিপ অডিও লেটেন্সি জানেন তবে আপনি রুক্ষ নিয়মটি ব্যবহার করতে পারেন: অডিও ইনপুট (এবং আউটপুট) লেটেন্সি হল সিগন্যাল প্রসেসিং ছাড়াই পাথের উপর রাউন্ড-ট্রিপ অডিও লেটেন্সির অর্ধেক।

রাউন্ড-ট্রিপ অডিও লেটেন্সি ডিভাইস মডেল এবং অ্যান্ড্রয়েড বিল্ডের উপর নির্ভর করে ব্যাপকভাবে পরিবর্তিত হয়। আপনি প্রকাশিত পরিমাপ পড়ে নেক্সাস ডিভাইসগুলির জন্য রাউন্ড-ট্রিপ লেটেন্সি সম্পর্কে একটি মোটামুটি ধারণা পেতে পারেন৷

আপনি একটি অ্যাপ তৈরি করে রাউন্ড-ট্রিপ অডিও লেটেন্সি পরিমাপ করতে পারেন যা একটি অডিও সিগন্যাল জেনারেট করে, সেই সিগন্যাল শোনে এবং এটি পাঠানো এবং গ্রহণ করার মধ্যে সময় পরিমাপ করে।

যেহেতু ন্যূনতম সিগন্যাল প্রসেসিং সহ অডিও পাথগুলিতে সর্বনিম্ন লেটেন্সি অর্জন করা হয়, আপনি একটি অডিও লুপব্যাক ডংগলও ব্যবহার করতে চাইতে পারেন, যা হেডসেট সংযোগকারীর উপর পরীক্ষা চালানোর অনুমতি দেয়৷

বিলম্ব কমাতে সর্বোত্তম অনুশীলন

অডিও কর্মক্ষমতা যাচাই

অ্যান্ড্রয়েড কম্প্যাটিবিলিটি ডেফিনিশন ডকুমেন্ট (CDD) একটি সামঞ্জস্যপূর্ণ অ্যান্ড্রয়েড ডিভাইসের হার্ডওয়্যার এবং সফ্টওয়্যার প্রয়োজনীয়তাগুলি গণনা করে৷ সামগ্রিক সামঞ্জস্যতা প্রোগ্রাম সম্পর্কে আরও তথ্যের জন্য অ্যান্ড্রয়েড সামঞ্জস্যতা এবং প্রকৃত CDD নথির জন্য CDD দেখুন৷

CDD-এ, রাউন্ড-ট্রিপ লেটেন্সি 20 ms বা কম হিসাবে নির্দিষ্ট করা হয় (যদিও সঙ্গীতশিল্পীদের সাধারণত 10 ms প্রয়োজন হয়)। এর কারণ হল গুরুত্বপূর্ণ ব্যবহারের ক্ষেত্রে যেগুলি 20 ms দ্বারা সক্রিয় করা হয়েছে৷

রানটাইমে অ্যান্ড্রয়েড ডিভাইসে যেকোনো পাথে অডিও লেটেন্সি নির্ধারণ করার জন্য বর্তমানে কোনো API নেই। আপনি, তবে, ডিভাইসটি লেটেন্সির জন্য কোনো গ্যারান্টি দেয় কিনা তা খুঁজে বের করতে নিম্নলিখিত হার্ডওয়্যার বৈশিষ্ট্য পতাকা ব্যবহার করতে পারেন:

  • android.hardware.audio.low_latency 45 ms বা তার কম একটি ক্রমাগত আউটপুট লেটেন্সি নির্দেশ করে৷
  • android.hardware.audio.pro 20 ms বা তার কম একটি ক্রমাগত রাউন্ড-ট্রিপ লেটেন্সি নির্দেশ করে৷

এই ফ্ল্যাগগুলি রিপোর্ট করার মানদণ্ড 5.6 অডিও লেটেন্সি এবং 5.10 পেশাদার অডিও বিভাগে CDD-তে সংজ্ঞায়িত করা হয়েছে।

জাভাতে এই বৈশিষ্ট্যগুলি কীভাবে পরীক্ষা করবেন তা এখানে:

কোটলিন

val hasLowLatencyFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)

val hasProFeature: Boolean =
        packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO)

জাভা

boolean hasLowLatencyFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY);

boolean hasProFeature =
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_PRO);

অডিও বৈশিষ্ট্যের সম্পর্ক সম্পর্কে, android.hardware.audio.low_latency বৈশিষ্ট্যটি android.hardware.audio.pro এর পূর্বশর্ত। একটি ডিভাইস android.hardware.audio.low_latency প্রয়োগ করতে পারে এবং android.hardware.audio.pro নয়, কিন্তু উল্টো নয়।

অডিও কর্মক্ষমতা সম্পর্কে কোন অনুমান করা

লেটেন্সি সমস্যা এড়াতে সাহায্য করার জন্য নিম্নলিখিত অনুমানগুলি থেকে সাবধান থাকুন:

  • অনুমান করবেন না যে মোবাইল ডিভাইসগুলিতে ব্যবহৃত স্পিকার এবং মাইক্রোফোনগুলিতে সাধারণত ভাল ধ্বনিবিদ্যা থাকে৷ তাদের ছোট আকারের কারণে, ধ্বনিবিদ্যা সাধারণত খারাপ হয় তাই শব্দের গুণমান উন্নত করতে সংকেত প্রক্রিয়াকরণ যোগ করা হয়। এই সংকেত প্রক্রিয়াকরণ লেটেন্সি প্রবর্তন করে।
  • অনুমান করবেন না যে আপনার ইনপুট এবং আউটপুট কলব্যাকগুলি সিঙ্ক্রোনাইজ করা হয়েছে৷ একযোগে ইনপুট এবং আউটপুটের জন্য, প্রতিটি পাশের জন্য পৃথক বাফার কিউ কমপ্লিশন হ্যান্ডলার ব্যবহার করা হয়। এই কলব্যাকগুলির আপেক্ষিক ক্রম বা অডিও ঘড়িগুলির সিঙ্ক্রোনাইজেশনের কোনও গ্যারান্টি নেই, এমনকি যখন উভয় পক্ষ একই নমুনা হার ব্যবহার করে। আপনার অ্যাপ্লিকেশন সঠিক বাফার সিঙ্ক্রোনাইজেশন সহ ডেটা বাফার করা উচিত।
  • অনুমান করবেন না যে প্রকৃত নমুনা হার নামমাত্র নমুনা হারের সাথে ঠিক মেলে। উদাহরণস্বরূপ, যদি নামমাত্র নমুনা হার 48,000 Hz হয়, তাহলে অডিও ঘড়ির অপারেটিং সিস্টেম CLOCK_MONOTONIC এর থেকে একটু ভিন্ন হারে অগ্রসর হওয়া স্বাভাবিক। কারণ অডিও এবং সিস্টেম ঘড়ি বিভিন্ন স্ফটিক থেকে উদ্ভূত হতে পারে।
  • অনুমান করবেন না যে প্রকৃত প্লেব্যাকের নমুনা হার প্রকৃত ক্যাপচার নমুনা হারের সাথে হুবহু মিলে যায়, বিশেষ করে যদি শেষ পয়েন্টগুলি পৃথক পথে থাকে। উদাহরণস্বরূপ, আপনি যদি 48,000 Hz নামমাত্র নমুনা হারে অন-ডিভাইস মাইক্রোফোন থেকে ক্যাপচার করছেন এবং 48,000 Hz নামমাত্র নমুনা হারে USB অডিও চালাচ্ছেন, তাহলে প্রকৃত নমুনার হার একে অপরের থেকে কিছুটা আলাদা হতে পারে।

সম্ভাব্য স্বাধীন অডিও ঘড়ির ফলাফল হল অ্যাসিঙ্ক্রোনাস নমুনা হার রূপান্তরের প্রয়োজন। অ্যাসিঙ্ক্রোনাস নমুনা হার রূপান্তরের জন্য একটি সহজ (যদিও অডিও মানের জন্য আদর্শ নয়) কৌশল হল একটি শূন্য-ক্রসিং পয়েন্টের কাছে প্রয়োজন অনুসারে নমুনাগুলি নকল করা বা ড্রপ করা৷ আরও পরিশীলিত রূপান্তর সম্ভব।

ইনপুট লেটেন্সি মিনিমাইজ করুন

একটি অন্তর্নির্মিত মাইক্রোফোন বা একটি বাহ্যিক হেডসেট মাইক্রোফোনের সাথে রেকর্ড করার সময় এই বিভাগটি আপনাকে অডিও ইনপুট লেটেন্সি কমাতে সাহায্য করার পরামর্শ প্রদান করে৷

  • যদি আপনার অ্যাপ ইনপুট নিরীক্ষণ করে, তাহলে আপনার ব্যবহারকারীদের একটি হেডসেট ব্যবহার করার পরামর্শ দিন (উদাহরণস্বরূপ, প্রথম রানে হেডফোন স্ক্রিন সহ একটি সেরা প্রদর্শন করে)। মনে রাখবেন যে শুধুমাত্র হেডসেট ব্যবহার করা সর্বনিম্ন সম্ভাব্য বিলম্বের গ্যারান্টি দেয় না। অডিও পাথ থেকে যেকোনো অবাঞ্ছিত সিগন্যাল প্রসেসিং অপসারণের জন্য আপনাকে অন্যান্য পদক্ষেপগুলি সম্পাদন করতে হতে পারে, যেমন রেকর্ড করার সময় VOICE_RECOGNITION প্রিসেট ব্যবহার করে৷
  • PROPERTY_OUTPUT_SAMPLE_RATE- এর জন্য getProperty(স্ট্রিং) দ্বারা রিপোর্ট করা 44,100 এবং 48,000 Hz-এর নামমাত্র নমুনা হারগুলি পরিচালনা করতে প্রস্তুত থাকুন৷ অন্যান্য নমুনা হার সম্ভব, কিন্তু বিরল।
  • PROPERTY_OUTPUT_FRAMES_PER_BUFFER এর জন্য getProperty(স্ট্রিং) দ্বারা রিপোর্ট করা বাফার আকার পরিচালনা করার জন্য প্রস্তুত থাকুন। সাধারণ বাফার আকার 96, 128, 160, 192, 240, 256, বা 512 ফ্রেম অন্তর্ভুক্ত, কিন্তু অন্যান্য মান সম্ভব।

আউটপুট বিলম্ব কম করুন

আপনি যখন আপনার অডিও প্লেয়ার তৈরি করেন তখন সর্বোত্তম নমুনা হার ব্যবহার করুন

সর্বনিম্ন লেটেন্সি পেতে, আপনাকে অবশ্যই অডিও ডেটা সরবরাহ করতে হবে যা ডিভাইসের সর্বোত্তম নমুনা হার এবং বাফার আকারের সাথে মেলে৷ আরও তথ্যের জন্য, হ্রাসকৃত বিলম্বের জন্য ডিজাইন দেখুন।

জাভাতে, আপনি নিম্নলিখিত কোড উদাহরণে দেখানো অডিও ম্যানেজার থেকে সর্বোত্তম নমুনা হার পেতে পারেন:

কোটলিন

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val sampleRateStr: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE)
var sampleRate: Int = sampleRateStr?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 44100 // Use a default value if property not found

জাভা

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String sampleRateStr = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
int sampleRate = Integer.parseInt(sampleRateStr);
if (sampleRate == 0) sampleRate = 44100; // Use a default value if property not found

একবার আপনি সর্বোত্তম নমুনা হার জানলে, আপনি আপনার প্লেয়ার তৈরি করার সময় এটি সরবরাহ করতে পারেন। এই উদাহরণটি OpenSL ES ব্যবহার করে:

// create buffer queue audio player
void Java_com_example_audio_generatetone_MainActivity_createBufferQueueAudioPlayer
        (JNIEnv* env, jclass clazz, jint sampleRate, jint framesPerBuffer)
{
   ...
   // specify the audio source format
   SLDataFormat_PCM format_pcm;
   format_pcm.numChannels = 2;
   format_pcm.samplesPerSec = (SLuint32) sampleRate * 1000;
   ...
}

দ্রষ্টব্য: samplesPerSec মিলিহার্টজ (1 Hz = 1000 mHz) চ্যানেল প্রতি নমুনা হারকে নির্দেশ করে।

অডিও ডেটা সারিবদ্ধ করতে সর্বোত্তম বাফার আকার ব্যবহার করুন

আপনি AudioManager API ব্যবহার করে সর্বোত্তম নমুনা হারের অনুরূপভাবে সর্বোত্তম বাফার আকার পেতে পারেন:

কোটলিন

val am = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val framesPerBuffer: String? = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)
var framesPerBufferInt: Int = framesPerBuffer?.let { str ->
    Integer.parseInt(str).takeUnless { it == 0 }
} ?: 256 // Use default

জাভা

AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
int framesPerBufferInt = Integer.parseInt(framesPerBuffer);
if (framesPerBufferInt == 0) framesPerBufferInt = 256; // Use default

PROPERTY_OUTPUT_FRAMES_PER_BUFFER বৈশিষ্ট্য HAL (হার্ডওয়্যার অ্যাবস্ট্রাকশন লেয়ার) বাফার ধরে রাখতে পারে এমন অডিও ফ্রেমের সংখ্যা নির্দেশ করে৷ আপনার অডিও বাফারগুলি তৈরি করা উচিত যাতে সেগুলিতে এই সংখ্যার সঠিক একাধিক থাকে৷ আপনি যদি সঠিক সংখ্যক অডিও ফ্রেম ব্যবহার করেন, তবে আপনার কলব্যাকগুলি নিয়মিত বিরতিতে ঘটে, যা ঝাঁকুনি কমিয়ে দেয়।

হার্ডকোডেড মান ব্যবহার না করে বাফারের আকার নির্ধারণ করতে API ব্যবহার করা গুরুত্বপূর্ণ, কারণ এইচএএল বাফারের আকার ডিভাইস জুড়ে এবং অ্যান্ড্রয়েড বিল্ড জুড়ে আলাদা।

সিগন্যাল প্রসেসিং জড়িত আউটপুট ইন্টারফেস যোগ করবেন না

শুধুমাত্র এই ইন্টারফেসগুলি দ্রুত মিক্সার দ্বারা সমর্থিত:

  • SL_IID_ANDROIDSIMPLEBUFFERQUEUE
  • SL_IID_VOLUME
  • SL_IID_MUTESOLO

এই ইন্টারফেসগুলি অনুমোদিত নয় কারণ এগুলি সিগন্যাল প্রক্রিয়াকরণের সাথে জড়িত এবং একটি ফাস্ট-ট্র্যাকের জন্য আপনার অনুরোধ প্রত্যাখ্যান করার কারণ হবে:

  • SL_IID_BASSBOOST
  • SL_IID_EFFECTSEND
  • SL_IID_ENVIRONMENTALREVERB
  • SL_IID_EQUALIZER
  • SL_IID_PLAYBACKRATE
  • SL_IID_PRESETREVERB
  • SL_IID_VIRTUALIZER
  • SL_IID_ANDROIDEFFECT
  • SL_IID_ANDROIDEFFECTSEND

আপনি যখন আপনার প্লেয়ার তৈরি করেন, নিশ্চিত করুন যে আপনি শুধুমাত্র দ্রুত ইন্টারফেস যোগ করেছেন, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:

const SLInterfaceID interface_ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };

আপনি একটি কম লেটেন্সি ট্র্যাক ব্যবহার করছেন তা যাচাই করুন

আপনি সফলভাবে একটি কম লেটেন্সি ট্র্যাক পেয়েছেন তা যাচাই করতে এই পদক্ষেপগুলি সম্পূর্ণ করুন:

  1. আপনার অ্যাপ্লিকেশন চালু করুন এবং তারপর নিম্নলিখিত কমান্ড চালান:
  2. adb shell ps | grep your_app_name
    
  3. আপনার অ্যাপের প্রসেস আইডি একটি নোট করুন।
  4. এখন, আপনার অ্যাপ থেকে কিছু অডিও চালান। টার্মিনাল থেকে নিম্নলিখিত কমান্ডটি চালানোর জন্য আপনার কাছে প্রায় তিন সেকেন্ড সময় আছে:
  5. adb shell dumpsys media.audio_flinger
    
  6. আপনার প্রসেস আইডির জন্য স্ক্যান করুন। আপনি যদি নাম কলামে একটি F দেখতে পান, তবে এটি একটি কম লেটেন্সি ট্র্যাকে রয়েছে (F এর অর্থ হল দ্রুত ট্র্যাক )৷

ওয়ার্মআপ লেটেন্সি কমিয়ে দিন

আপনি যখন প্রথমবারের জন্য অডিও ডেটা সারিবদ্ধ করেন, তখন ডিভাইসের অডিও সার্কিট গরম হতে একটি ছোট, কিন্তু এখনও উল্লেখযোগ্য, সময় লাগে। এই ওয়ার্মআপ লেটেন্সি এড়াতে, আপনি নীরবতা সহ অডিও ডেটার বাফারগুলি সারিবদ্ধ করতে পারেন, যেমনটি নিম্নলিখিত কোড উদাহরণে দেখানো হয়েছে:

#define CHANNELS 1
static short* silenceBuffer;
int numSamples = frames * CHANNELS;
silenceBuffer = malloc(sizeof(*silenceBuffer) * numSamples);
    for (i = 0; i<numSamples; i++) {
        silenceBuffer[i] = 0;
    }

যখন অডিও তৈরি করা উচিত, তখন আপনি বাস্তব অডিও ডেটা ধারণকারী সারিবদ্ধ বাফারগুলিতে স্যুইচ করতে পারেন।

দ্রষ্টব্য: ক্রমাগত অডিও আউটপুট করার ফলে উল্লেখযোগ্য শক্তি খরচ হয়। নিশ্চিত করুন যে আপনি onPause() পদ্ধতিতে আউটপুট বন্ধ করেছেন। ব্যবহারকারীর নিষ্ক্রিয়তার কিছু সময়ের পরে নীরব আউটপুট থামানোর কথাও বিবেচনা করুন।

অতিরিক্ত নমুনা কোড

অডিও লেটেন্সি প্রদর্শন করে একটি নমুনা অ্যাপ ডাউনলোড করতে, NDK স্যাম্পল দেখুন।

আরো তথ্যের জন্য

  1. অ্যাপ ডেভেলপারদের জন্য অডিও লেটেন্সি
  2. অডিও লেটেন্সিতে অবদানকারী
  3. অডিও লেটেন্সি পরিমাপ
  4. অডিও ওয়ার্মআপ
  5. লেটেন্সি (অডিও)
  6. রাউন্ড-ট্রিপ বিলম্বের সময়