Мазмуну:

Python жана Arduino боюнча MIDI барабан топтому: 5 кадам (сүрөттөр менен)
Python жана Arduino боюнча MIDI барабан топтому: 5 кадам (сүрөттөр менен)

Video: Python жана Arduino боюнча MIDI барабан топтому: 5 кадам (сүрөттөр менен)

Video: Python жана Arduino боюнча MIDI барабан топтому: 5 кадам (сүрөттөр менен)
Video: Драм-секвенсор Arduino: 8 дорожек, 16 шагов на такт, 8 тактов на паттерн 2024, Ноябрь
Anonim
Image
Image
Python жана Arduino боюнча MIDI барабан комплект
Python жана Arduino боюнча MIDI барабан комплект
Python жана Arduino боюнча MIDI барабан комплект
Python жана Arduino боюнча MIDI барабан комплект

Мен бала кезимден барабан үчүн комплект сатып алууну эңсечүмүн. Ал кезде, бардык музыкалык жабдууларда санариптик тиркемелер жок болчу, анткени бизде бүгүнкү күндө көп, демек, баалар күтүүлөр менен бирге өтө жогору болгон. Жакында мен eBayден эң арзан барабан комплектин сатып алууну чечтим, аны бир гана артыкчылык менен: аны бузуп, өзүнүн аппараттык жана программалык камсыздоону түзмөккө тиркөө мүмкүнчүлүгү.

Сатып алуу таптакыр көңүлдү чөгөргөн жок: 9 түрдүү үн блокноттору бар портативдүү барабан топтому, тепкичтүү барабан үчүн эки бут которгуч педалы жана микро-USB розеткасы. Чындыгында демотивация болгон нерсе - бул чыгуучу үндөр (Бул комплект үчүн чыныгы колдонуу - тышкы динамикти туташтыруу жана андан ырахат алуу). Ошентип, мен аны USB, Arduinoго негизделген MIDI барабан комплектине жана Pythonго негизделген колдонуучу интерфейси аркылуу, ыңгайлуу колдонуу жана оңдоп -түзөө үчүн, көлөмү, нотасы жана канал тандоолору аркылуу өзүмдүн программалоого айландырууну чечтим.

Аппараттын өзгөчөлүктөрү:

  • Төмөн баа
  • Барабан комплектин каалаган санарип кирүүдөн түзүү - ал тургай баскычтардын массиви
  • USB интерфейси аркылуу байланышты колдоо жана электр менен камсыздоо - UART конвертерине жана Arduino түзмөгүнө USB интеграциясы
  • Мининин бөлүктөрү туура иштеши үчүн
  • Пайдаланууга оңой Python негизделген UI
  • Толук MIDI колдоосу жөнгө салынуучу ылдамдык менен, нота жана Arduino казыктары
  • Түзмөктүн эс тутумунда сакталган барабандын конфигурацияларын сактоо жана жүктөө

Келгиле, долбоорго өтөлү…

1 -кадам: Иштөө теориясы

Операция теориясы
Операция теориясы
Операция теориясы
Операция теориясы
Операция теориясы
Операция теориясы

Блок диаграммасы

Биринчиден, долбоордун структурасына токтололу жана аны өзүнчө блокторго бөлөлү:

Барабан жыйнагы

Долбоордун негизги бирдиги. Ал 9 өзүнчө барабан аянтчасынан турат, мында ар бир блок логикалык абалын өзгөрткөн баскычтардын массиви. Анын структурасынан улам, бул барабан топтомун каалаган баскычтардан куруу мүмкүнчүлүгү бар. Ар бир барабан аянтчасы негизги электрондук тактадагы тартма каршылыкка туташтырылган, ошентип барабан аянтчасы бир нече жолу урулуп жатканда, контурдун контуруна конкреттүү которгуч байланган жана барабан тактасында логикалык LOW бар. Кысым көрсөтүлбөгөндө, барабандын блокноту ачык болот жана резистордун электр линиясына тартылышынан улам, логикалык HIGH барабан тактасында бар. Долбоордун максаты толук санарип MIDI түзмөгүн түзүү болгондуктан, негизги ПКБдагы аналогдук бөлүктөрдүн бардыгын этибарга албай койсо болот. Барабандын комплектинин тепкичтүү барабанга жана хи-шапкага арналган эки педалы бар экенин белгилей кетүү маанилүү, алар да тартылуучу резисторлорго байланган жана барабандын бардык блокноттору менен бирдей логиканы бөлүшөт (Биз муну бир аздан кийин талкуулайбыз)).

