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

Da Gambas-it.org - Wikipedia.

La libreria SampleRate consente, fra l'altro, di modificare la frequenza di campionamento (Sample Rate) di un file audio.

Per poter fruire in Gambas delle risorse della libreria Libsamplerate, bisognera installare e richiamare la libreria dinamica condivisa: "libsamplerate.so.0.1.8"


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

Private Enum SEEK_SET = 0, SEEK_CUR, SEEK_END


Library "libsamplerate:0.1.8"

Public Struct SRC_DATA
  data_in As Pointer
  data_out As Pointer
  input_frames As Long
  output_frames As Long
  input_frames_used As Long
  output_frames_gen As Long
  end_of_input As Integer
  src_ratio As Float
End Struct

' Tipo di interpolatore (Medium_Sinc_Interpolator = default)
Private Enum Best_Sinc_Interpolator = 0, Medium_Sinc_Interpolator, Fastest_Sinc_Interpolator, ZOH_Interpolator, Linear_Interpolator

' int src_is_valid_ratio (double ratio)
' Return TRUE if ratio is a valid conversion ratio.
Private Extern src_is_valid_ratio(ratio As Float) As Boolean

' const char *src_get_name (int converter_type)
' Return a string giving either a name or a more full description of each sample rate converter.
Private Extern src_get_name(converter_type As Integer) As String

' SRC_STATE* src_new (int converter_type, int channels, int *error)
' Standard initialisation function.
Private Extern src_new(converter_type As Integer, channels As Integer, errorP As Pointer) As Pointer

' const char* src_strerror (int error)
' Convert the error number into a string.
Private Extern src_strerror(errorI As Integer) As String

' int src_process (SRC_STATE *state, SRC_DATA *data)
' Standard processing function.
Private Extern src_process(state As Pointer, data As SRC_DATA) As Integer

' SRC_STATE* src_delete (SRC_STATE *state)
' Cleanup all internal allocations.
Private Extern src_delete(state As Pointer) As Pointer


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 = &10
Private Const SFM_WRITE As Integer = &20
Private Const SFC_SET_ADD_PEAK_CHUNK As Integer = &1050
Private Const SFC_SET_UPDATE_HEADER_AUTO As Integer = &1061
Private Const SFC_SET_CLIPPING As Integer = &10C0

' 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 Boolean)

' sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence)
' Seek within the waveform data chunk of the SNDFILE.
Private Extern sf_seek(snd As Pointer, frames As Long, whence As Integer) As Long

' 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 infile, outfile As Pointer
 Dim sfinfo As New SF_INFO
 Dim count As Long
 Dim new_sample_rate, converter As Integer
 Dim src_ratio, gain As Float
 Dim max_speed As Boolean
 Dim FileAudioIn As String = "/percorso/del/file/audio/originario"
 Dim FileAudioOut As String = "/percorso/del/file/audio/modificato"
 
  src_ratio = -1.0
  gain = 1.0
  converter = Medium_Sinc_Interpolator

' Imposta la nuova frequenza di campionamento da assegnare al file audio:
  new_sample_rate = 11050
   
  infile = sf_open(FileAudioIn, SFM_READ, sfinfo)
  If IsNull(infile) Then Error.Raise("Impossibile aprire il file audio in entrata !")
   
  Print "File audio in entrata: ", Null; FileAudioIn
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
  Print "Frame in ingresso: ", Null; CLong(sfinfo.frames)
   
  If new_sample_rate > 0 Then
    src_ratio = (1.0 * new_sample_rate) / sfinfo.samplerate
    sfinfo.samplerate = new_sample_rate
  Else If src_is_valid_ratio(src_ratio)
    sfinfo.samplerate = CInt(Floor(sfinfo.samplerate * src_ratio))
  Else
    Error.Raise("Impossibile determinare la nuova frequenza di campionamento !")
  Endif
   
  Print "\nSRC Ratio: ", Null, src_ratio
  Print "Converter: ", Null, src_get_name(converter)
   
  If src_is_valid_ratio(src_ratio) = 0 Then Error.Raise("Modifica della frequenza di campionamento al di fuori dell'ambito dei valori consentiti !")
   
  Print "\nFile audio in uscita: ", Null; FileAudioOut
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
    
  outfile = sf_open(FileAudioOut, SFM_WRITE, sfinfo)
  If IsNull(outfile) Then Error.Raise("Impossibile aprire il file d'uscita !")

  If max_speed Then
    sf_command(outfile, SFC_SET_ADD_PEAK_CHUNK, Null, False)
  Else
