Talaan ng mga Nilalaman:

MIDI Drum Kit sa Python at Arduino: 5 Hakbang (na may Mga Larawan)
MIDI Drum Kit sa Python at Arduino: 5 Hakbang (na may Mga Larawan)

Video: MIDI Drum Kit sa Python at Arduino: 5 Hakbang (na may Mga Larawan)

Video: MIDI Drum Kit sa Python at Arduino: 5 Hakbang (na may Mga Larawan)
Video: Web Programming - Computer Science for Business Leaders 2016 2024, Nobyembre
Anonim
Image
Image
MIDI Drum Kit sa Python at Arduino
MIDI Drum Kit sa Python at Arduino
MIDI Drum Kit sa Python at Arduino
MIDI Drum Kit sa Python at Arduino

Palagi kong nais na bumili ng isang drum kit mula noong bata ako. Noon, lahat ng mga kagamitang pangmusika ay walang lahat ng mga digital application dahil mayroon kaming maraming ngayon, kaya't ang mga presyo kasama ang mga inaasahan ay masyadong mataas. Kamakailan lamang nagpasya akong bumili ng isang murang drum kit mula sa eBay, na may lamang priyoridad: Kakayahang i-down ito at ilakip ang aking sariling hardware at software sa aparato.

Ang pagbili ay hindi nakakadismaya sa lahat: Portable roll-up drum kit na may 9 na magkakaibang mga pad ng tunog, dalawang paa na switch pedal para sa kick drum at hi-hat at micro-USB power socket. Kung ano talaga ang nagpo-demotivate, ito ang mga tunog ng output (Ang tunay na paggamit para sa kit na ito ay upang ikonekta ang panlabas na speaker at tangkilikin ito). Kaya't, nagpasya akong i-convert ito sa aking sariling mai-program sa pamamagitan ng USB, MIDI drum kit batay sa Arduino at User Interface batay sa Python, para sa madaling gamiting paggamit at madaling pagbabago tulad ng, dami, tala at mga pagpipilian ng channel.

Mga tampok ng aparato:

  • Mababa ang presyo
  • Lumilikha ng drum kit mula sa anumang mga digital na input - kahit na ang hanay ng mga push button
  • Suporta sa komunikasyon at power supply sa pamamagitan lamang ng USB interface - Pagsasama ng USB sa UART converter at Arduino device
  • Mga bahagi ng mininum para sa tamang operasyon
  • Madaling gamitin na UI na nakabatay sa Python
  • Kumpletuhin ang suporta sa MIDI na may naaayos na bilis, tala at mga pin ng Arduino
  • I-save at I-load ang mga pasadyang pagsasaayos ng drum na nakaimbak sa memorya ng aparato

Magpatuloy tayo sa proyekto…

Hakbang 1: Teorya ng Pagpapatakbo

Teorya ng Pagpapatakbo
Teorya ng Pagpapatakbo
Teorya ng Pagpapatakbo
Teorya ng Pagpapatakbo
Teorya ng Pagpapatakbo
Teorya ng Pagpapatakbo

I-block ang Diagram

Una sa lahat, mag-focus tayo sa istraktura ng proyekto, at hatiin ito sa magkakahiwalay na mga bloke:

Roll-Up Drum Kit

Ang pangunahing yunit ng proyekto. Binubuo ito ng 9 magkakahiwalay na drum pad, kung saan ang bawat pad ay isang hanay ng mga pindutan na binabago ang kanilang estado ng lohika habang na-hit. Dahil sa istraktura nito, may posibilidad na buuin ang partikular na drum kit na ito mula sa anumang mga pindutan ng itulak. Ang bawat drum pad ay konektado sa pull-up risistor sa pangunahing electronic board, kaya habang ang drum pad ay paulit-ulit na na-hit, isang tukoy na switch ay nakatali sa ground ng circuit at ang lohikal na LOW ay naroroon sa drum pad line. Kapag walang inilapat na presyon, bukas ang switch ng drum pad at dahil sa pull-up na risistor sa linya ng kuryente, naroroon ang lohikal na TAAS sa linya ng drum pad. Dahil ang layunin ng proyekto ay upang lumikha ng isang kumpletong aparato ng digital MIDI, ang lahat ng mga bahagi ng analog sa pangunahing PCB ay maaaring napabayaan. Mahalagang pansinin, na ang drum kit ay may dalawang pedal para sa kick drum at hi-hat, na nakatali din sa mga pull-up resistors at ibahagi ang parehong lohika sa pagpapatakbo tulad ng lahat ng mga drum pad (Tatalakayin natin ito nang kaunti mamaya).

