Differenze tra le versioni di "Eseguire un file audio mediante le funzioni esterne del API di PortAudio e SndFile"

Da Gambas-it.org - Wikipedia.
Riga 1: Riga 1:
 
'''PortAudio''' è una libreria ''open-source'' multipiattaforma per l'ingresso e l'uscita audio in tempo reale. La libreria fornisce funzioni che permettono al software di acquisire e di inviare flussi audio in tempo reale dalle interfacce audio hardware del computer.
 
'''PortAudio''' è una libreria ''open-source'' multipiattaforma per l'ingresso e l'uscita audio in tempo reale. La libreria fornisce funzioni che permettono al software di acquisire e di inviare flussi audio in tempo reale dalle interfacce audio hardware del computer.
 
+
<BR>Poiché ''PortAudio'' non è progettato per leggere i dati audio utili da un file audio, bisognerà ''appoggiarsi'', per questo, ad un'altra risorsa come, ad esempio, le funzioni della libreria ''SndFile''.
Poiché ''PortAudio'' non è progettato per leggere i dati audio utili da un file audio, bisognerà ''appoggiarsi'', per questo, ad un'altra risorsa come, ad esempio, le funzioni della libreria ''SndFile''.
 
  
 
'''SndFile''' è una libreria, scritta in C, per leggere e scrivere file che contengono suono campionato attraverso una libreria interfaccia standard.
 
'''SndFile''' è una libreria, scritta in C, per leggere e scrivere file che contengono suono campionato attraverso una libreria interfaccia standard.
Riga 8: Riga 7:
  
 
E' necessario avere installate nel proprio sistema e richiamare in Gambas le librerie condivise: "''libsndfile.so.1.0.28''" e "''libportaudio.so.2.0.0''"
 
E' necessario avere installate nel proprio sistema e richiamare in Gambas le librerie condivise: "''libsndfile.so.1.0.28''" e "''libportaudio.so.2.0.0''"
 
  
 
Mostreremo di seguito un semplice codice per l'esecuzione di un file audio:
 
Mostreremo di seguito un semplice codice per l'esecuzione di un file audio:
Riga 119: Riga 117:
 
   Dim padi As New PaDeviceInfo
 
   Dim padi As New PaDeviceInfo
 
   Dim err, frequenza, conto, quantum As Integer
 
   Dim err, frequenza, conto, quantum As Integer
 
+
 
 
 
  <FONT color=gray>' ''Se l'applicativo sta già eseguendo un file audio, esce dalla routine:''</font>
 
  <FONT color=gray>' ''Se l'applicativo sta già eseguendo un file audio, esce dalla routine:''</font>
    If Pa_IsStreamStopped(PaStream) = 0 Then Return
+
  If Pa_IsStreamStopped(PaStream) = 0 Then Return
 
   
 
   
    <FONT color=darkorange><B>data</b></font> = Alloc(BUFFER_LEN * 4)
+
  <FONT color=darkorange><B>data</b></font> = Alloc(BUFFER_LEN * 4)
 
   
 
   
 
  <FONT color=gray>' ''Inizializza la libreria di "PortAudio":''</font>
 
  <FONT color=gray>' ''Inizializza la libreria di "PortAudio":''</font>
    err = Pa_Initialize()
+
  err = Pa_Initialize()
    If err <> paNoError Then Error.Raise("Impossibile inizializzare la libreria 'PortAudio' !")
+
  If err <> paNoError Then Error.Raise("Impossibile inizializzare la libreria 'PortAudio' !")
 
   
 
   
    file_audio = "<FONT color=gray>''/percorso/del/file/audio''</font>"
+
  file_audio = "<FONT color=gray>''/percorso/del/file/audio''</font>"
 
    
 
    
 
  <FONT color=gray>' ''Apre il file audio:''</font>
 
  <FONT color=gray>' ''Apre il file audio:''</font>
    infile = sf_open(file_audio, SFM_READ, sfinfo)
+
  infile = sf_open(file_audio, SFM_READ, sfinfo)
 
      
 
      
 
  <FONT color=gray>' ''Ricava la frequenza di campionamento del file audio:''</font>
 
  <FONT color=gray>' ''Ricava la frequenza di campionamento del file audio:''</font>
    frequenza = sfinfo.samplerate
