Differenze tra le versioni di "Modificare la frequenza di campionamento di un file audio con l' API di Libsamplerate e Libsndfile"

Da Gambas-it.org - Wikipedia.
 
(4 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
 
La libreria '''SampleRate''' consente, fra l'altro, di modificare la frequenza di campionamento (''Sample Rate'') di un file audio.
 
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''"
+
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 ''SndFile''.
 
  
 +
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
 
  Private Enum SEEK_SET = 0, SEEK_CUR, SEEK_END
 
   
 
   
 
   
 
   
  Library "libsamplerate:0.1.8"
+
  Library "libsamplerate:0.2.2"
 
   
 
   
 
  Public Struct SRC_DATA
 
  Public Struct SRC_DATA
Riga 50: Riga 48:
 
   
 
   
 
   
 
   
  Library "libsndfile:1.0.25"
+
  Library "libsndfile:1.0.37"
 
   
 
   
 
  Public Struct SF_INFO
 
  Public Struct SF_INFO
Riga 56: Riga 54:
 
   samplerate As Integer
 
   samplerate As Integer
 
   channels As Integer   
 
   channels As Integer   
   formatI As Integer
+
   format_ As Integer
 
   sections As Integer
 
   sections As Integer
 
   seekable As Integer
 
   seekable As Integer
Riga 92: Riga 90:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Main()
 
   
 
   
 
   Dim infile, outfile As Pointer
 
   Dim infile, outfile As Pointer
Riga 100: Riga 98:
 
   Dim src_ratio, gain As Float
 
   Dim src_ratio, gain As Float
 
   Dim max_speed As Boolean
 
   Dim max_speed As Boolean
   Dim FileAudioIn As String = "''/percorso/del/file/audio/originario''"
+
   Dim FileAudioIn As String = "<FONT Color=darkgreen>''/percorso/del/file/audio/originario''</font>"
   Dim FileAudioOut As String = "''/percorso/del/file/audio/modificato''"
+
   Dim FileAudioOut As String = "<FONT Color=darkgreen>''/percorso/del/file/audio/modificato''</font>"
 
    
 
    
  src_ratio = -1.0
+
  src_ratio = -1.0
  gain = 1.0
+
  gain = 1.0
  converter = Medium_Sinc_Interpolator
+
  converter = Medium_Sinc_Interpolator
 
   
 
   
 
  <FONT Color=gray>' ''Imposta la nuova frequenza di campionamento da assegnare al file audio:''</font>
 
  <FONT Color=gray>' ''Imposta la nuova frequenza di campionamento da assegnare al file audio:''</font>
  new_sample_rate = 11050
+
  new_sample_rate = 11050
 
      
 
      
  infile = sf_open(FileAudioIn, SFM_READ, sfinfo)
+
  infile = sf_open(FileAudioIn, SFM_READ, sfinfo)
  If IsNull(infile) Then Error.Raise("Impossibile aprire il file audio in entrata !")
+
  If infile == 0 Then Error.Raise("Impossibile aprire il file audio in entrata !")
 
      
 
      
  Print "File audio in entrata: ", Null; FileAudioIn
+
  Print "File audio in entrata: ", Null; FileAudioIn
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
+
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
  Print "Frame in ingresso: ", Null; CLong(sfinfo.frames)
+
  Print "Frame in ingresso: ", Null; CLong(sfinfo.frames)
 
      
 
      
  If new_sample_rate > 0 Then
+
  If new_sample_rate > 0 Then
    src_ratio = (1.0 * new_sample_rate) / sfinfo.samplerate
+
    src_ratio = (1.0 * new_sample_rate) / sfinfo.samplerate
    sfinfo.samplerate = new_sample_rate
+
    sfinfo.samplerate = new_sample_rate
  Else If src_is_valid_ratio(src_ratio)
+
  Else If src_is_valid_ratio(src_ratio)
    sfinfo.samplerate = CInt(Floor(sfinfo.samplerate * src_ratio))
+
    sfinfo.samplerate = CInt(Floor(sfinfo.samplerate * src_ratio))
  Else
+
  Else
    Error.Raise("Impossibile determinare la nuova frequenza di campionamento !")
+
    Error.Raise("Impossibile determinare la nuova frequenza di campionamento !")
  Endif
+
  Endif
 
      
 
      
  Print "\nSRC Ratio: ", Null, src_ratio
+
  Print "\nSRC Ratio: ", Null, src_ratio
  Print "Converter: ", Null, src_get_name(converter)
+
  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 !")
+
  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 "\nFile audio in uscita: ", Null; FileAudioOut
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
+
  Print "Frequenza campio.nto: ", Null; sfinfo.samplerate
 
      
 
      
  outfile = sf_open(FileAudioOut, SFM_WRITE, sfinfo)
+
  outfile = sf_open(FileAudioOut, SFM_WRITE, sfinfo)
  If IsNull(outfile) Then Error.Raise("Impossibile aprire il file d'uscita !")
+
  If outfile == 0 Then Error.Raise("Impossibile aprire il file d'uscita !")
 
   
 
   
  If max_speed Then
+
  If max_speed Then
    sf_command(outfile, SFC_SET_ADD_PEAK_CHUNK, Null, False)
+
    sf_command(outfile, SFC_SET_ADD_PEAK_CHUNK, Null, False)
  Else
+
  Else
 
  <FONT Color=gray>' ''Aggiorna il file header dopo la scrittura dei nuovi dati audio:''</font>
 
  <FONT Color=gray>' ''Aggiorna il file header dopo la scrittura dei nuovi dati audio:''</font>
    sf_command(outfile, SFC_SET_UPDATE_HEADER_AUTO, Null, True)
+
    sf_command(outfile, SFC_SET_UPDATE_HEADER_AUTO, Null, True)
  Endif
+
  Endif
 
   
 
   
  sf_command(outfile, SFC_SET_CLIPPING, Null, True)
+
  sf_command(outfile, SFC_SET_CLIPPING, Null, True)
 
   
 
   
  count = Conversione_Frequenza(infile, outfile, converter, src_ratio, sfinfo.channels, VarPtr(gain))
+
  count = Conversione_Frequenza(infile, outfile, converter, src_ratio, sfinfo.channels, VarPtr(gain))
 
      
 
      
  Print "Frames d'uscita: ", Null; CLong(count)
+
  Print "Frames d'uscita: ", Null; CLong(count)
 
   
 
   
  sf_close(infile)
+
  sf_close(infile)
  sf_close(outfile)
+
  sf_close(outfile)
 
   
 
   
  '''End'''
+
  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
+
  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 inputS, outputS As New Single[4096]
Riga 165: Riga 163:
 
   Dim srcData As New SRC_DATA
 
   Dim srcData As New SRC_DATA
 
    
 
    
  sf_seek(infile, 0, SEEK_SET)
+
  sf_seek(infile, 0, SEEK_SET)
  sf_seek(outfile, 0, SEEK_SET)
+
  sf_seek(outfile, 0, SEEK_SET)
 
    
 
    
 
  <FONT Color=gray>' ''Inizializza il convertitore di frequenza:''</font>
 
  <FONT Color=gray>' ''Inizializza il convertitore di frequenza:''</font>
  src_state = src_new(converter, channels, VarPtr(err))
+
  src_state = src_new(converter, channels, VarPtr(err))
  If IsNull(src_state) Then Error.Raise("Impossibile inizializzare il convertitore di frequenza !")
+
  If src_state == 0 Then Error.Raise("Impossibile inizializzare il convertitore di frequenza !")
 
      
 
      
  srcData.data_in = inputS.Data
+
  With srcData
+
    .data_in = inputS.Data
  srcData.src_ratio = src_ratio
+
    .src_ratio = src_ratio
+
    .data_out = outputS.Data
  srcData.data_out = outputS.Data
+
    .output_frames = 4096 / channels
+
  End With
  srcData.output_frames = 4096 / channels
 
 
      
 
      
  While True
+
  Do
 
 
  <FONT Color=gray>' ''Se il buffer d'ingresso è vuoto, viene riempito:''</font>
 
  <FONT Color=gray>' ''Se il buffer d'ingresso è vuoto, viene riempito:''</font>
    If srcData.input_frames == 0 Then
+
    If srcData.input_frames == 0 Then
 
       srcData.input_frames = sf_readf_float(infile, inputS, 4096 / channels)
 
       srcData.input_frames = sf_readf_float(infile, inputS, 4096 / channels)
 
       srcData.data_in = inputS.Data
 
       srcData.data_in = inputS.Data
 
       If srcData.input_frames < 4096 / channels Then srcData.end_of_input = 1
 
       If srcData.input_frames < 4096 / channels Then srcData.end_of_input = 1
    Endif
+
    Endif  
     
+
    err = src_process(src_state, srcData)
    err = src_process(src_state, srcData)
+
    If err <> 0 Then Error.Raise("Errore: " & src_strerror(err))  
    If err <> 0 Then Error.Raise("Errore: " & src_strerror(err))
 
     
 
 
  <FONT Color=gray>' ''Termina il ciclo se ilprocesso è completato:''</font>
 
  <FONT Color=gray>' ''Termina il ciclo se ilprocesso è completato:''</font>
    If srcData.end_of_input And srcData.output_frames_gen = 0 Then Break
+
    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))
    maxF = Applica_Gain(srcData.data_out, srcData.output_frames_gen, channels, maxF, Float@(gain))
 
     
 
 
  <FONT Color=gray>' ''Scrive i dati in uscita:''</font>
 
  <FONT Color=gray>' ''Scrive i dati in uscita:''</font>
    sf_writef_float(outfile, outputS, srcData.output_frames_gen)
