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 condivisa: "libfluidsynth.so.3.2.2 ".


Mostriamo di seguito un esempio a riga di comando:

Library "libfluidsynth:3.2.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))
   
' 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]
   
 Repeat
   buffer = New Short[frequenza * 2]
   fluid_synth_write_s16(syn, frequenza, buffer, 0, 2, buffer, 1, 2)
   dati.Insert(buffer)
 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(frequenza, 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(freq As Float, 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)
 
 frequenza = CInt(freq)
  
' Vengono definiti gli elementi fondamentali del blocco d'intestazione del file wav: 
 canali = 2
 risoluzione = 16
 Print "Caratteristiche del futuro file WAV:"
 Print "- Frequenza:   "; frequenza; " hertz"
 Print "- Canali:      "; canali
 Print "- Risoluzione: "; risoluzione; " bit"
  
 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.Push(i2 And &FF)
 bb.Push(Shr(i2 And &FF00&, 8))
 bb.Push(Shr(i2 And &FF0000&, 16))
 bb.Push(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.Push(frequenza And &FF)
 bb.Push(Shr(frequenza And &FF00&, 8))
 bb.Push(Shr(frequenza And &FF0000&, 16))
 bb.Push(Shr(frequenza And &FF000000&, 24))
  
' Viene aggiunto il valore del "Byte rate per secondo":
 brps = frequenza * canali * (risoluzione / 8)
 bb.Push(brps And &FF)
 bb.Push(Shr(brps And &FF00&, 8))
 bb.Push(Shr(brps And &FF0000&, 16))
 bb.Push(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.Push(i And &FF)
 bb.Push(Shr(i And &FF00&, 8))
 bb.Push(Shr(i And &FF0000&, 16))
 bb.Push(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