+
  frequenza = sfinfo.samplerate
 
   
 
   
 
  <FONT color=gray>' ''Si ricavano alcune informazioni ed impostano alcuni parametri:''</font>
 
  <FONT color=gray>' ''Si ricavano alcune informazioni ed impostano alcuni parametri:''</font>
    Print "\n== Informazioni sul file audio =="
+
  Print "\n== Informazioni sul file audio =="
    Print "Nome:      "; File.Name(file_audio)
+
  Print "Nome:      "; File.Name(file_audio)
    Print "Dimensione: "; Stat(file_audio).Size; " byte"
+
  Print "Dimensione: "; Stat(file_audio).Size; " byte"
    Print "Frequenza:  "; frequenza
+
  Print "Frequenza:  "; frequenza
    Print "Canali:    "; sfinfo.channels
+
  Print "Canali:    "; sfinfo.channels
 
   
 
   
    With pasp
+
  With pasp
      .device = Pa_GetDefaultOutputDevice()
+
    .device = Pa_GetDefaultOutputDevice()
      .channelCount = sfinfo.channels
+
    .channelCount = sfinfo.channels
      .sampleFormat = paInt16
+
    .sampleFormat = paInt16
      padi = Pa_GetDeviceInfo(.device)
+
    padi = Pa_GetDeviceInfo(.device)
      .suggestedLatency = padi.defaultLowOutputLatency
+
    .suggestedLatency = padi.defaultLowOutputLatency
      .hostApiSpecificStreamInfo = Null
+
    .hostApiSpecificStreamInfo = Null
    End With
+
  End With
 
   
 
   
    Print "\n== Informazioni sul dispositivo audio =="
+
  Print "\n== Informazioni sul dispositivo audio =="
    With padi
+
  With padi
      Print "Nome del dispositivo:            "; String@(.name)
+
    Print "Nome del dispositivo:            "; String@(.name)
      Print "Numero massimo Canali in Entrata: "; .maxInputChannels
+
    Print "Numero massimo Canali in Entrata: "; .maxInputChannels
      Print "Numero massimo Canali in Uscita:  "; .maxOutputChannels
+
    Print "Numero massimo Canali in Uscita:  "; .maxOutputChannels
      Print
+
    Print
    End With
+
  End With
 
      
 
      
    err = Pa_OpenStream(VarPtr(PaStream), Null, pasp, frequenza, FRAMES_PER_BUFFER, paClipOff, Null, Null)
+
  err = Pa_OpenStream(VarPtr(PaStream), Null, pasp, frequenza, FRAMES_PER_BUFFER, paClipOff, Null, Null)
    If err <> paNoError Then Error.Raise("Impossibile aprire il flusso di dati !")
+
  If err <> paNoError Then Error.Raise("Impossibile aprire il flusso di dati !")
 
      
 
      
    If IsNull(PaStream) = False Then
+
  If IsNull(PaStream) = False Then
     
+
    err = Pa_StartStream(PaStream)
      err = Pa_StartStream(PaStream)
+
    If err <> paNoError Then Error.Raise("Impossibile avviare il flusso di dati !")
      If err <> paNoError Then Error.Raise("Impossibile avviare il flusso di dati !")
+
    conto = 1
 
      conto = 1
 
       
 
 
  <FONT color=gray>' ''Inizia il ciclo per la scrittura dei dati e per l'esecuzione del file audio:''</font>
 
  <FONT color=gray>' ''Inizia il ciclo per la scrittura dei dati e per l'esecuzione del file audio:''</font>
      While conto > 0
+
    While conto > 0
+
  <FONT color=gray>' ''Legge i dati del file audio e li carica nel buffer "data". La quantità unitaria di dati, caricati nella variabile "data", deve essere uguale alla dimensione della predetta variabile "data" moltiplicato per il numero dei canali del file:''</font>
<FONT color=gray>' ''Legge i dati del file audio e li carica nel buffer "data". La quantità unitaria di dati, caricati nella variabile "data",''
+
      conto = sf_read_short(infile, <FONT color=darkorange><B>data</b></font>, BUFFER_LEN * sfinfo.channels)
