Convertire un file audio mpeg in .wav con le funzioni esterne del API di mpg123

Da Gambas-it.org - Wikipedia.

Mostriamo un codice per convertire un file mpeg layers 1, 2 e 3 (ad esempio .mp3) in un file .wav mediante le funzioni esterne del API di mpg123 e della libreria libsndfile.

E' necessario avere installato nel sistema le seguenti librerie: libmpg123.so.0.36.6 e libsndfile.so.1.0.25.

Il codice prevede, fra l'altro, anche un oggetto ListBox, nel quale verranno mostrate alcune informazioni inerenti al file .wav creato:

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 MPG123_OK As Byte = 0
Private Const MPG123_ERR As Short = -1
Private Const MPG123_DONE As Short = -12
Private Const MPG123_ADD_FLAGS As Byte = 2
Private Const MPG123_FORCE_FLOAT As Short = 1024
Private Const SF_FORMAT_WAV As Integer = 65536      ' Microsoft WAV format (little endian default)
Private Const MPG123_ENC_SIGNED_16 As Byte = 208
Private Const SF_FORMAT_PCM_16 As Byte = 2          ' Signed 16 bit data
Private Const SF_FORMAT_FLOAT As Byte = 6           ' 32 bit float data
Private Const SFM_WRITE As Byte = 32                ' Modalità di apertura di un file

Private percorsoFile As String


Library "libmpg123:0.36.6"

' int mpg123_init   (void)  --> Function to initialise the mpg123 library.
Private Extern mpg123_init() As Integer

' const char* mpg123_plain_strerror   (int errcode)  --> Return a string describing that error errcode means.
Private Extern mpg123_plain_strerror(errcode As Integer) As String

' mpg123_handle *, mpg123_new (const char *decoder, int *error)  --> Create a handle with optional choice of decoder (named by a string)
Private Extern mpg123_new(decoder As String, errorI As Integer) As Pointer

' int mpg123_param   (mpg123_handle * mh, Enum mpg123_parms type, long value, double fvalue)
' Set a specific parameter, For a specific mpg123_handle, using a parameter type key chosen From the mpg123_parms enumeration, To the specified value.
Private Extern mpg123_param(mhP As Pointer, type As Integer, valueL As Long, fvalue As Float) As Integer

' int mpg123_open   (mpg123_handle * mh, const char * path)  --> Open and prepare to decode the specified file by filesystem path. This does not open HTTP urls.
Private Extern mpg123_open(mhP As Pointer, pathFile As String) As Integer

' const char* mpg123_strerror   (mpg123_handle * mh)  --> Give string describing what error has occured in the context of handle mh.
Private Extern mpg123_strerror(mhP As Pointer) As String

' int mpg123_getformat   (mpg123_handle * mh, long * rate, Int * channels, Int * encoding) --> Get the current output format written to the addresses given.
Private Extern mpg123_getformat(mhP As Pointer, ratP As Pointer, channP As Pointer, encodP As Pointer) As Integer

' int mpg123_format_none (mpg123_handle * mh)  --> Configure a mpg123 handle to accept no output format at all, use before specifying supported formats with mpg123_format.
Private Extern mpg123_format_none(mhP As Pointer) As Integer

' int mpg123_format   (mpg123_handle * mh, long rate, Int channels, Int encodings)  --> Set the audio Format support Of a mpg123_handle
Private Extern mpg123_format(mhP As Pointer, ratInt As Integer, canali As Integer, encodInt As Integer) As Integer

' size_t mpg123_outblock   (mpg123_handle * mh)  --> The max size of one frame's decoded output with current settings.
' Use that to determine an appropriate minimum buffer size for decoding one frame.
Private Extern mpg123_outblock(mhP As Pointer) As Integer

' int mpg123_read   (mpg123_handle * mh, unsigned char * outmemory, size_t outmemsize, size_t * done) --> Read from stream and decode up to outmemsize bytes.
Private Extern mpg123_read(mhP As Pointer, outmemory As Pointer, outmemsize As Integer, done As Pointer) As Integer

' int mpg123_encsize   (int encoding)  --> Return the size (in bytes) of one mono sample of the named encoding
Private Extern mpg123_encsize(encodInt As Integer) As Integer

' int mpg123_close   (mpg123_handle * mh)  --> Closes the source, if libmpg123 opened it
Private Extern mpg123_close(mhP As Pointer) As Integer