Arduino Pro-Micro

Барабан комплектинин мээси. Анын максаты - барабан аянтчасынан сигнал чыгып жаткандыгын аныктоо жана бардык керектүү параметрлер менен тиешелүү MIDI чыгарууну камсыз кылуу: Эскертүү, сигналдын ылдамдыгы жана узактыгы. Барабан аянтчаларынын санариптик табиятынан улам, аларды жөн гана arduino санариптик киришине байлап койсо болот (бардыгы 10 казык). Бардык керектүү орнотууларды жана MIDI маалыматын сактоо үчүн, биз анын эстутумун колдонобуз-EEPROM, демек, биз аппаратты иштеткен сайын, MIDI маалыматы EEPROMдон жүктөлүп, аны кайра программалоого жана кайра конфигурациялоого мүмкүндүк берет. Ошондой эле, Arduino Pro-Micro өтө кичинекей пакетте бар жана барабан комплектинин ички корпусуна оңой эле бөлүштүрүлүшү мүмкүн.

FTDI USB сериялык конвертерге

ПК тиркемесинин жардамы менен биздин түзмөктүн өзгөчөлүктөрүн программалоо жана аныктоо үчүн, USB интерфейсин сериялыкка айландыруу керек, анткени Arduino Pro-Microдо USB жок. Түзмөктөр ортосундагы байланыш UARTке негизделгендиктен, FTDI түзмөгү кошумча касиеттерине карабастан колдонуунун жөнөкөйлүгүнөн улам бул долбоордо колдонулат.

PC тиркемеси - Python

Колдонуучу интерфейстерин жана тез курула турган долбоорлорду өнүктүрүүгө келгенде, Python-бул эң сонун чечим. UI колдонуунун максаты - бул биздин барабан комплектинин MIDI касиеттерин кайра аныктоону, маалыматты сактоону, программалык түзүлүштү жана кодду кайра -кайра топтоонун кажети жок системалар ортосундагы байланышты түзүүнү кыйла ыңгайлуу кылуу. Барабан топтому менен байланышуу үчүн биз сериялык интерфейсти колдонуп жаткандыктан, интернеттин бардык жеринде сериялык байланыштын бардык түрлөрүн колдогон бекер модулдар көп. Мындан тышкары, кийинчерээк талкууланат, UART интерфейси үч казыктан турат: RXD, TXD жана DTR. DTR Arduino модулуна баштапкы абалга келтирүү үчүн колдонулат, андыктан биз MIDI тиркемесин иштетүүгө же UIди программалык түзмөккө туташтырууга кызыкдар болгонубузда, USB кабелин же башка нерсени кайра туташтыруунун кажети жок.

2 -кадам: Бөлүктөр жана инструменттер

Бөлүктөр

  • Барабан жыйнагы
  • 2 x Sustain Pedals (Адатта, DK пакетине киргизилген).
  • FTDI - USBден сериялык конвертерге
  • Arduino Pro Micro
  • Micro-USB кабели

Аспаптар

  • Лампочка/станция
  • Soldering Tin
  • Жука Диаметри Бир Өзөктүү зым
  • Пинцет
  • Cutter
  • Plier
  • Бычак
  • Screw Driver
  • 3D принтери (Милдеттүү эмес - ыңгайлаштырылган педаль аянтчалары үчүн)

Программалык камсыздоо

  • Arduino IDE
  • Python 3 же андан жогору
  • JetBrains Pycharm
  • Чачсыз MIDI интерфейси
  • loopMIDI

3 -кадам: ширетүү жана чогултуу

Лайкоо жана монтаждоо
Лайкоо жана монтаждоо
Лайкоо жана монтаждоо
Лайкоо жана монтаждоо
Лайкоо жана монтаждоо
Лайкоо жана монтаждоо