' ''deve essere uguale alla dimensione della predetta variabile "data" moltiplicato per il numero dei canali del file:''</font>
 
        conto = sf_read_short(infile, <FONT color=darkorange><B>data</b></font>, BUFFER_LEN * sfinfo.channels)
 
 
 
  <FONT color=gray>' ''Scrive i dati, presenti nel buffer "data", nella variabile "PaStream":''</font>
 
  <FONT color=gray>' ''Scrive i dati, presenti nel buffer "data", nella variabile "PaStream":''</font>
        err = Pa_WriteStream(PaStream, <FONT color=darkorange><B>data</b></font>, BUFFER_LEN)
+
      err = Pa_WriteStream(PaStream, <FONT color=darkorange><B>data</b></font>, BUFFER_LEN)
        If err <> paNoError Then Exit
+
      If err <> paNoError Then Exit
   
+
  <FONT color=gray>' ''Mostra il tempo trascorso dall'inizio dell'esecuzione del file audio:''</font>
 +
      quantum += BUFFER_LEN
 +
      Me.Text = Str(Time(0, 0, 0, (quantum / sfinfo.samplerate) * 1000))
 
  <FONT color=gray>' ''Attende un centesimo di secondo per consentire di agire su eventuali oggetti presenti sul Form.''
 
  <FONT color=gray>' ''Attende un centesimo di secondo per consentire di agire su eventuali oggetti presenti sul Form.''
 
  ' '''''Ovviamente nel caso di applicazione "a riga di comando" questa funzione di attesa non è necessaria.'''''</font>
 
  ' '''''Ovviamente nel caso di applicazione "a riga di comando" questa funzione di attesa non è necessaria.'''''</font>
        Wait 0.01
+
      Wait 0.01
       
+
    Wend
<FONT color=gray>' ''Mostra il tempo trascorso dall'inizio dell'esecuzione del file audio:''</font>
+
  Endif
        quantum += BUFFER_LEN
 
        Write #File.out, "\r" & CStr(Date(0, 0, 0, 0, 0, 0, (quantum / sfinfo.samplerate) * 1000))
 
   
 
      Wend
 
 
    Endif
 
 
      
 
      
 
  <FONT color=gray>' ''Va in chiusura:''</font>
 
  <FONT color=gray>' ''Va in chiusura:''</font>
      err = Pa_CloseStream(PaStream)
+
  err = Pa_CloseStream(PaStream)
      If err <> paNoError Then Error.Raise("Impossibile chiudere il flusso di dati !")
+
  If err <> paNoError Then Error.Raise("Impossibile chiudere il flusso di dati !")
 
        
 
        
      err = Pa_Terminate()
+
  err = Pa_Terminate()
      If err <> paNoError Then Error.Raise("Impossibile chiudere la libreria 'libportaudio' !")
+
  If err <> paNoError Then Error.Raise("Impossibile chiudere la libreria 'libportaudio' !")
 
   
 
   
      sf_close(infile)
+
  sf_close(infile)
 
   
 
   
      Free(<FONT color=darkorange><B>data</b></font>)
+
  Free(<FONT color=darkorange><B>data</b></font>)
 
        
 
        
 
  '''End'''
 
  '''End'''
Riga 214: Riga 201:
 
   
 
   
 
  <FONT color=gray>' ''Arresta l'esecuzione del file audio:''</font>
 
  <FONT color=gray>' ''Arresta l'esecuzione del file audio:''</font>
    err = Pa_StopStream(PaStream)
+
  err = Pa_StopStream(PaStream)
    If err <> paNoError Then Error.Raise("Impossibile arrestare l'esecuzione dei dati audio !")
+
  If err <> paNoError Then Error.Raise("Impossibile arrestare l'esecuzione dei dati audio !")
 
    
 
    
 
  '''End'''
 
  '''End'''

Versione delle 17:16, 26 giu 2021

PortAudio è una libreria open-source multipiattaforma per l'ingresso e l'uscita audio in tempo reale. La libreria fornisce funzioni che permettono al software di acquisire e di inviare flussi audio in tempo reale dalle interfacce audio hardware del computer.
Poiché PortAudio non è progettato per leggere i dati audio utili da un file audio, bisognerà appoggiarsi, per questo, ad un'altra risorsa come, ad esempio, le funzioni della libreria SndFile.

SndFile è una libreria, scritta in C, per leggere e scrivere file che contengono suono campionato attraverso una libreria interfaccia standard.

Bisognerà, dunque, coniugare entrambe le librerie, assegnando a SndFile il compito di leggere ed estrarre i dati del file audio, ed a PortAudio quello di eseguire tali dati audio.

