Eseguire una traccia di un CD-Audio con le risorse di linux/cdrom.h e le funzioni esterne di ALSA

Da Gambas-it.org - Wikipedia.

Di seguito mostriamo un esempio per eseguire una traccia di un CD-Audio mediante le risorse dichiarate nel file header "/usr/include/linux/cdrom.h " e le funzioni esterne di ALSA.
Dopo aver lanciato il programma, far scorrere in fondo la console/Terminale e immettere il numero della traccia a destra del testo azzurro mostrato.

Library "libc:6"

Public Struct cdrom_tochdr
  cdth_trk0 As Byte
  cdth_trk1 As Byte
End Struct

Public Struct cdrom_ti
  cdti_trk0 As Byte
  cdti_ind0 As Byte
  cdti_trk1 As Byte
  cdti_ind1 As Byte
End Struct

Public Struct cdrom_tocentry
  cdte_track As Byte
  cdte_adr As Byte
  cdte_format As Short
  minute As Byte
  second As Byte
  frame As Byte
  lba As Integer
  cdte_datamode As Byte
End Struct

Private Const CDROMREADTOCHDR As Long = &5305
Private Const CDROMREADTOCENTRY As Long = &5306
Private Const CDROMSTART As Long = &5308
Private Const CDROMREADAUDIO As Long = &530E
Private Const CDROM_MSF As Long = 2
Private Const CD_FRAMES As Integer = 75
Private Const CD_FRAMESIZE_RAW As Integer = 2352

' int ioctl (int __fd, unsigned long int __request, ...)
' Perform the I/O control operation specified by REQUEST on FD.
Private Extern ioctl_HDR(__fd As Integer, _request As Long, param As Cdrom_tochdr) As Integer Exec "ioctl"

' int ioctl (int __fd, unsigned long int __request, ...)
' Perform the I/O control operation specified by REQUEST on FD.
Private Extern ioctl_TOC(__fd As Integer, _request As Long, param As Cdrom_tocentry) As Integer Exec "ioctl"

' int ioctl (int __fd, unsigned long int __request, ...)
' Perform the I/O control operation specified by REQUEST on FD.
Private Extern ioctl_RA(__fd As Integer, _request As Long, param As Pointer) As Integer Exec "ioctl"

' int ioctl (int __fd, unsigned long int __request, ...)
' Perform the I/O control operation specified by REQUEST on FD.
Private Extern ioctl_STA(__fd As Integer, _request As Long, param As Pointer) As Integer Exec "ioctl"


Library "libasound:2.0.0"

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 fl As File
 Dim handle, buffer As Pointer
 Dim hdr As New Cdrom_tochdr
 Dim index As New Cdrom_ti
 Dim ttcc As Cdrom_tocentry[]
 Dim trc, m, s As Byte
 Dim sec1, sec2 As Short
 Dim ts As String
 Dim i, t, frame, somma, err As Integer
 Dim st As Stream
 Dim buf As New Byte[CD_FRAMES * CD_FRAMESIZE_RAW]
 
' Imposta e apre in lettura il file-device del driver del CDROM contenente il CD-Audio da eseguire:
 fl = Open "/dev/sr0" For Read
 If IsNull(fl) Then Error.Raise("ERRORE !")
 
' Rende subito attivo il lettore CDROM:
 ioctl_STA(fl.Handle, CDROMSTART, 0)
 
 ioctl_HDR(fl.Handle, CDROMREADTOCHDR, hdr)
 Print "Numero Tracce audio presenti: "; hdr.cdth_trk1
 
 ttcc = New Cdrom_tocentry[hdr.cdth_trk1 + 2]
 
' Acquisisce informazioni dalla TOC per ciascuna traccia presente nel CD-Audio:
 For i = 1 To hdr.cdth_trk1 + 1
   ttcc[i] = New Cdrom_tocentry
   t = i
' Se è stata scelta l'ultima traccia, il valore di "t" deve essere = &hAA (170),
' per ottenere la marcatura temporale della fine di tale ultima traccia, che corrisponde all'intera durata del CD-Audio:
   If t == hdr.cdth_trk1 + 1 Then t = &AA
   Tempo(fl.Handle, t, ttcc[i])
 Next
 
' Mostra la durata del CD-Audio e delle singole tracce audio presenti:
 Print "Durata totale del CD-Audio:   "; Date(0, 0, 0, 0, 0, 0, ((ttcc[ttcc.Max].minute * 60) + ttcc[ttcc.Max].second) * 1000)
 Print
 Print "Durata delle Tracce"
 For i = 1 To ttcc.Max - 1
   Print "Traccia: " &
   i, Date(0, 0, 0, 0, 0, 0, CInt((((ttcc[i + 1].minute * 60) + ttcc[i + 1].second)) - (((ttcc[i].minute * 60) + ttcc[i].second))) * 1000)
 Next
 Print
 
' Chiede che sia immesso nello spazio della console il numero della traccia audio da eseguire:
 Write #File.Out, "   \e[34mInserire il numero della traccia audio da eseguire... \e[1m\e[31m"
 Flush #File.Out
' Dopo aver scritto il numero della traccia da eseguire, attende che sia premuto il tasto "Invio":
 Input ts
 trc = CByte(Val(ts))
 
' Calcola la distanza temporale dell'inizio dei dati della traccia da eseguire rispetto all'inizio del CD-Audio:
 sec1 = (ttcc[trc].minute * 60) + ttcc[trc].second
' Calcola la distanza temporale dell'inizio dei dati della traccia, successiva a quella prescelta, dall'inizio del CD-Audio:
 sec2 = (ttcc[trc + 1].minute * 60) + ttcc[trc + 1].second
 
' Calcola la durata temporale della traccia audio da eseguire:
 Print "\n\e[0mEsecuzione audio della traccia n. "; ts &
 "  -  Durata: " & CStr(Date(0, 0, 0, 0, 0, 0, (sec2 - sec1) * 1000))
 Print
 
 index.cdti_trk0 = hdr.cdth_trk0
 index.cdti_trk1 = hdr.cdth_trk1
  
 buffer = Alloc(SizeOf(gb.Byte), 24)
 
 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))
 
 st = Memory buffer For Write
 Seek #st, 4
 Write #st, CDROM_MSF As Byte
 Seek #st, 8
 Write #st, CD_FRAMES As Integer
 Seek #st, 16
 Write #st, buf.Data As Pointer
 
' I due valori  del ciclo "For" sono espressi in secondi.
' Il primo valore corrisponde alla distanza temporale dell'inizio della traccia audio prescelta dall'inizio dei dati audio presenti nel CD-Audio;
' ovvero dall'inizio dell'audio sino alla fine della traccia, precedente a quella che deve essere eseguita, + 2 secondi senza dati.
' Il secondo valore del ciclo "For" rappresenta la distanza temporale dei primi dati audio della traccia successiva a quella prescelta.
' Tale valore viene comunque ridotto di un'unità per garantire che non si eseguano i primi dati audio di tale traccia successiva.
 For i = sec1 To (sec2 - 1)
   m = Fix(i / 60)
   s = i Mod 60
   Seek #st, 0
   Write #st, m As Byte     ' minuti
   Write #st, s As Byte     ' secondi
   
   ioctl_RA(fl.Handle, CDROMREADAUDIO, buffer)
   
   For frame = 0 To CD_FRAMES - 1
                                   ' Il secondo parametro potrà anche essere: buf.Data + (frame * CD_FRAMESIZE_RAW)
     somma += snd_pcm_writei(handle, Pointer@(buffer + 16) + (frame * CD_FRAMESIZE_RAW), CD_FRAMESIZE_RAW / 4)
     Write "\rTempo trascorso: " & Date(0, 0, 0, 0, 0, 0, (somma / 44100) * 1000)
   Next
 Next
 
 snd_pcm_drain(handle)
 
' Va in chiusura:
  st.Close
  Free(buffer)
  fl.Close
' Al termine dell'esecuzione chiude il subsistema PCM:
  err = snd_pcm_close(handle)
  If err == 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
  
End


Private Procedure Tempo(fd As Integer, t As Byte, ent As Cdrom_tocentry)
 
 ent.cdte_track = t
 ent.cdte_format = CDROM_MSF
 ioctl_TOC(fd, CDROMREADTOCENTRY, ent)
 
End