La gestione mediante le funzioni esterne del API di PortAudio e SndFile
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 (come il formato WAV ed il formato AIFF) attraverso una libreria interfaccia standard.
Pertanto, bisognerà 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.
Mostreremo di seguito un semplice codice per l'esecuzione di un file wav:
Public Struct SF_INFO frames As Long samplerate As Integer channels As Integer formatI As Integer sections As Integer seekable As Integer End Struct 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 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.25" ' 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 wav. 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" ' 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 As Integer ' Se l'applicativo sta già eseguendo un file wav, 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.wav" ' Apre il file wav: infile = sf_open(file_audio, SFM_READ, sfinfo) ' Ricava la frequenza di campionamento del file wav: 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 di campionamento: "; 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 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 wav: While conto > 0 ' Legge i dati del file wav e li carica nel buffer "data". La quantità unitaria di dati caricati nella variabile "data" ' deve essere uguale al doppio del 3° argomento della successiva funzione "Pa_WriteStream": 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 ' Attende un centesimo di secondo per consentire di agire su eventuali oggetti presenti sul Form: 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 wav e va in chiusura: err = Pa_StopStream(PaStream) If err <> paNoError Then Error.Raise("Impossibile arrestare l'esecuzione dei dati audio !") End