Convertire la frequenza di campionamento di un file WAV con le funzioni esterne del API di SDL2

Da Gambas-it.org - Wikipedia.

La libreria SDL 2 è un API multi-piattaforma contenente funzioni per la gestione multimediale dell'audio e del video.


Mostriamo di seguito un esempio di codice per convertire la frequenza di campionamento di un file WAV. In tal caso sarà necessario richiamare le seguenti librerie dell'API di SDL2:

  • libSDL2-2.0.so.0.2.0
  • libSDL2_mixer-2.0.so.0.0.0

Inoltre, poiché questo esempio richiede l'utilizzo di una Struttura molto complessa della libreria di SDL2, ossia la Struttura "SDL_RWops", provvederemo a creare una nostra apposita libreria dinamica condivisa esterna, scritta in C e che chiameremo ad esempio libadhoc.c, per effettuare una gestione sicura di quella Struttura esterna.


Pertanto, il codice Gambas sarà:

Library "libSDL2-2.0:0.2.0"

Public Struct SDL_AudioSpec
  freq As Integer
  format As Short
  channels As Byte
  silence As Byte
  samples As Short
  padding As Short
  size As Integer
  callback As Pointer
  userdata As Pointer
End Struct

Public Struct SDL_AudioCVT
  needed As Integer
  src_format As Short
  dst_format As Short
  rate_incr As Float
  buf As Pointer
  len As Integer
  len_cvt As Integer
  len_mult As Integer
  len_ratio As Float
  filters[10] As Pointer
  filter_index As Integer
End Struct

Private Const SDL_INIT_AUDIO As Integer = 16
Private Const SDL_AUDIO_MASK_BITSIZE As Integer = 127

' int SDL_Init(Uint32 flags)
' Initialize the SDL library.
Private Extern SDL_Init(flags As Integer) As Integer

' SDL_AudioSpec* SDL_LoadWAV_RW(SDL_RWops * src, int freesrc, SDL_AudioSpec* spec, Uint8** audio_buf, Uint32* audio_len)
' Loads a WAVE from the data source.
Private Extern SDL_LoadWAV_RW(SDL_RWops As Pointer, freesrc As Integer, SDL_spec As SDL_AudioSpec, audio_buf As Pointer, audio_len As Pointer) As SDL_AudioSpec

' int SDL_BuildAudioCVT(SDL_AudioCVT * cvt, SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate)
' Initialize an SDL_AudioCVT structure for conversion.
Private Extern SDL_BuildAudioCVT(SDL_cvt As SDL_AudioCVT, src_format As Integer, src_channels As Byte, src_rate As Integer, dst_format As Integer, dst_channels As Byte, dst_rate As Integer) As Integer

' int SDL_ConvertAudio(SDL_AudioCVT* cvt)
' Converts audio data to a desired audio format.
Private Extern SDL_ConvertAudio(SDL_cvt As SDL_AudioCVT) As Integer

' size_t SDL_WriteLE32(SDL_RWops* dst, Uint32 value)
' Writes 32 bits in native format to a SDL_RWops as little-endian data.
Private Extern SDL_WriteLE32(SDL_RWops As Pointer, value As Integer) As Integer

' size_t SDL_WriteLE16(SDL_RWops* dst, Uint16 value)
' Writes 16 bits in native format to a SDL_RWops as little-endian data.
Private Extern SDL_WriteLE16(SDL_RWops As Pointer, value As Short) As Integer

' void SDL_Quit(void)
' Clean up all initialized subsystems.
Private Extern SDL_Quit()


' SDL_RWops * SDL_RWFromFile(const char *file, const char *mode)
' Create a new SDL_RWops structure for reading from and/or writing to a named file. 
Private Extern SDL_RWFromFile(filewav As String, mode As String) As Pointer In "libSDL2_mixer-2.0:0.0.0"


' void *memcpy(void *str1, const void *str2, size_t n)
' Copies n characters from memory area str2 to memory area str1.
Private Extern memcpy(str1 As Pointer, str2 As Pointer, n As Integer) In "libc:6"


' int Scrive_Chiude_WAV(SDL_RWops * io, Uint8 *buf, int len_cvt)
' La nostra apposita libreria .so per la gestione sicura della Struttura esterna "SDL_RWops".
Private Extern Scrive_Chiude_WAV(SDL_RWops As Pointer, buffer As Pointer, lung As Integer) As Integer In "/tmp/libadhoc"


Public Sub Main()

 Dim filename As String = "/percorso/del/file.wav"
 Dim convertito As String = "/percorso/del/file.wav/convertito"
 Dim data, io As Pointer
 Dim lun, cvtfreq, err As Integer
 Dim bitsize, blockalign, avgbytes As Integer
 Dim spec As New SDL_AudioSpec
 Dim cvt As New SDL_AudioCVT
 
' Crea la nostra apposita libreria per la gestione sicura della Struttura "SDL_RWops":
  Creaso()
 
' Imposta la nuova frequenza di campionamento, nella quale il file wav sarà convertito:
  cvtfreq = 22050

' Inizializza la libreria SDL2:
  If SDL_Init(SDL_INIT_AUDIO) < 0 Then Error.Raise("Impossibile inizializzare la libreria SDL2 !")
  
  If IsNull(SDL_LoadWAV_RW(SDL_RWFromFile(filename, "rb"), 1, spec, VarPtr(data), VarPtr(lun))) Then 
    Error.Raise("Impossibile caricare il file audio: " & filename & " !")
  Endif
  
  If SDL_BuildAudioCVT(cvt, spec.format, spec.channels, spec.freq, spec.format, spec.channels, cvtfreq) = -1 Then
    Error.Raise("Impossibile realizzare il CVT !")
  Endif

  cvt.len = lun
  cvt.buf = Alloc(lun * cvt.len_mult)

  memcpy(cvt.buf, data, lun)
  
  If SDL_ConvertAudio(cvt) = -1 Then Error.Raise("Conversione fallita !")

' Scrive l'header del file WAV in uscita:
  io = SDL_RWFromFile(convertito, "wb")
  If io = 0 Then Error.Raise("Impossibile salvare il file audio convertito: " & convertito & " !")
   
  bitsize = spec.format And SDL_AUDIO_MASK_BITSIZE
  blockalign = (bitsize / 8) * spec.channels
  avgbytes = cvtfreq * blockalign
   
  SDL_WriteLE32(io, &46464952)  ' RIFF
  SDL_WriteLE32(io, lun * cvt.len_mult + 36)
  SDL_WriteLE32(io, &45564157)  ' WAVE
  SDL_WriteLE32(io, &20746D66)  ' fmt
  SDL_WriteLE32(io, 16) ' chunk size
  SDL_WriteLE16(io, 1)  ' uncompressed
  SDL_WriteLE16(io, spec.channels)  ' channels
  SDL_WriteLE32(io, cvtfreq)  ' sample rate
  SDL_WriteLE32(io, avgbytes) ' average bytes per second
  SDL_WriteLE16(io, blockalign) ' block align
  SDL_WriteLE16(io, bitsize)  ' significant bits per sample
  SDL_WriteLE32(io, &61746164)  ' data
  SDL_WriteLE32(io, cvt.len_cvt)  ' size
  
' Viene invocata la nostra apposta libreria per la scrittura e la chiusura del file WAV convertito:
  err = Scrive_Chiude_WAV(io, cvt.buf, cvt.len_cvt)
  If err = -1 Then Error.Raise("Impossibile chiudere il file audio convertito: " & convertito & " !")

' Va in chiusura:
  Free(cvt.buf)
  SDL_Quit()

End


Private Procedure Creaso()
  
  File.Save("/tmp/libadhoc.c", "#include <SDL2/SDL.h>" &
             "\n\n" &
             "int Scrive_Chiude_WAV(SDL_RWops * io, Uint8 *buf, int len_cvt) {\n" &
             "   SDL_RWwrite(io, buf, len_cvt, 1);\n" &
             "   if (SDL_RWclose(io) == -1)\n" &
             "     return -1;\n" &
             "   return 0;\n}")
 
  Shell "gcc -o /tmp/libadhoc.so /tmp/libadhoc.c -shared" Wait
 
End



Riferimenti