Fondere insieme due file MIDI: differenze tra le versioni

Da Gambas-it.org - Wikipedia.
Vai alla navigazione Vai alla ricerca
Nessun oggetto della modifica
Nessun oggetto della modifica
 
(4 versioni intermedie di uno stesso utente non sono mostrate)
Riga 2: Riga 2:


Va precisato che <SPAN Style="text-decoration:underline>la dinamica e modalità dell'esecuzione di un file Midi potrà essere influenzata in modo particolare da diverso [https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#Il_tipo_di_file_Midi tipo di file Midi], da diversa [https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#La_risoluzione_del_Tempo_Delta risoluzione del Tempo-Delta] e dagli eventuali ''[https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#I_Meta-eventi Meta-eventi]'' contenuti nell'altro file Midi</span>. Pertanto si suggerisce di usare il seguente codice con due file Midi, dei quali almeno uno appartenga al tipo '''1''' di file Midi, e che posseggano medesima risoluzione del Tempo-Delta, nonché medesimo valore del ''Meta-evento'' relativo al Tempo metronomico.
Va precisato che <SPAN Style="text-decoration:underline>la dinamica e modalità dell'esecuzione di un file Midi potrà essere influenzata in modo particolare da diverso [https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#Il_tipo_di_file_Midi tipo di file Midi], da diversa [https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#La_risoluzione_del_Tempo_Delta risoluzione del Tempo-Delta] e dagli eventuali ''[https://www.gambas-it.org/wiki/index.php/La_struttura_di_un_file_Midi#I_Meta-eventi Meta-eventi]'' contenuti nell'altro file Midi</span>. Pertanto si suggerisce di usare il seguente codice con due file Midi, dei quali almeno uno appartenga al tipo '''1''' di file Midi, e che posseggano medesima risoluzione del Tempo-Delta, nonché medesimo valore del ''Meta-evento'' relativo al Tempo metronomico.
  '''Public''' Sub Main()
  Public Sub Main()
   
   
   Dim s1, s2, mtrk1, mtrk2, file_finale As String
   Dim s1, s2, mtrk1, mtrk2, file_finale As String
   Dim nt1, nt2 As Byte
   Dim nt1, nt2 As Byte
   
   
   s1 = File.Load("<FONT color=gray>''/percorso/del/'''primo'''/file.mid''</font>")
   s1 = File.Load("<FONT color=darkgreen>''/percorso/del/'''primo'''/file.mid''</font>")
   s2 = File.Load("<FONT color=gray>''/percorso/del/'''secondo'''/file.mid''</font>")
   s2 = File.Load("<FONT color=darkgreen>''/percorso/del/'''secondo'''/file.mid''</font>")
   
   
  <FONT color=gray>' ''Innanzitutto verifica se almeno uno dei due file appartenga al tipo "1" di file Midi:''</font>
  <FONT color=gray>' ''Innanzitutto verifica se almeno uno dei due file appartenga al tipo "1" di file Midi:''</font>
Riga 14: Riga 14:
   
   
  <FONT color=gray>' ''Assicura che il 10° byte del Blocco d'intestazione del file Midi possegga il valore relativo al tipo "1" di file Midi.''
  <FONT color=gray>' ''Assicura che il 10° byte del Blocco d'intestazione del file Midi possegga il valore relativo al tipo "1" di file Midi.''
  ' ''Assegna il valore "1" mediante la funzione "kByte()", affinché sia assegnata precisamente la rappresentazione in memoria del valore 1 e non quella ASCII (che in memoria risulta essere &31). Si potrebbe usare in alternativa anche la funzione "'''Chr()'''".''</font>
  ' ''Assegna il valore "1" mediante la funzione "MkByte()", affinché sia assegnata precisamente la rappresentazione in memoria del valore 1 e non quella ASCII (che in memoria risulta essere &31). Si potrebbe usare in alternativa anche la funzione "'''Chr()'''".''</font>
   Mid(s1, 10, 1) = MkByte(1)
   Mid(s1, 10, 1) = MkByte(1)
   
   
  <FONT color=gray>' ''Individua il numero di tracce presenti nel primo file:''</font>
  <FONT color=gray>' ''Individua il numero di tracce presenti nel primo file:''</font>
   nt1 = Asc(s1[11, 1])
   nt1 = Asc(s1[11, 1]) <FONT color=gray>' ''...oppure anche:'' '''Byte@(s1[11, 1])''', che in questo caso è usata per restituire un risultato opposto a quello della funzione "MkByte()"</font>
  <FONT color=gray>' ''Individua il numero di tracce presenti nel secondo file:''</font>
  <FONT color=gray>' ''Individua il numero di tracce presenti nel secondo file:''</font>
   nt2 = Asc(s2[11, 1])
   nt2 = Asc(s2[11, 1]) <FONT color=gray>' ''...oppure anche:'' '''Byte@(s2[11, 1])''', che in questo caso è usata per restituire un risultato opposto a quello della funzione "MkByte()"</font>
    
    
  <FONT color=gray>' ''Raccoglie del 1° file Midi solo il valore della risoluzione del Tempo-Delta e la parte delle tracce "MTrk":''</font>
  <FONT color=gray>' ''Raccoglie del 1° file Midi solo il valore della risoluzione del Tempo-Delta e la parte delle tracce "MTrk":''</font>
   mtrk1 = s1[12, s1.Len]
   mtrk1 = s1[12, s1.Len]
  <FONT color=gray>' ''Raccoglie del 2° file Midi solo la parte delle tracce "MTrk":''</font>
  <FONT color=gray>' ''Raccoglie del 2° file Midi solo la parte delle tracce "MTrk":''</font>
   mtrk2 = s2[14, s2.Len]
   mtrk2 = s2[14, s2.Len]
<FONT color=gray>' ''Estrae e confronta il tempo metronomico dal primo blocco di tracce.''
' ''Tenere comunque in considerazione che secondo lo standard MIDI, se un file non contiene esplicitamente il meta-evento 0xFF 0x51 ("Set Tempo"), il tempo metronomico predefinito è uguale a 500.000 microsecondi per quarto di nota, cioè 120 bpm.''
' ''Pertanto, se il parser non trova il meta-evento Set Tempo, esso assume tempo = 500000 microsecondi.''</font>
  tempo1 = TrovaTempoMetronomico(mtrk1)
  tempo2 = TrovaTempoMetronomico(mtrk2)
  If tempo1 <> tempo2 Then
    Print "\e[31m\e[1mATTENZIONE: i due file hanno tempi metronomici differenti!\e[0m\n"
  Endif
   
   
   file_finale = s1[0, 11] & MkByte(nt1 + nt2) & mtrk1 & mtrk2
   file_finale = s1[0, 11] & MkByte(nt1 + nt2) & mtrk1 & mtrk2
Riga 33: Riga 41:
   File.Save("/tmp/nuovo.mid", file_finale)
   File.Save("/tmp/nuovo.mid", file_finale)
   
   
  '''End'''
  End
<FONT color=gray>' ''Funzione per trovare il meta-evento 0xFF 0x51 (Set Tempo)''</font>
Private Function TrovaTempoMetronomico(mtrk As String) As Integer
  Dim i As Integer = 0
  Dim tempo As Integer
  Dim delta, vlqLen As Integer
  While i < mtrk.Len - 5
<FONT color=gray>' ''Legge delta-time:''</font>
    delta = LeggiVLQ(mtrk, i, vlqLen)
    i += vlqLen
    If Asc(mtrk[i]) = &HFF Then
      If Asc(mtrk[i + 1]) = &H51 And Asc(mtrk[i + 2]) = 3 Then
        tempo = Asc(mtrk[i + 3]) * 65536 + Asc(mtrk[i + 4]) * 256 + Asc(mtrk[i + 5])
        Return tempo
      Else
<FONT color=gray>' ''Salta meta-evento generico:''</font>
        Dim metaLen As Integer
        metaLen = LeggiVLQ(mtrk, i + 2, vlqLen)
        i += 2 + vlqLen + metaLen
      Endif
    Else If Asc(mtrk[i]) >= &H80 And Asc(mtrk[i]) <= &HEF Then
<FONT color=gray>' ''Evento MIDI standard: salta 2 o 3 byte in base al tipo:''</font>
      Dim evType As Integer = Asc(mtrk[i]) \ 16
      Select Case evType
        Case 8 To 11
          i += 3
        Case 12, 13
          i += 2
        Case 14
          i += 3
        Case Else
          i += 1 ' fallback
      End Select
    Else
<FONT color=gray>' ''SysEx o byte non riconosciuto:''</font>
      i += 1
    Endif
  Wend
<FONT color=gray>' ''Se non trovato, ritorna valore di default (120 bpm = 500.000 μs):''</font>
  Return 500000
End
<FONT color=gray>' ''Funzione per leggere VLQ(Variable Length Quantity)da posizione 'start'''</font>
Private Function LeggiVLQ(dat As String, start As Integer, ByRef nbytes As Integer) As Integer
  Dim val As Integer = 0
  Dim b As Integer
  nbytes = 0
  Repeat
    b = Asc(dat[start + nbytes])
    val = (val * 128) Or (b And &H7F)
    Inc nbytes
  Until (b And &H80) = 0 Or nbytes > 4
  Return val
End

Versione attuale delle 15:14, 26 lug 2025

Mostriamo di seguito un codice, sufficientemente commentato, per fondere (mischiare) due file Midi con le sole funzioni di Gambas.

Va precisato che la dinamica e modalità dell'esecuzione di un file Midi potrà essere influenzata in modo particolare da diverso tipo di file Midi, da diversa risoluzione del Tempo-Delta e dagli eventuali Meta-eventi contenuti nell'altro file Midi. Pertanto si suggerisce di usare il seguente codice con due file Midi, dei quali almeno uno appartenga al tipo 1 di file Midi, e che posseggano medesima risoluzione del Tempo-Delta, nonché medesimo valore del Meta-evento relativo al Tempo metronomico.

Public Sub Main()

 Dim s1, s2, mtrk1, mtrk2, file_finale As String
 Dim nt1, nt2 As Byte

 s1 = File.Load("/percorso/del/primo/file.mid")
 s2 = File.Load("/percorso/del/secondo/file.mid")

' Innanzitutto verifica se almeno uno dei due file appartenga al tipo "1" di file Midi:
 If Asc(s1[9, 1]) + Asc(s2[9, 1]) == 0 Then Error.Raise("Nessuno dei due file appartiene al tipo '1' di file Midi !")

' Assicura che il 10° byte del Blocco d'intestazione del file Midi possegga il valore relativo al tipo "1" di file Midi.
' Assegna il valore "1" mediante la funzione "MkByte()", affinché sia assegnata precisamente la rappresentazione in memoria del valore 1 e non quella ASCII (che in memoria risulta essere &31). Si potrebbe usare in alternativa anche la funzione "Chr()".
 Mid(s1, 10, 1) = MkByte(1)

' Individua il numero di tracce presenti nel primo file:
 nt1 = Asc(s1[11, 1]) ' ...oppure anche: Byte@(s1[11, 1]), che in questo caso è usata per restituire un risultato opposto a quello della funzione "MkByte()"
' Individua il numero di tracce presenti nel secondo file:
 nt2 = Asc(s2[11, 1]) ' ...oppure anche: Byte@(s2[11, 1]), che in questo caso è usata per restituire un risultato opposto a quello della funzione "MkByte()"
 
' Raccoglie del 1° file Midi solo il valore della risoluzione del Tempo-Delta e la parte delle tracce "MTrk":
 mtrk1 = s1[12, s1.Len]
' Raccoglie del 2° file Midi solo la parte delle tracce "MTrk":
 mtrk2 = s2[14, s2.Len]

' Estrae e confronta il tempo metronomico dal primo blocco di tracce.
' Tenere comunque in considerazione che secondo lo standard MIDI, se un file non contiene esplicitamente il meta-evento 0xFF 0x51 ("Set Tempo"), il tempo metronomico predefinito è uguale a 500.000 microsecondi per quarto di nota, cioè 120 bpm.
' Pertanto, se il parser non trova il meta-evento Set Tempo, esso assume tempo = 500000 microsecondi.
 tempo1 = TrovaTempoMetronomico(mtrk1)
 tempo2 = TrovaTempoMetronomico(mtrk2)

 If tempo1 <> tempo2 Then
   Print "\e[31m\e[1mATTENZIONE: i due file hanno tempi metronomici differenti!\e[0m\n"
 Endif

 file_finale = s1[0, 11] & MkByte(nt1 + nt2) & mtrk1 & mtrk2

 File.Save("/tmp/nuovo.mid", file_finale)

End


' Funzione per trovare il meta-evento 0xFF 0x51 (Set Tempo)
Private Function TrovaTempoMetronomico(mtrk As String) As Integer

 Dim i As Integer = 0
 Dim tempo As Integer
 Dim delta, vlqLen As Integer

 While i < mtrk.Len - 5
' Legge delta-time:
   delta = LeggiVLQ(mtrk, i, vlqLen)
   i += vlqLen

   If Asc(mtrk[i]) = &HFF Then
     If Asc(mtrk[i + 1]) = &H51 And Asc(mtrk[i + 2]) = 3 Then
       tempo = Asc(mtrk[i + 3]) * 65536 + Asc(mtrk[i + 4]) * 256 + Asc(mtrk[i + 5])
       Return tempo
     Else
' Salta meta-evento generico:
       Dim metaLen As Integer
       metaLen = LeggiVLQ(mtrk, i + 2, vlqLen)
       i += 2 + vlqLen + metaLen
     Endif
   Else If Asc(mtrk[i]) >= &H80 And Asc(mtrk[i]) <= &HEF Then
' Evento MIDI standard: salta 2 o 3 byte in base al tipo:
     Dim evType As Integer = Asc(mtrk[i]) \ 16
     Select Case evType
       Case 8 To 11
         i += 3
       Case 12, 13
         i += 2
       Case 14
         i += 3
       Case Else 
         i += 1 ' fallback
     End Select
   Else
' SysEx o byte non riconosciuto:
     i += 1
   Endif
 Wend

' Se non trovato, ritorna valore di default (120 bpm = 500.000 μs):
 Return 500000

End


' Funzione per leggere VLQ(Variable Length Quantity)da posizione 'start'
Private Function LeggiVLQ(dat As String, start As Integer, ByRef nbytes As Integer) As Integer

 Dim val As Integer = 0
 Dim b As Integer
 nbytes = 0

 Repeat
   b = Asc(dat[start + nbytes])
   val = (val * 128) Or (b And &H7F)
   Inc nbytes
 Until (b And &H80) = 0 Or nbytes > 4

 Return val

End