+
    sf_writef_float(outfile, outputS, srcData.output_frames_gen)
    output_count += srcData.output_frames_gen
+
    output_count += srcData.output_frames_gen
     
+
    srcData.data_in += srcData.input_frames_used * channels
    srcData.data_in += srcData.input_frames_used * channels
+
    srcData.input_frames -= srcData.input_frames_used
    srcData.input_frames -= srcData.input_frames_used
+
  Loop
     
 
  Wend
 
 
      
 
      
  src_state = src_delete(src_state)
+
  src_state = src_delete(src_state)
 
      
 
      
  If maxF > 1.0 Then
+
  If maxF > 1.0 Then
    gm = 1.0 / maxF
+
    gm = 1.0 / maxF
    gain = VarPtr(gm)
+
    gain = VarPtr(gm)
    Error.Raise("Il risultato ha prodotto distorsione della forma d'onda !\nRiavviare la conversione per evitarla.")
+
    Error.Raise("Il risultato ha prodotto distorsione della forma d'onda !\nRiavviare la conversione per evitarla.")
  Endif
+
  Endif
 
   
 
   
  Return output_count
+
  Return output_count
 
    
 
    
  '''End'''
+
  End
 
   
 
   
 
   
 
   
  '''Private''' Function Applica_Gain(data As Pointer, frames As Long, channels As Integer, maxF As Float, gain As Float) As Float
+
  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 k As Long
Riga 225: Riga 215:
 
   Dim f As Float
 
   Dim f As Float
 
    
 
    
  st = Memory data For Read Write
+
  st = Memory data For Read Write
 
    
 
    
  For k = 0 To (frames * channels) - 1
+
  For k = 0 To (frames * channels) - 1
    Read #st, f
+
    Read #st, f
    f *= gain
+
    f *= gain
    Seek #st, Seek(st) - 8
+
    Seek #st, Seek(st) - 8
    Write #st, f As Float
+
    Write #st, f As Float
    If Abs(f) > maxF Then maxF = Abs(f)
+
    If Abs(f) > maxF Then maxF = Abs(f)
  Next
+
  Next
 
      
 
      
  st.Close
+
  st.Close
 
   
 
   
  Return maxF   
+
  Return maxF   
 
    
 
    
  '''End'''
+
  End
 
 
  
  

Versione attuale delle 17:35, 13 gen 2024

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