Eseguire i file Midi con le funzioni esterne dell'API di Alure

Da Gambas-it.org - Wikipedia.

ALURE è una libreria di utilità per aiutare a gestire le operazioni più comuni con applicazioni OpenAL. Questo include l'enumerazione e l'inizializzazione del dispositivo, il caricamento del file, e l'esecuzione. Lo scopo di questa API è quello di fornire funzionalità pre-organizzate per facilitare e velocizzare la programmazione dello sviluppatore.

La libreria condivisa contenente le funzioni esterne dell'API di OpenAlure, e che dovrà essere richiamata in Gambas, è attualmente la seguente:

libalure.so.1.2.0

Questa libreria di funzioni consente di eseguire anche file Midi. Per ottenere tale funzionalità, è necessario impostare la seguente funzione:

ALboolean alureSetStreamPatchset(alureStream * stream, const ALchar * patchset)

La documentazione ufficiale afferma che questa funzione specifica il patchset da utilizzare per i flussi MIDI. Per impostazione predefinita, il decoder FluidSynth ne cercherà uno nella variabile d'ambiente FLUID_SOUNDFONT, ma questo può essere usato utilizzarne anche uno diverso. Sui flussi non-MIDI, questa funzione, ancorché impostata, non ha alcun effetto.
V'è da precisare che il patchset sopra mensionato è sostanzialmente un file contenente il banco di suoni (soundfont bank) con estensione .sf2 utilizzato normalmente dai softsynth per ottenere l'ascolto effettivo dei flussi Midi. Pertanto sarà sufficiente in quel parametro indicare il percorso del file soundfont .sf2 che si intende utilizzare.

Mostriamo di seguito un semplice codice per ottenere l'esecuzione, la pausa, la ripresa e l'arresto di file Midi:

Private isdone As Integer
Private src As Integer
Private streamP As Pointer


Library "libalure:1.2.0"


' ALboolean alureInitDevice(const ALCchar * name, Const ALCint * attribs)
' Opens the named device, creates a context with the given attributes, and sets that context as current.
' The name and attribute list would be the same as what’s passed to alcOpenDevice and alcCreateContext respectively.
' Returns: AL_FALSE On error.
Private Extern alureInitDevice(name As String, attribs As String) As Boolean

' void alGenSources(Alsizei n, ALuint *sources)
Private Extern alGenSources(n As Integer, sources As Pointer) In "libalut:0.1.0"

' alureStream* alureCreateStreamFromFile(Const ALchar * fname, ALsizei chunkLength, ALsizei numBufs, ALuint * bufs)
' Opens a file and sets it up for streaming.  The given chunkLength is the number of bytes, or microseconds worth of bytes if alureStreamSizeIsMicroSec was last called with AL_TRUE,
' each buffer will fill with.  ALURE will optionally generate the specified number of buffer objects, fill them with the beginning of the data,
' then place the new IDs into the provided storage, before returning.  Requires an active context.
' Returns: An opaque handle used To control the opened stream, Or Null On error.
Private Extern alureCreateStreamFromFile(fname As String, chunkLength As Integer, numBufs As Integer, bufs As Pointer) As Pointer

' ALboolean alurePlaySourceStream(ALuint source, alureStream * stream, ALsizei numBufs, ALsizei loopcount, void( * eos_callback)(void * userdata, ALuint source), void * userdata)
' Starts playing a stream, using the specified source ID.  A stream can only be played if it is not already playing.
' You must call alureUpdate at regular intervals to keep the stream playing, or else the stream will underrun
' and cause a break in the playback until an update call can restart it.
' If an underrun occurs, the source will enter a stopped state until it is automatically restarted.  Instead, set a flag using the callback to indicate the stream being stopped.
' "eos_callback": This callback will be called when the stream reaches the end, no more loops are pending, and the source reaches a stopped state.
' It will also be called if an error occured and playback terminated.
' Returns: AL_FALSE On error.
Private Extern alurePlaySourceStream(source As Integer, strePnt As Pointer, numBufs As Integer, loopcount As Integer, eos_callback As Pointer, userdata As Pointer) As Boolean

' ALboolean alureSetStreamPatchset(alureStream * stream, Const ALchar * patchset)
' Specifies the patchset to use for MIDI streams.  By default, the FluidSynth decoder will look for one in the FLUID_SOUNDFONT environment variable,
' but this can be used to change it to something different.  On non-MIDI streams, this has no effect.
' Returns: AL_FALSE On error.
Private Extern alureSetStreamPatchset(strePnt As Pointer, patchset As String) As Boolean