E' necessario avere installate nel proprio sistema e richiamare in Gambas le librerie condivise: "libsndfile.so.1.0.28" e "libportaudio.so.2.0.0"

Mostreremo di seguito un semplice codice per l'esecuzione di un file audio:

Private Const paInt16 As Byte = 8
Private Const paClipOff As Byte = 1
Private Const FRAMES_PER_BUFFER As Short = 1024
Private Const BUFFER_LEN As Short = 8192
Private Const SFM_READ As Byte = 16
Private Const paNoError As Byte = 0
Private PaStream As Pointer
Private infile As Pointer


Library "libsndfile:1.0.28"

Public Struct SF_INFO
  frames As Long
  samplerate As Integer
  channels As Integer
  format_ As Integer
  sections As Integer
  seekable As Integer
End Struct

' SNDFILE * sf_open (const char *path, int mode, SF_INFO *sfinfo)
' Apre un file per la lettura.
Private Extern sf_open(path As String, mode As Integer, SFinf As SF_INFO) As Pointer

' sf_count_t  sf_read_short(SNDFILE *sndfile, float *ptr, sf_count_t items)
' Legge i dati del file audio.
Private Extern sf_read_short(sndfile As Pointer, ptr As Pointer, items As Integer) As Integer

' int  sf_close  (SNDFILE *sndfile)
' Chiude il file precedentemente aperto.
Private Extern sf_close(sndfile As Pointer) As Integer


Library "libportaudio:2.0.0"

Public Struct PaStreamParameters
  device As Integer
  channelCount As Integer
  sampleFormat As Long
  suggestedLatency As Integer
  hostApiSpecificStreamInfo As Pointer
End Struct

Public Struct PaDeviceInfo
  structVersion As Integer
  name As Pointer
  hostApi As Integer
  maxInputChannels As Integer
  maxOutputChannels As Integer
  defaultLowInputLatency As Integer
  defaultLowOutputLatency As Integer
  defaultHighInputLatency As Integer
  defaultHighOutputLatency As Integer
  defaultSampleRate As Integer
End Struct

' PaError Pa_Initialize( void )
' Library initialization function - call this before using PortAudio.
' This function initializes internal data structures and prepares underlying host APIs for use.
Private Extern Pa_Initialize() As Integer

' PaDeviceIndex Pa_GetDefaultOutputDevice( void ) --> Retrieve the index of the default output device.
Private Extern Pa_GetDefaultOutputDevice() As Integer

' PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
' Retrieve a pointer to a PaDeviceInfo structure containing information about the specified device.
Private Extern Pa_GetDeviceInfo(device As Integer) As PaDeviceInfo

' PaError Pa_OpenStream( PaStream** stream, const PaStreamParameters *inputParameters,
' const PaStreamParameters *outputParameters, double sampleRate, unsigned long framesPerBuffer,
' PaStreamFlags streamFlags, PaStreamCallback *streamCallback, void *userData )
' Opens a stream for either input, output or both.
Private Extern Pa_OpenStream(Pstream As Pointer, inputParameters As PaStreamParameters, outputParameters As PaStreamParameters, FsampleRate As Float, framesPerBuffer As Long, streamFlags As Byte, streamCallback As Pointer, userData As Pointer) As Integer

' PaError Pa_StartStream( PaStream *stream )
' Commences audio processing.
Private Extern Pa_StartStream(Pstream As Pointer) As Integer

' PaError Pa_WriteStream(PaStream * stream, const void * buffer, unsigned long frames)
Private Extern Pa_WriteStream(Pstream As Pointer, buffer As Pointer, frames As Integer) As Integer

' PaError Pa_CloseStream (PaStream *stream)
' Closes an audio stream.
Private Extern Pa_CloseStream(Pstream As Pointer) As Integer

' PaError Pa_IsStreamStopped (PaStream * stream)
' Returns one (1) when the stream is stopped, zero (0) when the stream is running.
Private Extern Pa_IsStreamStopped(Pstream As Pointer) As Integer

' PaError Pa_Terminate (void)
' Library termination function - call this when finished using PortAudio.
' This function deallocates all resources allocated by PortAudio since it was initialized by a call to Pa_Initialize().
Private Extern Pa_Terminate() As Integer

