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 condivisa: "libsamplerate.so.0.2.2 ".

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

Private Enum SEEK_SET = 0, SEEK_CUR, SEEK_END


Library "libsamplerate:0.2.2"

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.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 = &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 infile == 0 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 outfile == 0 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 src_state == 0 Then Error.Raise("Impossibile inizializzare il convertitore di frequenza !")
   
 With srcData
   .data_in = inputS.Data
   .src_ratio = src_ratio
   .data_out = outputS.Data
   .output_frames = 4096 / channels
 End With
   
 Do 
' 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
 Loop
   
 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