Modificare la frequenza di campionamento di un file audio con l'API di Libresample e Libsndfile

Da Gambas-it.org - Wikipedia.

La libreria Libresample, creata da Dominic Mazzoni, consente di modificare la frequenza di campionamento (Sample Rate) di un file audio.

Per poter fruire in Gambas delle risorse della libreria Libresample, bisognera installare e richiamare la libreria condivisa: "libresample.so.1.0 ".

Nell'esempio che segue si farà uso anche della libreria "libsndfile.so.1.0.37 ".

Library "libresample:1.0"

' void *resample_open(int highQuality, double minFactor, double maxFactor)
Private Extern resample_open(highQuality As Integer, minFactor As Float, maxFactor As Float) As Pointer

' int resample_process(void *handle, double factor, float *inBuffer, int inBufferLen, int lastFlag, int *inBufferUsed, float *outBuffer, int outBufferLen)
Private Extern resample_process(handleP As Pointer, factor As Float, inBUffer As Single[], inBufferLen As Integer, lastFlag As Integer, inBufferUsed As Pointer, outBuffer As Single[], outBufferLen As Integer) 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

Public Struct SF_FORMAT_INFO
  format_ As Integer
  name As Pointer
  extension As Pointer
End Struct

Private Const SFM_READ As Integer = &10
Private Const SFM_WRITE As Integer = &20
Private Enum SFC_GET_FORMAT_MAJOR_COUNT = &1030, SFC_GET_FORMAT_MAJOR
Private Const SF_FORMAT_SUBMASK As Integer = &0000FFFF

' 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

' int sf_command (SNDFILE *sndfile, int command, void *data, int datasize)
' Return TRUE if fields of the SF_INFO struct are a valid combination of values.
Private Extern sf_command(snd As Pointer, command As Integer, data As Pointer, datasize As Integer)

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

' sf_count_t sf_writef_float (SNDFILE *sndfile, const float *ptr, sf_count_t frames)
' Writes the data chunk in terms of frames. Passes data in the native float format.
Private Extern sf_writef_float(snd 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(snd As Pointer) As Integer


Public Sub Main()

 Dim srcinfo, dstinfo As New SF_INFO
 Dim formatinfo As New SF_FORMAT_INFO
 Dim srcfile, dstfile, p As Pointer
 Dim dsti, src, dst, srci As Single[]
 Dim handle As Pointer[]
 Dim dstrate, srcrate, ratio As Float
 Dim channels, numformats, i, c, srclen, dstlen As Integer
 Dim pos, bufferpos, outcount, block, lastFlag As Integer
 Dim inUsed, inused2, out, out2 As Integer
 Dim extension As String
 Dim FileAudioIn As String = "/percorso/del/file/audio/originario"
 Dim FileAudioOut As String = "/percorso/del/file/audio/finale/modificato"

' Imposta la nuova frequenza di campionamento del file audio:
 dstrate = 22050.00
 If (dstrate < 10.0) Or (dstrate > 100000.0) Then Error.Raise("Valore della frequenza di campionamento non consentita !")
   
 srcfile = sf_open(FileAudioIn, SFM_READ, srcinfo)
 If srcfile == 0 Then Error.Raise("Impossibile aprire il file audio in entrata !")

 Print "File audio in entrata:   "; FileAudioIn
 Print "Frequenza campionamento: "; srcinfo.samplerate; " Hertz"
 Print "Canali n.                "; srcinfo.channels

 srcrate = srcinfo.samplerate
   
 If dstrate = 0.0 Then
   dstrate = srcrate * ratio
 Else
   ratio = dstrate / srcrate
 Endif
   
 channels = srcinfo.channels

' Estrae il formato del file di destinazione:
 extension = File.Ext(FileAudioOut)
 If "." & extension Then
   sf_command(Null, SFC_GET_FORMAT_MAJOR_COUNT, VarPtr(numformats), SizeOf(gb.Integer))
   Print "\nLa libreria 'libresample' supporta "; numformats; " formati audio: "
   p = Alloc(Object.SizeOf(formatinfo))
   For i = 0 To numformats - 1
     formatinfo.format_ = i
     sf_command(Null, SFC_GET_FORMAT_MAJOR, p, Object.SizeOf(formatinfo))
     formatinfo = p
     Print String@(formatinfo.extension); "  ";
     If Not Comp(String@(formatinfo.extension), extension) Then
       Print "\nSi sta usando "; String@(formatinfo.name); " come formato in uscita."
       dstinfo.format_ = formatinfo.format_ Or (srcinfo.format_ And SF_FORMAT_SUBMASK)
       Break
     Endif            
   Next
   Free(p)
 Endif

 If Not dstinfo.format_ Then
   If (extension) Then Error.Raise("Attenzione: il formato in uscita " & extension & " non è riconosciuto. Usare lo stesso formato del file originario !")
   dstinfo.format_ = srcinfo.format_
 Endif
   
 dstinfo.samplerate = CInt(dstrate + 0.5)
 dstinfo.channels = channels
   
 dstfile = sf_open(FileAudioOut, SFM_WRITE, dstinfo)
 If dstfile == 0 Then Error.Raise("Impossibile aprire il file audio in uscita !")
   
 Print "\n\nFile sorgente:     "; FileAudioIn; " ("; srcinfo.frames; " frames, ", srcrate; " Hz)"
 Print "File destinazione: "; FileAudioOut; " ("; dstrate; " Hz, ratio = "; ratio
  
 srclen = 4096
 dstlen = (srclen * ratio + 1000)

 srci = New Single[](srclen * channels * SizeOf(gb.Single))
 dsti = New Single[](dstlen * channels * SizeOf(gb.Single))
 src = New Single[](srclen * SizeOf(gb.Single))
 dst = New Single[](dstlen * SizeOf(gb.Single))
 handle = New Pointer[](channels * SizeOf(gb.Pointer))

 For c = 0 To channels - 1
   handle[c] = resample_open(1, ratio, ratio)
 Next

 While pos < srcinfo.frames
   block = Min(srclen - bufferpos, srcinfo.frames - pos)
   lastFlag = (pos + block == srcinfo.frames)
   sf_readf_float(srcfile, srci, block)
   block += bufferpos
   For c = 0 To channels - 1
     For i = 0 To block - 1
       src[i] = srci[i * channels + c]
     Next
     inUsed = 0
     out = resample_process(handle[c], ratio, src, block, lastFlag, VarPtr(inUsed), dst, dstlen)
     If c = 0
       inUsed2 = inUsed
       out2 = out
     Else
       If (inUsed2 <> inUsed) Or (out2 <> out) Then Error.Raise("Grave errore: canali fuori sincronizzazione !")
     Endif
     For i = 0 To out - 1
       If dst[i] <= -1 Then
         dsti[i * channels + c] = -1
       Else If dst[i] >= 1
         dsti[i * channels + c] = 1
       Else
         dsti[i * channels + c] = dst[i]
       Endif
     Next
   Next
   sf_writef_float(dstfile, dsti, out)
   bufferpos = block - inUsed
   For i = 0 To i < (bufferpos * channels) - 1
     srci[i] = srci[i + (inUsed * channels)]
     pos += inUsed
     outcount += out
   Next
 Wend

' Va in chiusura:
 sf_close(srcfile)
 sf_close(dstfile)

End


Riferimenti