Arduino Pro-Micro

Ang utak ng drum kit. Ang layunin nito ay upang tuklasin kung mayroong isang senyas na lumalabas sa isang drum pad at magbigay ng naaangkop na output ng MIDI sa lahat ng kinakailangang mga parameter: Tandaan, tulin at tagal ng signal. Dahil sa digital na likas na katangian ng mga drum pad, maaari silang maiikot sa arduino digital input (10 kabuuan ng mga pin). Upang maiimbak ang lahat ng nais na setting at impormasyon ng MIDI, gagamitin namin ang memorya nito - EEPROM, samakatuwid sa tuwing pinapagana namin ang aparato, ang impormasyon ng MIDI ay nai-load mula sa EEPROM, ginagawa itong mai-program at mai-configure muli. Gayundin, ang Arduino Pro-Micro ay magagamit sa isang napakaliit na package at maaaring mailaan nang madali sa drum kit na panloob na kaso.

FTDI USB To Serial Converter

Upang mai-program at tukuyin ang mga tampok ng aming aparato sa tulong ng aplikasyon ng PC, kailangang i-convert ang USB interface sa serial, dahil ang Arduino Pro-Micro ay walang USB. Dahil ang komunikasyon sa pagitan ng mga aparato ay batay sa UART, ang aparato ng FTDI ay ginagamit sa proyektong ito, dahil sa pagiging simple ng paggamit nito anuman ang mga karagdagang katangian.

Application ng PC - Python

Pagdating sa pag-unlad ng mga interface ng gumagamit at mga proyekto na mabilis na mabuo, ang Python ay isang napakahusay na solusyon. Ang layunin ng aplikasyon ng UI ay upang gawing mas maginhawa upang muling tukuyin ang mga pag-aari ng MIDI para sa aming drum kit, mag-imbak ng impormasyon, aparato ng programa at gumawa ng komunikasyon sa pagitan ng mga system nang hindi kinakailangan ng pag-iipon ng code nang paulit-ulit. Dahil gumagamit kami ng serial interface upang makipag-usap sa drum kit, maraming mga walang bayad na mga module sa buong internet, na sumusuporta sa anumang uri ng serial na komunikasyon. Bilang karagdagan, dahil tatalakayin ito sa paglaon, ang interface ng UART ay binubuo ng kabuuang tatlong mga pin: RXD, TXD at DTR. Ginagamit ang DTR upang maisagawa ang pag-reset sa Arduino module, kaya't kapag interesado kaming patakbuhin ang MIDI app o ikonekta ang UI sa program ng aparato, ganap na hindi na kailangang muling ikabit ang USB cable o kung anupaman.

Hakbang 2: Mga Bahagi at Instrumento

Mga Bahagi

  • Roll-Up Drum Kit
  • 2 x Sustain Pedals (Karaniwan, kasama sa DK package).
  • FTDI - USB To Serial Converter
  • Arduino Pro Micro
  • Micro-USB Cable

Mga Instrumento

  • Paghihinang ng Bakal / Istasyon
  • Soldering Tin
  • Manipis na Diameter Single Core wire
  • Mga Tweezer
  • Pamutol
  • Plier
  • Kutsilyo
  • Screw Driver
  • 3D Printer (Opsyonal - para sa na-customize na platform ng pedal)

Software

  • Arduino IDE
  • Python 3 o Mas Mataas
  • JetBrains Pycharm
  • Walang buhok na interface ng MIDI
  • loopMIDI

Hakbang 3: Paghihinang at pagpupulong

Paghihinang at Assembly
Paghihinang at Assembly
Paghihinang at Assembly
Paghihinang at Assembly
Paghihinang at Assembly
Paghihinang at Assembly

Dahil may tatlong mga module na kailangang pagsamahin, ang proseso ng paghihinang at pagtitipon ay maikli at simple:

  • Maglakip ng sama-sama ng Arduino Pro-Micro na may FTDI aparato, tiyaking sumusunod ang mga koneksyon sa I / O na tinukoy sa bawat aparato:

    • VBUS-VBUS
    • GND-GND
    • DTR-DTR
    • RXD-TXD
    • TXD-RXD
  • Alisin ang lahat ng mga tornilyo mula sa enclosure ng drum plastic, tiyaking maaari kang tumuon sa pad-to-board cable, at mga pull-up resistor nito
  • Mga solder manipis na wires para sa Arduino-FTDI module na naitayo namin dati:

    • Mga digital na input: D [2:11]
    • VBUS
    • D +
    • D-
    • GND
  • Ipasok ang module sa loob ng case ng baterya upang ang mga wire ay lumulutang sa parehong bahagi ng mga pull-up resistors ng pad
  • Maghinang ng lahat ng mga digital na input sa mga terminal ng drum pad tulad ng ipinakita sa huling pigura.
  • Ang solder micro-USB bus (VBUS, D +, D-, GND) sa FTDI aparato, tiyakin na walang mga pagkakamali sa pagsunod sa mga wire na ito.
  • Ikabit ang Arduino-FTDI module na may hot-glue sa case ng baterya
  • I-assemble ang aparato na may naaangkop na attachment ng mga turnilyo

