// Knihovna pro generovani hudebnich zvuku dane frekvence a delky nebo
// dane noty temperovaneho ladeni a delky.
//
// Pro generovani nepouziva zadnou podporu HW, vse se generuje ciste SW.
//
// (c)miho 2003
//
// Historie
// 1.00 Uvodni verze
//
// Konfiguracni parametry
//#define SOUND_HI PIN_xx // Pozitivni vystup
//#define SOUND_LO PIN_xx // Komplementarni vystup
#ifndef SOUND_REZIE
#define SOUND_REZIE 72 // Piskvorcova konstanta zahrnuje celkovou rezii ve smycce
#endif
#ifndef SOUND_CLOCK
#define SOUND_CLOCK 4000000 // Frelvence krystalu v Hz
#endif
// Definice hudebnich tonu (not) pro proceduru SoundNote()
#define SOUND_C 0
#define SOUND_Cis 1
#define SOUND_D 2
#define SOUND_Dis 3
#define SOUND_E 4
#define SOUND_F 5
#define SOUND_Fis 6
#define SOUND_G 7
#define SOUND_Gis 8
#define SOUND_A 9
#define SOUND_Ais 10
#define SOUND_H 11
#define SOUND_Space 12 // Pomlka
// Prototypy verejnych procedur
void SoundBeep(unsigned int16 Frequency, unsigned int16 Duration);
// Predava se frekvence v Hz a doba trvani v ms (0 znamena ticho)
void SoundNote(unsigned int8 Note, Octave, unsigned int16 Duration);
// Predava se cislo noty (0 je C), posunuti v oktavach (0 nejnizsi ton,
// SOUND_Space je ticho), doba trvani v ms
// Alternativni makra pro generovani konstatnich tonu
// SoundBeepMacro(Frequency, Duration) - frekvence nesmi byt 0
// SoundNoteMacro(Note, Octave, Duration) - nepodporuje SOUND_Space
// SoundSpaceMacro(Duration) - hraje ticho
// Privatni cast
#ORG 0x100, 0x128 // Aby skok nebyl pres hranici 0x100
void DelaySpecial(unsigned int16 Time)
// Pomocna procedura pro mereni spozdeni s granularitou 1 instrukcni takt
// Cas v instrukcnich cyklech, skutecny cas je vetsi o _konstantni_ rezii
// Maximalni cas je 65536 us
{
unsigned int8 TimeHi; // Pro pristup k horni casti Time
*0x0A = LABEL_ADDRESS(Next)>>8; // Nastav PCLATH
#asm
movf Time,w // Zpracuj nejnizsi 3 bity
xorlw 7 // Pro hodnotu 0 skakej pres vsechny nopy
andlw 7 // Ber jen spodni 3 bity
addwf 2,f // Preskoc zadny az vsech 8 nopu (2 je PCL)
Next:
nop // Spozdeni s granularitou 1 takt
nop
nop
nop
nop
nop
nop
nop
#endasm
Time = Time >> 3; // Zahod spodni 3 bity
TimeHi=Time>>8; // Oddel horni cast citace
Time++; // Korekce na casovani typu dcfsz
TimeHi++;
#asm
Loop:
nop // Smycka musi trvat 8 taktu
nop // a ma 16 bitu dlouhy citac
nop
decfsz Time
goto Next1
decfsz TimeHi
Next1:
goto Loop
#endasm
}
unsigned int32 SoundCount; // Pocet pulperid geneovaneho signalu
unsigned int32 SoundPeriod; // Delka pulperiody v us (zmensene o SOUND_REZIE)
void SoundLoop()
// Pomocna funkce - hlavni zvukova smycka
{
int1 Data;
unsigned int16 i;
for(i=SoundCount;i>0;i--) // Pocet pulperiod
{
output_bit(SOUND_HI,Data); // Nastav vystup
output_bit(SOUND_LO,~Data);
Data=~Data; // Otoc stav vystupu
DelaySpecial(SoundPeriod); // Pockej po dobu plperiody
}
}
void SoundBeep(unsigned int16 Frequency, unsigned int16 Duration)
// Predava se frekvence v Hz a doba trvani v ms (0 znamena ticho)
// Rozumne frekvence jsou v rozsahu cca 10Hz az 5000Hz pro krystal 4MHz,
// cas do cca 5s (2*Cas/1000*Freq musi byt < nez 65536)
{
if (Frequency==0)
{
for(;Duration>0;Duration--)
{
DelaySpecial(1000); // Zhruba 1ms
}
return;
}
SoundPeriod=(SOUND_CLOCK/4/2)/Frequency-SOUND_REZIE;
SoundCount=Duration; // Vypocet poctu pulperiod signalu Duration*Frequency*2/1000
SoundCount*=Frequency;
SoundCount/=500;
SoundLoop(); // Pozor pouzivaji se globalni parametry
}
// Definice casu pulperody pro nejnizsi oktavu, v mikrosekundach
// Periody tonu v dalsich oktavach se ziskavaji rotaci vpravo
#define SOUND_Peri_C (30578*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 30578us
#define SOUND_Peri_Cis (28862*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 28862us
#define SOUND_Peri_D (27242*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 27242us
#define SOUND_Peri_Dis (25713*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 25713us
#define SOUND_Peri_E (24270*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 24270us
#define SOUND_Peri_F (22908*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 22908us
#define SOUND_Peri_Fis (21622*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 21622us
#define SOUND_Peri_G (20408*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 20408us
#define SOUND_Peri_Gis (19263*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 19263us
#define SOUND_Peri_A (18182*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 18182us
#define SOUND_Peri_Ais (17161*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 17161us
#define SOUND_Peri_H (16198*(SOUND_CLOCK/1000)/1000/4/2) // Perioda 16198us
#if SOUND_Peri_C > 65535
#error "Sound Clock too high (Note C requires delay > 65535 cycles)"
#endif
const int16 Table[12] = SOUND_Peri_C, SOUND_Peri_Cis, SOUND_Peri_D, SOUND_Peri_Dis,
SOUND_Peri_E, SOUND_Peri_F, SOUND_Peri_Fis, SOUND_Peri_G,
SOUND_Peri_Gis, SOUND_Peri_A, SOUND_Peri_Ais, SOUND_Peri_H;
void SoundNote(unsigned int8 Note, Octave, unsigned int16 Duration)
// Predava se cislo noty (0 je C), posunuti v oktavach (0 nejnizsi ton)
// doba trvani v ms (0 znamena ticho)
// Zahraje zadanou notu v zadane oktave dane delky
{
if (Note==SOUND_Space)
{
for(;Duration>0;Duration--)
DelaySpecial(1000); // Zhruba 1ms
return;
}
SoundPeriod=(Table[Note]>>Octave)-0; // Zde je chyba prekladace, nula musi zusat
SoundCount=Duration;
SoundCount*=1000;
SoundCount/=SoundPeriod;
SoundPeriod=SoundPeriod-SOUND_REZIE;
SoundLoop(); // Pozor pouzivaji se globalni parametry
}
// Sada maker, ktera neobsahuji slozity vypocet a jsou
// tedy vhodna pro jednoduche pipnuti. Parametry jsou stejne
// jako o funkci.
#define SoundBeepMacro(F,D) \
SoundPeriod=SOUND_CLOCK/4/2/F-SOUND_REZIE; \
SoundCount=D*F/500; \
SoundLoop();
#define SoundNoteMacro(N,O,D) \
SoundPeriod=(Table[N]>>O)-SOUND_REZIE; \
SoundCount=D*1000/(Table[N]>>O); \
SoundLoop();
#define SoundPauseMacro(D) \
{ \
#if D>255 \
unsigned int16 i=D; \
#else \
unsigned int8 i=D; \
#endif \
for(;i>0;i--) \
{ \
DelaySpecial(1000); \
} \
}
|