Introducere

Sequencer-ul este, principial, un instrument capabil de a automatiza redarea notelor muzicale. În ziua de astăzi, majoritatea programelor mai mult sau mai puțin profesionale de muzică (Fruityloops, Cakewalk SONAR) conțin un sequencer, de obicei bazat pe standardul MIDI. Practic, orice player MIDI poate fi considerat un sequencer, având rolul de a transforma informația legată de note (frecvență, durată, vibrato etc.) în semnal audio, folosind un sintetizator.

Sequencer-ul tradițional, numit și „step sequencer”, are un mod de funcționare mult simplificat: este dată o măsură de un anumit număr de note (de obicei 4, 8, 16 sau 32) ale căror frecvențe se pot modifica în orice moment. Măsura este de obicei redată într-o buclă, repetitiv. Primele melodii compuse folosind un 8-step sequencer au apărut la începutul anilor '70. Un exemplu de step sequencer analogic se poate găsi aici.

Proiectul își propune să implementeze un astfel de sequencer folosind un AVR. Se merge pe ideea folosirii unor componente cât mai simple (butoane, un buzzer, LED-uri). Accentul nu este pus pe obținerea unui sunet de calitate înaltă, ci mai degrabă ceva apropiat de sunetul unui PC Speaker sau a unei console Nintendo, astfel că se vor folosi cele mai rudimentare metode de sintetizare a sunetului.

Descriere generală

Schema conceptuală a proiectului este următoarea:

Schema bloc 8-step sequencer

Astfel, sequencer-ul are două stări, controlate de un buton. Prima stare este play, în care sunt redate notele, iar cea de-a doua stare este pause.

Deoarece schimbarea frecvenței notelor în timpul în care melodia rulează nu ar avea sens (melodia se poate derula la o viteză prea mare, de exemplu), butoanele de pitch control și BPM control pot fi comasate într-o singură pereche, care se comportă astfel: în timpul stării play, cele două butoane schimbă viteza melodiei (măsurată în Beats Per Minute), iar în timpul stării pause, aceleași două butoane sunt folosite pentru schimbarea frecvenței notei curente. Pentru selecționarea notei curente, se introduce butonul next note, a cărei apăsare are ca efect săritul la nota următoare.

Pentru a vedea care este nota curentă, utilizatorul are la dispoziție un display format din 8 LED-uri.

Funcționalitățile lipsă din această configurație sunt: afișarea frecvenței notei curente la un moment dat și afișarea BPM-ului melodiei.

Ca o observație, dezvoltările software și hardware a proiectului au mers în paralel și s-au orientat pe implementarea blocurilor sus-menționate în hardware, cu adăugarea treptată a funcționalităților software.

Hardware Design

Dezvoltarea conceptuală (scheme, calcule)

Primul „modul” dezvoltat a fost afișajul notelor, format din LED-uri. Acesta a fost conectat la portul A al microcontroller-ului și are următoarea schemă:

Port A - LED Display

Apoi, s-au conectat pe portul D un buzzer și patru butoane, având funcționalitatea specificată mai sus. Pentru a se evita lucrul cu pinii bootloader-ului USB și pentru menținerea unei eventuale funcționalități (programată în software) a LED-ului de pe placa de bază - conectat la PD7 - s-a ales următoarea configurație:

Port D - Buzzer and Control buttons

În cele din urmă, a fost construit un etaj de adaptare a semnalului la un nivel aproximativ egal cu cel al unui microfon, pentru a putea fi conectat la intrarea de microfon a plăcii de sunet. Modulul se conectează la portul B și arată astfel:

Port B - Microphone signal output

C_1 și R_1, puse la intrare, formează un filtru trece sus. Vor fi filtrate frecvențele mai mici de f_c, dată de relația:

f_c = \frac{1}{2\pi R_1C_1} = \frac{1}{2\pi\tau}

\tau = 15 \cdot 10^3 \cdot 100 \cdot 10^{-9} s = 1.5 ms, deci:

f_c = \frac{1}{2\pi 1.5ms} = \frac{1000}{2\pi 1.5} Hz \simeq 106.15 Hz

Luăm în considerare relația de calcul a tensiunii pentru AO inversor: V_o = -V_i \frac{R_f}{R_i}, unde R_f este rezistența pusă pe bucla de reacție și R_i reprezintă rezistența de la intrarea amplificatorului operațional.

În acest caz, semnalul este trecut prin două amplificatoare inversoare. Tensiunea este atenuată de rezistența de la intrarea celui de-al doilea amplificator operațional, rezultând:

V_o = V_i \cdot \frac{R_2}{R_1} \cdot \frac{R_4}{R_3} = \frac{V_i}{21}

Pentru o tensiune de intrare de 5V, vom avea: V_o \simeq 238.09 mV

Piese folosite: majoritatea pieselor folosite sunt uzuale (rezistențe, LED-uri). Buzzer-ul a fost ales mai mult pentru a face zgomot, mai puțin pentru calitatea sunetului. Amplificatorul operațional folosit este TL074CN, schema fiind concepută după ideea proiectului GuitarEffects de anul trecut.

Software Design

Programul este format din două fire de execuție: codul executat în main() și codul care tratează întreruperile cauzate de Timer 1, deci ISR(TIMER1_COMPA_vect).