' Aggiorna il file header dopo la scrittura dei nuovi dati audio:
    sf_command(outfile, SFC_SET_UPDATE_HEADER_AUTO, Null, True)
  Endif

  sf_command(outfile, SFC_SET_CLIPPING, Null, True)

  count = Conversione_Frequenza(infile, outfile, converter, src_ratio, sfinfo.channels, VarPtr(gain))
   
  Print "Frames d'uscita: ", Null; CLong(count)

  sf_close(infile)
  sf_close(outfile)

End


Private Function Conversione_Frequenza(infile As Pointer, outfile As Pointer, converter As Integer, src_ratio As Float, channels As Integer, gain As Pointer) As Long
 
 Dim inputS, outputS As New Single[4096]
 Dim err As Integer
 Dim maxF, gm As Float
 Dim output_count As Long
 Dim src_state As Pointer
 Dim srcData As New SRC_DATA
 
  sf_seek(infile, 0, SEEK_SET)
  sf_seek(outfile, 0, SEEK_SET)
  
' Inizializza il convertitore di frequenza:
  src_state = src_new(converter, channels, VarPtr(err))
  If IsNull(src_state) Then Error.Raise("Impossibile inizializzare il convertitore di frequenza !")
   
  srcData.data_in = inputS.Data

  srcData.src_ratio = src_ratio

  srcData.data_out = outputS.Data

  srcData.output_frames = 4096 / channels
   
  While True

' Se il buffer d'ingresso è vuoto, viene riempito:
    If srcData.input_frames == 0 Then
      srcData.input_frames = sf_readf_float(infile, inputS, 4096 / channels)
      srcData.data_in = inputS.Data
      If srcData.input_frames < 4096 / channels Then srcData.end_of_input = 1
    Endif
      
    err = src_process(src_state, srcData)
    If err <> 0 Then Error.Raise("Errore: " & src_strerror(err))
     
' Termina il ciclo se ilprocesso è completato:
    If srcData.end_of_input And srcData.output_frames_gen = 0 Then Break
     
    maxF = Applica_Gain(srcData.data_out, srcData.output_frames_gen, channels, maxF, Float@(gain))
     
' Scrive i dati in uscita:
    sf_writef_float(outfile, outputS, srcData.output_frames_gen)
    output_count += srcData.output_frames_gen
     
    srcData.data_in += srcData.input_frames_used * channels
    srcData.input_frames -= srcData.input_frames_used
     
  Wend
   
  src_state = src_delete(src_state)
   
  If maxF > 1.0 Then
    gm = 1.0 / maxF
    gain = VarPtr(gm)
    Error.Raise("Il risultato ha prodotto distorsione della forma d'onda !\nRiavviare la conversione per evitarla.")
  Endif

  Return output_count
 
End


Private Function Applica_Gain(data As Pointer, frames As Long, channels As Integer, maxF As Float, gain As Float) As Float
 
 Dim k As Long
 Dim st As Stream
 Dim f As Float
 
  st = Memory data For Read Write
 
  For k = 0 To (frames * channels) - 1
    Read #st, f
    f *= gain
    Seek #st, Seek(st) - 8
    Write #st, f As Float
    If Abs(f) > maxF Then maxF = Abs(f)
  Next
   
  st.Close

  Return maxF  
 
End



Riferimenti