Nagawa na namin ito, tipunin ang aparato. Ipagpatuloy natin ang code …

Hakbang 4: Programming A: Arduino

Programming A: Arduino
Programming A: Arduino

Hinahayaan nating Ilarawan ang aming sketch nang sunud-sunod:

Una sa lahat, kailangang magsama ng dalawang kinakailangang aklatan para sa wastong operasyon. Ang EEPROM ay paunang naka-install sa Arduino IDE, ngunit ang module ng debouncer para sa kick drum ay kailangang mai-install nang magkahiwalay

# isama ang # isama

Ang mga switch na ito ay ginagamit pangunahin sa mga pagkakasunud-sunod ng pag-debug. Kung nais mong subukan ang koneksyon ng mga terminal ng Arduino sa mga drum pad, at matukoy ang lahat ng mga digital na input, dapat tukuyin ang mga switch na ito

/ * Mga switch ng Developer: Uncomment na ninanais na mode para sa pag-debug o pagsisimula * /// # tukuyin ang LOAD_DEFAULT_VALUES // Mag-load ng mga pare-parehong halaga sa halip na EEPROM // # tukuyin ang PRINT_PADS_PIN_NUMBERS // I-print ang numero ng pin na konektado sa isang pad na na-hit sa pamamagitan ng serial port

Ang mga pare-pareho na patlang ay kumakatawan sa lahat ng mga default na halaga, kabilang ang pagpapabilang ng drum pad. Upang mapatakbo ang aparato sa kauna-unahang pagkakataon, kailangang malaman ang eksaktong koneksyon ng Hi-Hat at Kick pedals

/ * Enumerasyon ng uri ng drum * /

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

/ * Mga default na halaga * /

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};

/ * Ang tagal ng pagbolbol ng drum drum * /

const uint8_t KICK_DB_DURATION = 30;

Ginagamit ang EEPROM upang iimbak / i-load ang lahat ng data na nagmumula sa aplikasyon ng PC. Ang mga address span na inilarawan sa itaas, ay nagpapakita ng eksaktong lokasyon para sa impormasyon ng MIDI para sa bawat drum pad

/ * Pagma-map ang mga EEPROM Address

Mga Tala: | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 |

Mga Pin: | 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13 | Mga bilis | 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;

Ginagamit ang mga pandaigdigang variable upang matukoy ang estado ng bawat pad, at magsagawa ng komunikasyon na MIDI nang naaayon

/ * Mga Global Variable * /

uint8_t drumNotes [10], drumVelocities [10], drumPins [10]; // Mga Variable ng MIDI

uint8_t uartBuffer [64]; // UART Buffer para sa pagkolekta at pag-iimbak ng sipa ng MIDI Data Debouncer (DRUM_PINS [KICK], KICK_DB_DURATION); // object ng Debouncer para sa kick drum pabagu-bago ng loob bool nakaraangState [9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Drum pad nakaraang lohika ay nagsasaad ng pabagu-bago ng kasalukuyang boolState [9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // Drum pad kasalukuyang mga estado ng lohika

Mga Pag-andar ng EEPROM

/ * Mga setting ng tindahan sa EEPROM * /

