356 lines
10 KiB
C++
356 lines
10 KiB
C++
|
#include "effects.h"
|
||
|
|
||
|
#define TESTING 0
|
||
|
#define REVERB 1
|
||
|
#define CHORUS 2
|
||
|
#define FLANGER 3
|
||
|
#define ECHO 4
|
||
|
#define DISTORTION 5
|
||
|
#define TREMOLO 6
|
||
|
|
||
|
volatile uint8_t sampleReady = 0; //zmienna kotrolująca możliwość podania nowej wartości do DAC
|
||
|
volatile uint8_t adcVal = 0;
|
||
|
volatile uint8_t effect = REVERB; //wybrany efekt
|
||
|
volatile uint32_t counter = 0;
|
||
|
const uint8_t sine [101] = {0,0,0,0,1,1,2,2,3,4,5,6,7,8,10,11,12,14,16,17,19,21,23,25,27,29,32,34,36,39,41,44,46,49,
|
||
|
52,55,57,60,63,66,69,72,75,78,81,84,87,91,94,97,100,103,106,109,113,116,119,122,125,128,131,134,137,140,143,145,148,
|
||
|
151,154,156,159,161,164,166,168,171,173,175,177,179,181,183,184,186,188,189,190,192,193,194,195,196,197,198,198,199,
|
||
|
199,200,200,200,200};
|
||
|
volatile uint8_t effectSetting = 5;
|
||
|
const uint8_t effectSettingVector [9] = {0, 5, 15, 30, 55, 90, 130, 180, 255};
|
||
|
volatile uint8_t encoderDir = 0; //0 - w lewo, 1 - w prawo
|
||
|
volatile uint8_t encoderServed = 1;
|
||
|
|
||
|
//timer0 służy tylko do aktywacji odczytu przez ADC (i do debouncingu)
|
||
|
ISR(TIMER0_COMPA_vect){
|
||
|
counter++;
|
||
|
}
|
||
|
|
||
|
ISR(TIMER1_OVF_vect){
|
||
|
OCR1A = effectSettingVector[effectSetting -1];//20 * effectSetting - 15; //od 25 do 225
|
||
|
}
|
||
|
|
||
|
ISR(INT0_vect){
|
||
|
if(encoderServed){
|
||
|
if(!(PIND & 0x08)){
|
||
|
encoderDir = 0;
|
||
|
} else if(PIND & 0x08){
|
||
|
encoderDir = 1;
|
||
|
}
|
||
|
encoderServed = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ISR(ADC_vect){
|
||
|
adcVal = ADCH;
|
||
|
sampleReady = 1; //dopiero po odczytaniu nowej wartości ADC uruchomiona jest możliwość podania nowej wartości do DAC
|
||
|
}
|
||
|
|
||
|
int main(void){
|
||
|
//wyprowadzenie do sterowania wejsciem sygnalu nieprzetworzonego
|
||
|
DDRC |= 1<<DDC5;
|
||
|
|
||
|
//enkoder do zmiany parametru efektu
|
||
|
DDRD &= ~(1<<DDD2 | 1<<DDD3);
|
||
|
|
||
|
//przycisk do zmiany efektu
|
||
|
DDRD &= (1<<DDD4);
|
||
|
PORTD |= (1<<DDD4);
|
||
|
|
||
|
//przycisk do aktywacji efektu
|
||
|
DDRD &= (1<<DDD1);
|
||
|
PORTD |= (1<<DDD1);
|
||
|
|
||
|
//diody sygnalizujące wybrany efekt
|
||
|
DDRD |= (1<<DDD5) | (1<<DDD6) | (1<<DDD7);
|
||
|
|
||
|
//dioda sygnalizująca aktywację efektu
|
||
|
DDRB |= (1<<DDB0);
|
||
|
|
||
|
//dioda, której intensywność świecenia wskazuje na parametr efektu
|
||
|
DDRB |= (1<<DDB1);
|
||
|
|
||
|
|
||
|
//TIMER0
|
||
|
//Prescaler = 64; pojemność bufora 1850;
|
||
|
//250 000 / 41,67 kHz = 6 ; 1850 / 41,67 kHz = 44,4 ms
|
||
|
//250 000 / 20,83 kHz = 12; 1850 / 20,83 kHz = 88,8 ms
|
||
|
//250 000 / 10 kHz = 25; 1850 / 10 kHz = 185 ms
|
||
|
//250 000 / 8,065 kHz = 31; 1850 / 8,065 kHz = 229,4 ms
|
||
|
//250 000 / 5 kHz = 50; 1850 / 5 kHz = 370 ms
|
||
|
TCCR0A = (1<<WGM01); //CTC
|
||
|
TCCR0B = (1<<CS01) | (1<<CS00); //prescaler = 64
|
||
|
OCR0A = 25;
|
||
|
TIMSK0 = (1<<OCIE0A);
|
||
|
|
||
|
//TIMER1
|
||
|
//Do sterowania diodą (PWM)
|
||
|
TCCR1A = (1<<COM1A1) | (1<<WGM10); //OC1A, Clear on compare match; Fast PWM 8-bit
|
||
|
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); //Fast PWM 8-bit; fosc/1024
|
||
|
OCR1A = 128;
|
||
|
TIMSK1 = (1<<TOIE1); //Przerwanie przy przepełnieniu do aktualizacji wartości wypełnienia.
|
||
|
|
||
|
//ADC
|
||
|
ADMUX = (1<<REFS0) | (1<<ADLAR); //Vref = AVcc, przesuniecie do lewej, ADC0
|
||
|
ADCSRA = (1<<ADEN) | (1<<ADSC) | (1<<ADATE) | (1<<ADIE) | (1<<ADPS2) /*(1<<ADPS1) | (1<<ADPS0)*/; //częstotliwość zegara ADC = 1 MHz
|
||
|
ADCSRB = (1<<ADTS1) | (1<<ADTS0); //triggerowanie ADC przerwaniem od przepełnienia timera 0
|
||
|
DIDR0 = (1<<ADC0D);
|
||
|
|
||
|
//INT0
|
||
|
EICRA = 1<<ISC01; //wyzwalanie zboczem opadającym
|
||
|
EIMSK = 1<<INT0; //aktywacja
|
||
|
|
||
|
sei();
|
||
|
spiInit();
|
||
|
|
||
|
int16_t valueToSend = 128; //wartość do przekazania do DAC; int16_t (16 bitowy, ze znakiem) w celu implementacji nasycenia
|
||
|
|
||
|
//bufor na dane i jego inicjalizacja wartościami 128
|
||
|
uint8_t buffer [BUFFER_LENGTH] = {0};
|
||
|
for(uint16_t i = 0; i < BUFFER_LENGTH; i++){
|
||
|
buffer[i] = 128;
|
||
|
}
|
||
|
|
||
|
uint16_t bufferIndex = 0; //indeks najnowszej próbki
|
||
|
|
||
|
//Przyciski
|
||
|
uint32_t counterButton = 0;
|
||
|
uint8_t buttonPressed = 0;
|
||
|
|
||
|
uint32_t counterButton2 = 0;
|
||
|
uint8_t buttonPressed2 = 0;
|
||
|
uint8_t effectOn = 0;
|
||
|
|
||
|
//Enkoder
|
||
|
uint32_t counterEncoder = 0;
|
||
|
|
||
|
//Chorus
|
||
|
uint8_t sineIndex = 0; //indeks wartości z wektora z sinusem
|
||
|
uint8_t chorusCounter = 0; //licznik służacy do spowolnienia zmian w opóźnieniu
|
||
|
//użyty zamiast licznika od timera, żeby operować na wartości 8-bitowej zamiast 32-bitowej
|
||
|
uint8_t chorusDirChange = 0; //0 - inkrementacja, 1 - dekrementacja
|
||
|
|
||
|
//Flanger
|
||
|
uint8_t flangerDelayTime = _0MS;
|
||
|
uint8_t flangerCounter = 0;
|
||
|
uint8_t flangerDirChange = 0;
|
||
|
|
||
|
//Echo
|
||
|
uint16_t echoIndex = 0;
|
||
|
|
||
|
//Tremolo
|
||
|
uint8_t tremoloCounter = 0;
|
||
|
uint8_t tremoloDirChange = 0;
|
||
|
|
||
|
while(1){
|
||
|
//Obsługa przycisku od przełączania efektu
|
||
|
if(counter - counterButton >= _50MS){
|
||
|
if(!(PIND & 0x10) && !buttonPressed){
|
||
|
//Jeżeli przed zmianą na echo znajduje się coś w buforze, to istnieje ryzyko wystąpienia zapętlenia próbki,
|
||
|
//stąd zerowanie buforu.
|
||
|
if(effect == ECHO - 1) {
|
||
|
for(uint16_t i = 0; i < BUFFER_LENGTH; i++){
|
||
|
buffer[i] = 128;
|
||
|
}
|
||
|
}
|
||
|
effect++;
|
||
|
if(effect > TREMOLO) effect = REVERB;
|
||
|
buttonPressed = 1;
|
||
|
} else if(PIND & 0x10 && buttonPressed) buttonPressed = 0;
|
||
|
counterButton = counter;
|
||
|
}
|
||
|
|
||
|
//obsługa przycisku od aktywacji efektu i diody sygnalizującej aktywację efektu
|
||
|
if(counter - counterButton2 >= _50MS){
|
||
|
if(!(PIND & 0x02) && !buttonPressed2){
|
||
|
if(effectOn){
|
||
|
effectOn = 0;
|
||
|
PORTB &= ~(1<<PORTB0);
|
||
|
} else {
|
||
|
effectOn = 1;
|
||
|
PORTB |= (1<<PORTB0);
|
||
|
}
|
||
|
buttonPressed2 = 1;
|
||
|
} else if(PIND & 0x02 && buttonPressed2) buttonPressed2 = 0;
|
||
|
counterButton2 = counter;
|
||
|
}
|
||
|
|
||
|
if(counter - counterEncoder >= _20MS && !encoderServed){
|
||
|
if(encoderDir == 1){
|
||
|
if(effectSetting < 9) effectSetting++;
|
||
|
} else {
|
||
|
if(effectSetting > 1) effectSetting--;
|
||
|
}
|
||
|
counterEncoder = counter;
|
||
|
encoderServed = 1;
|
||
|
}
|
||
|
|
||
|
if(sampleReady){
|
||
|
if(effect < ECHO) buffer[bufferIndex] = adcVal; //zapisanie aktualnej próbki na najnowszą pozycję w buforze
|
||
|
//pod warunkiem że efekt to REVERB, CHORUS lub FLANGER
|
||
|
|
||
|
if(effect == TESTING){
|
||
|
valueToSend = adcVal;
|
||
|
PORTD &= ~((1<<PORTD7) | (1<<PORTD6) | (1<<PORTD5));
|
||
|
//PORTC &= ~(1<<PORTC5);
|
||
|
PORTC |= 1<<PORTC5;
|
||
|
|
||
|
} else if(effect == REVERB){
|
||
|
PORTC |= 1<<PORTC5;
|
||
|
|
||
|
valueToSend = reverb(buffer, bufferIndex, effectSetting);
|
||
|
PORTD |= (1<<PORTD5);
|
||
|
PORTD &= ~((1<<PORTD7) | (1<<PORTD6));
|
||
|
|
||
|
} else if(effect == CHORUS){
|
||
|
PORTC |= 1<<PORTC5;
|
||
|
|
||
|
valueToSend = chorusFlanger(buffer, bufferIndex, sine[sineIndex] + _40MS, 10 - effectSetting);
|
||
|
chorusCounter++;
|
||
|
if(chorusCounter >= 200){
|
||
|
if(chorusDirChange == 0){
|
||
|
sineIndex++;
|
||
|
if(sineIndex >= 100) chorusDirChange = 1;
|
||
|
} else {
|
||
|
sineIndex--;
|
||
|
if(sineIndex == 0) chorusDirChange = 0;
|
||
|
}
|
||
|
chorusCounter = 0;
|
||
|
}
|
||
|
PORTD |= (1<<PORTD6);
|
||
|
PORTD &= ~((1<<PORTD7) | (1<<PORTD5));
|
||
|
|
||
|
} else if(effect == FLANGER){
|
||
|
PORTC |= 1<<PORTC5;
|
||
|
|
||
|
valueToSend = chorusFlanger(buffer, bufferIndex, flangerDelayTime, 10 - effectSetting);
|
||
|
flangerCounter++;
|
||
|
if(flangerCounter >= 100){
|
||
|
if(flangerDirChange == 0){
|
||
|
flangerDelayTime++;
|
||
|
if(flangerDelayTime >= _10MS) flangerDirChange = 1;
|
||
|
} else {
|
||
|
flangerDelayTime--;
|
||
|
if(flangerDelayTime == _0MS) flangerDirChange = 0;
|
||
|
}
|
||
|
flangerCounter = 0;
|
||
|
}
|
||
|
PORTD |= (1<<PORTD5) | (1<<PORTD6);
|
||
|
PORTD &= ~(1<<PORTD7);
|
||
|
|
||
|
} else if(effect == ECHO){
|
||
|
PORTC |= 1<<PORTC5;
|
||
|
|
||
|
echoIndex = bufferIndex + (_16MS * effectSetting) + _41MS; //obliczenie miejsca zapisu próbki "na później"
|
||
|
if(echoIndex > BUFFER_LENGTH) echoIndex -= BUFFER_LENGTH;
|
||
|
buffer[echoIndex] = (adcVal - 128)/2 + 2*(buffer[bufferIndex] - 128)/3 + 128; // wpisanie z opóźnieniem i tłumieniem aktualnej wartości
|
||
|
valueToSend = buffer[bufferIndex];
|
||
|
|
||
|
PORTD |= (1<<PORTD7);
|
||
|
PORTD &= ~((1<<PORTD6) | (1<<PORTD5));
|
||
|
|
||
|
|
||
|
} else if (effect == DISTORTION){
|
||
|
if(effectOn){
|
||
|
PORTC &= ~(1<<PORTC5);
|
||
|
} else {
|
||
|
PORTC |= (1<<PORTC5);
|
||
|
}
|
||
|
|
||
|
valueToSend = distortion(164 - effectSetting*4, adcVal);
|
||
|
PORTD |= (1<<PORTD7) | (1<<PORTD5);
|
||
|
PORTD &= ~((1<<PORTD6));
|
||
|
|
||
|
} else if(effect == TREMOLO){
|
||
|
if(effectOn){
|
||
|
PORTC &= ~(1<<PORTC5);
|
||
|
} else {
|
||
|
PORTC |= (1<<PORTC5);
|
||
|
}
|
||
|
|
||
|
valueToSend = tremolo(adcVal, sine[sineIndex]);
|
||
|
tremoloCounter++;
|
||
|
if(tremoloCounter >= 1 + effectSetting){
|
||
|
if(tremoloDirChange == 0){
|
||
|
sineIndex++;
|
||
|
if(sineIndex >= 100) tremoloDirChange = 1;
|
||
|
} else {
|
||
|
sineIndex--;
|
||
|
if(sineIndex == 0) tremoloDirChange = 0;
|
||
|
}
|
||
|
tremoloCounter = 0;
|
||
|
}
|
||
|
|
||
|
PORTD |= (1<<PORTD7) | (1<<PORTD6);
|
||
|
PORTD &= ~((1<<PORTD5));
|
||
|
|
||
|
} else {
|
||
|
effect = REVERB;
|
||
|
}
|
||
|
|
||
|
//implementacja przesterowania programowego
|
||
|
if(valueToSend > 255){
|
||
|
valueToSend = 255;
|
||
|
} else if(valueToSend < 0){
|
||
|
valueToSend = 0;
|
||
|
}
|
||
|
|
||
|
if (effectOn) {
|
||
|
analogWrite(valueToSend);
|
||
|
} else {
|
||
|
analogWrite(128);
|
||
|
}
|
||
|
|
||
|
sampleReady = 0;
|
||
|
|
||
|
//zmiana indeksu dla kolejnej próbki
|
||
|
bufferIndex++;
|
||
|
if(bufferIndex >= BUFFER_LENGTH) bufferIndex = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Wykorzystana pamięć
|
||
|
|
||
|
//sampleReady = 1 B
|
||
|
//adcVal = 1 B
|
||
|
//effect = 1 B
|
||
|
//counter = 4 B
|
||
|
//sine = 101 B
|
||
|
//effectSetting = 1 B
|
||
|
//effectSettingVector = 9 B
|
||
|
//encoderLeft = 1 B
|
||
|
//encoderRight = 1 B
|
||
|
//encoderServed = 1 B
|
||
|
|
||
|
//valueToSend = 2 B
|
||
|
//bufor = 1851 B
|
||
|
//bufferIndex = 2 B
|
||
|
//counterButton1 = 4 B
|
||
|
//buttonPressed1 = 1 B
|
||
|
//counterButton2 = 4 B
|
||
|
//buttonPressed2 = 1 B
|
||
|
//effectOn = 1 B
|
||
|
|
||
|
//counterEncoder = 4 B
|
||
|
|
||
|
//chorusSineIndex = 1 B
|
||
|
//chorusCounter = 1 B
|
||
|
//chorusDirChange = 1 B
|
||
|
|
||
|
//flangerDelayTime = 1 B
|
||
|
//flangerCounter = 1 B
|
||
|
//flangerDirChange = 1 B
|
||
|
|
||
|
//echoIndex = 2 B
|
||
|
|
||
|
//14 zmiennych w reverbie
|
||
|
//echo#Index... = 14 B (7x2 B)
|
||
|
//echo#Temp... = 14 B (7x2 B)
|
||
|
|
||
|
//3 zmienne w chorusie/flangerze
|
||
|
//echoIndex = 2 B
|
||
|
//echoTemp = 2 B
|
||
|
|
||
|
//W najgorszym przypadku łącznie: 2031 B
|
||
|
//Pozostaje: (2048 - 2031) B = 17 B
|