Alsa e Gambas: Registrare messaggi Midi

Da Gambas-it.org - Wikipedia.
Versione del 18 dic 2011 alle 17:29 di Vuott (Discussione | contributi) (Ricezione e memorizzazione dei dati Midi)

In questa pagina tratteremo del caso in cui vengono ricevuti in entrata dati Midi e memorizzati, al fine di creare al termine un file Midi (.mid).
Possiamo pensare di effettuare la registrazione dei dati Midi in entrata:

  • attraverso l'utilizzazione di un altro specifico applicativo in C di supporto, come ad esempio arecordmidi, diciamo in modalità demone;
  • oppure mediante la realizzazione in Gambas di apposito algoritmo, come parte del nostro applicativo appositamente dedicata o come applicativo a se stante.

Qui prenderemo in considerazione ovviamente la seconda soluzione. Per ottenere il risultato finale, come per ogni altro caso, si potrà seguire il percorso che si preferisce ed utilizzare, dunque, le funzioni e le istruzioni ritenute più opportune. Appresso proponiamo una soluzione fra le molte possibili, sottolineando e ponendo in rilievo soprattutto i passaggi più importanti da considerare particolarmente.

Ricezione e memorizzazione dei dati Midi

L'esempio, che faremo per esporre l'argomento, prevede la ricezione di dati Midi (per semplicità terremo conto solo dei messaggi NoteON e NoteOFF) provenienti da un dispositivo esterno, per esempio una tastiera, senza uso delle funzioni esterne di ALSA, bensì mediante l'intercettazione dei dati del dispositivo dal file device presente, come già abbiamo avuto modo di imparare in altro capitolo, nel percorso: "/dev/snd/midiC2D0".

exStrum As File
d As Integer
a As New Byte[]
aa As New Float[]
dd As Integer
bpm As Integer = 120

f As Integer
bl As Integer
ndt As Integer

tick As New Byte[]
totaleMillisecondi As New Long[]

ggg[4] As Byte
s[4] As Byte
tdk As Integer
jk As Integer

numeratore As Byte = 4
denomSpin As Integer = 4
denominatore As Byte = 2
alterazioni As Byte = 0
modo As Byte = 0


Public Sub Form_Open()

 SpinBox1.Visible = False
 SpinBox2.Visible = False
 ComboBox1.Visible = False
 Label1.Visible = False

 exStrum = Open "/dev/snd/midiC2D0" For Read Watch

End

Public Sub File_Read()

Dim b As Byte

 Read #exStrum, b
 If b = 254 Then Return  ' Evita di raccogliere l'evento Midi: "Active Sensing"
 
  
a.Add(d)
a[d] = b

Inc d
  
f = d - 1

' ricava anno, mese, giorno, ora:muniti:secondi.millisecondi di ciascun evento:
' serviranno per determinare il Tempo Delta di ciascuno di essi.
  aa.Add(dd)
  aa[dd] = Now  
  Inc dd
  
End

Creare blocchi di dati

Una volta ricevuti tutti i dati dalla tastiera Midi esterna, con un tasto procederemo a calcolare innanzitutto quanti dati sono stati ricevuti, successivamente creeremo dei blocch contenenti 3 dati Midi. Tali blocchi rappresentano ciascuno un Messaggio Midi (NoteON oppure NoteOFF).

Public Sub Button1_Click()

 Dim k, j, n As Integer
 
 
 For k = 0 To (d - 1) / 3    '.../3 perché ogni messaggio Midi è formato da 3 dati
   
' muta tutti gli eventuali messaggi Midi NoteON con Velocità = 0 e di qualunque canale (es.: 90 nn 00)
' in corrispondenti e coerenti NoteOFF (80 nn 00):
 If a[n] > 143 Or a[n] < 160 And a[n + 2] = 0 Then
   a[n] = a[n] - 16
 Endif

 n = n + 3

   For j = 0 To 2
     blocco.Add(bl)  ' bl rappresenta il numero di blocchi da 3 dati (1 messaggio Midi)
     blocco[bl] = blocco[bl] & Chr(a[ndt])

     Inc ndt
   Next
   
     Inc bl
     
 Next

End

Calcolo del Tempo Delta

Creati i blocchi contenenti i dati di ciascun Messaggio Midi, possiamo passare ad individuare ed impostare il Tempo Delta che separa ciascun blocco (Messaggio Midi).

Public Sub Button2_Click()  ' calcola in Tempo Delta

Dim ee As Integer
Dim tick As New Integer[]
Dim hh As Integer
Dim intero As Long
Dim frazione As Integer

For ee = 0 To dd Step 3  ' Ogni tre dati (i 3 dati di ciascun Messaggio Midi: NoteON e NoteOFF)...
 If ee = dd Then Return
' restituisce i millisecondi trascorsi dalla "data Gambas" fino a ieri:
  intero = (Fix(aa[ee]) * 86400000) ' vedi nota {1}

' restituisce i millisecondi dalla mezzanotte del giorno attuale
' sino al presente attimo:
  frazione = (Fix(Frac(aa[ee]) * 100000000))

 totaleMillisecondi.Add(hh)
 totaleMillisecondi[hh] = intero + frazione

 tick.Add(hh)
 If hh > 0 Then
   
' procede ad ottenere il valore in tick del Tempo Delta
' basandolo sulla durata di un impulso (tick) relativo ad una data risoluzione del PPQN:
 tick[hh] = (totaleMillisecondi[hh] - totaleMillisecondi[hh - 1]) / (((60000000 / bpm) / 384) / 1000)

' corregge lo sfalzamento dell'orologio (in questo nostro caso di 157 ms)
tick[hh] = tick[hh] - ((tick[hh] * 157) / 1000)

' chiama la subroutine per la conversione e creazione del Tempo Delta in tick
rappr(tick[hh])
  
 Endif
   
   Inc hh
   
Next
 
End

Creare i valori per il Tempo Delta

Come abbiamo già avuto modo di vedere nella pagina "Ricevere dati da uno Standard Midi File", il Tempo Delta con valore superiore a 127 viene definito non con il suo valore reale, bensì con una sua rappresentazione. Pertanto, nel file Midi non avremo i valori reali dei Midi tick superiori al valore 127, tenuto conto della risoluzione per nota da 1/4 del Tempo Delta presente nell'Header Chunk, bensì una loro rappresentazione esadecimale.{2}

Public Sub rappr(deTick As Integer)

Dim dT As Integer
Dim aaa, bbb, ddd, eee, hhh As Integer
Dim fff As Byte

 dT = deTick

 bbb = dT Mod 128
 ddd = CInt(dT / 128)
 
  ggg[hhh] = bbb
 
 While ddd > 0
 
   eee = ddd Mod 128
   fff = eee Or 128
   ddd = CInt(ddd / 128)
   
 Inc hhh
 
   ggg[hhh] = fff

 Wend

  For aaa = hhh To 0 Step -1
   Inc tdk
   s[aaa] = ggg[aaa]
   bloccoTD[jk] = bloccoTD[jk] & Chr(s[aaa]) ' bloccoTD è la variabile riempita dal valore del Tempo Delta
  Next

' pulisce la variabile "s"
 For aaa = hhh To 0 Step -1
  s[aaa] = 0
 Next

Inc jk

End

Aggiunta dei principali Meta-Eventi Midi

Anche a scopo didattico aggiungeremo un'altra traccia nel file Midi, la traccia del Tempo, nella quale porremo i dati relativi ai Meta-eventi della Suddivisione della Misura, del Tempo metronomico e della Tonalità della Scala musicale del brano.

Public Sub Button3_Click() ' immette valore del tempo metronomico

 bpm = InputBox("Immetti bpm:")

End

Public Sub Button4_Click()  ' immette valori della suddivisione della misura

 SpinBox1.Visible = True
 SpinBox2.Visible = True

End

Public Sub SpinBox1_Change()  ' idem

 numeratore = SpinBox1.Value

End

Public Sub SpinBox2_Change()  ' idem

 denomSpin = SpinBox2.Value
 
 denominatore = Log(denomSpin) / Log(2)

End


Public Sub Button5_Click()  ' immette i valori della tonalità della Scala

 ComboBox1.Visible = True
 ComboBox1.Text = "Do M"
 alterazioni = 0
 Label1.Visible = True
 Label1.Text = "relativa di La m"

End

Public Sub ComboBox1_Change()   ' idem

 Select Case ComboBox1.Text
   Case "Do# M"
     alterazioni = 7
     Label1.Text = "relativa di La# m"
   Case "Fa# M"
     alterazioni = 6
     Label1.Text = "relativa di Re# m"
   Case "Si M"
     alterazioni = 5
     Label1.Text = "relativa di Sol# m"
   Case "Mi M"
     alterazioni = 4
     Label1.Text = "relativa di Do# m"
   Case "La M"
     alterazioni = 3
     Label1.Text = "relativa di Fa# m"
   Case "Re M"
     alterazioni = 2
     Label1.Text = "relativa di Si m"
   Case "Sol M"
     alterazioni = 1
     Label1.Text = "relativa di Mi m"
   Case "Do M"
     alterazioni = 0
     Label1.Text = "relativa di La m"
   Case "Fa M"
     alterazioni = -1
     Label1.Text = "relativa di Re m"
   Case "Sib M"
     alterazioni = -2
     Label1.Text = "relativa di Sol m"
   Case "Mib M"
     alterazioni = -3
     Label1.Text = "relativa di Do m"
   Case "Lab M"
     alterazioni = -4
     Label1.Text = "relativa di Fa m"
   Case "Reb M"
     alterazioni = -5
     Label1.Text = "relativa di Sib m"
   Case "Solb M"
     alterazioni = -6
     Label1.Text = "relativa di Mib m"
   Case "Dob M"
     alterazioni = -7
     Label1.Text = "relativa di Lab m"
 End Select

End


Ora siamo, dunque, pronti per l'ultima routine, quella che raggrupperà le istruzioni per la realizzazione dell'ultima fase: la costruzione del file standard Midi .mid, che esporremo nel seguente capitolo: "Salvataggio dei dati Midi", ed al quale rimandiamo.

Note

[1] Ricordiamo che la viariabile aa[] è di tipo Float.

[2] Dunque, prima ancora di poter scrivere, ossia di salvare, un file Midi dovremo - fra l'altro - individuare l'algoritmo per trasformare i valori decimali "reali" superiori a 127 del Tempo Delta (ossia del numero dei Midi tick, tenuto conto - ripetiamo - della risoluzione per nota da 1/4 presente nell'Header Chunk) nella loro rappresentazione esadecimale:

Public g[4] As Byte ' la variabile " g " è un array che conterrà i valori esadecimali costituenti la rappresentazione esadecimale dei Midi tick
Public h As Integer   ' la variabile " h " conterrà la quantità di byte che costituiscono la rappresentazione esadecimale dei Midi tick

Public Sub Button2_Click()

 Dim a, b, c, d, e As Integer
 Dim rob As Byte
 
 h = 0 
 
 a = ...' la variabile " a " raccoglie il valore reale decimale dei Midi tick:

 b = a Mod 128
 d = CInt(a / 128)
 
  g[h] = b
 
 While d > 0
 
   e = d Mod 128
   rob = e Or 128
   d = CInt(d / 128)
   
 Inc h
 
   g[h] = rob

 Wend

For a = h To 0 Step -1
  g[a] = Hex$(g[a], 2)
' facciamo mostrare distintamente a scopo didattico ciascun valore della "rappresentazione" esadecimale del valore dei Midi tick:
   Print g[a]
Next

End