void storeEEPROM () {

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

/ * I-load ang mga setting mula sa EEPROM * /

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

Ang paunang pagpapasimula ng mga variable at programming mode, sa kaso ng mga pedal at Arduino boot ay sabay na naisasaaktibo

walang bisa enterProgrammingMode () {

bool confirmBreak = false; uint8_t lineCnt = 0; uint8_t charCnt = 0; char readChar = 0; habang (! confirmBreak) {kung (Serial.available ()) {uartBuffer [charCnt] = Serial.read (); kung (charCnt> = 29) kumpirmahingBreak = totoo; iba pa charCnt ++; }} Serial.println ("OK"); storeEEPROM (); }

walang bisa initValues () {

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

Mga handler ng MIDI Communication na may pagkaantala ng 1ms na oras ng paghawak ng tala

/ * I-play ang pagpapaandar ng tala ng MIDI * /

void midiOut (enum DRUM_POSITION drumIn) {

kung (drumIn == HIHAT) {// Kung ang HI-HAT ay na-hit, kailangang magsagawa ng tseke kung pinindot ang pedal kung (! digitalRead (drumPins [HIHAT_PEDAL])) {noteOn (0x90, drumNotes [HIHAT_PEDAL], drumVelocities [HIHAT_PEDAL]); antala (1); noteOn (0x90, drumNotes [HIHAT_PEDAL], 0); } iba pa {noteOn (0x90, drumNotes [HIHAT], drumVelocities [HIHAT]); antala (1); noteOn (0x90, drumNotes [HIHAT], 0); }} iba pa {// Regular drum MIDI transmission noteOn (0x90, drumNotes [drumIn], drumVelocities [drumIn]); antala (1); noteOn (0x90, drumNotes [drumIn], 0); }}

void noteOn (int cmd, int pitch, int velocity) {Serial.write (cmd); Serial.write (pitch); Serial.write (bilis); }

pag-andar ng setup () at loop () na may walang katapusang loop ng pagpapatakbo ng aparato:

walang bisa ang pag-setup () {

Serial.begin (115200);

para sa (uint8_t i = 0; i <10; i ++) {pinMode (i + 2, INPUT); } #ifdef PRINT_PADS_PIN_NUMBERS habang (totoo) {// Infinite debug loop para sa (uint8_t i = 0; i <10; i ++) {if (! digitalRead (i + 2)) {Serial.print ("Pin No: D"); Serial.print (i + '0'); // convert number to ASCII character}}} #else initValues (); / * Mode ng mode: Kung ang dalawang pedal ay pinindot habang nag-boot - ang mode ay naaktibo * / kung (! DigitalRead (drumPins [KICK]) &&! DigitalRead (drumPins [HIHAT_PEDAL])) enterProgrammingMode (); #tapusin kung }

void loop () {para sa (uint8_t i = 1; i <9; i = i + 1) {currentState = digitalRead (drumPins ); kung (! currentState && nakaraangState ) midiOut (i); // Paghambingin ang mga estado at tuklasin ang pagbagsak ng nakaraangState = kasalukuyangState ; } kick.update (); // Kick drum ay gumagamit ng pasadyang debounce algorithm kung (kick.edge ()) kung (kick.falling ()) midiOut (KICK); }

Hakbang 5: Programming B: Python at User Interface

Programming B: Python at User Interface
Programming B: Python at User Interface
Programming B: Python at User Interface
Programming B: Python at User Interface
Programming B: Python at User Interface
Programming B: Python at User Interface

Ang Python User Interface ay medyo kumplikado upang maunawaan sa unang tingin, kaya susubukan naming ipaliwanag ang mga pangunahing kaalaman nito, kung paano gamitin, anong pagpapaandar ang mayroon ang bawat pindutan at kung paano i-program nang maayos ang aparato ng Arduino.

User Interface - Application

Ang UI ay isang graphic na representasyon para sa aming programmer ng drum kit, ginagawa itong talagang madaling gamitin at maginhawa upang mai-program ang Arduino device anumang oras. Ang UI ay binubuo ng maraming mga grapikong modyul na nakatali sa kanilang iminungkahing pagpapatakbo. isa-isa nating repasuhin ang mga ito:

  1. Larawan ng Drum Set: Ang Python UI ay gumagamit ng mga coordinate ng imahe ng X-Y upang matukoy kung aling uri ng drum ang napili. Kung napili ang wastong rehiyon ng tambol, lalabas ang pangalawang mensahe ng IO, na may mga patlang ng tala, tulin at Arduino terminal para sa nakatuon na drum pad. Matapos ang mga parameter na ito ay napatunayan ng gumagamit at naaprubahan, ang mga halagang ito ay maaaring maipadala nang direkta sa Arduino aparato.
  2. Panlabas na Imahe ng Controller: Upang magamit ang MIDI drum kit na may kapaligiran sa paglikha ng VST / Musika, kailangang patakbuhin ang interpreter ng Serial-To-MIDI. Gumamit ako ng Hairless, na magagamit nang libre at maaaring patakbuhin nang direkta mula sa aming UI, sa pamamagitan lamang ng pagpindot sa imahe nito.
  3. Listahan ng COM Port: Upang makipag-usap sa Arduino, kailangang tukuyin ang nakalakip na COM port. Ang listahan ay nai-refresh sa pamamagitan ng pagpindot sa pindutan ng I-refresh.
  4. Pag-configure ng Pag-load / I-save: Mayroong mga default na halagang MIDI na tinukoy sa code, na maaaring mabago ng gumagamit sa pamamagitan ng pakikipag-ugnay sa UI. Ang pag-configure ay tinukoy sa config.txt file sa isang tukoy na format, na maaaring mai-save o mai-load ng gumagamit.
  5. Button ng Device Device: Upang maiimbak ang lahat ng nabagong mga halaga ng MIDI sa Arduino EEPROM, kailangang pindutin ang dalawang mga pedal ng paa (Kick drum at Hi-hat pedal) pagkatapos nito, maghintay para makumpleto ang paghahatid ng data. Kung mayroong anumang mga isyu sa komunikasyon, ipapakita ang tamang pop-up. Kung magtagumpay ang paghahatid, ipapakita ng UI ang matagumpay na mensahe.
  6. Lumabas na Button: Lumabas lamang sa application, na may pahintulot ng gumagamit.

Mga Highlight ng Python Code

Maraming mga bagay na nangyayari sa code, kaya magpapalawak kami sa mga nakasulat na pag-andar kaysa sa buong code.

Una sa lahat, upang magamit ang UI, kailangang mag-download ng maraming mga module, upang gumana ang code:

i-import ang osimport threading import tkinter bilang tk mula sa tkinter import messagebox mula sa tkinter import * mula sa PIL import ImageTk, I-import ang imahe bilang isang pag-import ng serial import glob

Ang ilan sa mga module ay kasama sa default na package ng Python. Maraming mga module ang dapat na mai-install sa pamamagitan ng tool ng PIP:

pip install Pillow

pip install numpy pip install ScreenInfo

Mahigpit na inirerekumenda na magpatakbo ng application sa pamamagitan ng PyCharm. Sa hinaharap na paglabas, nagpaplano akong mag-export ng maipapatupad para sa proyekto.

Maikling Pagpapaliwanag ng Code

Mas madaling maintindihan ang code kung titingnan natin ang mga linya nito mula sa pananaw ng mga pagpapaandar at klase:

1. Ang pangunahing pagpapaandar - dito nagsisimula ang code

kung _name_ == '_main_': drumkit_gui ()

2. Ang mga Drum Kit ay nagpapatuloy, nagsasaayos at default na impormasyon ng MIDI

class Drums: DRUM_TYPES = ["Sipa", "Hihat", "Snare", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Ride", "Hihat Pedal "," Controller "]

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

COORDINATE_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 = ["Sipa", "Snare", "Hihat", "Ride", "Crash 1", "Crash 2", "Tom High", "Tom Mid", "Tom Low", "Hihat Pedal"]

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. Mga Pag-andar ng UI - Paghawak ng interface ng gumagamit at mga graphic na bagay

def set_active (ui)

def pangalawa_ui (drum_type)

klase SelectionUi (tk. Frame)

Application ng klase (tk. Frame)

def drumkit_gui ()

def event_ui_clicked (kaganapan)

def getorigin (sarili, kaganapan)

4. Serial na komunikasyon

def get_serial_ports ()

def communic_with_arduino (port)

5. Paggawa gamit ang mga file: Mga setting ng Store / Load mula sa txt file

def save_config ()

def load_config ()

6. Pagpapatakbo ng panlabas na application hairless.exe mula sa code gamit ang mga kakayahan sa Python Threading

klase ng ExternalExecutableThread (threading. Thread)

def run_hairless_executable ()

Upang patakbuhin ang code, mayroong isang listahan ng mga file na kailangang ikabit sa folder ng proyekto:

  • config.txt: File ng mga setting
  • hairless.exe: Walang buhok na converter ng MIDI
  • drumkit.png: Larawan na tumutukoy sa lahat ng mga na-click na drum pad sa aming UI (Dapat na ma-download mula sa hakbang na ito ng mga imahe na itinakda)
  • drumgui.py: Ang code ng proyekto

Iyon ang lahat ng kailangan nating bigyang-diin upang maisagawa ito. Napakahalaga na magdagdag ng mga file sa proyekto: drum set image, hairless.exe executable at setting setting config.txt.

At.. Dito na namin nagawa!:)

Inaasahan mong makikita mong kapaki-pakinabang ang pagtuturo na ito.

Salamat sa pagbabasa!:)

Inirerekumendang: