Arduino ChipTune – Redemption Song



Můj první ChipTune. Sice ještě ne opravdový, proto že jsem si hudbu nesložil sám, ale prvním logickým krokem je napsání potřebného rozhraní pro její přehrání.

Dnes jsem dokončil první z několika fází vedoucích k finálnímu chiptune demu. Konkrétně šlo o přehrávání jednotlivých tónů a frekvencí. K opravdovému chiptunu, se kterým bych byl spokojený mi chybí dopsat ještě mixer (pro přehrávání několika zvuků najednou) a sequencer (pro přehrávání předem nahraných samplů). Naštěstí mám dost dobrou představu o tom, jak bude přehrávání vícekanálového zvuku a práce se samply vypadat.

Pro dnešek jsem si zvolil písničku Redemption Song od Boba Marleyho (nečekaně ;) . Jak tento první pokus dopadnul o tom už se přesvědčíte sami.

Více se dozvíte z videa:

A tady už je slíbený kód:

harvie@harvie-ntb:~/sketchbook/Sound$ cat Sound.pde
/*
* Bit-bang sound
* <~~Harvie 2oo8
*/
#define sndout 11
void sound(char sndpin, float freq, float duration) { //Play bit-bang sound
if(duration<=0) return; if(freq<=0) { delay(duration); return; }
freq=((1000000/2)/freq); //Convert freq to delay (us)
duration*=1000; //Convert duration to us
pinMode(sndpin, OUTPUT);
for(;duration>0;duration-=2*freq) {
digitalWrite(sndpin, HIGH); delayMicroseconds(freq);
digitalWrite(sndpin, LOW);  delayMicroseconds(freq);
}
pinMode(sndpin, INPUT); //Close pin to avoid noise (optional)
}
float get_tone(char tone, char octave) { //Return tone frequency or 0
if(octave+tone<=0) return 0;
float freq;
switch (tone) { //THX2MS GW-BASIC Reference 4freqs muhaha ;D
case 'c': case 'C': freq=130.81; break;
case 'd': case 'D': freq=146.83; break;
case 'e': case 'E': freq=164.81; break;
case 'f': case 'F': freq=174.61; break;
case 'g': case 'G': freq=196; break;
case 'a': case 'A': freq=220; break;
case 'b': case 'B': freq=246.94; break;
default: return 0;
}
return (freq*pow(2,octave-1));
}
void sound_test() { //Check da' sound ;) )
sound(sndout, 440, 500);
sound(sndout, get_tone('a', 1), 500);
return;
}
void play_melody(char sndpin, char *melody, char octave, int duration) {
for(char i=0;melody[i]!=0;i++) sound(sndpin, get_tone(melody[i], octave), duration);
return;
}
void stupnice_priklad() {
char oktava = 1;
int delka = 500;
char stupnice[]="cdefgab";
play_melody(sndout, stupnice, oktava, delka);
sound(sndout, get_tone(stupnice[0], oktava+1), delka); //C
}
void setup() { // run once, when the sketch starts
}
void loop() { // run over and over again
//delay(10000); return;//Silence! I'll kill you!
//--->Fun with sound (uncomment one):
//sound_test();
//stupnice_priklad();
//while(1) sound(sndout, rand()/20, 10);
//int i=0; while(1) sound(sndout, i++, 10);
//float i=0; while(1) sound(sndout, sin(i+=0.01)*600, 10);
//float i=0; while(1) { sound(sndout, sin(i+=0.01)*550, 10); sound(sndout, cos(i)*400, 10); }
int octave, duration;
//You can use lot of more old siemens/ericson monotone ringtones found on net, google or http://www.gsmarena.com/ringlist.php3?idMaker=3
char melody[]="CCDEECCFFAAGGE-CCDEGGEFEDDCC-CCDEECCFFAAGGE-CCDEGGEFEDDC"; octave=1; duration=300; //Bob Marley's Redemption song - intro ;D
//char melody[]="GGGGEEEEEEEEEE" "EEEEEEEEGGCCCC" "DEEEEEEEE----FFFF" "FFFFFFFEEEE" "EEEEDDDD" "EEDDDDGGGGEEEE" "EEEEEEEEEEEEEE" "GGCCCCDEEEEEEEE----" "FFFFFFFFFFF" "EEEEEEGG" "GGFFDDCCCC"; octave=2; duration=100; //Jingle Bells
//char melody[]="D-F-GG-D-F-GGG--D-F-GG-F-DD"; octave=2; duration=180; //Depp Purple - Smoke on the water //alternative: "cpdpfppcpepffppcpdpfppdpc"
//char melody[]="CC-E-F-AGG-E-C-AFFFGG--FFFGAPCCCC"; octave=3; duration=180; //The Simpsons theme
//char melody[]="CFGG-GG-GG-GC-------CFGGG-GG-GG-GD"; octave=2; duration=300; //X-Files theme
//char melody[]="CDCEE--EE-EE----EEDEGG--EDECC"; octave=2; duration=300; //Beatles - Hard days night
//char melody[]="HHHH--AAGGAAHHAAGGAAGG--GG-----HHHH--GG--AAHHGGAA--"; octave=2; duration=300; //Offspring - Why don't you get a job
//char melody[]="AA-AEEGGAA-ADDBBAA-AEEGGAA-ADDBB"; octave=2; duration=300; //Off Spring - Pretty Flyv
//char melody[]="AAgacfaAgadDdcAgacfaAgadDdc AgaaffGv"; octave=2; duration=200; //Eiffel 65 - Blue
//char melody[]="HHHHHHAAAAAAGGAAA-HHHHHH" "AAAAAAFFGGEE-HHHHHHAAAAAA" "AAAAAA-HHHHHHAAAAAAFFGGEE"; octave=2; duration=300; //Bob Dylan - Knockin On Heavens Door
//char melody[]="bBbBpFFpbepdpcpbBbBpFpBepdpcpbBbBp FpbepdpbepCC"; octave=1; duration=200; //Star Wars
//char melody[]="f p c p d p e p f p c p d p e p f p c p d p e p F p p g c e g e G p g c f g f GG"; octave=2; duration=100; //NBA
play_melody(sndout, melody, octave, duration);
delay(2000);
}

Na závěr něco málo k bit-bang technologii na wikipedii.
Pozn.: Pokud si někdo budete hrát a přepíšete pro tohle další songy, POSTněte mi je prosím tady do komentářů, ať to nemusím dělat znova.




Líbí se vám článek? Chcete se o něj podělit? Přidejte ho! (volba topclanky.cz nevyžaduje registraci)

5 Responses to “Arduino ChipTune – Redemption Song”

  1. Bill.jr Says:

    Tohle je docela zajímavý – hrát obdélníkama. Já jsem se pokoušel rozchodit si D/A R2R převodník abych moh hrát analogově. Bohužel jsem skončil u měření výstupů voltmetrem, kdy odporová síť skutečně fungovala. Bohužel teď není na hraní čas.

  2. Harvie Says:

    u arduina je D/A prevodnik zrejme otazka 9ti rezistoru, ktery pripojis na 8 pinu (Pak dost). Driv se pouzivalo neco podobnyho jako zvukovka do paralelniho portu – tzv. COVOX: http://www.sheppard.ru/articles/fe/covox/scheme2.gif

  3. Ondra Says:

    Trochu jsem ten kód poupravil:
    //Proměnné
    #define sndout 13 //Pin PIEZA

    int oct, dur; //Oktáva a rychlost
    //Písně
    //Písně se zapisují v zápisu: CC-Čtvrťové C, C-Osminové C, -pomlka
    char melody[]=”CCDEECCFFAAGGE-CCDEGGEFEDDCC-CCDEECCFFAAGGE-CCDEGGEFEDDC”;
    char melody2[]=”CCDDEEFFGGhh–GFEDC”; //Stupnice

    char ml_1[]=”EDDD-EDDDECDD”;
    char ml_2[]=”EDDC”;
    char ml_3[]=”CCCC”;
    char ml_4[]=”CCCDC”;

    //Konec písní
    //Konec proměnných

    void sound(char sndpin, float freq, float duration) { //Přehrát song
    if(duration<=0) return; if(freq0;duration-=2*freq) {
    digitalWrite(sndpin, HIGH); delayMicroseconds(freq);
    digitalWrite(sndpin, LOW); delayMicroseconds(freq);
    }
    pinMode(sndpin, INPUT); //’Zavřít’ pin
    }

    float get_tone(char tone, int octave) { //Vrátí frekvenci tónu nebo vrátí nulu
    if(octave+tone<=0) return 0;
    float freq;

    //switch (tone) { //Frekvence jsou nastaveny na dvoučárkovanou oktávu: http://interval.cz/podklady/1999-2008/bittnerova/866/tony.html
    //case 'c': case 'C': freq=523; break;
    //case 'd': case 'D': freq=587; break;
    //case 'e': case 'E': freq=659; break;
    //case 'f': case 'F': freq=698; break;
    //case 'g': case 'G': freq=784; break;
    //case 'a': case 'A': freq=880; break;
    //case 'h': case 'H': freq=1; break;
    //case 'b': case 'B': freq=988; break;
    //default: return 0;
    //}

    //switch (tone) { //Frekvence jsou nastaveny na jednočárkovanou oktávu: THX2MS GW-BASIC Reference
    //case 'c': case 'C': freq=130.81; break;
    //case 'd': case 'D': freq=146.83; break;
    //case 'e': case 'E': freq=164.81; break;
    //case 'f': case 'F': freq=174.61; break;
    //case 'g': case 'G': freq=196; break;
    //case 'a': case 'A': freq=220; break;
    //case 'b': case 'B': freq=246.94; break;
    //default: return 0;
    //}

    switch (tone) { //Frekvence jsou nastaveny na jednočárkovanou oktávu: http://interval.cz/podklady/1999-2008/bittnerova/866/tony.html
    case 'c': case 'C': freq=262; break;
    case 'd': case 'D': freq=294; break;
    case 'e': case 'E': freq=330; break;
    case 'f': case 'F': freq=349; break;
    case 'g': case 'G': freq=392; break;
    case 'a': case 'A': freq=440; break;
    case 'b': case 'B': freq=494; break;
    default: return 0;
    }

    return (freq*pow(2,octave-1));
    }

    void play_melody(char sndpin, char *melody) { //zahrávací metoda
    for(char i=0;melody[i]!=0;i++) sound(sndpin, get_tone(melody[i], oct), dur);
    return;
    }

    void setup() {
    Serial.begin(9600);
    }

    void loop() {
    //příklad více oktáv
    oct=2; dur=300; //nastavit oktávu a rychlost pro celý song
    play_melody(sndout, ml_1); //přehrát část 1
    sound(sndout, get_tone('c', 2), dur); //generování jednotlivých tónů (je zbytečné na jednu nebo dvě noty psát část)
    sound(sndout, get_tone('d', 3), dur); // |–|
    sound(sndout, get_tone('d', 3), dur); // |–|
    sound(sndout, get_tone('b', 2), dur); // |–|
    sound(sndout, get_tone('c', 3), dur); // |–|
    sound(sndout, get_tone('b', 2), dur); // |–|
    sound(sndout, get_tone('a', 2), dur); // |–|
    oct=3; //nastavit oktávu pro část 2
    play_melody(sndout, ml_2); //přehrát část 2
    //a tak dále a tak dále
    sound(sndout, get_tone('b', 2), dur);
    oct=3;
    play_melody(sndout, ml_3);
    sound(sndout, get_tone('b', 2), dur);
    oct=3;
    play_melody(sndout, ml_4);
    sound(sndout, get_tone('b', 2), dur);
    sound(sndout, get_tone('b', 2), dur);
    sound(sndout, get_tone('c', 3), dur);
    delay(3000);

    oct=1; dur=300; //nastavit oktávu a rychlost pro song 1
    play_melody(sndout, melody); //přehrát song 1
    delay(3000); //počkat sekundu
    oct=1;dur=500; //nastavit na song 2
    play_melody(sndout, melody2); //přehrát
    delay(3000); //čekat 3 sekundy a hrát znova

    }

  4. Harvie Says:

    2Ondra: Diky, treba se to bude nekomu hodit :-) ja tedka premejslim o televiznim (Composite PAL) vystupu, ktery se da na arduinu zrealizovat relativne snadno, problem je ale v tom, jak srovnat casovani, aby korespondovalo s dalsim kodem ktery bych chtel spoustet… To uz je prakticky potreba nejaky realtime multitasking…

  5. Ondra Says:

    :-)
    To je dobrý. Chtělo by to nějaký nový článek o Arduinu.
    Bude?

Leave a Reply