' void mpg123_delete   (mpg123_handle * mh)  --> Delete handle, mh is either a valid mpg123 handle or NULL
Private Extern mpg123_delete(mhP As Pointer)

' void mpg123_exit (void)  --> Function to close down the mpg123 library.
Private Extern mpg123_exit()


Library "libsndfile:1.0.25"

' int    sf_close    (SNDFILE *sndfile)  --> Chiude il file precedentemente aperto.
Private Extern sf_close(sndfile As Pointer) As Integer
 
' 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, sfinStru As SF_INFO) As Pointer

' sf_count_t  sf_write_short   (SNDFILE *sndfile, short *ptr, sf_count_t items)
Private Extern sf_write_short(sndfile As Pointer, ptr As Pointer, items As Integer) As Integer

' sf_count_t  sf_write_float  (SNDFILE *sndfile, const float *ptr, sf_count_t items)
Private Extern sf_write_float(sndfile As Pointer, ptr As Pointer, items As Integer) As Integer



'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Public Sub Button1_Click()   ' decodifica

 Dim err, channels, encoding, buffer_size, more_samples, samples, done As Integer
 Dim rate As Long
 Dim mh, buffer, sndfile As Pointer
 Dim sfinfo As New SF_INFO



 err = mpg123_init()
 If err <> MPG123_OK Then Error.Raise("Configurazione di base errata ! " & mpg123_plain_strerror(err))
 
 mpg123_param(mh, MPG123_ADD_FLAGS, MPG123_FORCE_FLOAT, 0)
 
 mh = mpg123_new(Null, 0)

' Apre il file mpeg in lettura:
 err = mpg123_open(mh, "/percorso/del/file/mpeg")
 If err <> MPG123_OK Then Error.Raise("Attenzione ! Problemi con 'mpg123': " & mpg123_strerror(mh))
 
 mpg123_getformat(mh, VarPtr(rate), VarPtr(channels), VarPtr(encoding))
 
 mpg123_format_none(mh)

 mpg123_format(mh, rate, channels, encoding)
 
 With sfinfo
   .samplerate = rate
   .channels = channels
   If encoding = MPG123_ENC_SIGNED_16 Then
     .formatI = SF_FORMAT_WAV Or SF_FORMAT_PCM_16
   Else
     .formatI = SF_FORMAT_WAV Or SF_FORMAT_FLOAT
   Endif
 End With
 
 With ListBox1
   .Clear
   .Add("  ********   Creazione del file WAV   ********")
   .Add(Null)
   .Add("Nome:  " & File.BaseName(percorsoFile) & ".wav")
   .Add("Canali =  " & channels)
   .Add("Frequenza =  " & rate & " Hz")
 End With

' Apre il file in scrittura:
   sndfile = sf_open(File.Dir(percorsoFile) &/ File.BaseName(percorsoFile) & ".wav", SFM_WRITE, sfinfo)

 buffer_size = mpg123_outblock(mh)

 buffer = Alloc(buffer_size)

 
 Do
   err = mpg123_read(mh, buffer, buffer_size, VarPtr(done))
   If encoding And MPG123_ENC_SIGNED_16 Then
     more_samples = sf_write_short(sndfile, buffer, done / SizeOf(gb.Short))
   Else
     more_samples = sf_write_float(sndfile, buffer, done / SizeOf(gb.Float))
   Endif
     If (more_samples < 0) Or (more_samples * mpg123_encsize(encoding) <> done) Then
       Error.Raise("Attenzione: è stato scritto un numero di campioni che non corrisponde ai byte ottenuti da 'libmpg123': " & more_samples * mpg123_encsize(encoding) & " <> " & done)
     Endif
   samples += more_samples
 Loop While err = MPG123_OK


 If err <> MPG123_DONE Then
   If err = MPG123_ERR
     Error.Raise("Attenzione: decodifica terminata prematuramente ! " & mpg123_strerror(mh))
   Else
     Error.Raise("Attenzione: decodifica terminata prematuramente ! " & mpg123_plain_strerror(err))
   Endif
 Endif

 sf_close(sndfile)

 samples /= channels
 
 ListBox1.Add("Campioni audio scritti:  " & samples)


' Chiude l'handle ed esce dalla libreria:
 mpg123_close(mh)
 mpg123_delete(mh)
 mpg123_exit()

End