Differenze tra le versioni di "Inviare dati Midi da Arduino a Gambas"

Da Gambas-it.org - Wikipedia.
Riga 290: Riga 290:
 
   
 
   
 
    
 
    
  <FONT Color=gray>''°°°°°°°°°°° GESTIONE DEI SINGOLI MESSAGGI MIDI °°°°°°°°°°°''</font>
+
<FONT Color=gray>' °°°°°°°°°°° GESTIONE DEI SINGOLI MESSAGGI MIDI °°°°°°°°°°°</font>
 
   
 
   
 
  '''Public''' Sub noteon(channel As Byte, note As Byte, velocity As Byte)
 
  '''Public''' Sub noteon(channel As Byte, note As Byte, velocity As Byte)
Riga 365: Riga 365:
 
   
 
   
 
   
 
   
  <FONT Color=gray>''°°°°°°°°°°° GESTIONE DEGLI ERRORI °°°°°°°°°°°''</font>
+
  <FONT Color=gray>' °°°°°°°°°°° GESTIONE DEGLI ERRORI °°°°°°°°°°°</font>
 
   
 
   
 
  '''Public''' Sub errmsg(err As Integer) As String
 
  '''Public''' Sub errmsg(err As Integer) As String

Versione delle 16:52, 24 nov 2015

Nell'esempio preso in considerazione in questa pagina i dati Midi da inviare sono prestabiliti dal codice caricato in Arduino, e pertanto saranno inviati in modo automatizzato. In particolare 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