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).

Liboggz, invece, è una libreria per la lettura, la scrittura e la gestione di file Ogg.

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 e richiamare in Gambas le seguenti librerie condivise:

  • libfishsound.so.1.3.0
  • libsndfile.so.1.0.37
  • 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.37"

Public Struct SF_INFO
  frames As Long
  samplerate As Integer
  channels As Integer  
  format_ 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 snd == 0 Then Error.Raise("Impossibile aprire il file " & fileWAV & " !")

 oggz = oggz_open(fileOGG, OGGZ_WRITE)
 If oggz == 0 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)
 If fsound == 0 Then Error.Raise("Errore !")

 fish_sound_set_encoded_callback(fsound, encoded, oggz)
  
 fish_sound_set_interleave(fsound, 1)
   
 fish_sound_comment_add_byname(fsound, "Encoder", "fishsound-encode")

 Write "\e[5mAttendere...\e[0m" 
 Flush
  
 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:
 Write "\rConversione terminata !"
 fish_sound_delete(fsound)
 oggz_close(oggz)
 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