' void alureUpdate(void)
' Updates the running list of streams, and checks for stopped sources.
' This makes sure that sources played with alurePlaySourceStream are kept fed from their associated stream, and sources played with alurePlaySource are still playing.
' It will call their callbacks as needed.
Private Extern alureUpdate()

' ALboolean alureStopSource(ALuint source, ALboolean run_callback)
' Stops the specified source ID, and any associated stream.
' Returns: AL_FALSE On error.
Private Extern alureStopSource(source As Integer, run_callback As Boolean) As Boolean

' void alDeleteSources(ALsizei n, ALuint * sources)
' This function deletes one or more sources.
Private Extern alDeleteSources(n As Integer, sources As Pointer) In "libalut:0.1.0"

' ALboolean alureDestroyStream(alureStream * stream, ALsizei numBufs, ALuint * bufs)'
' Closes an opened stream.
' Returns: AL_FALSE On error.
Private Extern alureDestroyStream(strePnt As Pointer, numBufs As Integer, bufs As Pointer) As Boolean

' ALboolean alureShutdownDevice(void)
' Destroys the current context and closes its associated device.
' Returns: AL_FALSE On error.
Private Extern alureShutdownDevice() As Boolean

' ALboolean alurePauseSource(ALuint source)
' Pauses the specified source ID, and any associated stream. Returns AL_FALSE On Error.
Private Extern alurePauseSource(source As Integer) As Boolean

' ALboolean alureResumeSource(ALuint source)
' Resumes the specified source ID after being paused. Returns AL_FALSE On Error.
Private Extern alureResumeSource(source As Integer) As Boolean


' void exit(int status)
' Terminates the calling process immediately.
Private Extern C_exit(status As Integer) In "libc:6" Exec "exit"
 

Public Sub Button1_Click()

  Dim ver As Boolean
  Dim percorsoFile As String = "/percorso/del/file.mid"
  Dim lungh As Integer
  Dim tm As Date
  
  ver = alureInitDevice(Null, Null)
  If ver = False Then Error.Raise("Impossibile inizializzare la libreria 'Alure' !")
  
  alGenSources(1, VarPtr(src))
 
  lungh = Stat(percorsoFile).Size
 
' Se si preferisce impostare la durata in base alla dimensione del file  da eseguire,
' è opportuno passare il valore del secondo parametro almeno pari alla dimensione del file:
  streamP = alureCreateStreamFromFile(percorsoFile, lungh, 0, Null)
  If streamP == 0 Then Error.Raise("Impossibile caricare il file Midi !")
 
  ver = alureSetStreamPatchset(streamP, "/percorso/del/file/soundfont.sf2")
  If ver = False Then Error.Raise("Impossibile impostare il file del banco-suoni !")

' Il terzo parametro della seguente 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 Midi deve essere di almeno 2.
  ver = alurePlaySourceStream(src, streamP, 3, 0, eos_callback, Null)
  If ver = False Then Error.Raise("Impossibile eseguire il flusso dati audio !")

  tm = Now

  While isdone == 0
    Write "\r\e[0mTempo trascorso: \e[31m" & Str(Time(0, 0, 0, DateDiff(tm, Now, gb.Millisecond)))
    Wait 0.01
    alureUpdate()
  Wend
  
' Va in chiusura:
  AlureStop()
  isdone = 0
  
End

Private Function eos_callback(unused As Pointer, unused2 As Integer)

   isdone = 1
 
End

' Mette in pausa l'esecuzione:
Public Sub Button2_Click()

  alurePauseSource(src)

End

' Riprende l'esecuzione:
Public Sub Button3_Click()

  alureResumeSource(src)

End

' Arresta l'esecuzione:
Public Sub Button4_Click()

  AlureStop()

End

' Arresta l'esecuzione e chiude la finestra del programma:
Public Sub Button5_Click()
 
 AlureStop()

 Me.Close
 C_exit(0)
 
End

Private Procedure AlureStop()

 alureStopSource(src, False)
 alDeleteSources(1, VarPtr(src))
 alureDestroyStream(streamP, 0, Null)
 alureShutdownDevice()

End


Riferimenti