ميكروكنترولر AVR هي رخيصة إلى حد ما وعلى نطاق واسع. ربما ، يبدأ أي مطور مضمن تقريبًا معهم. وبين الهواة ، قواعد الكرة اردوينو ، قلبها هو عادة ATmega328p. بالتأكيد تساءل الكثير: كيف يمكنك جعلها سليمة؟
إذا نظرت إلى المشاريع الحالية ، فهي من عدة أنواع:
- مولدات نبض مربع. إنشاء باستخدام PWM أو دبابيس يانك في المقاطعات. في أي حال ، يتم الحصول على صوت صرير مميزة للغاية.
- استخدام أجهزة خارجية مثل وحدة فك ترميز MP3.
- استخدام PWM لإخراج صوت 8 بت (أحيانًا 16 بت) بتنسيق PCM أو ADPCM. بما أن الذاكرة في ميكروكنترولر من الواضح أنها ليست كافية لهذا ، فإنها عادة ما تستخدم بطاقة SD.
- باستخدام PWM لتوليد الصوت على أساس الجداول موجة مثل MIDI.
النوع الأخير كان مثيرا للاهتمام بالنسبة لي ، لأنه تقريبا لا يتطلب معدات إضافية. أقدم خياري للمجتمع. أولاً ، عرض صغير:
مهتمة ، أطلب القط.
لذلك ، المعدات:
- ATmega8 أو ATmega328. ليست عملية النقل إلى ATmega الأخرى أمرًا صعبًا. وحتى على ATtiny ، ولكن المزيد عن ذلك لاحقًا ؛
- المقاوم؛
- المكثف.
- مكبر الصوت أو سماعة الرأس ؛
- التغذية؛
مثل كل شيء.
يتم توصيل دائرة RC بسيطة مع مكبر الصوت إلى الإخراج من متحكم. الإخراج هو صوت 8 بت مع تردد أخذ العينات من 31250Hz. على تردد بلوري قدره 8 ميجاهرتز ، يمكن إنشاء ما يصل إلى 5 قنوات صوت + قناة ضوضاء واحدة للإيقاع. في هذه الحالة ، يتم استخدام وقت المعالج تقريبًا ، ولكن بعد ملء المخزن المؤقت ، يمكن شغل المعالج بشيء مفيد بالإضافة إلى الصوت:
يتناسب هذا المثال تمامًا مع ذاكرة ATmega8 ، حيث تتم معالجة 5 قنوات + ضجيج بتردد كريستال يبلغ 8 ميجاهرتز ، ولا يوجد وقت كافٍ للرسوم المتحركة على الشاشة.
في هذا المثال ، أردت أيضًا أن أثبت أنه يمكن استخدام المكتبة ليس فقط كبطاقة بريدية موسيقية عادية ، ولكن أيضًا لتوصيل الصوت بالمشاريع الحالية ، على سبيل المثال ، للإعلامات. وحتى عند استخدام قناة صوتية واحدة فقط ، يمكن أن تكون الإعلامات أكثر إثارة للاهتمام من مكبر الصوت البسيط.
والآن التفاصيل ...
موجة الجداول أو wavetables
الرياضيات بسيطة للغاية. هناك وظيفة لهجة دورية ، على سبيل المثال
لهجة (t) = sin (t * freq / (2 * Pi)) .
هناك أيضًا وظيفة لتغيير حجم النغمة الأساسية بمرور الوقت ، على سبيل المثال
volume (t) = e ^ (- t) .
في أبسط الحالات ، يكون صوت الأداة هو ناتج
أداة الوظائف هذه
(t) = النغمة (t) * مستوى الصوت (t) :
على الرسم البياني ، يبدو كل شيء مثل هذا:

بعد ذلك ، نأخذ جميع الأدوات التي تصدر صوتًا في وقت معين ونلخصها مع بعض عوامل الصوت (الكود الزائف):
for (i = 0; i < CHANNELS; i++) { value += channels[i].tone(t) * channels[i].volume(t) * channels[i].volume; }
من الضروري فقط تحديد مستوى الصوت بحيث لا يوجد فيض. وهذا كل شيء تقريبا.
تعمل قناة الضوضاء بنفس الطريقة تقريبًا ، ولكن بدلاً من وظيفة النغمة ، يتم إنشاء مولد تسلسل عشوائي عشوائي.
الإيقاع هو مزيج من قناة الضوضاء وموجة التردد المنخفض ، في حوالي 50-70 هرتز.
بالطبع ، الصوت عالي الجودة بهذه الطريقة يصعب تحقيقه. لكن لدينا فقط 8 كيلو بايت لكل شيء. آمل أن يغفر هذا.
ماذا يمكنني ضغط من 8 بت
في البداية ، ركزت على ATmega8. بدون الكوارتز الخارجي ، يعمل على تردد 8 ميجاهرتز ولديه PWM 8 بت ، والذي يعطي تردد أخذ العينات الأساسي من 8000000/256 = 31250 هرتز. يستخدم جهاز ضبط الوقت PWM لإخراج الصوت ، ويتسبب في حدوث انقطاع أثناء تجاوز السعة لإرسال القيمة التالية إلى مولد PWM. وفقًا لذلك ، لدينا 256 دورة على مدار الساعة لحساب قيمة العينة لكل شيء ، بما في ذلك النفقات العامة المقاطعة وتحديث معلمات قناة الصوت وتتبع الوقت الذي تحتاج فيه إلى تشغيل الملاحظة التالية ، إلخ.
للتحسين ، سنستخدم بنشاط الحيل التالية:
- نظرًا لوجود معالج ذي 8 بت ، سنحاول جعل المتغيرات كما هي. في بعض الأحيان سوف نستخدم 16 بت.
- يتم تقسيم الحسابات المشروطة إلى متكررة وليس كذلك. يجب حساب العينات الأولى لكل عينة ، والثانية - أقل كثيرًا ، مرة واحدة كل عدة عشرات / مئات من العينات.
- لتوزيع الحمل بالتساوي مع مرور الوقت ، نستخدم مخزن مؤقت دائري. في الحلقة الرئيسية من البرنامج ، نملأ المخزن المؤقت ونطرحه في المقاطعة. إذا كان كل شيء على ما يرام ، فسيملأ المخزن المؤقت بشكل أسرع مما يفرغ ويكون لدينا وقت لشيء آخر.
- رمز مكتوب في C مع الكثير من المضمنة. تدل الممارسة على أنها أسرع بكثير.
- كل ما يمكن حسابه من قبل المعالج ، خاصة بمشاركة القسمة ، يتم بواسطة المعالج المسبق.
أولاً ، قم بتقسيم الوقت إلى فواصل زمنية تبلغ 4 مللي ثانية (دعوت إليها علامات التجزئة). عند تردد أخذ العينات يبلغ 31250 هرتز ، نحصل على 125 عينة لكل علامة. يجب أن تحسب حقيقة أن كل عينة يجب أن تقرأ كل عينة ، والباقي - مرة واحدة لكل علامة أو أقل. على سبيل المثال ، في غضون علامة واحدة ، سيكون حجم الأداة ثابتًا:
الأداة (t) = النغمة (t) * currentVolume ؛ سيتم إعادة حساب التيار ومحتوياته مرة واحدة لكل علامة مع الأخذ في الاعتبار مستوى الصوت (ر) ومستوى الصوت المحدد لقناة الصوت.
تم اختيار مدة علامة تبلغ 4 مللي ثانية استنادًا إلى حد بسيط يبلغ 8 بت: مع عداد عينة من 8 بت ، يمكنك العمل بتردد أخذ عينات يصل إلى 64 كيلو هرتز ، مع عداد علامات بثمانية بت يمكننا قياس الوقت حتى ثانية واحدة.
بعض الكود
القناة نفسها موصوفة بواسطة هذا الهيكل:
typedef struct { // Info about wave const int8_t* waveForm; // Wave table array uint16_t waveSample; // High byte is an index in waveForm array uint16_t waveStep; // Frequency, how waveSample is changed in time // Info about volume envelope const uint8_t* volumeForm; // Array of volume change in time uint8_t volumeFormLength; // Length of volumeForm uint8_t volumeTicksPerSample; // How many ticks should pass before index of volumeForm is changed uint8_t volumeTicksCounter; // Counter for volumeTicksPerSample // Info about volume uint8_t currentVolume; // Precalculated volume for current tick uint8_t instrumentVolume; // Volume of channel } waveChannel;
بشروط ، يتم تقسيم البيانات هنا إلى 3 أجزاء:
- معلومات حول الموجي ، المرحلة ، التردد.
waveForm: معلومات حول وظيفة النغمة (t): إشارة إلى صفيف يبلغ طوله 256 بايت. ضبط النغمة ، صوت الجهاز.
waveSample: البايت العالي يشير إلى الفهرس الحالي لصفيف waveForm.
waveStep: يحدد التردد الذي سيتم به زيادة waveSample عند حساب العينة التالية.
كل عينة تعتبر شيئا مثل هذا:
int8_t tone = channelData.waveForm[channelData.waveSample >> 8]; channelData.waveSample += channelaData.waveStep; return tone * channelData.currentVolume;
- حجم المعلومات. يضبط وظيفة تغيير الحجم بمرور الوقت. نظرًا لأن مستوى الصوت لا يتغير كثيرًا ، فيمكنك إعادة فرز الأصوات أقل مرة واحدة لكل علامة. يتم ذلك مثل هذا:
if ((channel->volumeTicksCounter--) == 0 && channel->volumeFormLength > 0) { channel->volumeTicksCounter = channel->volumeTicksPerSample; channel->volumeFormLength--; channel->volumeForm++; } channel->currentVolume = channel->volumeForm * channel->instrumentVolume >> 8;
- يضبط حجم القناة والحجم الحالي المحسوب.
يرجى ملاحظة: الطول الموجي هو 8 بت ، وحجم هو أيضا 8 بت ، والنتيجة هي 16 بت. مع فقدان طفيف في الأداء ، يمكنك جعل الصوت (تقريبًا) 16 بت.
في الصراع من أجل الإنتاجية ، اضطررت إلى اللجوء إلى بعض السحر الأسود.
مثال رقم 1. كيفية إعادة حساب حجم القنوات:
if ((tickSampleCounter--) == 0) { // tickSampleCounter = SAMPLES_PER_TICK – 1; // - } // volume recalculation should no be done so often for all channels if (tickSampleCounter < CHANNELS_SIZE) { recalculateVolume(channels[tickSampleCounter]); }
وبالتالي ، تحسب جميع القنوات مستوى الصوت مرة واحدة لكل علامة ، ولكن ليس في نفس الوقت.
مثال رقم 2. الحفاظ على معلومات القناة في بنية ثابتة أرخص من الصفيف. دون الخوض في تفاصيل تنفيذ wavechannel.h ، سأقول أن هذا الملف تم إدراجه في الكود عدة مرات (مساو لعدد القنوات) بتوجيهات مختلفة قبل المعالج. ينشئ كل إدراج متغيرات عمومية جديدة ووظيفة حساب قناة جديدة ، والتي يتم تضمينها بعد ذلك في الكود الرئيسي:
مثال رقم 3. إذا بدأنا تشغيل الملاحظة التالية في وقت لاحق ، فلن يلاحظها أحد. دعونا نتخيل الموقف: تناولنا المعالج بشيء ، وخلال هذا الوقت كان المخزن المؤقت فارغًا تقريبًا. ثم نبدأ في تعبئته وفجأة اتضح أن هناك إجراءً جديدًا قادمًا: نحتاج إلى تحديث الملاحظات الحالية ، والقراءة من الصفيف التالي ، إلخ. إذا لم يكن لدينا وقت ، فسيكون هناك تلعثم مميز. من الأفضل بكثير ملء المخزن المؤقت بالبيانات القديمة ، وبعد ذلك فقط يتم تحديث حالة القنوات.
while ((samplesToWrite) > 4) { // fillBuffer(SAMPLES_PER_TICK); // - updateMusicData(); // }
بطريقة جيدة ، سيكون من الضروري إعادة ملء المخزن المؤقت بعد الحلقة ، ولكن نظرًا لأن لدينا كل شيء مضمّن تقريبًا ، فإن حجم الكود مضخم بشكل ملحوظ.
موسيقى
يتم استخدام عداد التجزئة ثمانية بت. عندما يتم الوصول إلى الصفر ، يبدأ قياس جديد ، يتم تعيين العداد مدة القياس (بالعلامات) ، وبعد ذلك بقليل يتم التحقق من مجموعة الأوامر الموسيقية.
يتم تخزين بيانات الموسيقى في مجموعة من وحدات البايت. هو مكتوب شيء مثل هذا:
const uint8_t demoSample[] PROGMEM = { DATA_TEMPO(160), // Set beats per minute DATA_INSTRUMENT(0, 1), // Assign instrument 1 (see setSample) to channel 0 DATA_INSTRUMENT(1, 1), // Assign instrument 1 (see setSample) to channel 1 DATA_VOLUME(0, 128), // Set volume 128 to channel 0 DATA_VOLUME(1, 128), // Set volume 128 to channel 1 DATA_PLAY(0, NOTE_A4, 1), // Play note A4 on channel 0 and wait 1 beat DATA_PLAY(1, NOTE_A3, 1), // Play note A3 on channel 1 and wait 1 beat DATA_WAIT(63), // Wait 63 beats DATA_END() // End of data stream };
كل ما يبدأ بـ DATA_ هي وحدات ما قبل المعالجة التي تعمل على توسيع المعلمات إلى العدد المطلوب من وحدات بايت البيانات.
على سبيل المثال ، يتم توسيع أمر DATA_PLAY إلى وحدتي بايت يتم تخزينهما: علامة الأمر (1 بت) ، والإيقاف المؤقت قبل الأمر التالي (3 بت) ، ورقم القناة الذي سيتم تشغيل الملاحظة عليه (4 بتات) ، ومعلومات حول الملاحظة (8 بتات). القيد الأكثر أهمية هو أنه لا يمكن استخدام هذا الأمر للإيقاف المؤقت الطويل ، بحد أقصى 7 دورات. إذا كنت بحاجة إلى المزيد ، فأنت بحاجة إلى استخدام الأمر DATA_WAIT (حتى 63 قياسًا). لسوء الحظ ، لم أجد ما إذا كان يمكن توسيع الماكرو إلى عدد مختلف من وحدات البايت للصفيف وفقًا لمعلمة الماكرو. وحتى تحذير أنا لا أعرف كيفية عرض. ربما تقول لي.
استخدام
يوجد في الدليل التوضيحي العديد من الأمثلة على ميكروكنترولر مختلفة. ولكن باختصار ، إليك قطعة من الملف التمهيدي ، ليس لدي شيء أضيفه:
إذا كنت تريد القيام بشيء آخر إلى جانب الموسيقى ، فيمكنك زيادة حجم المخزن المؤقت باستخدام BUFFER_SIZE. يجب أن يكون حجم المخزن المؤقت 2 ^ n ، ولكن مع الأسف ، بحجم 256 ، يحدث تدهور الأداء. حتى أحسب بها.
لزيادة الإنتاجية ، يمكنك زيادة التردد مع الكوارتز الخارجي ، يمكنك تقليل عدد القنوات ، يمكنك تقليل وتيرة أخذ العينات. مع الخدعة الأخيرة ، يمكنك استخدام الاستيفاء الخطي ، والذي يعوض إلى حد ما عن انخفاض جودة الصوت.
أي تأخير لا ينصح ، ل يضيع الوقت وحدة المعالجة المركزية. بدلاً من ذلك ، يتم تطبيق الأسلوب الخاص به في
ملف microsound / delay.h ، والذي ، بالإضافة إلى الإيقاف المؤقت نفسه ، يشارك في ملء المخزن المؤقت. قد لا تعمل هذه الطريقة بدقة شديدة على فترات توقف قصيرة ، ولكن في فترات توقف طويلة أكثر أو أقل عقلانية.
صنع الموسيقى الخاصة بك
إذا كتبت الأوامر يدويًا ، فأنت بحاجة إلى الاستماع إلى ما يحدث. صب كل تغيير في متحكم ليست مريحة ، وخاصة إذا كان هناك بديل.
هناك
wavepot.com خدمة مضحكة إلى
حد ما - محرر JavaScript على الإنترنت ، حيث تحتاج إلى ضبط وظيفة إشارة الصوت من وقت لآخر ، ويتم إخراج هذه الإشارة إلى بطاقة الصوت. أبسط مثال:
function dsp(t) { return 0.1 * Math.sin(2 * Math.PI * t * 440); }
قمت بتحويل المحرك إلى JavaScript ، وهو موجود في
demos / wavepot.js . يجب إدراج محتويات الملف في المحرر
wavepot.com ويمكنك إجراء تجارب. نكتب بياناتنا إلى مجموعة soundData ، والاستماع ، لا تنسى أن حفظ.
يجب أن نذكر أيضًا متغير simulate8bits. هي ، وفقًا للاسم ، تحاكي صوتًا من ثمانية بتات. إذا بدا فجأة أن الطبول تسمع صوتًا وضوضاء في الأجهزة المبللة بصوت هادئ ، فهذه هي تشويشًا صوتي من 8 بتات. يمكنك محاولة تعطيل هذا الخيار والاستماع إلى الفرق. المشكلة أقل بكثير إذا لم يكن هناك صمت في الموسيقى.
صلة
في إصدار بسيط ، تبدو الدائرة كما يلي:
+5V ^ MCU | +-------+ +---+VC | R1 | Pin+---/\/\--+-----> OUT | | | +---+GN | === C1 | +-------+ | | | --- Grnd --- Grnd
دبوس الإخراج يعتمد على متحكم. يجب اختيار المقاوم R1 والمكثف C1 على أساس الحمل ، مكبر للصوت (إن وجد) ، إلخ. أنا لست مهندسًا إلكترونيًا ولن أعطي الصيغ ؛ فهي سهلة الاستخدام إلى جانب الآلات الحاسبة عبر الإنترنت.
لدي R1 = 130 أوم ، C1 = 0.33 الجبهة المتحدة. إلى الإخراج أقوم بتوصيل سماعات الصينية العادية.
ما كان هناك حوالي 16 بت الصوت؟
كما قلت أعلاه ، عندما نضرب رقمين من ثمانية بتات (التردد والحجم) ، نحصل على رقم 16 بت. لا يمكنك تقريبه إلى ثمانية بتات ، لكن يمكنك إخراج البايتات في قناتين PWM. إذا قمت بخلط هاتين القناتين بنسبة 1/256 ، فيمكننا الحصول على صوت 16 بت. من السهل على وجه الخصوص الفرق مع الثمانية بت سماع الأصوات والبراميل التي تتلاشى بسلاسة في لحظات عندما تصدر أداة واحدة فقط.
اتصال الإخراج 16 بت:
+5V ^ MCU | +-------+ +---+VCC | R1 | PinH+---/\/\--+-----> OUT | | | | | R2 | | PinL+---/\/\--+ +---+GND | | | +-------+ === C1 | | --- Grnd --- Grnd
من المهم خلط النواتج 2 بشكل صحيح: يجب أن تكون مقاومة R2 أكبر بمقدار 256 مرة من مقاومة R1. أكثر دقة ، كان ذلك أفضل. لسوء الحظ ، حتى المقاومات ذات الخطأ 1٪ لا تعطي الدقة المطلوبة. ومع ذلك ، حتى مع وجود مجموعة غير دقيقة للغاية من المقاومات ، يمكن تخفيف التشوه بشكل ملحوظ.
لسوء الحظ ، عند استخدام الصوت 16 بت ، فإن الأداء المتدني و 5 قنوات + ضوضاء لم يعد لديهم وقت للمعالجة في دورات الساعة المخصصة 256.
هل من الممكن على اردوينو؟
نعم يمكنك ذلك. ليس لدي سوى استنساخ نانو صيني على ATmega328p ، إنه يعمل عليه. على الأرجح ، يجب أن تعمل arduins الأخرى على ATmega328p أيضًا. يبدو أن ATmega168 لديه نفس سجلات التحكم المؤقت. على الأرجح أنها ستعمل دون تغيير. على ميكروكنترولر أخرى تحتاج إلى التحقق ، قد تحتاج إلى إضافة برنامج تشغيل.
يوجد رسم تخطيطي في
demos / arduino328p ، ولكن حتى يتم فتحه بشكل طبيعي في
IDdu Arduino ، تحتاج إلى نسخه إلى جذر المشروع.
في المثال ، يتم إنشاء صوت 16 بت ويتم استخدام المخرجات D9 و D10. لتبسيط ، يمكنك قصر نفسك على صوت 8 بت واستخدام إخراج D9 واحد فقط.
نظرًا لأن جميع arduins تعمل بسرعة 16 ميغاهرتز ، إذن ، إذا رغبت في ذلك ، يمكنك زيادة عدد القنوات إلى 8.
ماذا عن ATtiny؟
ATtiny لا يوجد لديه الضرب الأجهزة. يعد تعدد البرامج الذي يستخدمه المترجم بطيئًا بشكل كبير ويتم تجنبه على أفضل وجه. عند استخدام أداة إدراج مجمّع محسّنة ، ينخفض الأداء بمعدل مرتين مقارنةً بـ ATmega. يبدو أنه لا فائدة من استخدام ATtiny على الإطلاق ، ولكن ...
بعض ATtiny لديها مضاعف التردد ، PLL. وهذا يعني أنه في مثل هذه الميكروكونترولر ، هناك ميزتان مثيرتان للاهتمام:
- تردد مولد PWM هو 64 ميجا هرتز ، مما يعطي فترة PWM من 250 كيلو هرتز ، وهو أفضل بكثير من 31250 هرتز في 8 ميجا هرتز أو 62500 هرتز مع الكوارتز في 16 ميغاهيرتز على أي ATmega.
- يسمح مضاعف التردد نفسه للبلور بالساعة عند 16 ميجاهيرتز بدون كوارتز.
ومن هنا الاستنتاج: بعض ATtiny يمكن استخدامها لتوليد الصوت. تمكنوا من معالجة نفس 5 أدوات + قناة الضوضاء ، ولكن في 16 ميغاهيرتز وأنها لا تحتاج الكوارتز الخارجي.
الجانب السلبي هو أنه لا يمكن زيادة التردد بعد الآن ، وأن الحسابات تستغرق تقريبًا كل الوقت. لتحرير الموارد ، يمكنك تقليل عدد القنوات أو معدل العينة.
آخر ناقص هو الحاجة إلى استخدام مؤقتين في وقت واحد: واحد ل PWM ، والثاني للمقاطعة. هذا هو المكان الذي ينتهي عادة الموقتات.
من ميكروكنترولر PLL التي أعرفها ، يمكنني أن أذكر ATtiny85 / 45/25 (8 أرجل) ، ATtiny861 / 461/261 (20 أرجل) ، ATtiny26 (20 أرجل).
بالنسبة للذاكرة ، فإن الفرق مع ATmega ليس رائعًا. في 8 كيلو بايت ، ستناسب العديد من الآلات والألحان بشكل مثالي. في 4 كيلو بايت يمكنك وضع 1-2 أدوات و 1-2 نغمات. من الصعب وضع شيء ما في 2 كيلو بايت ، ولكن إذا كنت تريد حقًا ، فيمكنك ذلك. من الضروري الفصل بين الطرق وتعطيل بعض الوظائف مثل التحكم في مستوى الصوت عبر القنوات وتقليل تردد أخذ العينات وعدد القنوات. بشكل عام ، لأحد الهواة ، ولكن هناك مثال عملي على ATtiny26.
المشاكل
هناك مشاكل. والمشكلة الأكبر هي سرعة الحوسبة. تمت كتابة التعليمات البرمجية بالكامل في C مع إدراج الضرب صغيرة المجمّع لـ ATtiny. يتم إعطاء الأمثل للمترجم ويتصرف في بعض الأحيان بشكل غريب. مع التغييرات الصغيرة التي لا يجب أن تؤثر على أي شيء ، يمكنك الحصول على انخفاض ملحوظ في الأداء. علاوة على ذلك ، فإن التغيير من -Os إلى -O3 لا يساعد دائمًا. مثال واحد هو استخدام مخزن مؤقت 256 بايت. من الأمور غير السارة بشكل خاص عدم وجود ضمان في الإصدارات الجديدة من المحول البرمجي أننا لن نحصل على انخفاض في الأداء على نفس الكود.
مشكلة أخرى هي أن آلية التخفيف قبل الملاحظة التالية لم يتم تنفيذها على الإطلاق. أي عندما يتم استبدال ملاحظة واحدة على قناة واحدة بآخر ، يتم مقاطعة الصوت القديم بشكل مفاجئ ، وأحيانًا يتم سماع نقرة صغيرة. أرغب في إيجاد طريقة للتخلص من هذا دون أن تفقد الأداء ، لكن حتى الآن.
لا توجد أوامر لزيادة / خفض حجم بسلاسة. يعد هذا الأمر ضروريًا بشكل خاص لنغمات رنين الإخطار القصيرة ، حيث تحتاج في النهاية إلى التخفيف السريع من مستوى الصوت بحيث لا يكون هناك انقطاع حاد في الصوت. جزء من المشكلة هو كتابة سلسلة من الأوامر مع ضبط مستوى الصوت وإيقاف مؤقت يدويًا.
النهج المختار ، من حيث المبدأ ، غير قادر على توفير صوت طبيعي للأدوات. للحصول على صوت أكثر طبيعية ، تحتاج إلى تقسيم أصوات الأدوات إلى إطلاق للاعتداء ، واستخدام الجزءين الأولين على الأقل مع مدة أطول بكثير من فترة التذبذب. ولكن بعد ذلك ستحتاج بيانات الأداة إلى المزيد. كانت هناك فكرة لاستخدام جداول موجات أقصر ، على سبيل المثال ، في 32 بايت بدلاً من 256 ، ولكن بدون الاستيفاء ، تنخفض جودة الصوت بشكل كبير ، ومع انخفاض الاستيفاء ، ينخفض الأداء. ومن الواضح أن 8 بتات أخرى من أخذ العينات ليست كافية للموسيقى ، ولكن يمكن التحايل على ذلك.
يقتصر حجم المخزن المؤقت على 256 عينة. هذا يتوافق مع ما يقرب من 8 ميلي ثانية ، وهذا هو الحد الأقصى لفترة زمنية لا يتجزأ التي يمكن أن تعطى للمهام الأخرى. في الوقت نفسه ، لا يزال تنفيذ المهام معلقًا بشكل دوري بسبب الانقطاعات.
استبدال التأخير القياسي لا يعمل بشكل دقيق جدًا للتوقف المؤقت.
أنا متأكد من أن هذه ليست قائمة كاملة.
مراجع