Ottenere un file WAV da un file Midi con le funzioni esterne del API di FluidSynth

Da Gambas-it.org - Wikipedia.

Con le risorse della libreria Libfluidsynth è possibile ottenere un file WAV da un file Midi.

E' necessario avere installata e richiamare in Gambas la libreria dinamica condivisa: "libfluidsynth:1.5.2"


Mostriamo di seguito un esempio a riga di comando:

Library "libfluidsynth:1.5.2"

' fluid_settings_t* new_fluid_settings(void)
' Create a new settings object.
Private Extern new_fluid_settings() As Pointer

' fluid_synth_t * new_fluid_synth(fluid_settings_t * settings)
' Create new FluidSynth instance.
Private Extern new_fluid_synth(settings As Pointer) As Pointer

' int fluid_is_soundfont(const char * filename)
' Check if a file is a SoundFont file.
Private Extern fluid_is_soundfont(filename As String) As Integer

' int fluid_synth_sfload(fluid_synth_t * synth, const char * filename, nt reset_presets)
' Load a SoundFont file.
Private Extern fluid_synth_sfload(synth As Pointer, filename As String, reset_presets As Integer) As Integer

' int fluid_settings_getnum(fluid_settings_t* settings, const char *name, double* val)
' Get the numeric value of a named setting.
Private Extern fluid_settings_getnum(settings As Pointer, name As String, dval As Pointer) As Integer

' void fluid_synth_set_gain(fluid_synth_t *synth, float gain)
' Set synth output gain value.
Private Extern fluid_synth_set_gain(synth As Pointer, gain As Single)

' fluid_player_t* new_fluid_player(fluid_synth_t * synth)
' Create a new MIDI player.
Private Extern new_fluid_player(synth As Pointer) As Pointer

' int fluid_is_midifile(const char *filename)
' Check if a file is a MIDI file.
Private Extern fluid_is_midifile(filename As String) As Integer

' int fluid_player_add(fluid_player_t * player, Const char * midifile)
' Add a MIDI file to a player queue.
Private Extern fluid_player_add(player As Pointer, midifile As String) As Integer

' int fluid_player_play(fluid_player_t * player)
' Activates play mode for a MIDI player if not already playing.
Private Extern fluid_player_play(player As Pointer) As Integer

' int fluid_synth_write_s16(fluid_synth_t* synth, int len, void* lout, int loff, int lincr, void* rout, int roff, int rincr)
' Synthesize a block of 16 bit audio samples to audio buffers.
Private Extern fluid_synth_write_s16(synth As Pointer, ilen As Integer, luot As Short[], loff As Integer, lincr As Integer, rout As Short[], forr As Integer, rincr As Integer) As Integer

' int fluid_player_get_status(fluid_player_t * player)
' Get MIDI player status.
Private Extern fluid_player_get_status(player As Pointer) As Integer

' int delete_fluid_player(fluid_player_t * player)
' Delete a MIDI player instance.
Private Extern delete_fluid_player(player As Pointer) As Integer

' int delete_fluid_synth(fluid_synth_t * synth)
' Delete a FluidSynth instance.
Private Extern delete_fluid_synth(synth As Pointer) As Integer

' void delete_fluid_settings(fluid_settings_t * settings)
' Delete the provided settings object.
Private Extern delete_fluid_settings(settings As Pointer)


Public Sub Main()

 Dim sfb, fileMidi, dati_grezzi As String
 Dim sett, syn, pl As Pointer
 Dim i As Integer
 Dim frequenza As Float
 Dim buffer, dati As Short[]
 Dim fl As File
 
' Imposta il file soundfont-bank che sarà utilizzato:
  sfb = "/usr/share/sounds/sf2/FluidR3_GM.sf2"
  
' Verifica se il file caricato è effettivamente un soundfont-bank .sf2:
  i = fluid_is_soundfont(sfb)
  If i < 0 Then Error.Raise("Errore: il file non è un soundfont-bank !")
  
' Imposta il file Midi, dal quale si genererà un file audio wav:
  fileMidi = "/percorso/del/file.mid"
   
' Verifica se il file è effettivamente uno file Midi standard:
  i = fluid_is_midifile(fileMidi)
  If i < 0 Then Error.Raise("Errore: il file non è un file Midi standard !")
   
  sett = new_fluid_settings()
  If sett = 0 Then Error.Raise("Impossibile creare un oggetto 'settings' !")
   
  syn = new_fluid_synth(sett)
  If syn = 0 Then Error.Raise("Impossibile creare un'istanza di FluidSynth !")
   
  i = fluid_synth_sfload(syn, sfb, 1)
  If i < 0 Then Error.Raise("Impossibile caricare il file soundfont-bank !")
  
  fluid_settings_getnum(sett, "synth.sample-rate", VarPtr(frequenza))
  Print "Frequenza del futuro file audio wav: "; frequenza; " hertz"
   
' Imposta il volume del file audio wav finale:
  fluid_synth_set_gain(syn, 0.5)
   
  pl = new_fluid_player(syn)
  If pl = 0 Then Error.Raise("Impossibile creare un esecutore Midi !")
   
  i = fluid_player_add(pl, fileMidi)
  If i < 0 Then Error.Raise("Errore alla funzione 'fluid_player_add()' !")
  
  i = fluid_player_play(pl)
  If i < 0 Then Error.Raise("Errore alla funzione 'fluid_player_play()' !")
   
  dati = New Short[frequenza * 2]
   
  Do
    buffer = New Short[frequenza * 2]
    fluid_synth_write_s16(syn, frequenza, buffer, 0, 2, buffer, 1, 2)
    dati.Insert(buffer)
  Loop Until fluid_player_get_status(pl) <> 1
  
  dati_grezzi = "/tmp/dati_grezzi"
  fl = Open dati_grezzi For Create
  dati.Write(fl, 0, dati.Count)
  fl.Close
   
' Va a generare il file audio wav:
  Genera_File_wav(dati_grezzi)
  
   
' Va in chiusura:
  delete_fluid_player(pl)
  delete_fluid_synth(syn)
  delete_fluid_settings(sett)
   
  Print "\nTermine creazione del file audio wav."
  
End


Private Procedure Genera_File_wav(dati_audio As String)
 
 Dim s, ini As String
 Dim fl As File
 Dim bh, bb As New Byte[]
 Dim canali, risoluzione, blal As Byte
 Dim i, i2, frequenza, brps As Integer
  
  s = File.Load(dati_audio)
  
' Vengono definiti gli elementi fondamentali del blocco d'intestazione del file wav: 
  canali = 2
  frequenza = 44100
  risoluzione = 16 
  
  fl = Open "/tmp/audio.wav" For Create
  
  ini = "RIFF"
  
  bb = Byte[].FromString(ini)
  
  i = Len(s)
  
  i2 = i + 36
  
' Imposta il valore dimensionale di 4 byte a partire dal 5° byte del futuro file: 
  bb.Add(i2 And &FF)
  bb.Add(Shr(i2 And &FF00&, 8))
  bb.Add(Shr(i2 And &FF0000&, 16))
  bb.Add(Shr(i2 And &FF000000&, 24))
  
' Vengono aggiunti: il tipo di formato di file e l'identificativo del formato del blocco dei dati audio:
  bb.Insert(bh.FromString("WAVEfmt "))
  
' Viene aggiunto il valore della lunghezza dei dati del formato (in questo caso il PCM):
  bh = [&10, &00, &00, &00]
  bb.Insert(bh)
  
' Viene aggiunto il valore del formato audio (1 = PCM):
  bb.Insert(bh.FromString(Chr(&01) & Chr(&00)))
  
' Viene aggiunto il numero dei canali di uscita:
  bb.Insert(bh.FromString(Chr(canali) & Chr(&00)))
  
' Viene aggiunto il valore della frequenza di campionamento:
  bb.Add(frequenza And &FF)
  bb.Add(Shr(frequenza And &FF00&, 8))
  bb.Add(Shr(frequenza And &FF0000&, 16))
  bb.Add(Shr(frequenza And &FF000000&, 24))
  
' Viene aggiunto il valore del "Byte rate per secondo":
  brps = frequenza * canali * (risoluzione / 8)
  bb.Add(brps And &FF)
  bb.Add(Shr(brps And &FF00&, 8))
  bb.Add(Shr(brps And &FF0000&, 16))
  bb.Add(Shr(brps And &FF000000&, 24))
  
' Viene aggiunto il valore del "Block Align":
  blal = canali * risoluzione / 8
  bb.Insert(bh.FromString(Chr(blal) & Chr(&00)))
  
' Viene aggiunto il valore della risoluzione di campionamento:
  bb.Insert(bh.FromString(Chr(risoluzione) & Chr(&00)))
  
' Viene aggiunto l'identificativo del Blocco dei dati audio grezzi:
  bb.Insert(bh.FromString("data"))
  
' Imposta il valore dimensionale di 4 byte a partire dal 41° byte del futuro file
' e relativo alla dimensione dei dati audio grezzi:
  bb.Add(i And &FF)
  bb.Add(Shr(i And &FF00&, 8))
  bb.Add(Shr(i And &FF0000&, 16))
  bb.Add(Shr(i And &FF000000&, 24))
  
  bb.Insert(Byte[].FromString(s))
  
' Crea il nuovo file wav:
  bb.Write(fl, 0, bb.Count)
  
  fl.Close
  
End



Riferimenti