AAudio হল Android O রিলিজে প্রবর্তিত একটি নতুন Android C API। এটি উচ্চ-পারফরম্যান্স অডিও অ্যাপ্লিকেশনগুলির জন্য ডিজাইন করা হয়েছে যার জন্য কম লেটেন্সি প্রয়োজন৷ স্ট্রীমগুলিতে ডেটা পড়ার এবং লেখার মাধ্যমে অ্যাপগুলি AAudio-এর সাথে যোগাযোগ করে৷
AAudio API ডিজাইন অনুসারে ন্যূনতম, এটি এই ফাংশনগুলি সম্পাদন করে না:
- Audio device enumeration
- Automated routing between audio endpoints
- ফাইল I/O
- Decoding of compressed audio
- একটি একক কলব্যাকে সমস্ত ইনপুট/স্ট্রিমের স্বয়ংক্রিয় উপস্থাপনা।
শুরু হচ্ছে
You can call AAudio from C++ code. আপনার অ্যাপে AAudio বৈশিষ্ট্য সেট যোগ করতে, AAudio.h হেডার ফাইলটি অন্তর্ভুক্ত করুন:
#include <aaudio/AAudio.h>
অডিও স্ট্রীম
AAudio আপনার অ্যাপ এবং আপনার Android ডিভাইসে অডিও ইনপুট এবং আউটপুটগুলির মধ্যে অডিও ডেটা স্থানান্তর করে। আপনার অ্যাপটি অডিও স্ট্রীম থেকে পড়া এবং লেখার মাধ্যমে ডেটা পাস করে, যা গঠন AAudioStream দ্বারা প্রতিনিধিত্ব করে৷ রিড/রাইট কল ব্লকিং বা নন-ব্লকিং হতে পারে।
A stream is defined by the following:
- অডিও ডিভাইস যা স্ট্রীমের ডেটার উৎস বা সিঙ্ক।
- শেয়ারিং মোড যা নির্ধারণ করে যে একটি স্ট্রীমের একটি অডিও ডিভাইসে একচেটিয়া অ্যাক্সেস আছে কিনা যা অন্যথায় একাধিক স্ট্রিমের মধ্যে শেয়ার করা হতে পারে।
- The format of the audio data in the stream.
অডিও ডিভাইস
প্রতিটি স্ট্রিম একটি একক অডিও ডিভাইস সংযুক্ত করা হয়.
একটি অডিও ডিভাইস হল একটি হার্ডওয়্যার ইন্টারফেস বা ভার্চুয়াল এন্ডপয়েন্ট যা ডিজিটাল অডিও ডেটার একটানা প্রবাহের জন্য উৎস বা সিঙ্ক হিসেবে কাজ করে। একটি অডিও ডিভাইস (একটি অন্তর্নির্মিত মাইক বা ব্লুটুথ হেডসেট) অ্যান্ড্রয়েড ডিভাইস (ফোন বা ঘড়ি) যেটি আপনার অ্যাপটি চালাচ্ছে তার সাথে বিভ্রান্ত করবেন না।
আপনার Android ডিভাইসে উপলব্ধ অডিও ডিভাইসগুলি আবিষ্কার করতে আপনি AudioManager
পদ্ধতি getDevices()
ব্যবহার করতে পারেন। পদ্ধতিটি প্রতিটি ডিভাইসের type
সম্পর্কে তথ্য প্রদান করে।
অ্যান্ড্রয়েড ডিভাইসে প্রতিটি অডিও ডিভাইসের একটি অনন্য আইডি রয়েছে। আপনি একটি নির্দিষ্ট অডিও ডিভাইসে একটি অডিও স্ট্রিম আবদ্ধ করতে ID ব্যবহার করতে পারেন। যাইহোক, বেশির ভাগ ক্ষেত্রেই আপনি AAudio-কে ডিফল্ট প্রাথমিক ডিভাইস বেছে নিতে দিতে পারেন, বরং নিজে থেকে একটি নির্দিষ্ট করার পরিবর্তে।
একটি স্ট্রীমের সাথে সংযুক্ত অডিও ডিভাইসটি নির্ধারণ করে যে স্ট্রিমটি ইনপুট বা আউটপুটের জন্য। A stream can only move data in one direction. আপনি যখন একটি স্ট্রিম সংজ্ঞায়িত করেন তখন আপনি তার দিকনির্দেশও সেট করেন। আপনি যখন একটি স্ট্রিম খুলবেন তখন অডিও ডিভাইস এবং স্ট্রিমের দিক সম্মত কিনা তা নিশ্চিত করতে Android চেক করে।
শেয়ারিং মোড
A stream has a sharing mode:
-
AAUDIO_SHARING_MODE_EXCLUSIVE
মানে স্ট্রিমটির অডিও ডিভাইসে একচেটিয়া অ্যাক্সেস রয়েছে; ডিভাইসটি অন্য কোনো অডিও স্ট্রিম দ্বারা ব্যবহার করা যাবে না। যদি অডিও ডিভাইসটি ইতিমধ্যেই ব্যবহার করা হয়, তাহলে স্ট্রীমের জন্য একচেটিয়া অ্যাক্সেস থাকা সম্ভব নাও হতে পারে৷ এক্সক্লুসিভ স্ট্রীমগুলির বিলম্ব কম হওয়ার সম্ভাবনা রয়েছে, তবে সেগুলির সংযোগ বিচ্ছিন্ন হওয়ার সম্ভাবনাও বেশি৷ আপনার আর প্রয়োজন না হওয়ার সাথে সাথে আপনার এক্সক্লুসিভ স্ট্রীমগুলি বন্ধ করা উচিত, যাতে অন্যান্য অ্যাপগুলি ডিভাইসটি অ্যাক্সেস করতে পারে। এক্সক্লুসিভ স্ট্রীম সর্বনিম্ন সম্ভাব্য বিলম্ব প্রদান করে। -
AAUDIO_SHARING_MODE_SHARED
অডিওকে অডিও মিশ্রিত করার অনুমতি দেয়। AAudio একই ডিভাইসে বরাদ্দ করা সমস্ত শেয়ার করা স্ট্রীম মিশ্রিত করে।
আপনি যখন একটি স্ট্রীম তৈরি করেন তখন আপনি ভাগ করে নেওয়ার মোডটি স্পষ্টভাবে সেট করতে পারেন৷ By default, the sharing mode is SHARED
.
অডিও বিন্যাস
একটি স্ট্রীমের মাধ্যমে পাস করা ডেটাতে সাধারণ ডিজিটাল অডিও বৈশিষ্ট্য রয়েছে। এগুলি নিম্নরূপ:
- Sample data format
- Channel count (samples per frame)
- নমুনা হার
AAudio permits these sample formats:
aaudio_format_t | C data type | নোট |
---|---|---|
AAUDIO_FORMAT_PCM_I16 | int16_t | common 16-bit samples, Q0.15 format |
AAUDIO_FORMAT_PCM_FLOAT | ভাসা | -1.0 থেকে +1.0 |
Aaudio_format_pcm_i24_packed | 3 এর দলে uint8_t | প্যাক 24-বিট নমুনা, Q0.23 ফর্ম্যাট |
Aaudio_format_pcm_i32 | int32_t | সাধারণ 32-বিট নমুনা, Q0.31 ফর্ম্যাট |
Aaudio_format_iec61937 | uint8_t | HDMI বা S/PDIF পাসথ্রু-এর জন্য IEC61937-এ মোড়ানো সংকুচিত অডিও |
আপনি যদি একটি নির্দিষ্ট নমুনা বিন্যাসের অনুরোধ করেন তবে স্ট্রিমটি সেই বিন্যাসটি ব্যবহার করবে, এমনকি বিন্যাসটি ডিভাইসের জন্য অনুকূল না হলেও। আপনি যদি একটি নমুনা বিন্যাস নির্দিষ্ট না করেন, তাহলে AAudio সর্বোত্তম একটি বেছে নেবে। স্ট্রীম খোলার পরে আপনাকে অবশ্যই নমুনা ডেটা ফর্ম্যাটটি জিজ্ঞাসা করতে হবে এবং তারপরে প্রয়োজনে ডেটা রূপান্তর করতে হবে, যেমন এই উদাহরণে:
aaudio_format_t dataFormat = AAudioStream_getDataFormat(stream);
//... later
if (dataFormat == AAUDIO_FORMAT_PCM_I16) {
convertFloatToPcm16(...)
}
একটি অডিও স্ট্রিম তৈরি করা
AAudio লাইব্রেরি একটি বিল্ডার ডিজাইন প্যাটার্ন অনুসরণ করে এবং AAudioStreamBuilder প্রদান করে।
- একটি এওডিওস্ট্রেমবিল্ডার তৈরি করুন:
AAudioStreamBuilder *builder; aaudio_result_t result = AAudio_createStreamBuilder(&builder);
- বিল্ডারে অডিও স্ট্রিম কনফিগারেশন সেট করুন, বিল্ডার ফাংশনগুলি ব্যবহার করে যা স্ট্রিম প্যারামিটারের সাথে মিলে যায়। এই al চ্ছিক সেট ফাংশন উপলব্ধ:
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);
মনে রাখবেন যে এই পদ্ধতিগুলি ত্রুটির রিপোর্ট করে না, যেমন একটি অনির্ধারিত ধ্রুবক বা মান পরিসীমার বাইরে।
আপনি ডিভাইসআইডি নির্দিষ্ট না করলে, ডিফল্টটি প্রাথমিক আউটপুট ডিভাইস। আপনি স্ট্রীম দিক নির্দিষ্ট না করলে, ডিফল্ট একটি আউটপুট স্ট্রীম। For all other parameters, you can explicitly set a value, or let the system assign the optimal value by not specifying the parameter at all or setting it to
AAUDIO_UNSPECIFIED
.নিরাপদ থাকার জন্য, আপনি এটি তৈরি করার পরে অডিও স্ট্রিমের অবস্থা পরীক্ষা করুন, নীচের ধাপ 4 এ ব্যাখ্যা করা হয়েছে।
- AAudioStreamBuilder কনফিগার করা হলে, একটি স্ট্রিম তৈরি করতে এটি ব্যবহার করুন:
AAudioStream *stream; result = AAudioStreamBuilder_openStream(builder, &stream);
- স্ট্রীম তৈরি করার পরে, এর কনফিগারেশন যাচাই করুন। আপনি যদি নমুনা বিন্যাস, নমুনা হার, বা ফ্রেম প্রতি নমুনা নির্দিষ্ট করেন তবে সেগুলি পরিবর্তন হবে না। যদি আপনি শেয়ারিং মোড বা বাফার ক্ষমতা নির্দিষ্ট করে থাকেন তবে তারা স্ট্রিমের অডিও ডিভাইস এবং অ্যান্ড্রয়েড ডিভাইসের সক্ষমতাগুলির উপর নির্ভর করে এটি পরিবর্তন করতে পারে যেখানে এটি চলছে। ভাল প্রতিরক্ষামূলক প্রোগ্রামিং এর বিষয় হিসাবে, আপনি এটি ব্যবহার করার আগে স্ট্রিম এর কনফিগারেশন পরীক্ষা করা উচিত। স্ট্রীম সেটিং পুনরুদ্ধার করার ফাংশন রয়েছে যা প্রতিটি নির্মাতার সেটিং এর সাথে মিলে যায়:
- আপনি নির্মাতাকে সংরক্ষণ করতে পারেন এবং আরও স্ট্রীম তৈরি করতে ভবিষ্যতে এটি পুনরায় ব্যবহার করতে পারেন। কিন্তু আপনি যদি এটি আর ব্যবহার করার পরিকল্পনা না করেন তবে আপনার এটি মুছে ফেলা উচিত।
AAudioStreamBuilder_delete(builder);
একটি অডিও স্ট্রিম ব্যবহার করে
রাষ্ট্রীয় রূপান্তর
একটি AAudio স্ট্রীম সাধারণত পাঁচটি স্থিতিশীল অবস্থার একটিতে থাকে (ত্রুটির অবস্থা, সংযোগ বিচ্ছিন্ন, এই বিভাগের শেষে বর্ণনা করা হয়েছে):
- খোলা
- শুরু হয়েছে
- বিরতি দেওয়া হয়েছে
- ফ্লাশড
- থেমে গেল
স্ট্রীমটি স্টার্টেড অবস্থায় থাকলেই ডাটা প্রবাহিত হয়। রাজ্যগুলির মধ্যে একটি স্ট্রীম সরাতে, একটি ফাংশন ব্যবহার করুন যা একটি রাজ্য স্থানান্তরের অনুরোধ করে:
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
থেকে আলাদা না হওয়া পর্যন্ত এটি অপেক্ষা করে, যা আপনি উল্লেখ করেছেন।
উদাহরণস্বরূপ, বিরতি দেওয়ার জন্য অনুরোধ করার পরে, একটি স্ট্রিম তাত্ক্ষণিকভাবে ক্ষণস্থায়ী রাষ্ট্রের বিরতি দিয়ে প্রবেশ করতে হবে এবং কিছু পরে বিরতিযুক্ত অবস্থায় পৌঁছাতে হবে - যদিও এটির কোনও গ্যারান্টি নেই। যেহেতু আপনি বিরতি দেওয়া অবস্থার জন্য অপেক্ষা করতে পারবেন না, তাই Pausing ছাড়া অন্য কোনো অবস্থার জন্য অপেক্ষা করতে 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
স্ট্রিমের বর্তমান অবস্থা দেখায়।
ইনপুটস্টেট হিসাবে সংশ্লিষ্ট ক্ষণস্থায়ী অবস্থা ব্যবহার করে আপনি অনুরোধ শুরু, বন্ধ বা ফ্লাশ কল করার পরে এই একই কৌশলটি ব্যবহার করতে পারেন। AAudioStream_close()
কল করার পরে waitForStateChange()
এ কল করবেন না কারণ স্ট্রীমটি বন্ধ হওয়ার সাথে সাথে মুছে ফেলা হবে। এবং AAudioStream_close()
কল করবেন না যখন waitForStateChange()
অন্য থ্রেডে চলছে।
একটি অডিও স্ট্রিমে পড়া এবং লেখা
একটি স্ট্রীম শুরু হওয়ার পরে ডেটা প্রক্রিয়া করার দুটি উপায় রয়েছে:
- একটি উচ্চ অগ্রাধিকার কলব্যাক ব্যবহার করুন।
-
AAudioStream_read(stream, buffer, numFrames, timeoutNanos)
এবংAAudioStream_write(stream, buffer, numFrames, 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);
}
আপনি স্ট্রীম শুরু করার আগে স্ট্রীমের বাফার প্রাইম করতে পারেন এতে ডেটা লিখে বা সাইলেন্স করে। এটি অবশ্যই একটি নন-ব্লকিং কলে করা উচিত যেখানে টাইমআউটNanos শূন্য সেট করা হয়েছে।
বাফারের ডেটা অবশ্যই AAudioStream_getDataFormat()
দ্বারা প্রত্যাবর্তিত ডেটা বিন্যাসের সাথে মেলে।
একটি অডিও স্ট্রিম বন্ধ করা
আপনি যখন কোনও স্ট্রিম ব্যবহার শেষ করেন, এটি বন্ধ করুন:
AAudioStream_close(stream);
আপনি একটি স্ট্রিম বন্ধ করার পরে আপনি এটি কোনো AAudio স্ট্রিম-ভিত্তিক ফাংশনের সাথে ব্যবহার করতে পারবেন না।
সংযোগ বিচ্ছিন্ন অডিও স্ট্রিম
এই ইভেন্টগুলির মধ্যে একটি ঘটলে একটি অডিও স্ট্রীম যেকোনো সময় সংযোগ বিচ্ছিন্ন হয়ে যেতে পারে:
- সংশ্লিষ্ট অডিও ডিভাইসটি আর সংযুক্ত থাকে না (উদাহরণস্বরূপ যখন হেডফোনগুলি আনপ্লাগ করা হয়)।
- অভ্যন্তরীণভাবে একটি ত্রুটি ঘটে।
- একটি অডিও ডিভাইস আর প্রাথমিক অডিও ডিভাইস নয়।
যখন কোনও স্ট্রিম সংযোগ বিচ্ছিন্ন হয়ে যায়, তখন এটি রাষ্ট্র "সংযোগ বিচ্ছিন্ন" এবং এওডিওস্ট্রিম_ রাইট () কার্যকর করার যে কোনও প্রচেষ্টা বা অন্যান্য ফাংশনগুলি একটি ত্রুটি ফিরিয়ে দেবে। ত্রুটি কোড নির্বিশেষে আপনাকে সর্বদা একটি সংযোগ বিচ্ছিন্ন স্ট্রীম বন্ধ এবং বন্ধ করতে হবে।
আপনি যদি কোনও ডেটা কলব্যাক ব্যবহার করছেন (সরাসরি পঠন/লেখার পদ্ধতির বিপরীতে) তবে স্ট্রিমটি সংযোগ বিচ্ছিন্ন হয়ে গেলে আপনি কোনও রিটার্ন কোড পাবেন না। যখন এটি ঘটে তখন জানানোর জন্য একটি AAudioStream_errorCallback ফাংশন লিখুন এবং AAudioStreamBuilder_setErrorCallback() ব্যবহার করে এটি নিবন্ধন করুন।
যদি আপনি একটি ত্রুটি কলব্যাক থ্রেড সংযোগ বিচ্ছিন্ন সম্পর্কে অবহিত করা হয় তাহলে স্ট্রিম বন্ধ এবং বন্ধ অন্য থ্রেড থেকে করা আবশ্যক. অন্যথায় আপনার একটি অচলাবস্থা থাকতে পারে।
মনে রাখবেন যে আপনি যদি একটি নতুন স্ট্রীম খোলেন তবে এটির মূল স্ট্রিম থেকে ভিন্ন সেটিংস থাকতে পারে (উদাহরণস্বরূপ ফ্রেম পারবার্স্ট):
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()
ব্যবহার করুন।
একটি অ্যাপকে বাফারের সম্পূর্ণ ক্ষমতা ব্যবহার করতে হবে না। AAudio একটি আকার পর্যন্ত একটি বাফার পূরণ করে যা আপনি সেট করতে পারেন। একটি বাফারের আকার তার ক্ষমতার চেয়ে বড় হতে পারে না এবং এটি প্রায়শই ছোট হয়। বাফার আকার নিয়ন্ত্রণ করে আপনি এটি পূরণ করার জন্য প্রয়োজনীয় বিস্ফোরণের সংখ্যা নির্ধারণ করেন এবং এইভাবে লেটেন্সি নিয়ন্ত্রণ করেন। বাফার আকারের সাথে কাজ করতে 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);
}
}
}
একটি ইনপুট স্ট্রীমের জন্য বাফার আকার অপ্টিমাইজ করতে এই কৌশলটি ব্যবহার করার কোন সুবিধা নেই। ইনপুট স্ট্রীমগুলি যত দ্রুত সম্ভব চালায়, বাফার করা ডেটার পরিমাণ ন্যূনতম রাখার চেষ্টা করে এবং তারপর অ্যাপটি প্রিম্পট করা হলে তা পূরণ করে৷
একটি উচ্চ অগ্রাধিকার কলব্যাক ব্যবহার করে
যদি আপনার অ্যাপটি একটি সাধারণ থ্রেড থেকে অডিও ডেটা পড়ে বা লেখে, তবে এটি প্রিমম্পড হতে পারে বা টাইমিং বিটার অভিজ্ঞতা হতে পারে। This can cause audio glitches. বৃহত্তর বাফার ব্যবহার করা এই ধরনের সমস্যা থেকে রক্ষা পেতে পারে, কিন্তু একটি বড় বাফার দীর্ঘ অডিও লেটেন্সিও প্রবর্তন করে। কম লেটেন্সি প্রয়োজন এমন অ্যাপ্লিকেশনগুলির জন্য, একটি অডিও স্ট্রীম আপনার অ্যাপে এবং থেকে ডেটা স্থানান্তর করতে একটি অ্যাসিঙ্ক্রোনাস কলব্যাক ফাংশন ব্যবহার করতে পারে। AAudio একটি উচ্চ-অগ্রাধিকার থ্রেডে কলব্যাক চালায় যার পারফরম্যান্স আরও ভাল।
কলব্যাক ফাংশনে এই প্রোটোটাইপ রয়েছে:
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(
AAudioStream *stream,
void *userData,
void *audioData,
int32_t numFrames);
কলব্যাক নিবন্ধন করতে স্ট্রিম বিল্ডিং ব্যবহার করুন:
AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);
সবচেয়ে সহজ ক্ষেত্রে, স্ট্রীম পর্যায়ক্রমে তার পরবর্তী বিস্ফোরণের জন্য ডেটা অর্জন করতে কলব্যাক ফাংশনটি চালায়।
কলব্যাক ফাংশনটি যে স্ট্রীমটি এটিকে আমন্ত্রণ জানিয়েছে সেটিতে পড়া বা লিখতে হবে না৷ যদি কলব্যাকটি কোনও ইনপুট স্ট্রিমের অন্তর্গত হয় তবে আপনার কোডটি অডিওডাটা বাফারে সরবরাহ করা ডেটা প্রক্রিয়া করা উচিত (তৃতীয় যুক্তি হিসাবে নির্দিষ্ট করা)। যদি কলব্যাক একটি আউটপুট স্ট্রীমের অন্তর্গত হয়, আপনার কোডটি বাফারে ডেটা স্থাপন করা উচিত।
উদাহরণস্বরূপ, আপনি ক্রমাগত একটি সাইন ওয়েভ আউটপুট তৈরি করতে একটি কলব্যাক ব্যবহার করতে পারেন:
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 ফাইল প্লেয়ার৷
এওডিওর বর্তমান সংস্করণে, সর্বনিম্ন সম্ভাব্য বিলম্ব অর্জনের জন্য আপনাকে অবশ্যই একটি উচ্চ-অগ্রাধিকার কলব্যাকের সাথে 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 মিউটেক্স ব্যবহার করা এড়িয়ে যায়, যা থ্রেড প্রিম্পশন এবং গ্লিচের কারণ হতে পারে।
নিরাপদ থাকার জন্য, AAudioStream_waitForStateChange()
কল করবেন না বা দুটি ভিন্ন থ্রেড থেকে একই স্ট্রীমে পড়ুন বা লিখবেন না। একইভাবে, অন্য থ্রেডে পড়ার বা লেখার সময় একটি থ্রেডে একটি স্ট্রিম বন্ধ করবেন না।
যে কলগুলি স্ট্রিম সেটিংস ফেরত দেয়, যেমন AAudioStream_getSampleRate()
এবং AAudioStream_getChannelCount()
, থ্রেড নিরাপদ৷
এই কলগুলিও নিরাপদ থ্রেড:
-
AAudio_convert*ToText()
-
AAudio_createStreamBuilder()
-
AAudioStream_get*()
ছাড়াAAudioStream_getTimestamp()
পরিচিত সমস্যা
- রাইট() ব্লক করার জন্য অডিও লেটেন্সি বেশি কারণ Android O DP2 রিলিজ ফাস্ট ট্র্যাক ব্যবহার করে না। কম বিলম্ব পেতে একটি কলব্যাক ব্যবহার করুন।
অতিরিক্ত সম্পদ
আরও জানতে, নিম্নলিখিত সংস্থানগুলির সুবিধা নিন: