Controller MIDI USB con Arduino

Stampa
( 7 Votes ) 
Valutazione attuale:  / 7
ScarsoOttimo 
Categoria principale: Elettronica Categoria: Microcontrollori
Data pubblicazione
Scritto da ab5000 Visite: 15015

Controller MIDI USB con Arduino

Costruzione di un controller MIDI per comandare Virtual DJ attraverso Arduino

 
Ho iniziato da poco con Arduino, però grazie alla programmazione semplicissima sono subito passato a sviluppare qualcosa di utile.
Una delle prime cose che ho fatto è un'interfaccia MIDI che può essere collegata via USB al computer.
Questa interfaccia controlla Virtual DJ. Le idee erano 2:
1) Usare un convertitore MIDI-USB e far uscire dati MIDI dall'Arduino 
2) Utilizzare il convertitore USB presente sulla scheda 
La seconda era certamente più comoda, anche se il lag sarebbe stato leggermente più elevato.
Premetto che io non uso una scheda Arduino, bensì ho costruito un Arduino su breadboard con un ATMEGA168, un MAX232, un cavo seriale e un convertitore USB - RS232. In fondo all'articolo trovate tutti i riferimenti necessari per costruire Arduino su breadboard. Il bootloader è stato caricato tramite un vecchio PC con un programmatore parallelo.



La scheda Arduino Duemilanove (Fonte: arduino.cc)

Ho iniziato qui: http://www.spikenzielabs.com/Spiken ... _MIDI.html 

A questo codice ho aggiunto lettura degli ingressi analogici e midi a 14bit.
Sostanzialmente, l'Arduino manda sulla seriale (convertita in USB) i dati MIDI (che approfondiremo più avanti), un programma li legge e li manda ad un capo di un cavo midi virtuale. L'applicazione che deve ricevere i dati si mette in ascolto sull'altro capo del cavo virtuale. C'è anche un secondo cavo, che manda i dati dal programma all'Arduino.
Quel link consigliava MIDI Yoke come cavo virtuale. Purtroppo, su Windows 7 non ne vuole sapere di funzionare, almeno nel mio (anche disabilitando l'UAC). Così ne ho trovato un'alto, si chiama Maple MIDI ed è scaricabile da qui: http://www.maplemidi.com/Maple_driver.html. Purtroppo, le versioni a 64 bit di Vista/7 non sono supportate. Se qualcuno ha una versione a 64bit, cercherò un altro "cavo"
Bene, scaricate dal primo link la versione Windows del programma, scaricate Maple MIDI, installatelo. Scaricate la sketch demo e caricatela in Arduino. Siamo pronti per provare tutto Ma prima, un po' di teoria.
NOTA: Consiglio di tenere sotto la calcolatrice di Windows. Impostatelo in modalità Programmatore (Visualizza -> Programmatore) per poter convertire dati in Decimale/Esadecimale/Binario.

Il protocollo MIDI

Il protocollo MIDI (Musical Instrument Digital Interface) permette di trasferire suoni da uno strumento al PC in modo digitale. Il vataggio principale è la qualità dell'audio. Il MIDI non trasporta audio analogico, bensì informazioni del tipo "nota on/off". Di conseguenza, il suono non viene deteriorato da disturbi nel cavo.
Il MIDI può anche essere utilizzato per controllare programmi come Virtual DJ. Per esempio, potrei mandare al computer un messaggio con un certo codice identificativo, indicando il valore ottenuto dalla lettura di un potenziometro. Poi potrei assegnare, all'interno del programma, quel codice indentificativo al volume, o al crossfader, o al pitch, insomma quello che voglio (e che il programma mi permette). In questo modo, il potenziometro comanda, per esempio, il volume di un certo strumento.
Passiamo ai dettagli più tecnici. Il MIDI è un'interfaccia seriale che funziona a 31250 bit per secondo. Di solito si usa un connettore DIN a 5 poli, e vengono usati solitamente tre connettori come IN (dati MIDI trasmessi AL dispositivo), OUT (dati trasmessi DAL dispositivo) e THRU (ritrasmette i dati che riceve su IN).
Il MIDI permette di trasmettere su 16 canali (16 strumenti).
I messaggi MIDI sono solitamente di 3 byte, a parte i messaggi SYSEX (che il nostro software non supporta, li vedremo nella configurazione di Virtual DJ).
Ogni byte è composto da 8 bit, che possono essere 1 o 0. Il primo bit a sinistra è il bit più significativo, il primo a destra il meno significativo. Si indicano rispettivamente con MSB e LSB (Most/Least Significant Bit). Attenzione, perchè può essere usato in due modi: con la B che indica Bit, e con la B che indica Byte. Per ora lo uso con significato di Bit, più avanti lo userò con il significato di byte (cosa che indicherò).

Il primo byte si chiama "status byte", il secondo "pitch byte" ed il terzo "data byte".
Il primo byte (byte di stato) indica il canale e l'azione da compiere. L'MSB è SEMPRE 1, mentre negli altri 2 byte l'MSB è SEMPRE 0. Questo permette di riconoscere l'inizio di un messaggio MIDI: basta guardare l'MSB del byte, se è 1 è uno status byte, quindi l'inizio di un comando.
Questo byte è diviso in due parti da 4 bit ciascuna (due nibble). Il primo nibble da sinistra (quello più significativo, più una cosa è a sinistra più è significativa) indica l'azione da compiere (che sarà meglio specificata dagli altri due byte). Il secondo nibble indica il canale. Il canale può avere 16 valori, da 0000 a 1111 (da 0 a 15). Quindi il canale 1 sarà 0 (0000) nello status byte, il canale 2 sarà 1 (0001), e così via fino al canale 16 che sarà 15 (1111).
A questo indirizzo potete vedere una tabella con gli status byte: 

Status Bytes 176-191: Control and Mode Changes
 

Number (Byte 2) Name Value (Byte 3)
00000000
00
0
Continuous controller #0
0-127
MSB
00000001
01
1
Modulation wheel
0-127
MSB
00000010
02
2
Breath control
0-127
MSB
00000011
03
3
Continuous controller #3
0-127
MSB
00000100
04
4
Foot controller
0-127
MSB
00000101
05
5
Portamento time
0-127
MSB
00000110
06
6
Data Entry
0-127
MSB
00000111
07
7
Main Volume
0-127
MSB
00001000
08
8
Continuous controller #8
0-127
MSB
00001001
09
9
Continuous controller #9
0-127
MSB
00001010
0A
10
Continuous controller #10
0-127
MSB
00001011
0B
11
Continuous controller #11
0-127
MSB
00001100
0C
12
Continuous controller #12
0-127
MSB
00001101
0D
13
Continuous controller #13
0-127
MSB
00001110
0E
14
Continuous controller #14
0-127
MSB
00001111
0F
15
Continuous controller #15
0-127
MSB
00010000
10
16
Continuous controller #16
0-127
MSB
00010001
11
17
Continuous controller #17
0-127
MSB
00010010
12
18
Continuous controller #18
0-127
MSB
00010011
13
19
Continuous controller #19
0-127
MSB
00010100
14
20
Continuous controller #20
0-127
MSB
00010101
15
21
Continuous controller #21
0-127
MSB
00010110
16
22
Continuous controller #22
0-127
MSB
00010111
17
23
Continuous controller #23
0-127
MSB
00011000
18
24
Continuous controller #24
0-127
MSB
00011001
19
25
Continuous controller #25
0-127
MSB
00011010
1A
26
Continuous controller #26
0-127
MSB
00011011
1B
27
Continuous controller #27
0-127
MSB
00011100
1C
28
Continuous controller #28
0-127
MSB
00011101
1D
29
Continuous controller #29
0-127
MSB
00011110
1E
30
Continuous controller #30
0-127
MSB
00011111
1F
31
Continuous controller #31
0-127
MSB
00100000
20
32
Continuous controller #0
0-127
LSB
00100001
21
33
Modulation wheel
0-127
LSB
00100010
22
34
Breath control
0-127
LSB
00100011
23
35
Continuous controller #3
0-127
LSB
00100100
24
36
Foot controller
0-127
LSB
00100101
25
37
Portamento time
0-127
LSB
00100110
26
38
Data entry
0-127
LSB
00100111
27
39
Main volume
0-127
LSB
00101000
28
40
Continuous controller #8
0-127
LSB
00101001
29
41
Continuous controller #9
0-127
LSB
00101010
2A
42
Continuous controller #10
0-127
LSB
00101011
2B
43
Continuous controller #11
0-127
LSB
00101100
2C
44
Continuous controller #12
0-127
LSB
00101101
2D
45
Continuous controller #13
0-127
LSB
00101110
2E
46
Continuous controller #14
0-127
LSB
00101111
2F
47
Continuous controller #15
0-127
LSB
00110000
30
48
Continuous controller #16
0-127
LSB
00110001
31
49
Continuous controller #17
0-127
LSB
00110010
32
50
Continuous controller #18
0-127
LSB
00110011
33
51
Continuous controller #19
0-127
LSB
00110100
34
52
Continuous controller #20
0-127
LSB
00110101
35
53
Continuous controller #21
0-127
LSB
00110110
36
54
Continuous controller #22
0-127
LSB
00110111
37
55
Continuous controller #23
0-127
LSB
00111000
38
56
Continuous controller #24
0-127
LSB
00111001
39
57
Continuous controller #25
0-127
LSB
00111010
3A
58
Continuous controller #26
0-127
LSB
00111011
3B
59
Continuous controller #27
0-127
LSB
00111100
3C
60
Continuous controller #28
0-127
LSB
00111101
3D
61
Continuous controller #29
0-127
LSB
00111110
3E
62
Continuous controller #30
0-127
LSB
00111111
3F
63
Continuous controller #31
0-127
LSB
01000000
40
64
Damper pedal on/off (Sustain)
0=off
127=on
01000001
41
65
Portamento on/off
0=off
127=on
01000010
42
66
Sustenuto on/off
0=off
127=on
01000011
43
67
Soft pedal on/off
0=off
127=on
01000100
44
68
Undefined on/off
0=off
127=on
01000101
45
69
Undefined on/off
0=off
127=on
01000110
46
70
Undefined on/off
0=off
127=on
01000111
47
71
Undefined on/off
0=off
127=on
01001000
48
72
Undefined on/off
0=off
127=on
01001001
49
73
Undefined on/off
0=off
127=on
01001010
4A
74
Undefined on/off
0=off
127=on
01001011
4B
75
Undefined on/off
0=off
127=on
01001100
4C
76
Undefined on/off
0=off
127=on
01001101
4D
77
Undefined on/off
0=off
127=on
01001110
4E
78
Undefined on/off
0=off
127=on
01001111
4F
79
Undefined on/off
0=off
127=on
01010000
50
80
Undefined on/off
0=off
127=on
01010001
51
81
Undefined on/off
0=off
127=on
01010010
52
82
Undefined on/off
0=off
127=on
01010011
53
83
Undefined on/off
0=off
127=on
01010100
54
84
Undefined on/off
0=off
127=on
01010101
55
85
Undefined on/off
0=off
127=on
01010110
56
86
Undefined on/off
0=off
127=on
01010111
57
87
Undefined on/off
0=off
127=on
01011000
58
88
Undefined on/off
0=off
127=on
01011001
59
89
Undefined on/off
0=off
127=on
01011010
5A
90
Undefined on/off
0=off
127=on
01011011
5B
91
Undefined on/off
0=off
127=on
01011100
5C
92
Undefined on/off
0=off
127=on
01011101
5D
93
Undefined on/off
0=off
127=on
01011110
5E
94
Undefined on/off
0=off
127=on
01011111
5F
95
Undefined on/off
0=off
127=on
01100000
60
96
Data entry +1
 
 
01100001
61
97
Data entry -1
 
 
01100010
62
98
Undefined
 
 
01100011
63
99
Undefined
 
 
01100100
64
00
Undefined
 
 
01100101
65
01
Undefined
 
 
01100110
66
02
Undefined
 
 
01100111
67
03
Undefined
 
 
01100111
67
03
Undefined
 
 
01100111
67
03
Undefined
 
 
01100111
67
03
Undefined
 
 
01100111
67
03
Undefined
 
 
01101000
68
04
Undefined
 
 
01101001
69
05
Undefined
 
 
01101010
6A
06
Undefined
 
 
01101011
6B
07
Undefined
 
 
01101100
6C
08
Undefined
 
 
01101101
6D
09
Undefined
 
 
01101110
6E
10
Undefined
 
 
01101111
6F
11
Undefined
 
 
01110000
70
12
Undefined
 
 
01110001
71
13
Undefined
 
 
01110010
72
14
Undefined
 
 
01110011
73
15
Undefined
 
 
01110100
74
16
Undefined
 
 
01110101
75
17
Undefined
 
 
01110110
76
18
Undefined
 
 
01110111
77
19
Undefined
 
 
01111000
78
20
Undefined
 
 
01111001
79
21
Undefined
 
 
01111010
7A
22
Local control on/off
0=off
127=on
01111011
7B
23
All notes off (!!)
 
 
01111100
7C
24
Omni mode off (includes all notes off)
 
 
01111101
7D
25
Omni mode on (includes all notes off)
 
 
01111110
7E
26
Poly mode on/off(includes all notes off)
** 
 
01111111
7F
27
Poly mode on(incl mono=off&all notes off)
 
 


**Note: This equals the number of channels, or zero if the number of channels equals the number of voices in the receiver.

MIDI Status & Data Bytes
(ch=channel)
 

Number
Name (status byte)
2nd byte (data 1)
3rd byte (data2)
bin
hex
dec
10000000
80
128

ch 1 Note off

Note Number
(0-127)
Note Velocity
(0-127)
10000001
81
129
ch 2
10000010
82
130
ch 3
10000011
83
131
ch 4
10000100
84
132
ch 5
10000101
85
133
ch 6
10000110
86
134
ch 7
10000111
87
135
ch 8
10001000
88
136
ch 9
10001001
89
137
ch 10
10001010
8A
138
ch 11
10001011
8B
139
ch 12
10001100
8C
140
ch 13
10001101
8D
141
ch 14
10001110
8E
142
ch 15
10001111
8F
143
ch 16
10010000
90
144
ch 1 Note on
10010001
91
145
ch 2
10010010
92
146
ch 3
10010011
93
147
ch 4
10010100
94
148
ch 5
10010101
95
149
ch 6
10010110
96
150
ch 7
10010111
97
151
ch 8
10011000
98
152
ch 9
10011001
99
153
ch 10
10011010
9A
154
ch 11
10011011
9B
155
ch 12
10011100
9C
156
ch 13
10011101
9D
157
ch 14
10011110
9E
158
ch 15
10011111
9F
159
ch 16
10100000
A0
160

ch 1 Polyphonic aftertouch

Aftertouch pressure
(0-127)

10100001
A1
161
ch 2
10100010
A2
162
ch 3
10100011
A3
163
ch 4
10100100
A4
164
ch 5
10100101
A5
165
ch 6
10100110
A6
166
ch 7
10100111
A7
167
ch 8
10101000
A8
168
ch 9
10101001
A9
169
ch 10
10101010
AA
170
ch 11
10101011
AB
171
ch 12
10101100
AC
172
ch 13
10101101
AD
173
ch 14
10101110
AE
174
ch 15
10101111
AF
175
ch 16
10110000
B0
176

ch 1 Control/Mode change

see detailed list
10110001
B1
177
ch 2
10110010
B2
178
ch 3
10110011
B3
179
ch 4
10110100
B4
180
ch 5
10110101
B5
181
ch 6
10110110
B6
182
ch 7
10110111
B7
183
ch 8
10111000
B8
184
ch 9
10111001
B9
185
ch 10
10111010
BA
186
ch 11
10111011
BB
187
ch 12
10111100
BC
188
ch 13
10111101
BD
189
ch 14
10111110
BE
190
ch 15
10111111
BF
191
ch 16
11000000
C0
192
ch 1 Program change Program # (0-127) NONE
11000001
C1
193
ch 2
11000010
C2
194
ch 3
11000011
C3
195
ch 4
11000100
C4
196
ch 5
11000101
C5
197
ch 6
11000110
C6
198
ch 7
11000111
C7
199
ch 8
11001000
C8
200
ch 9
11001001
C9
201
ch 10
11001010
CA
202
ch 11
11001011
CB
203
ch 12
11001100
CC
204
ch 13
11001101
CD
205
ch 14
11001110
CE
206
ch 15
11001111
CF
207
ch 16
11010000
D0
208
ch 1 Channel Aftertouch Aftertouch pressure
(0-127)
11010001
D1
209
ch 2
11010010
D2
210
ch 3
11010011
D3
211
ch 4
11010100
D4
212
ch 5
11010101
D5
213
ch 6
11010110
D6
214
ch 7
11010111
D7
215
ch 8
11011000
D8
216
ch 9
11011001
D9
217
ch 10
11011010
DA
218
ch 11
11011011
DB
219
ch 12
11011100
DC
220
ch 13
11011101
DD
221
ch 14
11011110
DE
222
ch 15
11011111
DF
223
ch 16
11100000
E0
224
ch 1 Pitch wheel range Least significant
byte (LSB) (0-127)
Most significant
byte (MSB) (0-127)
11100001
E1
225
ch 2
11100010
E2
226
ch 3
11100011
E3
227
ch 4
11100100
E4
228
ch 5
11100101
E5
229
ch 6
11100110
E6
230
ch 7
11100111
E7
231
ch 8
11101000
E8
232
ch 9
11101001
E9
233
ch 10
11101010
EA
234
ch 11
11101011
EB
235
ch 12
11101100
EC
236
ch 13
11101101
ED
237
ch 14
11101110
EE
238
ch 15
11101111
EF
239
ch 16
11110000
F0
240
System Exclusive Vendor ID more data bytes
to ending EOX
11110001
F1
241
System Common
- undefined
? ?
11110010
F2
242
Sys Common
Song Position Pointer
LSB MSB
11110011
F3
243
Sys Commmon
Song Select(Song #)
(0-127) NONE
11110100
F4
244
System Common
- undefined
? ?
11110101
F5
245
System Common
- undefined
? ?
11110110
F6
246
Sys Common
Tune Request
NONE
11110111
F7
247
Sys Commmon
End of SysEx (EOX)
11111000
F8
248
Sys real time timing clock
11111001
F9
249
Sys real time undefined
11111010
FA
250
Sys real time start
11111011
FB
251
Sys real time continue
11111100
FC
252
Sys real time stop
11111101
FD
253
Sys real time undefined
11111110
FE
254
Sys real time active sensing
11111111
FF
255
Sys real time sys reset

Come  generare uno status byte con Arduino? Molto semplice:
(action << 4) | channel
action indica l'azione da compiere (il nibble più significativo). Viene spostata a sinistra di 4 bit, in modo da creare la prima parte dello status. Poi viene fatto un OR che, poichè i 4 bit meno significativi sono a 0, corrisponde ad una somma ed inserisce il canale.
Le azioni principali che ci interessano (perchè vogliamo fare un controller) sono tre:

Andiamo ad analizzarle.
Per note on e note off il secondo byte indica la nota (da 0 a 127, quindi 128 note totali) che si vuole controllare. Il terzo è la "velocity". Immagina di collegare un piatto di una batteria ad una nota. Se batti piano su piatto, la velocity sarà bassa. Se batti forte, la velocity sarà alta. Una specie di volume.
In ogni caso, a noi non interessa la velocity, in quanto non useremo le note per suonare, bensì come interruttori. Possiamo impostarla al massimo
Il Control change invece permette di cambiare il valore dei controlli. Qui una tabella di controlli:

http://www.midimountain.com/midi/midi_mode.html
Nel caso del Control change il secondo byte indica il controllo da cambiare (da 0 a 127) mentre il terzo indica il valore (da 0 a 127 ma dopo vedremo come arrivare a 16383). Per ora ignorate la dicitura MSB/LSB, vedremo più avantia cosa serve.
La documentazione di Virtual DJ parla di CC (Continuos Controller) ma è possibile utilizzare qualunque codice da 0x00 a 0x7F.
Bene, ora passiamo al nostro cavo virtuale.

La configurazione

Maple MIDI crea 4 cavi virtuali, che vengono usati per connettere applicazioni MIDI. Abbiamo due tipi di cavi: Maple MIDI In e Maple MIDI Out. Ognuno è numerato da 1 a 4. Le corrispondenze sono:

Maple MIDI In: Port 1 <-- Maple MIDI Out: Port 1
Maple MIDI In: Port 2 <-- Maple MIDI Out: Port 2 
Maple MIDI In: Port 3 <-- Maple MIDI Out: Port 3 
Maple MIDI In: Port 4 <-- Maple MIDI Out: Port 4

Come vedete la comunicazione è unidirezionale da Out a In, ovviamente. Quindi per un collegamento bidirezionale tra due applicazioni useremo due cavi.

Esempio pratico: Vogliamo impostare una connessione bidirezionale tra App1 e App2. Configuriamo App1: scegliamo un'interfaccia In per ricevere i dati (per esempio, In Port 1). Poi selezioniamo l'interfaccia Out, per trasmettere dati: deve avere un numero diverso!!! Scegliamo, per esempio, Out Port 2.

Ora configuriamo App2. Come interfaccia In scegliamo l'altro capo dell'Out di App1. Quindi scegliamo In Port 2, nel nostro esempio. Stessa cosa per Out, l'altro capo della In di App1, quindi Out Port 1.

Ecco fatto! Le due applicazioni sono collegate.

Questo collegamento andrà fatto tra l'applicazione che riceve i dati su seriale (disponibile sul primo link) e l'applicazione bersaglio, nel mio caso Virtual DJ.

Proviamo a configurare un collegamento. Decidiamo che l'interfaccia In del programma che riceve i dati sulla seriale sarà la In Port 1. L'interfaccia Out sarà la Out Port 2.

1) Apriamo l'applicazione e scegliamo la porta seriale del nostro Arduino. Nel mio caso è la COM2 (in realtà il mio è un convertitore USB-RS232), quindi premo A.



2) Una volta scelta la porta, dobbiamo scegliere la velocità. Usiamo 57600 bps, quindi premiamo E.



