Convertire un file WAV in formato OggVorbis con le funzioni esterne del API di libfishsound, libsndfile e liboggz

Da Gambas-it.org - Wikipedia.

La libreria Libfishsound fornisce una semplice interfaccia di programmazione di livello superiore per la decodifica e la codifica di dati audio utilizzando i codec di Xiph.org (FLAC, Speex e Vorbis).
Per poter fruire in Gambas delle sue risorse, è necessario installare e richiamare la libreria condivisa: "libfishsound.so.1.3.0"


Liboggz, invece, è una libreria per la lettura, la scrittura e la gestione di file Ogg.
Per poter fruire in Gambas delle sue riorse, è necessario installare e richiamare la libreria condivisa: "liboggz.so.2.6.0"


Mostriamo di seguito un semplice esempio, il quale utilizzando congiuntamente le funzioni esterne del API di libfishsound, libsndfile e liboggz, consente di convertire un file di formato WAV in un file audio di formato OGG-Vorbis. E' necessaria, pertanto, la installazione nel sistema delle seguenti librerie condivise:

  • libfishsound.so.1.3.0
  • libsndfile.so.1.0.25
  • liboggz.so.2.6.0
Public Struct ogg_packet
  packet As Pointer
  bytes As Long
  b_o_s As Long
  e_o_s As Long
  granulepos As Long
  packetno As Long
End Struct

Private Const ENCODE_BLOCK_SIZE As Long = 1152
Private serialno As Long
Private b_o_s As Integer = 1

Library "libfishsound:1.3.0"

Public Struct FishSoundInfo
  samplerate As Integer
  channels As Integer
  formatFSI As Integer
End Struct

Private Enum FISH_SOUND_UNKNOWN = 0, FISH_SOUND_VORBIS
Private Const FISH_SOUND_ENCODE_ As Integer = &20

' FishSound * fish_sound_new (int mode, FishSoundInfo * fsinfo)
' Instantiate a new FishSound* handle.
Private Extern fish_sound_new(mode As Integer, FSInfo As FishSoundInfo) As Pointer

' int fish_sound_set_encoded_callback (FishSound * fsound, FishSoundEncoded encoded, void * user_data)
' Set the callback for libfishsound to call when it has a block of encoded data ready.
Private Extern fish_sound_set_encoded_callback(FS As Pointer, FishSoundEncoded As Pointer, data As Pointer) As Integer

' int fish_sound_set_interleave (FishSound * fsound, int interleave)
' Set the PCM format used by a FishSound object. DEPRECATED FUNCTION.
Private Extern fish_sound_set_interleave(FS As Pointer, interleave As Integer) As Integer

' int fish_sound_comment_add_byname (FishSound * fsound, const char * name, const char * value)
' Add a comment by name and value.
Private Extern fish_sound_comment_add_byname(FS As Pointer, name As String, value As String) As Integer

' long fish_sound_encode (FishSound * fsound, float ** pcm, long frames)
' Encode a block of audio. DEPRECATED FUNCTION.
Private Extern fish_sound_encode(FS As Pointer, pcm As Pointer, frames As Long) As Long

' long fish_sound_flush (FishSound * fsound)
' Flush any internally buffered data, forcing encode.
Private Extern fish_sound_flush(FS As Pointer) As Long

' long fish_sound_get_frameno (FishSound * fsound)
' Query the current frame number of a FishSound object.
Private Extern fish_sound_get_frameno(FS As Pointer) As Long

' int fish_sound_delete (FishSound * fsound)
' Delete a FishSound object.
Private Extern fish_sound_delete(FS As Pointer) As Integer


Library "libsndfile:1.0.25"

Public Struct SF_INFO
  frames As Long
  samplerate As Integer
  channels As Integer  
  formatI As Integer
  sections As Integer
  seekable As Integer
End Struct

Private Const SFM_READ As Integer = 16

