Hav1bit
HAV1BIT, Primer Sintetitzador Audiovisual fet per Telenoika, de la mà dels Luthiers Drapaires
HAV1BIT és un sintetitzador audiovisual d’1 bit de hardware obert basat en arduino, té una sortida minijack, una VGA (640*480px) i 6 potenciometres+6 botons. Són 3 oscil·ladors d’ona quadrada que es modulen entre si, una espècie d’aproximació a la síntesi FM, però audiovisual. Cada oscil·lador modula un dels canals de color RGB.
Ara mateix podeu veure l’esquema, el codi, la descripció i un vídeo d’exemple d’aquest sinte en fase beta, tot i que ja s’està treballant en les noves funcionalitats que se li volen donar:
-guardat de presets
-seqüenciació via serial amb maxforlive o midi (a través del port usb)
-manipulació de senyal de sincronia
-possible mode 8 bits
Tot el disseny està basat en Arduino, amb col·laboració de molts membres de la comunitat a través dels forums. El aparell té unes quantes funcionalitats interessants:
Modes de funcionament:
-Simple: control gruix (ms) i fi (microsegons) de la frequencia dels osciladors
-Rampes: generació de rampes (ones de serra) amb estat inicial, estat final, i temps de la rampa ajustables (moltes gràcies al Emannuelle Mazza pel codi d’aquesta part!)
-Random: frequencia aleatoria per canal, dins un rang definible i amb canvi cada temps definit
-Rampes+Random: Una mescla extraterrestre de les dos funcionalitats anteriors
Connectivitat
-L’aparell pot funcionar de forma autònoma, mitjançant una interfície amb 6 switches i 6 potenciometres
-Es pot connectar a través d’USB a un ordinador i controlar la síntesi a través del port Serial. Estem treballant en un patch per a PureData que permetrà fer MOLTES coses guapes.
DIY
-Com sempre, els nostres dissenys son en part depenents del coneixements que altres han estat a bé compartir, així que en retorn a la comunitat aqui va tot el necessàri per a construir-se’n un d’aquests:
La construcció és molt fàcil, nomes cal 6 botons i 6 potenciometres connectats a arduino, a més del port minijack i el port VGA.
El Firmware el podeu descarregar a continuació: AV3.pde
//AV1BIT Audiovisual MonoChord 1Bit Synthesizer
//VGA Sync Generator by DWAN
//Faster AnalogRead code by jmknapp
//Ramps code by Emanuelle Mazza
/* VGA Sync Generator
* 11/11/08 - dwan : dwanafite@yahoo.fr
*
* Based on :
* - RG Matrix Example v.2 8/1/08, by BroHogan, from the Arduino Playground
* - Simplest universal VGA/PAL terminal, by Ibragimov Maxim Rafikovich, http://www.vga-avr.narod.ru/main.html
* - the very useful Timer/Counter/Prescaler Calculator, http://www.et06.dk/atmega_timers/
* - Arduino.cc Port Manipulation Tutorial
*
* This program outputs pretty accurate VGA syncronization signals. It's using a timer interrupt on timer2, so hopefully you can make other cool things in loop().
*/
// 640 * 480 @ 60Hz - FvSync = 60.3 Hz / FhSync = 31.3 kHz
// HSync : pin 7 Arduino, pin 13 VGA
// VSync : pin 6 Arduino, pin 14 VGA
// Arduino's pin 5 is HIGH when video can be sent, LOW otherwise. I use it to power a transistor. See this wonderful page : http://www.anatekcorp.com/driving.htm
// 1 NOP = 62,5 ns wasted
#define NOP asm("nop")
#define vga_field_line_count 525 // number of VGA lines
#define ISR_FREQ 0x3F // Sets the speed of the ISR - LOWER IS FASTER - 62
volatile unsigned int linecount;
//FASTER ANALOG READS
//Prescaler/16 (faster analogReads)
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
// Pins per a sortida (RGB i à udio):
const byte R = 9;
const byte G = 10;
const byte B = 11;
const byte A = 12;
//pins d'entrada digital (switch)
const int Rswitch1 = 1; // output as an input
const int Rswitch2 = 4; // output as an input
const int Gswitch1 = 2; // output as an input
const int Gswitch2 = 5; // output as an input
const int Bswitch1 = 3; // output as an input
const int Bswitch2 = 8; // output as an input
const int Randomizer = 13;
// Variables d'estat:
byte RState = LOW;
byte GState = LOW;
byte BState = LOW;
unsigned long previousMicros=0;
unsigned long previousMillis=0;
unsigned long interval=0;
//declara temps d'inicialitzacio de les rampes
long unsigned int initRamp1;
long unsigned int initRamp2;
long unsigned int initRamp3;
//Llegeix els potes
unsigned long int Rpote1(){
unsigned long int r= analogRead(0);
return r;
}
unsigned long int Rpote2(){
unsigned long int r= analogRead(3);
return r;
}
unsigned long int Gpote1(){
unsigned long int g= analogRead(1);
return g;
}
unsigned long int Gpote2(){
unsigned long int g= analogRead(4);
return g;
}
unsigned long int Bpote1(){
unsigned long int b= analogRead(2);
return b;
}
unsigned long int Bpote2(){
unsigned long int b= analogRead(5);
return b;
}
//declara randoms
int random1=0;
int random2=0;
int random3=0;
void setup()
{
// Serial.begin(115200);
DDRD |= B11100000; // it sets pins 7, 6 and 5 as output without changing the value of pins 0 & 1, which are RX & TX
// 76543210 <- pin translation
PORTD |= B11000000; // sets pins 7 (hSync) and 6 (vSync) HIGH
// 76543210;
setISRtimer(); // setup the timer
startISR(); // start the timer to toggle shutdown
// set prescale to 16: faster analog reads
#if FASTADC
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif
pinMode(R, OUTPUT);
pinMode(G, OUTPUT);
pinMode(B, OUTPUT);
pinMode(Rswitch1, INPUT);
pinMode(Rswitch2, INPUT);
pinMode(Gswitch1, INPUT);
pinMode(Gswitch2, INPUT);
pinMode(Bswitch1, INPUT);
pinMode(Bswitch2, INPUT);
pinMode(Randomizer, INPUT);
initRamp1 = millis();
initRamp2 = millis();
initRamp3 = millis();
}
void loop()
{
long unsigned int a= Rpote1();
long unsigned int b= Rpote2();
long unsigned int c= Gpote1();
long unsigned int d= Gpote2();
long unsigned int e= Bpote1();
long unsigned int f= Bpote2();
if (a+b <=2044){
Rswitch(a,b);
}
if (c+d <=2044){
Gswitch(c,d);
}
if (e+f <=2044){
Bswitch(e,f);
}
}
//Gestio dels modes de funcionament (a través dels switches)
void Rswitch(long unsigned int pote1, long unsigned int pote2){
byte Rsw1 = digitalRead(Rswitch1);
byte Rsw2 = digitalRead(Rswitch2);
if (Rsw1==HIGH && Rsw2==HIGH){
RandRampR(pote1, pote2);
}
if (Rsw1==HIGH && Rsw2==LOW){
RampR(pote1, pote2);
}
if (Rsw1==LOW && Rsw2==LOW){
SimpleR(pote1, pote2);
}
if (Rsw1==LOW && Rsw2==HIGH){
RandomR(pote1, pote2);
}
}
void Gswitch(long unsigned int pote1, long unsigned int pote2){
byte Gsw1 = digitalRead(Gswitch1);
byte Gsw2 = digitalRead(Gswitch2);
if (Gsw1==HIGH && Gsw2==HIGH){
RandRampG(pote1, pote2);
}
if (Gsw1==HIGH && Gsw2==LOW){
RampG(pote1, pote2);
}
if (Gsw1==LOW && Gsw2==LOW){
SimpleG(pote1, pote2);
}
if (Gsw1==LOW && Gsw2==HIGH){
RandomG(pote1, pote2);
}
}
void Bswitch(long unsigned int pote1, long unsigned int pote2){
byte Bsw1 = digitalRead(Bswitch1);
byte Bsw2 = digitalRead(Bswitch2);
if (Bsw1==HIGH && Bsw2==HIGH){
RandRampB(pote1, pote2);
}
if (Bsw1==HIGH && Bsw2==LOW){
RampB(pote1, pote2);
}
if (Bsw1==LOW && Bsw2==LOW){
SimpleB(pote1, pote2);
}
if (Bsw1==LOW && Bsw2==HIGH){
RandomB(pote1, pote2);
}
}
//Modes de funcionament
void RampR(long unsigned int a, long unsigned int b){
SimpleR(0,ramp1(0,a*100,b*10));
}
void RandomR(long unsigned int a, long unsigned int b){
SimpleR(0,randomize1(a*100,b));
}
void RandRampR(long unsigned int a, long unsigned int b){
SimpleR(0,randomize1(a*50,0)+ramp1(0,50000,b));
}
void RampG(long unsigned int a, long unsigned int b){
SimpleG(0,ramp2(0,a*100,b*10));
}
void RandomG(long unsigned int a, long unsigned int b){
SimpleG(0,randomize2(a*100,b));
}
void RandRampG(long unsigned int a, long unsigned int b){
SimpleG(0,randomize2(a*50,0)+ramp2(0,50000,b));
}
void RampB(long unsigned int a, long unsigned int b){
SimpleB(0,ramp3(0,a*100,b*10));
}
void RandomB(long unsigned int a, long unsigned int b){
SimpleB(0,randomize3(a*100,b));
}
void RandRampB(long unsigned int a, long unsigned int b){
SimpleB(0,randomize3(a*50,0)+ramp3(0,50000,b));
}
////modes Simples
void SimpleR(long unsigned int a, long unsigned int b){
unsigned long int rdel=(a*500)+b;
unsigned long currentMicros = micros();
if (micros() - previousMicros >= rdel) {
previousMicros = previousMicros+rdel;
if (rdel <512000){
RState =! RState;
digitalWrite(R, RState);
digitalWrite(A, RState);
}
}
}
void SimpleG(long unsigned int a, long unsigned int b){
unsigned long int gdel=(a*500)+b;
unsigned long currentMicros = micros();
if (micros() - previousMicros >= gdel) {
previousMicros = previousMicros+gdel;
if (gdel <512000){
GState =! GState;
digitalWrite(G, GState);
digitalWrite(A, GState);
}
}
}
void SimpleB(long unsigned int a, long unsigned int b){
unsigned long int bdel=(a*500)+b;
unsigned long currentMicros = micros();
if (micros() - previousMicros >= bdel) {
previousMicros = previousMicros+bdel;
if (bdel <512000){
BState =! BState;
digitalWrite(B, BState);
digitalWrite(A, BState);
}
}
}
/////Rampes
unsigned int ramp1(unsigned int inicio, unsigned int fin, long unsigned int duracion){
unsigned int frecuencia = inicio;
if((millis() - initRamp1) < duracion){
frecuencia = map((millis() - initRamp1),0,duracion,inicio,fin);
}else{
initRamp1 = millis();
}
return frecuencia;
}
unsigned int ramp2(unsigned int inicio, unsigned int fin, long unsigned int duracion){
unsigned int frecuencia = inicio;
if((millis() - initRamp2) < duracion){
frecuencia = map((millis() - initRamp2),0,duracion,inicio,fin);
}else{
initRamp2 = millis();
}
return frecuencia;
}
unsigned int ramp3(unsigned int inicio, unsigned int fin, long unsigned int duracion){
unsigned int frecuencia = inicio;
if((millis() - initRamp3) < duracion){
frecuencia = map((millis() - initRamp3),0,duracion,inicio,fin);
}else{
initRamp3 = millis();
}
return frecuencia;
}
///Randomitzador
int randomize1(unsigned int v, unsigned int n){
if (millis() - previousMillis >= n) {
previousMillis = previousMillis+n;
random1=random(v);
}
return random1;
}
int randomize2(unsigned int v, unsigned int n){
if (millis() - previousMillis >= n) {
previousMillis = previousMillis+n;
random2=random(v);
}
return random2;
}
int randomize3(unsigned int v, unsigned int n){
if (millis() - previousMillis >= n) {
previousMillis = previousMillis+n;
random3=random(v);
}
return random3;
}
//Timer per a VGA
///////////////////////////// ISR Timer Functions ///////////////////////////
ISR(TIMER2_COMPA_vect)
{
// Stop video
// pin 5 LOW
PORTD &= ~(1 << 5);
// Front porch
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
// Count number of lines
if (++linecount == vga_field_line_count)
{
linecount = 0;
}
// can it be vsync tiem nao ?
if ((linecount == 10 )||(linecount == 11 ))
{
// hsync LOW
// vsync LOW
PORTD &= ~(1 << 7);
PORTD &= ~(1 << 6);
}
else // ,hsync only
{
// hsync LOW
// vsync HIGH
PORTD &= ~(1 << 7);
PORTD |= (1 << 6);
}
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
// nonetheless,
// hsync HIGH
PORTD |= (1 << 7);
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;
NOP;
// Start video if we are in a visible area
if (linecount > 45)
{
// pin 5 HIGH
PORTD |= (1 << 5);
}
}
void setISRtimer(){ // setup ISR timer controling toggleing
TCCR2A = 0x02; // WGM22=0 + WGM21=1 + WGM20=0 = Mode2 (CTC)
TCCR2B = (1 << CS01); // /8 prescaler (2MHz)
TCNT2 = 0; // clear counter
OCR2A = ISR_FREQ; // set TOP (divisor) - see #define
}
void startISR(){ // Starts the ISR
TCNT2 = 0; // clear counter (needed here also)
TIMSK2|=(1<
Actualitzacions:
Anirem actualitzant el firmware de la màquina, afegint-hi noves funcionalitats.
Tenim en ment implementar un sistema de gravació de presets i un sistema de sequenciació per midi. Mica en mica anirem afegint aquestes coses a la web, estigeu atents si us interessa.