La gestione dei file audio mediante il componente gb.openal

Da Gambas-it.org - Wikipedia.

Il componente gb.openal utilizza le risorse dell'API di Openal.

E' possibile eseguire un file audio sia mediante le risorse delle Classi Al ed Alure [Nota 1] del componente gb.openal, sia solo con le Classi Alc e Al.


Eseguire un file audio con le risorse della Classe Al e della Classe Alure

Vi sono almeno tre modalità - sia in ambiente garfico che a riga di comando - per eseguire un file audio mediante le risorse della Classe Al e della Classe Alure del componente gb.openal.
E' possibile riprodurre file audio dei seguenti tipi:

  • WAV;
  • MP3;
  • OGG.


1a modalità

Private src As Integer[]
Private ast As Alurestream


Public Sub Form_Open()

  Dim err As Boolean

' Inizializza la libreria "Alure":
   err = Alure.InitDevice(Null, Null)
   If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")

End


Public Sub Button1_Click()    ' Avvia l'esecuzione del file audio

 Dim fileAudio As String = "/percorso/del/file/audio"
 Dim lungh As Integer
 Dim l As Byte = 1
  
  src = Al.GenSources(1)
  If IsNull(src) Then Error.Raise("Errore !")
  
' Individua la lunghezza del file audio caricato da eseguire:
  lungh = Stat(fileAudio).Size
  
' Se il file audio è un "mp3" o un "ogg", allora il valore della variabile "lungh"
' deve essere moltiplicato almeno per 4, altrimenti l'esecuzione audio terminerà anticipatamente:
  If Lower(File.Ext(fileAudio)) <> "wav" Then l = 4
  
  ast = Alure.CreateStreamFromFile(fileAudio, lungh * l, 0)
  If IsNull(ast) Then Error.Raise("Errore !")

' Esegue il flusso di dati audio.
' Il terzo parametro della funzione rappresenta il numero dei buffer utilizzati da accodare alla fonte di "OpenAL".
' Ogni buffer verrà riempito con la lunghezza del "chunk" specificato quando il flusso è stato creato.
' Tale valore nell'esecuzione di un file audio deve essere di almeno 2.
  Alure.PlaySourceStream(src[0], ast, 3, 0)

End


Public Sub Button2_Click()    ' Arresta l'esecuzione del file audio

  If IsNull(ast) Then Return
  Alure.StopSource(src[0])
    
End


Public Sub Button3_Click()    ' Arresta l'esecuzione del file audio e chiude la finestra del programma
      
  If IsNull(ast) = False Then
    Alure.DestroyStream(ast)
    al.DeleteSources(src)
    Alure.StopSource(src[0])
  Endif
   
  Alure.ShutdownDevice()
 
  Me.Close
 
End


2a modalità

Private source As Integer


Public Sub Form_Open()

  Dim err As Boolean

' Inizializza la libreria "Alure":
   err = Alure.InitDevice(Null, Null)
   If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")

End


Public Sub Button1_Click()    ' Avvia l'esecuzione del file audio

 Dim buffer As Integer
  
  buffer = alure.CreateBufferFromFile("/percorso/del/file/audio")
 
' Crea una sorgente sonora:
  source = al.GenSources(1)[0]
 
' Viene utilizzata la variabile "buffer" per riempire la sorgente.
' Il secondo argomento indica  al buffer di fornire campioni sonori:</font>
  al.Sourcei(source, al.BUFFER, buffer)
 
' Esegue il flusso di dati audio.
  al.SourcePlay(source)

End


Public Sub ToggleButton1_Click()

   If ToggleButton1.Value
' Mette in pausa l'esecuzione del file audio
     al.SourcePause(source)
   Else
' Fa proseguire l'esecuzione del file audio
     al.SourcePlay(source)
   Endif

End


Public Sub Button2_Click()    ' Arresta l'esecuzione del file audio

   al.SourceStop(source)

End


3a modalità

Private ags As Integer
 