În continuare vom ilustra în pseudocod comportamentul funcției main():

int main(void)
{
  inițializare_porturi(); /* init_ports() */
  setare_melodie_default(); /* default_notes() */
  setare_stare_inițială(); /* initial_state() */
 
  activare_întreruperi_și_timer(); /* sei(); init_timer1(); */
 
  do
  {
    setează_frecvență_timer();
    verifică_release_butoane();
    if (buton_released())
      schimbă_stare_sequencer();
  } forever;
}

init_ports() inițializează cele trei porturi folosite: setarea intrărilor/ieșirilor, setarea rezistențelor pull-up. default_notes() setează o secvență implicită de note, iar initial_state() pune sequencer-ul într-o stare inițială (prin setarea variabilelor de stare: paused, upp, downp, single_note etc.). Se activează Timer 1 pe prescaler-ul 64, în mod TCT (se va scoate undă dreptunghiulară la ieșire).

Setarea frecvenței se face în felul următor: se pune în registrul OC1A (registrul de comparare) pitch-ul asociat index-ului din notes[currnote], unde currnote este nota curentă. Frecvențele notelor din notația standard se rețin în vectorul pitchv.

Verificarea unei apăsări anterioare a butonului este făcută prin variabilele upp, downp, pausep și nextp. Astfel, dacă butonul este acum liber și a fost anterior apăsat, se face schimbarea stării sequencer-ului în felul următor:

  • pentru up, respectiv down: Dacă starea este paused, se incrementează/decrementează notes[nota_curentă], care reține un indice în pitchv. Altfel, se verifică viteza maximă/minimă și este crescută/scăzută.
  • pentru pause: Se schimbă starea din paused în playing sau invers.
  • pentru next: se incrementează modulo 8 notele, se setează portul A la valoarea următoarei note și se activează single_note.

Timer 1 are drept rol principal acela de a reda o notă de o frecvență oarecare, astfel că intervalul întreruperilor este dat de frecvența notei. De aceea, se reține informație adițională despre pasul curent în variabila step, care la sfârșitul fiecărui pas/începutul unui nou pas este reinițializată după relația:

step = STEPSIZE/pitchv[notes[currnote]]*bpm;

unde STEPSIZE este un pas maxim generic, care se împarte la frecvența/numărul de impulsuri ale timer-ului și se înmulțește cu variabila bpm. Observație: bpm măsoară o valoare generică și nu valoarea propriu-zisă a bătăilor pe minut. Mai mult, ea se incrementează/decrementează invers față de BPM-ul normal (a se vedea definița macro-urilor BPM_UP și BPM_DOWN).

Relația a fost aleasă empiric, bazat pe faptul că numărul de impulsuri ale undei dreptunghiulare date de timer scade cu creșterea frecvenței notei, astfel că step-ul trebuie menținut aproximativ constant față de BPM pe măsură ce se dau întreruperile. Astfel:

 step * nr_impulsuri_timer1 = step * pitchv[notes[currnote]] = STEPSIZE * bpm;

care este constant, pentru un BPM fix.

Funcția de tratare a întreruperii, ISR(TIMER1_COMPA_vect) are următorul comportament:

ISR(TIMER1_COMPA_vect)
{
  verifică_butoane_apăsate(); /* setează upp, downp etc. */
  if (!paused || single_note)
  {
    inversează_forme_de_undă();
    step--;
    if (step == 0) /* sfârșitul pasului */
    {
      if (!paused) 
        pasul_următor();
      if (single_note) 
        redă_notă(SINGLE_BPM); /* updatează pasul curent după un BPM fix */
      else redă_notă(bpm); /* updatează pasul după BPM-ul setat anterior */
    }
  }
  else închide_sunetul(); /* resetează pinii */
}

Alte observații: Implementarea s-a făcut sub GNU/Linux, folosind vim pentru editare, GNU Make pentru Makefile și avr-gcc pentru compilare. Pentru încărcarea firmware-ului în procesor s-a folosit softul de încărcare din cadrul laboratorului, avrusbboot.

Rezultate Obţinute

Placa finală, împreună cu toate modulele periferice atașate:

Concluzii

Rezultatul final este, după cum se poate observa mai sus, un sequencer capabil să redea practic orice măsură de 8/8 constând într-o combinație de note din cele trei octave oferite în software.

Inițial, proiectul a fost gândit să fie extins pentru capabilități de sequencing mai avansat: redare de alte forme de undă (triunghi, sinusoidă), citirea unor note (eventual în format MIDI) de pe un card și redarea lor, dar atât s-a putut face în timpul alocat.

Cel mai mult timp a luat detectarea diverselor bug-uri, de obicei la nivel hardware (lipituri reci, scurturi etc.), motiv pentru care, în anumite cazuri, circuitele au fost refăcute de la zero. De asemenea, un neajuns este calitatea sunetului scos prin mufa jack, deteriorată în mare parte de zgomot. Această problemă ar fi putut evitată prin folosirea unor metode mai bune de izolare a semnalului.

Download

Bibliografie/Resurse

pm/prj2010/dtudose/8-step-sequencer.txt · Last modified: 2021/04/14 17:07 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0