Eseguire una traccia di un CD audio con le funzioni esterne del API di libcdio e di ALSA

Da Gambas-it.org - Wikipedia.

In questa pagina mostreremo un codice per leggere ed eseguire una traccia audio di un CD-Audio mediante le funzioni esterne del API di Libcdio e del API di ALSA.

Sarà necessario, dunque, avere installate nel sistema e richiamare nell'applicazione Gambas le seguenti librerie dinamiche condivise:

  • libcdio.so.13.0.0
  • libasound.so.2.0.0


Con le funzioni della libreria di CDIO verranno individuati in particolare gli indirizzi sul CD-Audio di ciascuna traccia audio ivi presente. Tali indirizzi rappresentano il punto ove iniziano i dati audio grezzi di ogni traccia audio.

Poiché i dati audio grezzi di ciascuna traccia sono memorizzati sul CD-Audio in gruppi (Settori) formati ciascuno da 2352 byte, la differenza fra il valore che rappresenta l'indirizzo di una traccia e quello della traccia immeditamente successiva, indica quanti Settori (formati da 2352 byte di dati audio) compongono la traccia in cosiderazione. In altre parole i dati audio grezzi di una taccia sono suddivisi e memorizzati sul CD-Audio in blocchi formati ciascuno da 2352 byte.