Айкалыштыруу керек болгон үч модуль болгондуктан, ширетүү жана чогултуу процесси кыска жана жөнөкөй:

  • Arduino Pro-Micro менен FTDI түзмөгүн бириктирип, туташуулардын ар бир түзмөктө аныкталган I/O менен шайкештигин текшериңиз:

    • VBUS-VBUS
    • GND-GND
    • DTR-DTR
    • RXD-TXD
    • TXD-RXD
  • Барабандын пластикалык корпусундагы бардык бурамаларды алып салгыла, тактага такталган кабелге жана анын тартылуу каршылыгына көңүл бура алаарыңызды текшериңиз.
  • Биз мурда курган Arduino-FTDI модулу үчүн жука жиптер:

    • Санарип киргизүү: D [2:11]
    • VBUS
    • D+
    • D-
    • GND
  • Модулду батарейканын корпусунун ичине салыңыз, ошондо зымдар подкладка каршылыгынын резисторлору менен бир тарапта сүзүп кетет
  • Акыркы сүрөттө көрүнүп тургандай, барабан блок терминалдарына бардык санариптик кирүүлөрдү кошуңуз.
  • FTDI түзмөгүнө чакан микро-USB шинасы (VBUS, D+, D-, GND), бул зымдарды кароодо эч кандай каталар жок экенин текшериңиз.
  • Arduino-FTDI модулун батарейканын корпусуна ысык клей менен бекиткиле
  • Тиешелүү бурамалар менен түзмөктү кураштырыңыз

Биз муну жасадык, түзмөк чогултулду. Келгиле, кодду уланта берели …

4 -кадам: Программалоо A: Arduino

Программалоо A: Arduino
Программалоо A: Arduino

Эскизибизди этап-этабы менен сүрөттөп берели:

Биринчиден, туура иштеши үчүн эки керектүү китепкананы кошуу керек. EEPROM мурунтан эле Arduino IDEде орнотулган, бирок барабан үчүн дебютор модулу өзүнчө орнотулушу керек

#кошуу #кошуу

Бул которгучтар негизинен мүчүлүштүктөрдү оңдоо тизмектеринде колдонулат. Эгерде сиз барабан аянтчаларына Arduino терминалдарынын туташуусун сынап көргүңүз келсе жана бардык санариптик кирүүлөрдү аныктай турган болсоңуз, анда бул өчүргүчтөр аныкталышы керек

/ * Иштеп которгучтар: Мүчүлүштүктөрдү оңдоо же баштоо үчүн каалаган режимге комментарий бербеңиз * ///#LOAD_DEFAULT_VALUES аныктайт // EEPROMдун ордуна туруктуу маанилерди жүктөңүз //#PRINT_PADS_PIN_NUMBERS'ти аныктаңыз // Сериялык порт аркылуу урунган тактага туташкан пин номерин басып чыгарыңыз.

Туруктуу талаалар барабан аянтчасын эсепке алууну камтыган бардык демейки маанилерди билдирет. Түзмөктү биринчи жолу иштетүү үчүн Hi-Hat жана Kick педальдарынын так туташуусун билүү керек

/ * Барабандын түрүн саноо */

DRUM_POSITION {KICK = 0, SNARE, HIHAT, RIDE, CYMBAL1, CYMBAL2, TOM_HIGH, TOM_MID, TOM_LO, HIHAT_PEDAL};

/ * Демейки маанилер */

const uint8_t DRUM_NOTES [10] = {36, 40, 42, 51, 49, 55, 47, 45, 43, 48}; const uint8_t DRUM_VELOCITIES [10] = {110, 100, 100, 110, 110, 110, 110, 110, 110, 110}; const uint8_t DRUM_PINS [10] = {8, 6, 4, 3, 11, 9, 5, 10, 2, 7};

/ * Барабандын үзүлүү мөөнөтү */

const uint8_t KICK_DB_DURATION = 30;

EEPROM PC колдонмосунан келген бардык маалыматтарды сактоо/жүктөө үчүн колдонулат. Жогоруда сүрөттөлгөн даректер ар бир барабан аянтчасы үчүн MIDI маалыматынын так жайгашкан жерин көрсөтөт

/* EEPROM Даректерди картага түшүрүү

Эскертүүлөр: | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 |

Пиндер: | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13 | Ылдамдыктар | 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23 | */ const uint8_t NOTES_ADDR = 0x00; const uint8_t VELOCITIES_ADDR = 0x14; const uint8_t PINS_ADDR = 0x0A;

