Fondere insieme due file MIDI: differenze tra le versioni

Da Gambas-it.org - Wikipedia.
Vai alla navigazione Vai alla ricerca
(Creata pagina con 'Per fondere (mischiare) due file MIDI con le sole funzioni di Gambas, potrà essere utilizzato il seguente codice: '''Public''' Sub Main() Dim s1, s2, ss2, nt$, file_fina...')
 
Nessun oggetto della modifica
 
(14 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
Per fondere (mischiare) due file MIDI con le sole funzioni di Gambas, potrà essere utilizzato il seguente codice:
Mostriamo di seguito un codice, sufficientemente commentato, per fondere (mischiare) due file Midi con le sole funzioni di Gambas.
'''Public''' Sub Main()
 
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()
   
   
   Dim s1, s2, ss2, nt$, 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=darkgreen>''/percorso/del/'''primo'''/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>
  If Asc(s1[9, 1]) + Asc(s2[9, 1]) == 0 Then Error.Raise("Nessuno dei due file appartiene 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 "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)
<FONT color=gray>' ''Individua il numero di tracce presenti nel primo file:''</font>
  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>
  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>
  s1 = File.Load("''/percorso/del/primo/file.mid''")
   mtrk1 = s1[12, s1.Len]
  s2 = File.Load("''/percorso/del/secondo/file.mid''")
<FONT color=gray>' ''Raccoglie del 2° file Midi solo la parte delle tracce "MTrk":''</font>
      
  mtrk2 = s2[14, s2.Len]
  <FONT color=gray>' ''Individua il numero di tracce presenti nel primo file:''</font>
  nt1 = Asc(Mid(s1, 12, 1))
<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.Save("/tmp/nuovo.mid", file_finale)
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
   
   
<FONT color=gray>' ''Individua il numero di tracce presenti nel secondo file:''</font>
  Dim val As Integer = 0
  nt2 = Asc(Mid(s2, 12, 1))
  Dim b As Integer
  nbytes = 0
   
   
  nt$ = CStr(nt1 + nt2)
  Repeat
    b = Asc(dat[start + nbytes])
    val = (val * 128) Or (b And &H7F)
    Inc nbytes
  Until (b And &H80) = 0 Or nbytes > 4
   
   
<FONT color=gray>' ''Raccoglie solo la parte delle tracce "MTrk" del secondo file:''</font>
  Return val
  ss2 = Right(s2, Len(s2) - 14)
   
  file_finale = Left(s1, 11) & Chr(nt$) & Right(s1, Len(s1) - 12) & ss2
   
  File.Save("/tmp/nuovo.mid", file_finale)
   
   
  '''End'''
  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