L'indirizzo della traccia, presa in considerazione, rappresentando, dunque, l'inizio dei dati audio della traccia, rappresenta ovviamente anche il primo Settore. I seguenti dati audio della traccia sono memorizzati nei successivi Settori, scorrendo l'indice di un'unità ciascuna. Così, ad esempio, se l'indirizzo iniziale di una traccia è 12345, il primo blocco (Settore) di dati si troverà quell'indirizzo (dunque i primi 2352 byte di dati audio della traccia in csiderazione saranno prelevabili a quell'indirizzo iniziale); il secondo blocco di dati audio di quella traccia si troverà all'indirizzo di valore d'indice immediatamente superiore di un'unità: 12346; il terzo a 12347, e così via, finché non si raggiungerà l'indirizzo iniziale della traccia successiva.

I dati audio dell'ultima traccia terminano finché non si raggiungerà l'indirizzo iniziale della speciale traccia chiamata Leadout.


Di seguito il codice:

Library "libcdio:13.0.0"

Private Enum DRIVER_UNKNOWN = 0, DRIVER_AIX, DRIVER_BSDI, DRIVER_FREEBSD, DRIVER_NETBSD, DRIVER_LINUX, DRIVER_SOLARIS,
             DRIVER_OS2, DRIVER_OSX, DRIVER_WIN32, DRIVER_CDRDAO, DRIVER_BINCUE, DRIVER_NRG, DRIVER_DEVICE
Private Const CDIO_INVALID_LSN As Integer = -45301
Private Const CDIO_CDROM_LEADOUT_TRACK As Integer = &AA

' CdIo_t * cdio_open (const char *psz_source, driver_id_t driver_id)
' Sets up to read from place specified by psz_source and driver_id.
Private Extern cdio_open(psz_source As String, driver_id As Integer) As Pointer

' track_t cdio_get_num_tracks(const CdIo_t * p_cdio)
' Get the number of tracks on the CD.
Private Extern cdio_get_num_tracks(p_cdio As Pointer) As Byte
 
' lsn_t cdio_get_track_lsn(const CdIo_t * p_cdio, track_t u_track)
' Return the starting LSN for track number i_track in p_cdio.
Private Extern cdio_get_track_lsn(p_cdio As Pointer, u_track As Integer) As Integer

' driver_return_code_t cdio_read_audio_sector (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn)
' Read an audio sector.
Private Extern cdio_read_audio_sector(p_cdio As Pointer, p_buf As Byte[], i_lsn As Integer) As Integer

' void cdio_destroy(CdIo_t * p_cdio)
' Free any resources associated with p_cdio.
Private Extern cdio_destroy(p_cdio As Pointer)


Library "libasound:2"

Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0
Private Const SND_PCM_FORMAT_S16_LE As Integer = 2
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Integer = 3

' int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
' Opens a PCM.
Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an Error code.
Private Extern snd_strerror(errnum As Integer) As String

' int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatI As Integer, accessI As Integer, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
' Write interleaved frames to a PCM.
Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer

' int snd_pcm_drain (snd_pcm_t *pcm)
' Stop a PCM preserving pending frames.
Private Extern snd_pcm_drain(pcm As Pointer) As Integer

' int snd_pcm_close (snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer) As Integer


Public Sub Main()

 Dim cd As Pointer
 Dim traccia, tracce, i As Byte
 Dim cbf As Integer
 Dim lsn As New Integer[]
 Dim esec As String
 
' Impostiamo il numero di una traccia che intendiamo far eseguire:
  traccia = 4
  
' Il valore della variabile "cbf" è data dal prodotto del numero dei canali per la risoluzione in bit per la frequenza della traccia audio:
  cbf = 2 * 16 * 44100
 
  cd = cdio_open(Null, DRIVER_LINUX)
  If cd = 0 Then Error.Raise("Impossibile trovare il driver per il cd-audio !")
  
  tracce = cdio_get_num_tracks(cd)
  Print "Tracce del CD-Audio (1 - "; tracce; ")"
  Print " #:  Durata"
' Individua l'indirizzo del settore logico di ciascuna traccia:
  For i = 1 To tracce
    lsn.Push(cdio_get_track_lsn(cd, i))
  Next
  lsn.Push(cdio_get_track_lsn(cd, CDIO_CDROM_LEADOUT_TRACK))
  For i = 0 To tracce - 1
    If i + 1 = traccia Then esec = "< in esecuzione"
    If (CDIO_INVALID_LSN <> lsn[i]) Then
      Print Format(i + 1, "#0:"), Date(0, 0, 0, 0, 0, 0, ((((lsn[i + 1] - lsn[i]) * 2352) * 8) / cbf) * 1000), esec
    Endif
    esec = Null
  Next
  
  lsn.Push(cdio_get_track_lsn(cd, CDIO_CDROM_LEADOUT_TRACK))
  
  Esecuzione(cd, lsn, traccia)
   
' Va in chiusura:
  cdio_destroy(cd)
   
End
  
  
Private Procedure Esecuzione(pcd As Pointer, sett As Integer[], tr As Byte)
  
 Dim handle As Pointer
 Dim err, i, frames, somma_frames As Integer
 Dim buf As Byte[]
  
  err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
  
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
  
  buf = New Byte[2352]
  
  For i = 0 To (sett[tr] - sett[tr - 1]) - 1
    cdio_read_audio_sector(pcd, buf, sett[tr - 1] + i)
    frames = snd_pcm_writei(handle, buf.Data, 2352 / 4)
    somma_frames += frames
    Write #File.Out, "\rTempo trascorso: " & Date(0, 0, 0, 0, 0, 0, (somma_frames / 44100) * 1000)
    Sleep 0.01
  Next
  
  snd_pcm_drain(handle)
  
' Alla fine dell'esecuzione chiude il subsistema PCM:
  err = snd_pcm_close(handle)
  If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
    
End


Eseguire una traccia oppure tutte le tracce del CD audio

Di seguito mostriamo il precedente codice, opportunamente modificato per consntire all'utente di scegliere di eseguire una particolare traccia oppure l'intero CD audio.

Library "libcdio:13.0.0"

Private Enum DRIVER_UNKNOWN = 0, DRIVER_AIX, DRIVER_BSDI, DRIVER_FREEBSD, DRIVER_NETBSD, DRIVER_LINUX, DRIVER_SOLARIS,
             DRIVER_OS2, DRIVER_OSX, DRIVER_WIN32, DRIVER_CDRDAO, DRIVER_BINCUE, DRIVER_NRG, DRIVER_DEVICE
Private Const CDIO_INVALID_LSN As Integer = -45301
Private Const CDIO_CDROM_LEADOUT_TRACK As Integer = &AA

' CdIo_t * cdio_open (const char *psz_source, driver_id_t driver_id)
' Sets up to read from place specified by psz_source and driver_id.
Private Extern cdio_open(psz_source As String, driver_id As Integer) As Pointer

' track_t cdio_get_num_tracks(const CdIo_t * p_cdio)
' Get the number of tracks on the CD.
Private Extern cdio_get_num_tracks(p_cdio As Pointer) As Byte
 
' lsn_t cdio_get_track_lsn(const CdIo_t * p_cdio, track_t u_track)
' Return the starting LSN for track number i_track in p_cdio.
Private Extern cdio_get_track_lsn(p_cdio As Pointer, u_track As Integer) As Integer

' driver_return_code_t cdio_read_audio_sector (const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn)
' Read an audio sector.
Private Extern cdio_read_audio_sector(p_cdio As Pointer, p_buf As Byte[], i_lsn As Integer) As Integer

' void cdio_destroy(CdIo_t * p_cdio)
' Free any resources associated with p_cdio.
Private Extern cdio_destroy(p_cdio As Pointer)


Library "libasound:2"

Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0
Private Const SND_PCM_FORMAT_S16_LE As Integer = 2
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Integer = 3

' int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
' Opens a PCM.
Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an Error code.
Private Extern snd_strerror(errnum As Integer) As String

' int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatI As Integer, accessI As Integer, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
' Write interleaved frames to a PCM.
Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer

' int snd_pcm_drain (snd_pcm_t *pcm)
' Stop a PCM preserving pending frames.
Private Extern snd_pcm_drain(pcm As Pointer) As Integer

' int snd_pcm_close (snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer) As Integer


Public Sub Main()

 Dim cd As Pointer
 Dim traccia, tracce, n_tr, i As Byte
 Dim cbf As Integer
 Dim lsn As New Integer[]
 Dim s As String
 
' Il valore della variabile "cbf" è data dal prodotto del numero dei canali per la risoluzione in bit per la frequenza della traccia audio:
'   canali bit frequenza
  cbf = 2 * 16 * 44100
 
  cd = cdio_open(Null, DRIVER_LINUX)
  If cd = 0 Then Error.Raise("Impossibile trovare il driver per il cd-audio !")
 
  tracce = cdio_get_num_tracks(cd)
  Print "Tracce del CD-Audio (1 - "; tracce; ")"
  Print " #:  Durata"
' Individua l'indirizzo del settore logico di ciascuna traccia:
  For i = 1 To tracce
    lsn.Push(cdio_get_track_lsn(cd, i))
  Next
  lsn.Push(cdio_get_track_lsn(cd, CDIO_CDROM_LEADOUT_TRACK))
 
  For i = 0 To tracce - 1
    If (CDIO_INVALID_LSN <> lsn[i]) Then
      Print Format(i + 1, "#0:"), Date(0, 0, 0, 0, 0, 0, ((((lsn[i + 1] - lsn[i]) * 2352) * 8) / cbf) * 1000)
    Endif
  Next
     
'''''''''''''''''''
  n_tr = 1
  
' Si sceglie se eseguire una sola traccia e l'intero CD audio:
  Print "\nPer eseguire una sola traccia, inviare il numero corrispondente."
  Print "Per eseguire tutte le tracce del CD audio, premere 'Invio.'\n"
  Input s
  
' Se si è scelto di eseguire solo una traccia, imposta il suo numero:
  If IsDigit(s) Then
    n_tr = Val(s)
    tracce = n_tr
  Endif
  
  For traccia = n_tr To tracce
    For i = 0 To tracce - 1
      If i + 1 = traccia Then
        Print Format(i + 1, "#0:"), Date(0, 0, 0, 0, 0, 0, ((((lsn[i + 1] - lsn[i]) * 2352) * 8) / cbf) * 1000)
      Endif
    Next
    lsn.Push(cdio_get_track_lsn(cd, CDIO_CDROM_LEADOUT_TRACK))
    Esecuzione(cd, lsn, traccia)
  Next
  
' Va in chiusura:
  cdio_destroy(cd)
  
End
 
 
Private Procedure Esecuzione(pcd As Pointer, sett As Integer[], tr As Byte)
 
 Dim handle As Pointer
 Dim err, i, frames, somma_frames As Integer
 Dim buf As Byte[]
 
  err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
 
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, 44100, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
  
  buf = New Byte[2352]
  
  For i = 0 To (sett[tr] - sett[tr - 1]) - 1
    cdio_read_audio_sector(pcd, buf, sett[tr - 1] + i)
    frames = snd_pcm_writei(handle, buf.Data, 2352 / 4)
    somma_frames += frames
    Write #File.Out, "\rTempo trascorso: " & Date(0, 0, 0, 0, 0, 0, (somma_frames / 44100) * 1000)
    Sleep 0.01
  Next
 
  snd_pcm_drain(handle)
 
' Alla fine dell'esecuzione chiude il subsistema PCM:
  err = snd_pcm_close(handle)
  If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
     
End



Riferimenti