Глобалдык өзгөрмөлөр ар бир блоктун абалын аныктоо үчүн колдонулат жана ошого жараша MIDI байланышын аткарат

/ * Глобалдык өзгөрмөлөр */

uint8_t drumNotes [10], drumVelocities [10], drumPins [10]; // MIDI өзгөрмөлөрү

uint8_t uartBuffer [64]; // MIDI Data Debouncer тепкенди чогултуу жана сактоо үчүн UART буфери (DRUM_PINS [KICK], KICK_DB_DURATION); // Дебунер объекти тепкен барабан үчүн туруксуз bool previousState [9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Барабан тактасы мурунку логикада туруксуз bool currentState [9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Барабан тактасы учурдагы логикалык абалдар

EEPROM функциялары

/* Орнотууларды EEPROMдо сактоо*/

боштук storeEEPROM () {

memcpy (drumNotes, uartBuffer, 10); memcpy (drumPins, uartBuffer + 10, 10); memcpy (drumVelocities, uartBuffer + 20, 10); for (uint8_t i = 0; i <10; i ++) EEPROM.write (NOTES_ADDR+i, drumNotes ); үчүн (uint8_t i = 0; i <10; i ++) EEPROM.write (PINS_ADDR+i, drumPins ); үчүн (uint8_t i = 0; i <10; i ++) EEPROM.write (VELOCITIES_ADDR+i, drumVelocities ); }

/* EEPROMдон орнотууларды жүктөө*/

void loadEEPROM () {for (uint8_t i = 0; i <10; i ++) drumNotes = EEPROM.read (NOTES_ADDR+i); for (uint8_t i = 0; i <10; i ++) drumPins = EEPROM.read (PINS_ADDR+i); for (uint8_t i = 0; i <10; i ++) drumVelocities = EEPROM.read (VELOCITIES_ADDR+i); }

Өзгөрмөлөрдү жана программалоо режимин баштоо, учурда педаль жана Arduino жүктөө бир эле учурда иштетилет

жараксыз enterProgrammingMode () {

bool confirmBreak = false; uint8_t lineCnt = 0; uint8_t charCnt = 0; char readChar = 0; while (! confirmBreak) {if (Serial.available ()) {uartBuffer [charCnt] = Serial.read (); if (charCnt> = 29) confirmBreak = true; else charCnt ++; }} Serial.println ("OK"); storeEEPROM (); }