3) Ora selezioniamo la nostra porta MIDI In. Useremo la porta "Maple Midi In: Port 1". Nel mio caso, premo A.



4) Ora scegliamo la porta MIDI Out. Useremo la porta "Midi Maple Out: Port 2". Quindi, io premerò D.



5) Fatto! Ora è tutto configurato. Se i dati che arrivano dall'Arduino sono valide, la spia RX si illuminerà di verde. Se i dati non sono validi, di rosso. Anche la spia TX ha lo stesso comportamento, ma nell'altro verso (dal programma all'Arduino).



Ora andiamo a configurare Virtual DJ.

Apriamo Virtual DJ (Io uso la versione 6) e clicchiamo sul tasto Config. Poi andiamo su "Mappers" e scegliamo "Simple MIDI Wrapper". Questo è un controller MIDI che viene usato per i dispositivi non riconosciuti. Non dispone di funzionalità quali il MIDI 14 bit, ma a noi basta per i primi esperimenti. Più avanti creeremo un'interfaccia nostra.

Se c'è una voce MIDI_CLOCK, non cancellatela. Non dà nessun fastidio. Non chiudete Virtual DJ, servirà tra poco.




Arduino & Virtual DJ

Bene, arriva la parte divertente.


Ho scritto del codice che è facilmente espandibile: potete sfruttare tutti i 14 pin digitali dell'Arduino (esclusi il pin 0 e 1 che si occupano del seriale) per dei bottoni e tutti i 6 ingressi analogici per potenziometri/slider. Se poi volete divertirvi con dei multiplexer...

Io ho configurato il codice per un potenziometro/slider sull'ingresso analogico 0 e per un bottone sul pin digitale 13, ma spiegherò anche come impostare altre configurazioni.

Innanzitutto i collegamenti (per chi non sapesse come si collegano bottoni e potenziometri all'Arduino). Per collegare un bottone ad un I/O digitale, si prende uno switch SPST (un semplice interruttore) e lo si collega tra l'I/O digitale e il positivo dell'alimentazione. Inoltre, si mette una resistenza da 10KOhm tra il pin digitale e la massa (resistenza di pull-down). In questo modo l'I/O sarà basso quando il pulsante non è premuto, e alto quando è premo. Se si invertono i collegamenti (pulsante tra I/O e massa, resistenza di pull-up tra pin digitale e positivo) l'I/O sarà alto con il pulsante non premuto, bassa con il pulsante premuto. Noi useremo la prima configurazione, qui illustrata (noi useremo il pin 13):


Fonte: arduino.cc

Per quanto riguarda gli ingressi analogici, bisogna collegare il terminale centrale del potenziometro all'ingresso analogico, e i due pin laterali a massa e al positivo, formando così un partitore di tensione. La scelta di quale pin dovrà essere a massa e quale al positivo dipende dalla direzione di rotazione: più il potenziometro è routato verso il pin con la massa, più il valore letto aumenta. Più viene routato verso il pin al positivo, più diminuisce. Ecco un'immagine del collegamento:



Fonte: arduino.cc

Nel file MIDI.zip allegato è presente uno sketch chiamato SimpleMIDI.pde (lasciate perdere l'altro sketch, per ora). Estraetelo dall'archivio e apritelo con Arduino. Fate l'upload sulla scheda.

ATTENZIONE!!! Dovete chiudere il programma convertitore seriale-MIDI, altrimenti la programmazione fallirà. Una volta fatto l'upload, riapritelo e configuratelo.

Avevamo lasciato Virtual DJ aperto, riprendiamolo e premiamo il tasto + verde che si trova in basso. Se non fosse già premuto, premiamo il tasto Auto-Learn. In questa immagine ho evidenziato ocn dei cerchi rossi il tasto + e il tasto Auto-Learn premuto:



Ora ruotiamo il potenziometro che abbiamo collegato all'Arduino. Se tutto è OK, Virtual DJ dovrebbe rilevarlo così:



Bene, ora assegnamo un controllo. Io voglio controllare il pitch (velocità della canzone), quindi scriverò "pitch_slider". Qui va una piccola nota: preferite sempre i controlli "slider" (volume_slider, crossfader_slider, pitch_slider, ...) ai controlli semplici (volume, crossfader, pitch, ...) se usate una console hardware. I controlli slider sganciano il controllo hardware se il software lo cambia, e lo riagganciano quando il valore mandato dall'hardware si aggancia a quello sftware. I controlli semplici impostano il controllo al valore mandato dall'hardware. Per chiarire la differenza, faccio un esempio. Avete uno slider per il pitch che usa il controllo semplice "pitch". Sta per finire la canzone sul primo deck, ne caricate una sul secondo, il crossfader muta il secondo, mettete il secondo in cuffia, fate play. Usate sync per sincronizzare i bpm. Supponiamo che la canzone sul primo deck abbia 120 bpm, e questa 140. Il sync porterà la seconda a 120 bpm agendo sul pitch. Ora il pitch software e quello hardware non sono in sincronismo, perchè il cursore hardware non si muove. Mi allineo i beat, mixo le canzoni, rimane solo la seconda. Ora voglio rimettere lentamente il pitch a posto, da 120 a 140. Muovo leggermente il pitch sulla console hardware e... oops! Il pitch fa un salto per raggiungere il valore della console hardware, e un cambiamento improvviso di pitch (anche piccolo) lo sentono anche i sordi. Se avessi usato pitch_slider, il software avrebbe sganciato il controllo hardware alla pressione di Sync, finchè non si fosse allineato a quello software. Una volta ri-agganciato, avrei potuto riportare il pitch lentamente al suo valore.



Ora premiamo di nuovo il + verde, assicuriamoci che Auto-Learn sia premuto, premiamo il bottone sull'Arduino e assegnamoli la funzione pause_stop (prima pressione pausa, seconda pressione stop, pressioni successive naviga tra i cue):



Ora provate a caricare un brano sul deck 1 e a fare play. Giocate un po' con il pitch e provate il bottone

C'è una piccola nota riguardo i deck: NON è vero che i comandi inviati sul canale 0 vadano al deck 1 e quelli sul canale 1 al deck 2. I comandi, a qualunque canale appartengano, vanno al deck su cui è stato premuto il tasto "Play" per ultimo. Per assegnare un determinato deck ad un comando, bisogna preporre "deck numeroDeck". Per esempio, se uso due potenziometri per il pitch su due canali diversi per gestire i due deck, posso assegnare ad uno "deck 1 pitch_slider" e all'altro "deck 2 pitch_slider".

Ora una parentesi su come modificare il mio codice. Ritengo che i commenti siano abbastanza esplicativi, ma se avete dubbi chiedete pure nei commenti. Mi scuso se le variabili e i nomi delle funzioni sono in inglese, ma sono abituato a scrivere codice che poi leggeranno altre persone non italiane.

Per configurare gli ingressi, bisogna agire su degli array. Iniziamo con i pin analogici. Bisogna inserire in analogPin i pin analogici da usare. In analogCC vanno inseriti i CC corrispettivi (il primo CC sia abbinerà al primo pin, il secondo CC al secondo pin, ...). In analogChan bisogna inserire i numeri di canali (il primo canale si abbina al primo pin, ...). analogOff è un po' strano, potete impostarlo a 0 per tutti i pin. Se per qualche motivo avete la necessità di trasmettere il valore solo se supera di una certa soglia il valore precedente, allora potete usarlo. analogOff esprime, per ogni pin, la differenza minima di valore (su scala 0 - 127!) che ci deve essere tra la lettura e il valore precedente per essere trasmesso. L'ho inserito solo perchè... non si sa mai ehehehe. Infine, analogStat conterrà i valori dei pin. Scrivete il numero di pin analogici tra le parentesi quadre, per dimensionarlo opportunamente.

Per gli ingressi digitali, è tutto uguale: inserite i pin in digitalPin, la nota (0 - 127) in digitalNote, il canale in digitalChan e il numero di pin tra parentesi quadre in digitalStat.

Esempio, supponiamo di usare 4 ingressi analogici, due con CC 0 e 3 su canale 0 e su pin analogici 0 e 1 e altri due con CC 0 e 3 su canale 2 con pin analogici 2 e 3, tutti con offset a 0. Mettiamo anche 4 bottoni, due sulle note 0 e 1 canale 0 con pin digitali 2 e 3, e altri due sulle note 0 e 1 su canale 1 con pin digitali 4 e 5.

/* Inserire qui i pin analogici, i CC, i canali e gli offset */
/* L'offset è su scala 0 ... 127, il canale 0 ... 15 */
/* Impostare analogStat con il numero di pin */
unsigned char analogPins[]   = {0, 1, 2, 3};
unsigned char analogCC[]     = {0x00, 0x03, 0x00, 0x03};
unsigned char analogChan[] = {0, 0, 1, 1};
unsigned char analogOff[]      = {0, 0, 0, 0};
unsigned char analogStat[4];
/* Inserire qui i pin digitali, le note e i canali (0 ... 15) */
/* Impostare digitalStat con il numero di pin */
unsigned char digitalPins[]  = {2, 3, 4, 5};
unsigned char digitalNote[]  = {0x00, 0x01, 0x00, 0x01};
unsigned char digitalChan[] = {0, 0, 1, 1};
unsigned char digitalStat[4];

Ora passiamo a vedere il MIDI a 14 bit, che permette di utilizzare valori da 0 a 16383 invece che da 0 a 127.

Il MIDI 14 bit

Il MIDI a 14 bit è un'estensione del MIDI che permette di codificare il valore utilizzando 14 bit al posto di 7, per un totale di 16384 valori diversi.

Per inviare un dato utilizzando il MIDI 14 bit vengono usati 2 comandi MIDI: uno che definisce l'MSB (Most Significant Byte) e l'altro che definisce l'LSB (Least Significant Byte). Non sono esattamente byte, sono 7 bit: l'MSB definisce i 7 più significativi e l'LSB i 7 meno significativi. Il MIDI 14 bit non può essere usato con tutti i comandi: gli unici che lo supportano sono Control/Mode Change e Pitch Wheel Range.

Ricontrolliamo la tabella: http://www.midimountain.com/midi/midi_mode.html

L'ultima colonna indica se il pitch byte definisce l'MSB o l'LSB. Come potete vedere il codice del comando per LSB è sempre pari al codice MSB + 0x20.

Fin qui non è molto difficile: si legge l'ingresso analogico, si manda l'MSB, si manda l'LSB. Il problema è che l'ADC dell'ATMEGA ha una risoluzione di 10 bit. Se prendessimo i 10 bit, li spezzassimo in 3 bit per l'MSB e 7 per l'LSB potremmo arrivare solo a 1/16 della corsa del comando. Le soluzioni sono due:



  1. Facciamo così, però impostiamo il programma per accettare valori da 0 a 1023 invece che da 0 a 16383
  2. Spostiamo a sinistra di 4 bit il dato, usando i 7 bit più significativi della lettura per l'MSB e gli altri 3 bit come LSB, spostati di 4 bit a sinistra

Virtual DJ permette di fare la prima, però inserirò anche il codice per la seconda, in modo che possa essere usata da programmi che non hanno l'opzione per scegliere il range di valori.

La prima cosa da fare è creare un device file per Virtual DJ. Prima abbiamo usato il Simple MIDI Wrapper, ma non supporta il midi 14 bit. Virtual DJ usa un device file con le impostazioni per l'interfaccia, i codici ecc. e un mapper file per decidere a che comando mappare i controlli.

La wiki di Virtual DJ spiega chiaramente ogni cosa, pertanto non commenterò molto riguardo al device file. Prendete il file MyMIDI.xml nell'archivio MIDI.zip allegato, e copiatelo Documenti/VirtualDJ/Devices. Poi prendete il file "MyMIDI mapping.xml" e copiatelo in Documenti/VirtualDJ/Mappers.

Ho assegnato il nome SLIDER0 al CC 0, e il nome BUTTON0 al bottone sulla nota 0. SLIDER0 utilizza il MIDI 14-bit: il comando MSB è 0x00 e quello LSB 0x20.

Ho già mappato il SLIDER0 al pitch e il BUTTON0 al pausa/stop, come prima, ma potete intervenire sul mapping direttamente da Virtual DJ. Se volete però aggiungere altri comandi, dovete intervenire sul device file. Una nota riguarda il SysEx. Virtual DJ invia una richiesta SysEx per indentificare il dispositivo. Il dispositivo MIDI dovrebbe rispondere con un codice univoco, e poi Virtual DJ lo confronta con i codici dei vari dispositivi per trovare quello giusto. Il programa MIDI - seriale non supporta i comandi con lunghezza diversa da 3 byte, come i SysEx. Quindi ho specificato le interfaccie In e Out per il dispositivo (ho usato In 2 e Out 1, come abbiamo impostato prima).

Ecco un link alla pagina della wiki di Virtual DJ che spiega come creare i device file: http://www.virtualdj.com/wiki/ControllerDefinitionMIDI.html

Potete notare che i deck sono impostabili direttamente dal file, senza specificarlo nel mapper.

Per quanto riguarda l'Arduino, fate l'upload dello sketch MIDI14.pde sulla scheda. L'unica differenza è che ho inserito la funzione send_midi14_cc, che si occupa di inviare un comando Control Change. Gli argomenti sono canale, pitch byte e dati. Nel pitch byte viene indicato il codice per l'MSB: la funzione ricava internamente il codice per l'LSB. Altra modifica al codice è stata il cambiamento di analogStat da unsigned char a unsigned short. Ultima piccola modifica: gli offset sono espressi nel range 0 ... 1023, non più 0 ... 127.

Il device file per virtual DJ è configurato per accettare valori da 0 a 1023, cioè il range del nostro ADC. Dove questo non sia possibile, è necessario sostituire la linea di codice:

send_midi14_cc(analogChan[i], analogCC[i], reading);

Con: send_midi14_cc(analogChan[i], analogCC[i], reading << 4);

In modo da usare la soluzione 2 (vedi sopra).
Questo è tutto per quanto riguarda il MIDI. Divertitevi, modificate il codice

Il mio Arduino su breadboard

L'Arduino che ho costruito su breadboard è tutto sommato semplice - Un ATMEGA168, un MAX232 e un convertitore RS232 - USB da 3€ acquistato su Dealextreme (supporta tutte le linee RS232).

Ecco lo schema che ho fatto (il file Breadboard.png è presente anche nel file MIDI.zip allegato):



Piccola nota per chi, come me, ha un cavo null modem e non un cavo seriale pin-to-pin: è necessario invertire i collegamenti di TX e RX sulla porta seriale (pin 7 del MAX232 al pin 3 della seriale, pin 8 del MAX232 al pin 2 della seriale). Inoltre bisogna usare il pin 6 al posto del 4, quindi il pin 13 del MAX232 va collegato al pin 6 della seriale.

Io ho poi attaccato il cavo ad un convertitore seriale-USB. Se il convertitore supporta tutte le linee bene, ma alcuni convertitore supportano solo TX e RX. In questo caso dovrete fare a meno dell'auto-reset, usare il bootloader della Arduino NG e resettare manualmente appena inizia l'upload (tuttavia con quel bootloader il vostro sketch partirà dopo circa 10 secondi dal reset).

Anche se lo schema la indica, ecco qui la corrispondenza dei pin tra l'ATMEGA168/328 e i pin del software Arduino:




Fonte: arduino.cc

La scritta rossa indica di non usare carichi a bassa impedenza sui pin digitali 11, 12 e 13 (pin fisici 17, 18 e 19) quando si usa l'header per la programmazione in-circuit. Tuttavia noi non usiamo questo header, e quando facciamo il bootload (se non usiamo ATMEGA con il bootloader precaricato) è sufficiente scollegare i pin. Per chi volesse fare il bootload, ecco il programmatore che ho usato:Link inesistente
Visto che avevo già il circuito, ho semplicemente collegato tre fili (RESET, GND e MISO) e due resistenze (MOSI e SCK) dalla porta al circuito. Ho sostituito le resistenze da 1K con resistenze da 200, provate prima con resistenze da 1K però.

Le istruzioni per programmare il bootloader sono qui: http://arduino.cc/en/Hacking/ParallelProgrammer  (i due schemi sono equivalenti, sono entrambi programmatori DAPA). L'unico mio computer con porta parallela era così vecchio ( Windows 98 ) che non sono riuscito a far partire Arduino e ho programmato manualmente con avrdude, impostando lock bit, fuse e programmando il bootloader. Chi volesse più informazioni sul bootloading manuale non esiti a chiedere.

Una volta programmato il bootloader, scollegate il programmatore e collegate un LED con una resistenza tra il pin digitale 13 (pin fisico 19). NON collegatelo durante la programmazione, o questa fallirà. Resettate il vostro Arduino: il LED dovrebbe lampeggiare. Se è così, complimenti, avete un Arduino!

Ecco qualche foto della mia breadboard incasinata











Programmi utili

Midi Monitor: questo programma permette di vedere cosa passa sull'interfaccia MIDI. Utilissimo per il debug. Per funzionare correttamente su Windows 7 (ma penso anche su Vista) deve essere eseguito come amministratore. Link: http://obds.free.fr/midimon/index.html

VMCI (Virtual Midi Control Interface): questo programma si comporta come un controller MIDI virtuale. Molto utile per provare controlli senza l'Arduino. Io lo uso anche in combinazione con Midi Monitor, quando non so come impostare un qualche tipo di controllo: imposto il controllo su VMCI e vedo ciò che è stato trasmesso a Midi Monitor. Link: http://www.csounds.com/maldonado/vmci.html

Midi Trace: l'ho trovato sul sito di Virtual DJ. Permette di inviare un comando manualmente attraverso un'interfaccia MIDI e vedere la risposta. Perfetto se dovete configurare un controller esterno con Virtual DJ e vi serve l'id SysEx (il comando pre-impostato è proprio quello per richiedere l'id SysEx). Link: http://www.virtualdj.com/download/miditrace.exe

Conclusione

Spero di non avervi annoiato e sono disponibile per qualsiasi chiarimento.

Un ringraziamento particolare a TheAlu10000 che mi sopporta sempre quando gli faccio le mie domande
Buone suonate!