Public Sub Button1_Click()    ' Avvia l'esecuzione del file audio
 
 Dim err As Boolean
 Dim fileAudio As String = "/percorso/del/file/audio"
 Dim agb As Integer


' Inizializza la libreria "Alure":
  err = Alure.InitDevice(Null, Null)
  If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")

' Genera le sorgenti sonore ed i buffer:
  ags = al.GenSources(1)[0]

  agb = al.GenBuffers(ags)[0]

  err = alure.BufferDataFromFile(fileAudio, agb)
  If err = False Then Error.Raise("Impossibile creare un buffer-data dal file audio !")

  al.Sourcei(ags, al.BUFFER, agb)

' Esegue il file audio:
  al.SourcePlay(ags)

End


Public Sub ToggleButton1_Click()

  If ToggleButton1.Value
' Mette in pausa l'esecuzione del file audio
    al.SourcePause(ags)
  Else
' Fa proseguire l'esecuzione del file audio
    al.SourcePlay(ags)
  Endif

End


Public Sub Button2_Click()    ' Arresta l'esecuzione del file audio

  al.SourceStop(ags)

End


Caso di applicazione a riga di comando

Nel caso in cui l'applicazione per l'esecuzione di file audio sia stata realizzata senza componenti grafici, e dunque a riga di comando, al termine del codice sarà assolutamente necessario prevedere un ciclo.

E' possibile porre in Pausa l'esecuzione immettendo nel Terminale o nell'apposito spazio della console il carattere "p" e quindi premendo il tasto Invio. Per riprendere l'esecuzione basterà premere il solo tasto "Invio". E' altresì possibile arrestare l'esecuzione immettendo nel Terminale o nell'apposito spazio della console uno o più caratteri (ovviamente diversi dalla lettera "p"), quindi premendo il tasto Invio.


1a modalità

Nel seguente esempio il percorso del file audio va inserito nell'apposito spazio sottostante della console (ovvero nel Terminale, ma in questo caso il percorso deve essere privo di apici). Alla fine dell'esecuzione o anche durante l'esecuzione medesima di un file audio è possibile - sempre con la modalità prima descritta inserire il percorso di un altro file audio ed eseguirlo.

Private s As String


Public Sub Main()

 Dim err As Boolean
 Dim src, lungh As Integer
 Dim ast As Alurestream
 Dim l As Byte = 1

' Il ciclo dura fino a che non viene inserito nell'apposito spazio della console (o nel Terminale, ma senza apici) il percorso di un file audio:
  Do
    Wait 0.01
  Loop Until s Begins "/"
   
' Inizializza la libreria "Alure":
  err = Alure.InitDevice(Null, Null)
  If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")
  
  src = Al.GenSources(1)[0]
  
  lungh = Stat(s).Size

  If Lower(File.Ext(s)) <> "wav" Then l = 4

  ast = Alure.CreateStreamFromFile(s, lungh * l, 0)
  If IsNull(ast) Then Error.Raise("Errore !")

  Alure.PlaySourceStream(src, ast, 3, 0)
  
  s = Null
   
' Inviando il carattere "p" dal Terminale o dalla console, l'esecuzione è posta in "Pausa",
' inviando ogni altro carattere essa viene terminata:
  Do
    Input s
    If s = "p" Then Pausa(src)
  Loop Until s <> Null

  Alure.DestroyStream(ast)
  al.DeleteSources([src])
  Alure.StopSource(src)
  Alure.ShutdownDevice()
  
  If s Begins "/" Then
    Main()
  Else
    Quit
  Endif
  
End
 

Private Procedure Pausa(sAl As Integer)
 
 Dim ripresa As String
 
  al.SourcePause(sAl)
  Print "Pausa !"
  
' Per riprendere l'esecuzione, è sufficiente premere il tasto "Invio":
  Input ripresa
  al.SourcePlayv([sal])
  
  Print "Riprende esecuzione."
  s = Null
 
End


Public Sub Application_Read()

  Line Input s
 
End


2a modalità

Private s As String


Public Sub Main()

 Dim err As Boolean
 Dim buffer, source As Integer
 
' Inizializza la libreria "Alure":
  err = Alure.InitDevice(Null, Null)
  If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")
  
  buffer = alure.CreateBufferFromFile("/percorso/del/file/audio")

' Crea una sorgente sonora:
  source = al.GenSources(1)[0]
 
' Viene utilizzata la variabile "buffer" per riempire la sorgente.
' Il secondo argomento indica  al buffer di fornire campioni sonori:</font>
  al.Sourcei(source, al.BUFFER, buffer)
 
' Esegue il flusso di dati audio.
  al.SourcePlay(source)

' Inviando il carattere "p" dal Terminale o dalla console, l'esecuzione è posta in "Pausa",
' inviando ogni altro carattere essa viene terminata:
  Do
    Input s
    If s = "p" Then Pausa(source)
  Loop Until s <> Null

  al.DeleteBuffers([buffer])
  al.DeleteSources([source])
  Alure.StopSource(source)
  Alure.ShutdownDevice()
  
End


Private Procedure Pausa(sAl As Integer)
 
 Dim ripresa As String
 
  al.SourcePause(sAl)
  Print "Pausa !"
  
' Per riprendere l'esecuzione, è sufficiente premere il tasto "Invio":
  Input ripresa
  al.SourcePlay(sAl)
  
  Print "Riprende esecuzione."
  s = Null
 
End


3a modalità

Private s As String


Public Sub Main()

 Dim err As Boolean
 Dim fileAudio As String
 Dim agb, ags As Integer
 
  fileAudio = "/percorso/del/file/audio" 
  
' Inizializza la libreria "Alure":
  err = Alure.InitDevice(Null, Null)
  If err = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")

  ags = al.GenSources(1)[0]

  agb = al.GenBuffers(ags)[0]

  err = alure.BufferDataFromFile(fileAudio, agb)
  If err = False Then Error.Raise("Impossibile creare un buffer-data dal file audio !")

  al.Sourcei(ags, al.BUFFER, agb)
  
  al.SourcePlay(ags)

' Inviando il carattere "p" dal Terminale o dalla console, l'esecuzione è posta in "Pausa",
' inviando ogni altro carattere essa viene terminata:
  Do
    Input s
    If s = "p" Then Pausa(ags)
  Loop Until s <> Null
  
  al.DeleteBuffers([agb])
  al.DeleteSources([ags])
  Alure.StopSource(ags)
  Alure.ShutdownDevice()
      
End


Private Procedure Pausa(sAl As Integer)
 
 Dim ripresa As String
 
  al.SourcePause(sAl)
  Print "Pausa !"
  
' Per riprendere l'esecuzione, è sufficiente premere il tasto "Invio":
  Input ripresa
  al.SourcePlay(sAl)
  
  Print "Riprende esecuzione."
  s = Null
 
End


Eseguire un file audio con l'uso delle Classi Alc e Al e caricando direttamente i dati audio grezzi nel buffer audio

In quest'altra modalità non si farà uso della Classe Alure, bensì le risorse delle Classi Al ed Alc (con la quale saranno impostati il dispositivo ed il contesto audio). Inoltre si provvederà a caricare direttamente i soli dati audio grezzi del file audio nel Buffer audio impostato con la Classe Al.

Mostriamo un esempio con un file di formato WAV:

Public Struct InfoWAV
  fileWAV As String
  canali As Short
  frequenza As Integer
  bit As Short
  block_align As Short
  durata As Float
  sonora As Byte[]
End Struct


Public Sub Main()
 
 Dim dispositivo As AlcDevice
 Dim contesto As AlcContext
 Dim src, buffer As Integer[]
 Dim err As Boolean
 Dim iw As New InfoWAV
 Dim formato As Integer
 
' Imposta il dispositivo audio con la Classe "Alc":
  dispositivo = Alc.OpenDevice(Null)
  If IsNull(dispositivo) Then Error.Raise("Impossibile impostare il dispositivo audio !")
  
' Imposta il contesto audio con la Classe "Alc":
  contesto = Alc.CreateContext(dispositivo)
  If IsNull(contesto) Then Error.Raise("Impossibile impostare il contesto audio !")
 
  err = contesto.MakeCurrent()
  If err = False Then Error.Raise("Impossibile creare il contesto audio !")

  src = Al.GenSources(1)
  If IsNull(src) Then Error.Raise("Errore !")

' Imposta il buffer audio:
  buffer = Al.GenBuffers(1)
  If IsNull(buffer) Then Error.Raise("Errore !")
  
  iw.fileWAV = "/percorso/del/file.wav"
  
  datiAudio(iw)
 
  Select Case iw.canali + iw.bit
    Case 9
      formato = Al.FORMAT_MONO8
    Case 10
      formato = Al.FORMAT_STEREO8
    Case 17
      formato = Al.FORMAT_MONO16
    Case 18
      formato = Al.FORMAT_STEREO16
  End Select

' I dati audio grezzi del file wav sono caricati nel buffer audio:
  Al.BufferData(buffer[0], formato, iw.sonora.ToString(0, iw.sonora.Count), iw.sonora.Count, iw.frequenza)
  
' Connette il buffer audio al sorgente audio:
  Al.Sourcei(src[0], Al.BUFFER, buffer[0])
  
' Esegue il sorgente audio:
  Al.SourcePlay(src[0])
  
' Consente l'esecuzione per l'intera durata del brano:
  Sleep iw.durata
  
  Al.SourceStop(src[0])
  
' Va in chiusura:
  Al.DeleteBuffers(buffer)
  Al.DeleteSources(src)
  Alc.DestroyContext(contesto)
  Alc.CloseDevice(dispositivo)
  
End


Private Function datiAudio(iwav As InfoWAV)
 
 Dim fl As File
 Dim d As Short
 
  Print "File wav caricato:";; iwav.fileWAV
  
' Carica il file audio Wav:
  fl = Open iwav.fileWAV For Read

  d = InStr(File.Load(iwav.fileWAV), "data")
 
  iwav.sonora = New Byte[Lof(fl) - (d + 7)]

' Spostiamo il puntatore all'interno del flusso del file sul primo byte successivo all'header del file wav,
' per caricare tutti i dati audio grezzi:
  Seek #fl, d + 7
  iwav.sonora.Read(fl, 0, Lof(fl) - (d + 7))
 
' Leggiamo il numero dei canali:
  Seek #fl, 22
  iwav.canali = Read #fl As Short
  Print "Numero canali:";; iwav.canali
 
' Leggiamo la frequenza di campionamento dei dati audio:
  Seek #fl, 24
  iwav.frequenza = Read #fl As Integer
  Print "Frequenza di campionamento: hz";; iwav.frequenza
 
' Leggiamo il valore del "block align":
  Seek #fl, 32 
  iwav.block_align = Read #fl As Short
 
' Leggiamo la risoluzione bit del campionamento:
  Seek #fl, 34 
  iwav.bit = Read #fl As Short
  Print "Risoluzione:";; iwav.bit;; "bit"
 
' Calcoliamo la durata del brano audio:
  iwav.durata = (iwav.sonora.Count / iwav.block_align) / iwav.frequenza
  Print "Durata esecuzione:";; CStr(Date(0, 0, 0, 0, 0, 0, iwav.durata * 1000))
 
  fl.Close
  
End


Il seguente codice è come il precedente con la differenza che esso consente di vedere il tempo trascorso dall'inizio dell'esecuzione, ed è possibile terminare l'esecuzione del file wav, scrivendo nell'apposito spazio inferiore della console uno o più caratteri qualsiasi e premendo il tasto "Invio" della tastiera.

Public Struct InfoWAV
  fileWAV As String
  canali As Short
  frequenza As Integer
  bit As Short
  block_align As Short
  durata As Float
  sonora As Byte[]
End Struct

Private s As String


Public Sub Main()
 
 Dim dispositivo As AlcDevice
 Dim contesto As AlcContext
 Dim src, buffer As Integer[]
 Dim err As Boolean
 Dim iw As New InfoWAV
 Dim formato As Integer
 Dim t As Date
 
' Imposta il dispositivo audio con la Classe "Alc":
  dispositivo = Alc.OpenDevice(Null)
  If IsNull(dispositivo) Then Error.Raise("Impossibile impostare il dispositivo audio !")
  
' Imposta il contesto audio con la Classe "Alc":
  contesto = Alc.CreateContext(dispositivo)
  If IsNull(contesto) Then Error.Raise("Impossibile impostare il contesto audio !")
 
  err = contesto.MakeCurrent()
  If err = False Then Error.Raise("Impossibile creare il contesto audio !")

  src = Al.GenSources(1)
  If IsNull(src) Then Error.Raise("Errore !")

' Imposta il buffer audio:
  buffer = Al.GenBuffers(1)
  If IsNull(buffer) Then Error.Raise("Errore !")
  
  iw.fileWAV = "/percorso/del/file.wav"
  
  datiAudio(iw)
 
  Select Case iw.canali + iw.bit
    Case 9
      formato = Al.FORMAT_MONO8
    Case 10
      formato = Al.FORMAT_STEREO8
    Case 17
      formato = Al.FORMAT_MONO16
    Case 18
      formato = Al.FORMAT_STEREO16
  End Select

' I dati audio grezzi del file wav sono caricati nel buffer audio:
  Al.BufferData(buffer[0], formato, iw.sonora.ToString(0, iw.sonora.Count), iw.sonora.Count, iw.frequenza)
  
' Connette il buffer audio al sorgente audio:
  Al.Sourcei(src[0], Al.BUFFER, buffer[0])
  
' Esegue il sorgente audio:
  Al.SourcePlay(src[0])
  
  t = Time
  
' Consente l'esecuzione per l'intera durata del brano:
  While DateDiff(t, Time, gb.Millisecond) < iw.durata * 1000
' La funzione "Wait" è necessaria per consentire al programma di ricevere il carattere inviato dalla console con il tasto "Invio" della tastiera:
    Wait 0.001
' Scrive nello "standard output" il tempo trascorso dall'inizio dell'esecuzione del file wav:
    Write #File.Out, CStr(Date(0, 0, 0, 0, 0, 0, DateDiff(t, Time, gb.Millisecond))) & "\r"
' Se riceve dallo "standard input" uno o più caratteri qualsiasi, esce dal ciclo:
    If s <> Null Then Exit
  Wend
  
  Al.SourceStop(src[0])
  
' Va in chiusura:
  Al.DeleteBuffers(buffer)
  Al.DeleteSources(src)
  Alc.DestroyContext(contesto)
  Alc.CloseDevice(dispositivo)
  Quit
  
End


Public Sub Application_Read()   ' Arresta l'esecuzione
  
  Input s
 
End


Private Function datiAudio(iwav As InfoWAV)
 
 Dim fl As File
 Dim d As Short
 
  Print "File wav caricato:          "; iwav.fileWAV
  
' Carica il file audio Wav:
  fl = Open iwav.fileWAV For Read

  d = InStr(File.Load(iwav.fileWAV), "data")
 
  iwav.sonora = New Byte[Lof(fl) - (d + 7)]

' Spostiamo il puntatore all'interno del flusso del file sul primo byte successivo all'header del file wav, per caricare tutti i dati audio grezzi:
  Seek #fl, d + 7
  iwav.sonora.Read(fl, 0, Lof(fl) - (d + 7))
 
' Leggiamo il numero dei canali:
  Seek #fl, 22
  iwav.canali = Read #fl As Short
  Print "Numero canali audio:        "; iwav.canali
 
' Leggiamo la frequenza di campionamento dei dati audio:
  Seek #fl, 24
  iwav.frequenza = Read #fl As Integer
  Print "Frequenza di campionamento: "; iwav.frequenza
 
' Leggiamo il valore del "block align":
  Seek #fl, 32 
  iwav.block_align = Read #fl As Short
 
' Leggiamo la risoluzione bit del campionamento:
  Seek #fl, 34 
  iwav.bit = Read #fl As Short
  Print "Risoluzione audio:          "; iwav.bit; " bit"
 
' Calcoliamo la durata del brano audio:
  iwav.durata = (iwav.sonora.Count / iwav.block_align) / iwav.frequenza
  Print "Durata esecuzione:          "; CStr(Date(0, 0, 0, 0, 0, 0, iwav.durata * 1000))
 
  fl.Close
  
End


In quest'altro esempio il file audio di formato WAV sarà letto e gestito come stringa:

Public Sub Main()
 
 Dim per, s As String
 Dim d, i, campionamento, formato, durata As Integer
 Dim canali, bit As Short
 Dim disp As AlcDevice
 Dim cont As AlcContext
 Dim src, buffer As Integer[]
 Dim err As Boolean
 Dim dati As Short[]

' Estrae alcune informazioni sul file:
 per = "/percorso/del/file.wav"
 Print "Percorso del file audio:    "; per
 s = File.Load(per)
 Print "Dimensione del file:        "; Len(s); " byte"
 d = InStr(s, "data", 0, gb.Binary)
 i = Int@(VarPtr(s) + d + 3)
 Print "Totale dei soli byte audio: "; i; " byte"
 d = InStr(s, "fmt ", 0, gb.Binary)
 canali = Short@(VarPtr(s) + d + 9)
 Print "Numero canali audio:        "; canali
 campionamento = Int@(VarPtr(s) + d + 11)
 Print "Frequenza di campionamento: "; campionamento; " hertz"
 bit = Short@(VarPtr(s) + d + 21)
 Select Case bit + canali
   Case 9
     formato = Al.FORMAT_MONO8
   Case 10
     formato = Al.FORMAT_STEREO8
   Case 17
     formato = Al.FORMAT_MONO16
   Case 18
     formato = Al.FORMAT_STEREO16
 End Select
 Print "Profondità campionamento:   "; bit; " bit"
 durata = (i * 8) / (campionamento * bit * canali)
 Print "Durata del brano audio:     "; CStr(Date(0, 0, 0, 0, 0, 0, durata * 1000))
 
' Configura il dispositivo audio con la Classe "Alc":
 disp = Alc.OpenDevice(Null)
 If IsNull(disp) Then Error.Raise("Impossibile configurare il dispositivo audio !")
 
' Configura il contesto audio con la Classe "Alc":
 cont = Alc.CreateContext(disp)
 If IsNull(cont) Then Error.Raise("Impossibile configurare il contesto audio !")
 
 err = cont.MakeCurrent()
 If err = False Then Error.Raise("Impossibile creare il contesto audio !")
 
 src = Al.GenSources(1)
 If IsNull(src) Then Error.Raise("Errore !")
 
' Configura il buffer audio:
 buffer = Al.GenBuffers(1)
 If IsNull(buffer) Then Error.Raise("Errore !")
 
' Raccoglie i soli byte dei dati audio grezzi:
 s = Right(s, i)
 
' I dati audio sono caricati nel buffer audio:
 Al.BufferData(buffer[0], formato, s, i, campionamento)
 
' Connette il buffer audio al sorgente audio:
 Al.Sourcei(src[0], Al.BUFFER, buffer[0])
 
' Esegue il sorgente audio:
 Al.SourcePlay(src[0])
 
' Consente l'esecuzione per l'intera durata dell'onda sonora:
 Sleep durata
 
 Al.SourceStop(src[0])
 
' Libera la memoria:
 Al.DeleteBuffers(buffer)
 Al.DeleteSources(src)
 Alc.DestroyContext(cont)
 Alc.CloseDevice(disp)
  
End



Note

[1] La Classe Alure del componente gb.openal consente di gestire le funzioni di ALURE, che è una libreria di supporto di Openal.