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.
(5 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 | + | 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 | Private Enum SEEK_SET = 0, SEEK_CUR, SEEK_END | ||
− | Library "libsamplerate:0. | + | Library "libsamplerate:0.2.2" |
Public Struct SRC_DATA | Public Struct SRC_DATA | ||
Riga 50: | Riga 48: | ||
− | Library "libsndfile:1.0. | + | 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 | ||
− | + | format_ As Integer | |
sections As Integer | sections As Integer | ||
seekable As Integer | seekable As Integer | ||
Riga 92: | Riga 90: | ||
− | + | 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 | |
− | + | gain = 1.0 | |
− | + | 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 | |
− | + | 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 | |
<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) | |
− | + | 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 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(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)) | |
− | + | 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 | |
− | |||
<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 | |
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 | |
− | + | err = src_process(src_state, srcData) | |
− | + | 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 | |
− | + | 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) | |
− | + | 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 k As Long | ||
Riga 225: | Riga 215: | ||
Dim f As Float | 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 | |
− | |||
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