' SNDFILE * sf_open (const char *path, int mode, SF_INFO *sfinfo)
' Open the specified file for read, write or both.
Private Extern sf_open(path As String, mode As Integer, SFinf As SF_INFO) As Pointer

' sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames)
' Function for reading the data chunk in terms of frames. Passes data in the native float format.
Private Extern sf_readf_float(sndfile As Pointer, ptr As Single[], frames As Long) As Long

' int  sf_close  (SNDFILE *sndfile)
' Close the SNDFILE and clean up all memory allocations associated with this file.
Private Extern sf_close(sndfile As Pointer) As Integer


Library "liboggz:2.6.0"

Private Const OGGZ_WRITE As Integer = 1

' OGGZ * oggz_open (const char * filename, int flags)
' Open an Ogg file, creating an OGGZ handle for it.
Private Extern oggz_open(filename As String, flags As Integer) As Pointer

' long oggz_serialno_new (OGGZ * oggz)
' Request a new serialno, as required for a new stream.
Private Extern oggz_serialno_new(OGG As Pointer) As Long

' long oggz_run (OGGZ * oggz)
' Run an OGGZ until completion, or error.
Private Extern oggz_run(OGG As Pointer) As Long

' int oggz_write_feed (OGGZ * oggz, ogg_packet * op, long serialno, int flush, int * guard)
' Add a packet to \a oggz's packet queue.
Private Extern oggz_write_feed(OGGZ As Pointer, ogg_p As Ogg_packet, ser As Long, flushI As Integer, guard As Pointer) As Integer

' int oggz_close (OGGZ * oggz)
' Close an OGGZ handle.
Private Extern oggz_close(OGG As Pointer) As Integer


Public Sub Main()

 Dim fileWAV, fileOGG As String
 Dim snd, oggz, fsound As Pointer
 Dim sfinfo As New SF_INFO
 Dim fsinfo As New FishSoundInfo
 Dim formato, rit As Integer
 Dim pcm As New Single[2048]

  formato = FISH_SOUND_VORBIS

  fileWAV = "/percorso/del/file.wav"
  fileOGG = "/percorso/del/file.ogg"
   
  snd = sf_open(fileWAV, SFM_READ, sfinfo)
  If IsNull(snd) Then Error.Raise("Impossibile aprire il file " & fileWAV & " !")

  oggz = oggz_open(fileOGG, OGGZ_WRITE)
  If IsNull(oggz) Then Error.Raise("Impossibile aprire il file in scrittura !")
   
  serialno = oggz_serialno_new(oggz)
   
  With fsinfo
    .channels = sfinfo.channels
    .samplerate = sfinfo.samplerate
    .formatFSI = formato
  End With
   
  fsound = fish_sound_new(FISH_SOUND_ENCODE_, fsinfo)
   
  fish_sound_set_encoded_callback(fsound, encoded, oggz)
  
  fish_sound_set_interleave(fsound, 1)
   
  fish_sound_comment_add_byname(fsound, "Encoder", "fishsound-encode")
   
  While sf_readf_float(snd, pcm, ENCODE_BLOCK_SIZE) > 0
    fish_sound_encode(fsound, pcm.data, ENCODE_BLOCK_SIZE)
    oggz_run(oggz)
  Wend
  
  fish_sound_flush(fsound)
   
  oggz_run(oggz)    
  

' Va in chiusura:
  oggz_close(oggz)
  fish_sound_delete(fsound)
  sf_close(snd)

End


Private Function encoded(fsound As Pointer, buf As Pointer, bytes As Long, user_data As Pointer) As Integer
  
 Dim err As Integer
 Dim ogp As New Ogg_packet
 
  With ogp
    .packet = buf
    .bytes = bytes
    .b_o_s = b_o_s
    .e_o_s = 0
    .granulepos = fish_sound_get_frameno(fsound)
    .packetno = -1
  End With

  err = oggz_write_feed(user_data, ogp, serialno, 0, 0)
  If (err) Then Print "err: "; err
 
  b_o_s = 0

  Return 0
 
End



Riferimenti