жараксыз initValues () {

#ifdef LOAD_DEFAULT_VALUES memcpy (drumNotes, DRUM_NOTES, 10); memcpy (drumVelocities, DRUM_VELOCITIES, 10); memcpy (drumPins, DRUM_PINS, 10); #else loadEEPROM (); #endif}

MIDI Байланыш иштетүүчүлөрү 1 мс нота кармоо убактысын кечиктиришет

/ * MIDI эскертүү функциясын ойнотуу */

vidi midiOut (enum DRUM_POSITION drumIn) {

if (drumIn == HIHAT) {// Эгерде HI-HAT басылган болсо, анда педалдын басылгандыгын текшерүү керек, эгерде (! digitalRead (drumPins [HIHAT_PEDAL]))) {noteOn (0x90, drumNotes [HIHAT_PEDAL], drumVelocities [HIHAT_PEDAL]); кечигүү (1); noteOn (0x90, drumNotes [HIHAT_PEDAL], 0); } else {noteOn (0x90, drumNotes [HIHAT], drumVelocities [HIHAT]); кечигүү (1); noteOn (0x90, drumNotes [HIHAT], 0); }} else {// кадимки барабан MIDI берүү noteOn (0x90, drumNotes [drumIn], drumVelocities [drumIn]); кечигүү (1); noteOn (0x90, drumNotes [drumIn], 0); }}

void noteOn (int cmd, int pitch, int speed) {Serial.write (cmd); Serial.write (кадам); Serial.write (ылдамдык); }

setup () жана loop () функциялары чексиз аппараттын иштөө цикли менен:

жараксыз орнотуу () {

Serial.begin (115200);

for (uint8_t i = 0; i <10; i ++) {pinMode (i+2, INPUT); } #ifdef PRINT_PADS_PIN_NUMBERS while (true) {// Infinite debug loop for for (uint8_t i = 0; i <10; i ++) {if (! digitalRead (i+2)) {Serial.print ("Pin No: D"); Serial.print (i + '0'); // Санды ASCII символуна айландыруу}}} #else initValues (); / * Программалоо режими: Жүктөө учурунда эки педаль басылса - режим иштетилет */ if (! DigitalRead (drumPins [KICK]) &&! DigitalRead (drumPins [HIHAT_PEDAL])) enterProgrammingMode (); #endif}

void loop () {for (uint8_t i = 1; i <9; i = i + 1) {currentState = digitalRead (drumPins ); if (! currentState && previousState ) midiOut (i); // Штаттарды салыштырып, кулап бараткан четин табыңыз previousState = currentState ; } kick.update (); // Kick барабаны колдонуучунун ажыратуу алгоритмин колдонот if (kick.edge ()) if (kick.falling ()) midiOut (KICK); }

5 -кадам: B программалоо: Python & Колдонуучу интерфейси

Программалоо B: Python & Колдонуучу интерфейси
Программалоо B: Python & Колдонуучу интерфейси
Программалоо B: Python & Колдонуучу интерфейси
Программалоо B: Python & Колдонуучу интерфейси
Программалоо B: Python & Колдонуучу интерфейси
Программалоо B: Python & Колдонуучу интерфейси

Python Колдонуучу Интерфейси бир караганда түшүнүү үчүн бир аз татаал, ошондуктан биз анын негиздерин, кантип колдонууну, ар бир баскычтын кандай функциясы бар экенин жана Arduino түзмөгүн кантип туура программалоону түшүндүрүүгө аракет кылмакпыз.

Колдонуучу интерфейси - Колдонмо

UI - бул биздин барабан комплектинин графикалык көрүнүшү, аны колдонууну оңой жана Arduino түзмөгүн каалаган убакта программалоого ыңгайлуу кылат. UI бир нече графикалык модулдардан турат, алар сунушталган операцияга байланган. аларды бир -бирден карап чыгалы:

  1. Барабан топтомунун сүрөтү: Python UI кайсы барабандын түрү тандалганын аныктоо үчүн X-Y сүрөтүнүн координаттарын колдонот. Эгерде жарактуу барабан аймагы тандалган болсо, анда кошумча IO билдирүүсү, барабан аянтчасы үчүн эскертүү, ылдамдык жана Arduino терминалы менен көрсөтүлөт. Бул параметрлер колдонуучу тарабынан текшерилип, бекитилгенден кийин, бул баалуулуктар Arduino түзмөгүнө түз берилиши мүмкүн.
  2. Тышкы контролердун сүрөтү: MIDI барабан комплектин VST/Music түзүү чөйрөсү менен колдонуу үчүн Serial-To-MIDI котормочусун иштетүү керек. Мен чачсызды колдондум, ал бекер жеткиликтүү жана аны биздин интерфейсибизден түз эле иштетсе болот, анын сүрөтүн басуу менен.
  3. COM порт тизмеси: Arduino менен байланышуу үчүн анын тиркелген COM портун көрсөтүү керек. Тизме Refresh баскычын басуу менен жаңыртылып жатат.
  4. Конфигурацияны жүктөө/сактоо: коддо аныкталган демейки MIDI баалуулуктары бар, аларды UI менен өз ара аракеттенүү аркылуу колдонуучу өзгөртө алат. Конфигурация config.txt файлында белгилүү бир форматта аныкталат, аны колдонуучу сактап же жүктөй алат.
  5. Программалык түзмөктүн баскычы: Бардык өзгөртүлгөн MIDI баалуулуктарын Arduino EEPROMдо сактоо үчүн, андан кийин эки бут педалын (Kick барабаны жана Hi-hat педалын) басуу керек, маалыматтын берилишин күтөбүз. Эгерде кандайдыр бир байланыш көйгөйлөрү болсо, туура калкыма терезе көрсөтүлөт. Эгерде өткөрүп берүү ийгиликтүү болсо, UI өзүнүн ийгиликтүү билдирүүсүн көрсөтөт.
  6. Чыгуу баскычы: Колдонуучунун уруксаты менен, колдонмодон чыгуу.

Python кодунун урунттуу учурлары

Кодексте көп нерселер болуп жатат, ошондуктан биз бүтүндөй кодду эмес, жазуу функцияларын кеңейтебиз.

Биринчиден, UIди колдонуу үчүн, коддун иштеши үчүн бир нече модулду жүктөп алуу керек:

импорт osimport threading импорт tkinter tkterден tkter импорттук билдирүү кутусу * tkinter импортунан * PIL импортунан ImageTk, Image import numpy катары np импорттук сериялык импорттук глобус

Кээ бир модулдар демейки Python пакетине киргизилген. Бир нече модулдар PIP куралы аркылуу орнотулушу керек:

жаздыкты орнотуу

пип орнотуу numpy пип ScreenInfo орнотуу

PyCharm аркылуу тиркемени иштетүү сунушталат. Келечектеги релиздерде мен долбоордун аткарылуучу файлын экспорттоону пландап жатам.

Коддун кыскача түшүндүрмөсү

Эгерде биз анын линияларын функцияларга жана класстарга карай турган болсок, анда кодду түшүнүү оңой болот:

1. Негизги функция - бул жерде код башталат

if _name_ == '_main_': drumkit_gui ()

2. Drum Kit константалары, координаттары жана демейки MIDI маалыматы

класс барабандары: DRUM_TYPES = ["Теп", "Хихат", "Тузак", "Кырсык 1", "Кырсык 2", "Том Жогорку", "Том Мид", "Том Лоу", "Райд", "Хихат Педалы "," Controller "]

COORDINATES_X = [323, 117, 205, 173, 565, 271, 386, 488, 487, 135, 79]

COORDINATES_Y = [268, 115, 192, 40, 29, 107, 104, 190, 71, 408, 208] DIMS_WIDTH = [60, 145, 130, 120, 120, 70, 70, 130, 120, 70, 145] DIMS_LENGTH = [60, 60, 80, 35, 35, 40, 40, 70, 35, 100, 50]

DRUM_ENUM = ["Теп", "Тузак", "Хихат", "Жүрүү", "Кырсык 1", "Кырсык 2", "Том Жогорку", "Том Мид", "Том Лоу", "Хихат Педал"]

DRUM_NOTES = [36, 40, 42, 51, 49, 55, 47, 45, 43, 48] DRUM_VELOCITIES = [110, 100, 100, 110, 110, 110, 110, 110, 110, 110] DRUM_PINS = [8, 6, 4, 3, 11, 9, 5, 10, 2, 7]

3. UI функциялары - колдонуучу интерфейси жана графикалык объекттер менен иштөө

def set_active (ui)

def Second_ui (drum_type)

класс SelectionUi (tk. Frame)

класс колдонмо (tk. Frame)

def drumkit_gui ()

def event_ui_clicked (окуя)

def getorigin (өзүн, окуя)

4. Сериялык байланыш

def get_serial_ports ()

def communication_with_arduino (порт)

5. Файлдар менен иштөө: txt файлынан орнотууларды сактоо/жүктөө

def save_config ()

def load_config ()

6. Python Threading мүмкүнчүлүктөрүн колдонуу менен коддон тышкы hairless.exe тиркемесин иштетүү

класс ExternalExecutableThread (threading. Thread)

def run_hairless_executable ()

Кодду иштетүү үчүн, долбоордун папкасына тиркелиши керек болгон файлдардын тизмеси бар:

  • config.txt: Орнотуулар файлы
  • hairlessess.exe: Чачсыз MIDI алмаштыргыч
  • drumkit.png: Биздин UIдеги бардык басылуучу барабан аянтчаларын аныктоочу сүрөт (Бул кадамдын сүрөттөр топтомунан жүктөп алуу керек)
  • drumgui.py: Долбоордун коду

Мунун баары иштеши үчүн баса белгилешибиз керек. Долбоорго файлдарды кошуу абдан маанилүү: барабан сүрөтү, hairless.exe аткарылуучу жана config.txt файлынын орнотуулары.

Жана.. Мына биз кылдык!:)

Бул көрсөтмө пайдалуу болот деп үмүттөнөбүз.

Окуганыңыз үчүн рахмат!:)

Сунушталууда: