Fondere insieme due file MIDI: differenze tra le versioni
Vai alla navigazione
Vai alla ricerca
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| (13 versioni intermedie di uno stesso utente non sono mostrate) | |||
| Riga 1: | Riga 1: | ||
Mostriamo di seguito un codice per fondere (mischiare) due file Midi con le sole funzioni di Gambas. | Mostriamo di seguito un codice, sufficientemente commentato, per fondere (mischiare) due file Midi con le sole funzioni di Gambas. | ||
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, | 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> | |||
mtrk1 = s1[12, s1.Len] | |||
<FONT color=gray>' ''Raccoglie del 2° file Midi solo la parte delle tracce "MTrk":''</font> | |||
mtrk2 = s2[14, s2.Len] | |||
<FONT color=gray>' '' | |||
<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 | |||
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