' PaError Pa_StopStream (PaStream * stream)
' Terminates audio processing. It waits until all pending audio buffers have been played before it returns.
Private Extern Pa_StopStream(Pstream As Pointer) As Integer
 

Public Sub Button1_Click()

 Dim data As Pointer
 Dim file_audio As String 
 Dim sfinfo As New SF_INFO
 Dim pasp As New PaStreamParameters
 Dim padi As New PaDeviceInfo
 Dim err, frequenza, conto, quantum As Integer
  
' Se l'applicativo sta già eseguendo un file audio, esce dalla routine:
 If Pa_IsStreamStopped(PaStream) = 0 Then Return

 data = Alloc(BUFFER_LEN * 4)

' Inizializza la libreria di "PortAudio":
 err = Pa_Initialize()
 If err <> paNoError Then Error.Raise("Impossibile inizializzare la libreria 'PortAudio' !")

 file_audio = "/percorso/del/file/audio"
 
' Apre il file audio:
 infile = sf_open(file_audio, SFM_READ, sfinfo)
   
' Ricava la frequenza di campionamento del file audio:
 frequenza = sfinfo.samplerate

' Si ricavano alcune informazioni ed impostano alcuni parametri:
 Print "\n== Informazioni sul file audio =="
 Print "Nome:       "; File.Name(file_audio)
 Print "Dimensione: "; Stat(file_audio).Size; " byte"
 Print "Frequenza:  "; frequenza
 Print "Canali:     "; sfinfo.channels

 With pasp
   .device = Pa_GetDefaultOutputDevice()
   .channelCount = sfinfo.channels
   .sampleFormat = paInt16
   padi = Pa_GetDeviceInfo(.device)
   .suggestedLatency = padi.defaultLowOutputLatency
   .hostApiSpecificStreamInfo = Null
 End With

 Print "\n== Informazioni sul dispositivo audio =="
 With padi
   Print "Nome del dispositivo:             "; String@(.name)
   Print "Numero massimo Canali in Entrata: "; .maxInputChannels
   Print "Numero massimo Canali in Uscita:  "; .maxOutputChannels
   Print
 End With
    
 err = Pa_OpenStream(VarPtr(PaStream), Null, pasp, frequenza, FRAMES_PER_BUFFER, paClipOff, Null, Null)
 If err <> paNoError Then Error.Raise("Impossibile aprire il flusso di dati !")
   
 If IsNull(PaStream) = False Then
   err = Pa_StartStream(PaStream)
   If err <> paNoError Then Error.Raise("Impossibile avviare il flusso di dati !")
   conto = 1
' Inizia il ciclo per la scrittura dei dati e per l'esecuzione del file audio:
   While conto > 0
 ' Legge i dati del file audio e li carica nel buffer "data". La quantità unitaria di dati, caricati nella variabile "data", deve essere uguale alla dimensione della predetta variabile "data" moltiplicato per il numero dei canali del file:
     conto = sf_read_short(infile, data, BUFFER_LEN * sfinfo.channels)
' Scrive i dati, presenti nel buffer "data", nella variabile "PaStream":
     err = Pa_WriteStream(PaStream, data, BUFFER_LEN)
     If err <> paNoError Then Exit
' Mostra il tempo trascorso dall'inizio dell'esecuzione del file audio:
     quantum += BUFFER_LEN
     Me.Text = Str(Time(0, 0, 0, (quantum / sfinfo.samplerate) * 1000))
' Attende un centesimo di secondo per consentire di agire su eventuali oggetti presenti sul Form.
' Ovviamente nel caso di applicazione "a riga di comando" questa funzione di attesa non è necessaria.
     Wait 0.01
   Wend
 Endif
   
' Va in chiusura:
 err = Pa_CloseStream(PaStream)
 If err <> paNoError Then Error.Raise("Impossibile chiudere il flusso di dati !")
     
 err = Pa_Terminate()
 If err <> paNoError Then Error.Raise("Impossibile chiudere la libreria 'libportaudio' !")

 sf_close(infile)

 Free(data)
     
End


Public Sub Button2_Click()

 Dim err As Integer

' Arresta l'esecuzione del file audio:
 err = Pa_StopStream(PaStream)
 If err <> paNoError Then Error.Raise("Impossibile arrestare l'esecuzione dei dati audio !")
 
End


Riferimenti