Inviare dati Midi da Arduino a Gambas
Questa pagina prende in considerazione due casi:
- il caso in cui i dati Midi da inviare sono prestabiliti dal codice caricato in Arduino, e pertanto saranno inviati in modo automatizzato, ossia con i tempi stabiliti dal predetto codice;
- il caso il cui i dati Midi sono inviati quando l'utente chiude un circuito con un interruttore.
Dati inviati da Arduino in modalità automatizzata prestabilita da codice
In questo caso il ciclo infinito di Arduino procederà come segue:
- invio di un messaggio Midi Program Change per impostare lo strumento musicale da utilizzare;
- invio di un messaggio Note On (status 144, nota 64, velocità 100);
- attesa per 700 millisecondi;
- invio di un messaggio Note Off (status 128, nota 64, velocità 0);
- attesa per 200 millisecondi;
- incremento del 2° valore del Program Change relativo allo strumento musicale da utilizzare.
Dunque il codice per Arduino sarà il seguente:
byte noteON[] = {144, 64, 100}; byte noteOFF[] = {128, 64, 0}; byte controlchange[] = {176, 0, 0}; int count = 0; int str = 0; void setup() { Serial.begin(57600); /* Invia il messaggio di Control Change */ for (count=0;count<3;count++) { Serial.write(controlchange[count]); } } void loop() { /* Invia il messaggio di Program Change */ Serial.write(192); Serial.write(str); /* Invia il messaggio di Note On */ for (count=0;count<3;count++) { Serial.write(noteON[count]); } delay(700); /* Invia il messaggio di Note Off */ for (count=0;count<3;count++) { Serial.write(noteOFF[count]); } delay(200); /* Incrementa la variabile "str" per cambiare strumento musicale del soundfont bank utilizzato */ ++str; if (str==128) str = 0; }
Il programma Gambas raccoglierà i valori inviati da Arduino (è necessario attivare il Componente gb.net) e li invierà ad Alsa - attraverso un'apposita Classe secondaria che chiameremo CAlsa - per l'esecuzione sonora.
Pertanto il codice della Classe principale sarà il seguente:
Private SerialPort1 As SerialPort Private bb As New Byte[] Private Const outdevice As Integer = 14 ' client 14: «Midi Through» Private Const outport As Integer = 0 ' porta d'uscita Public alsa As CAlsa Public Sub Form_Open() Me.Center ' Crea la classe "Clasa" per poterla usare e gestire le funzioni di Alsa: With alsa = New CAlsa As "alsa" ' Apre Alsa e gli assegna un nome: .alsa_open("Gambas-Midi-Arduino") ' Sceglie la periferica su cui suonare: .setdevice(outdevice, outport) End With End Public Sub Button1_Click() With SerialPort1 = New SerialPort As "portaseriale" .PortName = "/dev/ttyUSB0" .Speed = 57600 .Parity = 0 .DataBits = 8 .StopBits = 1 .FlowControl = 0 .Open End With End Public Sub portaseriale_Read() Dim b As Byte ' Legge i dati dalla porta... Read #SerialPort1, b bb.Push(b) Select Case bb[0] Case 128 To 143 If bb.Count = 3 Then Print "NoteOff: ", bb[0], bb[1], bb[2] Print "----------------------------" bb[0] = bb[0] And 15 alsa.noteoff(bb[0], bb[1], bb[2]) alsa.flush() bb.Clear Endif Case 144 To 159 If bb.Count = 3 Then Print "NoteOn: ", bb[0], bb[1], bb[2] bb[0] = bb[0] And 15 alsa.noteon(bb[0], bb[1], bb[2]) alsa.flush() bb.Clear Endif Case 176 To 191 If bb.Count = 3 Then Print "Control Change: ", bb[0], bb[1], bb[2] bb[0] = bb[0] And 15 alsa.controller(bb[0], bb[1], bb[2]) alsa.flush() bb.Clear Endif Case 192 To 207 If bb.Count = 2 Then Print "Program Change: ", bb[0], bb[1] bb[0] = bb[0] And 15 alsa.programchange(bb[0], bb[1]) alsa.flush() bb.Clear Endif End Select End Public Sub Form_Close() If SerialPort1.Status = Net.Active Then SerialPort1.Close End
Per la gestione dei dati Midi con il sub-sistema seq di Alsa mediante alcune sue funzioni esterne creeremo un'apposita Classe secondaria, chiamata CAlsa, il cui codice sarà il seguente:
Private handle As Pointer Private id As Integer Private outport As Integer Private outq As Integer Private dclient As Byte Private dport As Byte Public ev As Pointer Private p As Stream Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2 Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3 Private Const SND_SEQ_EVENT_NOTEON As Byte = 6 Private Const SND_SEQ_EVENT_NOTEOFF As Byte = 7 Private Const SND_SEQ_EVENT_CONTROLLER As Byte = 10 Private Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11 Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 Private Const SIZE_OF_SEQEV As Integer = 32 Library "libasound:2" ' int snd_seq_open (snd_seq_t **seqp, Private Const char * name, Int streams, Int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer ' int snd_seq_set_client_name(snd_seq_t* seq, Private Const char* name) ' Set client name. Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, Private Const char* name, unsigned int caps, unsigned int type) ' Create a port - simple version. Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer ' int snd_seq_client_id (snd_seq_t * seq) ' Get the client id. Private Extern snd_seq_client_id(seq As Pointer) As Integer ' int snd_seq_alloc_named_queue (snd_seq_t * seq, const char * name) ' Allocate a queue with the specified name. Private Extern snd_seq_alloc_named_queue(seq As Pointer, name As String) As Integer ' int snd_seq_connect_to (snd_seq_t *seq, int my_port, int dest_client, int dest_port) ' Simple subscription. Private Extern snd_seq_connect_to(seq As Pointer, myport As Integer, src_client As Integer, src_port As Integer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event onto the lib buffer without draining buffer. Private Extern snd_seq_event_output_buffer(handle As Pointer, ev As Pointer) As Integer ' int snd_seq_drain_output (snd_seq_t *handle) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(handle As Pointer) As Integer ' const char* snd_strerror (int errnum) ' Returns the message for an error code. . Private Extern snd_strerror(errnum As Integer) As Pointer Public Sub alsa_open(nome As String) Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0) printerr("Apertura Alsa", err) If err < 0 Then error.RAISE("Error opening alsa") snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID="; id err = snd_seq_create_simple_port(handle, "Seq-Out", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta d'Uscita = "; err If err < 0 Then error.Raise("Error creating output port") outport = err err = snd_seq_alloc_named_queue(handle, "outqueue") printerr("Creazione della coda dei dati ", err) If err < 0 Then error.Raise("Error creating out queue") outq = err ' Alloca un evento per gestirlo: ev = Alloc(SIZE_OF_SEQEV) p = Memory ev For Write End Public Sub setdevice(client As Integer, port As Integer) Dim err As Integer dclient = client dport = port err = snd_seq_connect_to(handle, outport, client, dport) printerr("Subscribe outport", err) If err < 0 Then error.Raise("Errore nella sottoscrizione del dispositivo di Uscita !") End Private Sub prepareev(type As Byte) As Pointer Dim i As Integer Dim ts, flags, tag As Byte ' Pulisce innanzitutto l'area di memoria dei dati dell'evento Midi: For i = 0 To SIZE_OF_SEQEV - 1 Seek #p, i Write #p, 0 As Byte Next Seek #p, 0 Write #p, type As Byte ts = 0 flags = 0 Write #p, flags As Byte tag = 0 Write #p, tag As Byte Write #p, outq As Byte Write #p, ts As Integer ts = 0 Write #p, ts As Integer Write #p, id As Byte Write #p, outport As Byte Write #p, dclient As Byte Write #p, dport As Byte End ' °°°°°°°°°°° GESTIONE DEI SINGOLI MESSAGGI MIDI °°°°°°°°°°° Public Sub noteon(channel As Byte, note As Byte, velocity As Byte) Dim err As Integer prepareev(SND_SEQ_EVENT_NOTEON) Write #p, channel As Byte Write #p, note As Byte Write #p, velocity As Byte err = snd_seq_event_output_buffer(handle, ev) printerr("Noteon = ", err) End Public Sub noteoff(channel As Byte, note As Byte, velocity As Byte) Dim err As Integer prepareev(SND_SEQ_EVENT_NOTEOFF) Write #p, channel As Byte Write #p, note As Byte Write #p, velocity As Byte err = snd_seq_event_output_buffer(handle, ev) printerr("Note OFF = ", err) End Public Sub controller(channel As Byte, valore1 As Integer, valore2 As Integer) Dim err As Integer prepareev(SND_SEQ_EVENT_CONTROLLER) Write #p, channel As Byte Seek #p, 20 Write #p, valore1 As Integer Write #p, valore2 As Integer err = snd_seq_event_output_buffer(handle, ev) printerr("Controller = ", err) End Public Sub programchange(channel As Byte, valore1 As Byte) Dim err As Integer prepareev(SND_SEQ_EVENT_PGMCHANGE) Write #p, channel As Byte Seek #p, 24 Write #p, valore1 As Byte err = snd_seq_event_output_buffer(handle, ev) printerr("Program Change = ", err) End Public Sub flush() Dim err As Integer err = snd_seq_drain_output(handle) Printerr("Flush", err) End ' °°°°°°°°°°° GESTIONE DEGLI ERRORI °°°°°°°°°°° Public Sub errmsg(err As Integer) As String Return String@(snd_strerror(err)) End Private Sub printerr(operation As String, err As Integer) If err < 0 Then Print operation; ": err = "; err; " ("; errmsg(err); ")" End
Dati inviati da Arduino da parte dell'utente chiudendo il circuito con un interruttore
Paragrafo in costruzione !