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 dinamica condivisa: "libresample.so.1.0"


Nell'esempio che segue si farà uso anche della libreria SndFile.

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

Public Struct SF_FORMAT_INFO
  formatI 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 IsNull(srcfile) 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.formatI = 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.formatI = formatinfo.formatI Or (srcinfo.formatI And SF_FORMAT_SUBMASK)
        Break
      Endif            
    Next
    
    Free(p)
    
  Endif

  If Not dstinfo.formatI Then
    If (extension) Then Error.Raise("Attenzione: il formato in uscita " & extension & " non è riconosciuto. Usare lo stesso formato del file originario !")
    dstinfo.formatI = srcinfo.formatI
  Endif
   
  dstinfo.samplerate = CInt(dstrate + 0.5)
  dstinfo.channels = channels
   
  dstfile = sf_open(FileAudioOut, SFM_WRITE, dstinfo)
  If IsNull(dstfile) 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