Differenze tra le versioni di "Conoscere la durata di un file Midi con le sole risorse di Gambas"

Da Gambas-it.org - Wikipedia.
 
(6 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
Il procedimento, seguito per ottenere la durata in secondi di un file Midi di tipo "1", deve tenere in debita considerazione la circostanza che all'interno di un brano musicale possono insistere una o più variazioni del ''tempo metronomico''. La durata del file Midi rappresenta così la somma delle singole durate determinate dagli eventuali cambi del ''tempo metronomico'' all'interno del file Midi.
+
Il procedimento, seguito per ottenere la durata in secondi di un file Midi deve tenere in debita considerazione la circostanza che all'interno di un brano musicale possono insistere una o più variazioni del ''tempo metronomico''. La durata del file Midi rappresenta così la somma delle singole durate determinate dagli eventuali cambi del ''tempo metronomico'' all'interno del file Midi.
  
 
Ciò richiede innanzitutto la conoscenza dei seguenti elementi:
 
Ciò richiede innanzitutto la conoscenza dei seguenti elementi:
Riga 10: Riga 10:
 
Successivamente bisogna ricavare anche il ''tempo metronomico'' e la quantità di ''tick'' reali maggiore fra le tracce (MTrk), e da questi due elementi, unitamente alla costante risoluzione del ''Tempo Delta'', si otterrà la durata in secondi del "tratto finale" (per "tratto finale" qui intendiamo la quantità di ''Tempo Delta'' compresa fra l'ultimo Meta-evento FF 51 e il Meta-evento di chiusura FF 2F 00 della traccia avente la quantità maggiore di tick rispetto alle altre).
 
Successivamente bisogna ricavare anche il ''tempo metronomico'' e la quantità di ''tick'' reali maggiore fra le tracce (MTrk), e da questi due elementi, unitamente alla costante risoluzione del ''Tempo Delta'', si otterrà la durata in secondi del "tratto finale" (per "tratto finale" qui intendiamo la quantità di ''Tempo Delta'' compresa fra l'ultimo Meta-evento FF 51 e il Meta-evento di chiusura FF 2F 00 della traccia avente la quantità maggiore di tick rispetto alle altre).
  
In ogni caso va ricordato che il valore predefinito del ''tempo metronomico'' è pari a 120 bpm.
+
In ogni caso va ricordato che il valore <SPAN Style="text-decoration:underline">predefinito</span> del ''tempo metronomico'' è pari a 120 bpm.
  
 
Si potrà avere dunque, ad esempio, una situazione di questo genere:
 
Si potrà avere dunque, ad esempio, una situazione di questo genere:
Riga 42: Riga 42:
  
  
Dunque, per conoscere la durata di un <SPAN Style="text-decoration:underline">file</span> Midi di tipo 1 mediante le sole risorse di Gambas, è possibile utilizzare un codice come il seguente, nel quale si otterranno anche le durate delle singole tracce ''MTrk'' presenti nel file ed alcune generiche informazioni:  
+
Dunque, per conoscere la durata di un file Midi mediante le sole risorse di Gambas, è possibile utilizzare un codice come il seguente, nel quale si otterranno anche le durate delle singole tracce ''MTrk'' presenti nel file ed alcune generiche informazioni:  
  
 
  '''Public''' Sub Main()
 
  '''Public''' Sub Main()
Riga 54: Riga 54:
 
   Dim f, durata As Float
 
   Dim f, durata As Float
 
    
 
    
  fileMidi = "<FONT Color=gray>''/percorso/del/file.mid''</font>"
+
  fileMidi = "<FONT Color=gray>''/percorso/del/file.mid''</font>"
  Print "File Midi:        "; fileMidi
+
  Print "File Midi:        "; fileMidi
 
+
 
  fl = Open fileMidi For Read
+
  fl = Open fileMidi For Read
  If IsNull(fl) Then Error.Raise("Impossibile aprire il file !")
+
  If IsNull(fl) Then Error.Raise("Impossibile aprire il file !")
 
+
 
  Read #fl, s, 4
+
  Read #fl, s, 4
  If s <> "MThd" Then Error.Raise("Il file non è uno standard Midi !")
+
  If s <> "MThd" Then Error.Raise("Il file non è uno standard Midi !")
  Print "Dimensione:    "; Lof(fl); " byte"
+
  Print "Dimensione:    "; Lof(fl); " byte"
  Seek #fl, 9
+
  Seek #fl, 9
  Read #fl, b
+
  Read #fl, c
  If b <> 1 Then Error.Raise("Il tipo " & b & " non è supportato dal programma !")
+
  Print "Tipo file:        "; c
  Seek #fl, 11
+
  Read #fl, tr
  Read #fl, tr
+
  Print "Numero tracce:  "; Format(tr, "###")
  Print "Numero tracce:  "; Format(tr, "###")
+
  Read #fl, td
  Read #fl, td
+
  td = Rol(td, 8)
  td = Rol(td, 8)
+
  Print "Risoluzione TΔ:  "; Format(td, "###")
  Print "Risoluzione TΔ:  "; Format(td, "###")
+
  Avanza(fl, 8)
  Avanza(fl, 8)
+
 
 
+
  bpm = 120
  bpm = 120
+
 
 
+
  Repeat
  Repeat
+
    tot = 0
    tot = 0
+
    Do
    Do
+
      tot += legge_var(fl)
      tot += legge_var(fl)
+
      Read #fl, b
      Read #fl, b
+
      If b And 128 Then  <FONT Color=gray>' ''Status running''</font>
      If b And 128 Then  <FONT Color=gray>' ''Status running''</font>
+
        cmd = b
        cmd = b
+
        If b < &F1 Then ultimo = cmd
        ultimo = cmd
+
        av = 2
        av = 2
+
      Else
      Else
+
        cmd = ultimo
        cmd = ultimo
+
        av = 1
        av = 1
+
      Endif
      Endif
+
      tipocmd = Shr(cmd, 4)
      tipocmd = Shr(cmd, 4)
+
      Select Case tipocmd
      Select Case tipocmd
+
        Case 8 To 11
        Case 8 To 11
+
          Avanza(fl, av)
          Avanza(fl, av)
+
        Case 12 To 13  
        Case 12 To 13  
+
          Avanza(fl, 1)
          Avanza(fl, 1)
+
        Case 14
        Case 14
+
          Avanza(fl, av)
          Avanza(fl, av)
+
        Case 15
        Case 15
+
          If b = &F0 Then          <FONT Color=gray>' ''Evento SysEx''</font>
          If b = &F0 Then          <FONT Color=gray>' ''Evento SysEx''</font>
+
            Repeat
            Repeat
+
              b = Read #fl As Byte
              b = Read #fl As Byte
+
            Until b = &F7
            Until b = &F7
+
          Else
          Else
+
            Read #fl, b
            Read #fl, b
+
            Select Case b
            Select Case b
+
              Case &51
              Case &51
+
                durata += DurataParziale(bpm, td, tot)  
                durata += DurataParziale(bpm, td, tot)  
+
                tratto1 += tot
                tratto1 += tot
+
                tot = 0
                tot = 0
+
                Avanza(fl, 1)
                Avanza(fl, 1)
+
                hbpm = 0
                hbpm = 0
+
                For c = 2 To 0 Step -1
                For c = 2 To 0 Step -1
+
                  Read #fl, n
                  Read #fl, n
+
                  hbpm += Shl(CInt(n), 8 * c)
                  hbpm += Shl(CInt(n), 8 * c)
+
                Next
                Next
+
                bpm = 60000000 / hbpm
                bpm = 60000000 / hbpm
+
              Case &2F
              Case &2F
+
                mx = Max(mx, tot)
                mx = Max(mx, tot)
+
                Exit
                Exit
+
              Case Else
              Case Else
+
                Read #fl, b
                Read #fl, b
+
                Avanza(fl, b)
                Avanza(fl, b)
+
            End Select
            End Select
+
          Endif
          Endif
+
      End Select
      End Select
+
    Loop
    Loop
+
    Dec tr
    Dec tr
+
    If tr > 0 Then Avanza(fl, 9)
    If tr > 0 Then Avanza(fl, 9)
+
  Until tr = 0
  Until tr = 0
+
 
 
+
  fl.Close
  fl.Close
+
 
 
+
  f = DurataParziale(bpm, td, Max(mx, tratto1) - tratto1)
  f = DurataParziale(bpm, td, Max(mx, tratto1) - tratto1)
+
 
 
+
  Print "\nDURATA:      \e[31m\e[1m"; CStr(Time(0, 0, 0, (f + durata) * 1000)); "\e[0m"
  Print "\nDURATA:      \e[31m\e[1m"; CStr(Date(0, 0, 0, 0, 0, 0, (f + durata) * 1000)); "\e[0m"
+
 
 
 
 
  '''End'''
 
  '''End'''
 
    
 
    
Riga 143: Riga 142:
 
   Dim b, n As Byte
 
   Dim b, n As Byte
 
    
 
    
  For b = 1 To av
+
  For b = 1 To av
    Read #mf, n
+
    Read #mf, n
  Next
+
  Next
 
+
   
 
  '''End'''
 
  '''End'''
 
    
 
    
 
  '''Private''' Function legge_var(mf As File) As Integer  <FONT Color=gray>' ''Legge un numero a lunghezza-variabile (Tempo Delta)''</font>
 
  '''Private''' Function legge_var(mf As File) As Integer  <FONT Color=gray>' ''Legge un numero a lunghezza-variabile (Tempo Delta)''</font>
 
   
 
   
Dim vl As Integer
+
  Dim vl As Integer
Dim c As Byte
+
  Dim c As Byte
 
    
 
    
 
   Repeat
 
   Repeat

Versione attuale delle 17:27, 12 giu 2021

Il procedimento, seguito per ottenere la durata in secondi di un file Midi deve tenere in debita considerazione la circostanza che all'interno di un brano musicale possono insistere una o più variazioni del tempo metronomico. La durata del file Midi rappresenta così la somma delle singole durate determinate dagli eventuali cambi del tempo metronomico all'interno del file Midi.

Ciò richiede innanzitutto la conoscenza dei seguenti elementi:

  • tempo metronomico: movimenti (battute) per minuto (bpm), che si otterrà a sua volta dallo specifico Meta-evento FF 51;
  • tick reali dell'intera traccia Midi più grande, ossia la quantità di tick ottenuti dalle somme dei Tempi Delta (quest'ultimi convertiti ciascuno dal proprio formato a lunghezza variabile, nel quale sono espressi nel file Midi) fra ogni evento Midi e quello immediatamente successivo;
  • risoluzione del Tempo Delta (TΔ), espressa in ordine Big-Endian nel 13° e nel 14° byte del file Midi.

Pertanto, per ciascun "tratto" parziale di durata devono essere ottenuti il tempo metronomico (bpm), leggendolo dall'apposito Meta-evento FF 51, e i tick reali. Da tali elementi, considerando anche la risoluzione del Tempo Delta (TΔ), si otterrà per ogni "tratto" la durata in secondi, adottando la seguente formula:

((60 / bpm) / risoluzione_TΔ) * tick_reali = secondi

Successivamente bisogna ricavare anche il tempo metronomico e la quantità di tick reali maggiore fra le tracce (MTrk), e da questi due elementi, unitamente alla costante risoluzione del Tempo Delta, si otterrà la durata in secondi del "tratto finale" (per "tratto finale" qui intendiamo la quantità di Tempo Delta compresa fra l'ultimo Meta-evento FF 51 e il Meta-evento di chiusura FF 2F 00 della traccia avente la quantità maggiore di tick rispetto alle altre).

In ogni caso va ricordato che il valore predefinito del tempo metronomico è pari a 120 bpm.

Si potrà avere dunque, ad esempio, una situazione di questo genere:

0------FF51--------FF51----------FF51--------------FF2F00   (Traccia Midi di maggiore durata)
| 120        bmp1         bmp2   |        bmp3     |
|______|___________|_____________|_________________|
     tratto iniziale durata         tratto finale
|__________________________________________________|
           Durata totale del file Midi

In altri termini si procederà a:

1) estrarre il numero dei tick reali compresi fra l'inizio assoluto delle tracce Midi (punto TΔ = 0) e l'ulimo Meta-evento FF 51, ossia fino all'ultimo Tempo Delta precedente l'ultimo Meta-evento FF 51): "1° tratto" della durata del file Midi. Questo andrà ottenuto estraendo i tick reali per ogni tratto compreso tra i vari Meta-eventi FF 51 del "1° tratto".

2) estrarre il numero dei tick reali più grande fra le tracce esistenti del file Midi. Ciò significa che si dovrà ottenere il numero di tick reali della traccia che dura di più.

3) ottenere i tick reali del "tratto finale" più lungo fra le tracce esistenti, sottraendo i tick reali del "tratto iniziale" (punto 1) ai tick reali della traccia più duratura (punto 2).

4) convertire in secondi separatamente i tick reali di ciascun tratto costituente il "tratto iniziale".

5) convertire in secondi i tick reali del "tratto finale".

6) sommare i valori temporali, espressi in secondi, dei singoli tratti (punto 4) costituenti il "tratto iniziale" della durata del file Midi.


Esempio pratico:
a) 77959 tick reali della traccia più duratura;
b) 52992 tick reali del "tratto iniziale";
c) 77952 - 52992 = 24960 tick reali del "tratto finale" più lungo fra le tracce esistenti;
d) conversione in secondi di ciascun singolo tratto costituente il "tratto iniziale" della durata del file Midi;
e) secondi_tratto_iniziale + secondi_tratto_finale = durata effettiva del file Midi


Dunque, per conoscere la durata di un file Midi mediante le sole risorse di Gambas, è possibile utilizzare un codice come il seguente, nel quale si otterranno anche le durate delle singole tracce MTrk presenti nel file ed alcune generiche informazioni:

Public Sub Main()
 
 Dim fl As File
 Dim fileMidi, s As String
 Dim hbpm, tot, mx, tratto1 As Integer
 Dim td, bpm, c As Short
 Dim tr, b, n, av As Byte
 Dim cmd, tipocmd, ultimo As Byte
 Dim f, durata As Float
  
 fileMidi = "/percorso/del/file.mid"
 Print "File Midi:         "; fileMidi
 
 fl = Open fileMidi For Read
 If IsNull(fl) Then Error.Raise("Impossibile aprire il file !")
 
 Read #fl, s, 4
 If s <> "MThd" Then Error.Raise("Il file non è uno standard Midi !")
 Print "Dimensione:    "; Lof(fl); " byte"
 Seek #fl, 9
 Read #fl, c
 Print "Tipo file:         "; c
 Read #fl, tr
 Print "Numero tracce:   "; Format(tr, "###")
 Read #fl, td
 td = Rol(td, 8)
 Print "Risoluzione TΔ:  "; Format(td, "###")
 Avanza(fl, 8)
 
 bpm = 120
 
 Repeat
   tot = 0
   Do
     tot += legge_var(fl)
     Read #fl, b
     If b And 128 Then   ' Status running
       cmd = b
       If b < &F1 Then ultimo = cmd
       av = 2
     Else
       cmd = ultimo
       av = 1
     Endif
     tipocmd = Shr(cmd, 4)
     Select Case tipocmd
       Case 8 To 11
         Avanza(fl, av)
       Case 12 To 13 
         Avanza(fl, 1)
       Case 14
         Avanza(fl, av)
       Case 15
         If b = &F0 Then          ' Evento SysEx
           Repeat
             b = Read #fl As Byte
           Until b = &F7
         Else
           Read #fl, b
           Select Case b
             Case &51
               durata += DurataParziale(bpm, td, tot) 
               tratto1 += tot
               tot = 0
               Avanza(fl, 1)
               hbpm = 0
               For c = 2 To 0 Step -1
                 Read #fl, n
                 hbpm += Shl(CInt(n), 8 * c)
               Next
               bpm = 60000000 / hbpm
             Case &2F
               mx = Max(mx, tot)
               Exit
             Case Else
               Read #fl, b
               Avanza(fl, b)
           End Select
         Endif
     End Select
   Loop
   Dec tr
   If tr > 0 Then Avanza(fl, 9)
 Until tr = 0
 
 fl.Close
 
 f = DurataParziale(bpm, td, Max(mx, tratto1) - tratto1)
 
 Print "\nDURATA:       \e[31m\e[1m"; CStr(Time(0, 0, 0, (f + durata) * 1000)); "\e[0m"
 
End
 
 
Private Procedure Avanza(mf As File, av As Byte)
 
 Dim b, n As Byte
  
 For b = 1 To av
   Read #mf, n
 Next
   
End
 
Private Function legge_var(mf As File) As Integer   ' Legge un numero a lunghezza-variabile (Tempo Delta)

 Dim vl As Integer
 Dim c As Byte
 
 Repeat
   Read #mf, c
   vl = Shl(vl, 7) Or (c And 127)
 Until Not (c And 128)
 
 Return vl
 
End
 
Private Function DurataParziale(bpm As Integer, del As Short, par As Integer) As Float
 
  Return CFloat((((60000000 / bpm) / del) * par) / 1000000)
 
End