Modificare il volume e la frequenza di campionamento di file audio WAV e AIFF con l'API di Libaudio

Da Gambas-it.org - Wikipedia.

La libreria Libaudio, appartenente al protoccolo audio Network Audio System (NAS), consente di gestire alcuni formati di file audio.

Per poter fruire delle risorse di Libaudio, è necessario installare nel sistema e richiamare nel programma Gambas la libreria dinamica condivisa: "libaudio.so.2.4"


Mostriamo un esempio per modificare il volume e/o la frequenza di campionamento di un file audio di formato WAV o AIFF:

Library "libaudio:2.4"

Public Struct Sound
  fileFormat As Integer
  dataFormat As Integer
  numTracks As Integer
  sampleRate As Integer
  numSamples As Integer
  comment As String
  formatInfo As Pointer
End Struct

Private Const SoundUnknownNumSamples As Integer = &FFFFFFFF

' Sound SoundOpenFileForReading(const char * filename)
' Open an audio file for reading.
Private Extern SoundOpenFileForReading(filename As String) As Sound

' int SoundReadFile(char * buffer, int num_bytes, Sound sound)
' Read audio data from an audio file.
Private Extern SoundReadFile(buffer As Pointer, bytes As Integer, Ssound As Sound) As Integer

' int AuConvertDataToShort(int data_format, int num_bytes, AuPointer data)
' Convert audio data from the specified format to signed short integer.
Private Extern AuConvertDataToShort(Sformat As Integer, bytes As Integer, buffer As Pointer) As Integer

' int SoundValidDataFormat(int _f, int _d)
Private Extern SoundValidDataFormat(S_f As Integer, S_d As Integer) As Integer

' Sound SoundCreate(int file_format, int data_format, int num_tracks, int sample_rate, int num_samples, char * comment)
' Create a description of an audio file.
Private Extern SoundCreate(file_format As Integer, data_format As Integer, num_tracks As Integer, sample_rate As Integer, num_samples As Integer, comment As String) As Sound

' int AuConvertShortToData(int format, int num_bytes, AuPointer data)
' Convert signed short integers to the specified format.
Private Extern AuConvertShortToData(file_format As Integer, bytes As Integer, buffer As Pointer) As Integer

' Sound SoundOpenFileForWriting(char *filename, Sound sound)
' Open an audio file for writing.
Private Extern SoundOpenFileForWriting(filename As String, Ssound As Sound) As Sound

' int SoundWriteFile(char *buffer, int num_bytes, Sound sound)
' Write audio data to an audio file.
Private Extern SoundWriteFile(buffer As Pointer, bytes As Integer, Ssound As Sound) As Integer

' int SoundCloseFile(Sound sound)
' Close an audio file description.
Private Extern SoundCloseFile(Ssound As Sound) As Integer

' void bcopy(const void *src, void *dest, size_t n)
' Copy byte sequence.
Private Extern bcopy(src As Pointer, dest As Pointer, n As Integer) In "libc:6"


Public Sub Main()

 Dim inFl, exFl As String
 Dim sndIn, sndOut As New Sound
 Dim frequenza, numBytes, err As Integer
 Dim volume, conto, outputDataFormat, fileFormat As Integer
 Dim dati As Pointer     ' = buffer
 Dim st As Stream
 Dim sh As Short
 
  inFl = "/percorso/del/file/audio/da/modificare"
  exFl = "/percorso/del/nuovo/file/come/modificato"
  
  sndIn = SoundOpenFileForReading(inFl)
  If IsNull(sndIn) Then Error.Raise("Impossibile aprire il file '" & inFl & "' !")
   
  numBytes = sndIn.numSamples * sndIn.numTracks * SizeOf(gb.Short)
   
' Modifichiamo il volume: il valore va inteso in percentuale rispetto al volume originario
  volume = 20
  
' Modifichiamo la frequenza originaria di campionamento dei dati audio dimezzandola:
  frequenza = sndIn.sampleRate / 2
   
  If (volume = 100) And (frequenza = sndIn.sampleRate) Then
    Print "\nPoiché non si intende effettuare alcuna variazione, il programma verrà terminato !"
    Quit
  Endif
   
  dati = Alloc(numBytes)
   
  If SoundReadFile(dati, numBytes, sndIn) <> numBytes Then Error.Raise("Errore di lettura del file audio d'ingresso !")
  
  err = AuConvertDataToShort(sndIn.dataFormat, numBytes, dati)
  If err <> 0 Then Error.Raise("Errore nella conversione dei dati di Entrata !")
   
' Effettua la modifica del volume:
  If volume < 100 Then
     
' Per scrivere i nuovi valori dei dati audio nell'area di memoria puntata da "Puntatore" usiamo i "Memory Stream":
    st = Memory dati For Read Write
     
    For conto = 0 To (numBytes / SizeOf(gb.Short)) - 1
      Read #st, sh
      sh *= CSingle(volume / 100)
      Seek #st, Seek(st) - 2       ' Torniamo dietro di 2 byte, perché si tratta di uno Short
      Write #st, sh As Short
    Next
    st.Close
     
  Endif
      
  err = SoundValidDataFormat(sndIn.fileFormat, sndIn.dataFormat)
  If Not err Then Error.Raise("Formato di file non supportato !")
     
  sndOut = SoundCreate(sndIn.fileFormat, sndIn.dataFormat, sndIn.numTracks, frequenza, SoundUnknownNumSamples, Null)
  If IsNull(sndOut) Then Error.Raise("Impossibile creare il file sonoro di Uscita !")
     
  err = AuConvertShortToData(sndIn.dataFormat, numBytes, dati)
  If err <> 0 Then Error.Raise("Errore di conversione dei dati audio in Uscita !")
     
  If Not SoundOpenFileForWriting(exFl, sndOut) Then Error.Raise("Impossibile aprire il file in scrittura dati !")
   
' Effettua la modifica della frequenza di campionamento:
  If sndIn.sampleRate = sndOut.sampleRate Then
    err = SoundWriteFile(dati, numBytes, sndOut)
    If err <> numBytes Then Error.Raise("Errore nella scrittura del file audio in Uscita !")
  Else
    rateConvert(sndIn, sndOut, numBytes, dati)
  Endif
   
  If SoundCloseFile(sndOut) Then Error.Raise("Impossibile chiudere il file audio di Uscita !")
   
  Free(dati)
  
End


Private Procedure rateConvert(soIn As Sound, soOut As Sound, nb As Integer, data As Pointer)
 
 Dim dimen, phase, err As Integer
 Dim ultimum As Pointer
 
  dimen = SizeOf(gb.Short)
  
  ultimum = Alloc(dimen)
  If IsNull(ultimum) Then Error.Raise("Errore nell'allocazione di memoria !")
  
  While nb
     
    While phase >= 0
      If nb = 0 Then
        Free(ultimum)
        Return
      Endif
      phase -= soOut.sampleRate
      bcopy(data, ultimum, dimen)
      data += dimen
      nb -= dimen
    Wend
     
    phase += soIn.sampleRate
  
    err = SoundWriteFile(ultimum, dimen, soOut)
    If err <> dimen Then Error.Raise("Errore nella scrittura del file audio in Uscita !")
  
  Wend
 
End



Riferimenti