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.

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 Byte
  allin1 As Byte
  minute As Byte
  second As Byte
  frame As Byte
  allin2 As Byte
  cdte_datamode As Byte
End Struct

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

' 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_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_TOC(__fd As Integer, _request As Long, param As Cdrom_tocentry) As Integer Exec "ioctl"


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 fl As File
 Dim handle, buffer As Pointer
 Dim hdr As New Cdrom_tochdr
 Dim index As New Cdrom_ti
 Dim toc As New Cdrom_tocentry
 Dim trc, m, s As Byte
 Dim sec1, sec2 As Short
 Dim ts As String
 Dim i, frame, somma, err As Integer
 Dim st As Stream
 Dim buf As New Byte[CD_FRAMES * CD_FRAMESIZE_RAW]
 
' Chiede che sia immesso nello spazio della console il numero della traccia audio da eseguire:
 Write #File.Out, "Inserire il numero della traccia audio da eseguire... \e[1m\e[31m"
 Flush #File.Out
' Dopo aver scritto il numero della traccia da eseguire, va premuto il tasto "Invio":
 Input ts
 trc = CByte(Val(ts))
 
' 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 !")
 
 ioctl_HDR(fl.Handle, CDROMREADTOCHDR, hdr)
 
 Inizio(fl.Handle, trc, toc)
 sec1 = (toc.minute * 60) + toc.second
  
' Se è impostata l'ultima traccia, il valore di "trc" deve essere = &hAA (170), per ottenere la marcatura temporale
' della fine di tale traccia (qui si sottrae di un'unità, poiché nella chiamata di funzione il valore del 2° parametro è incrementato):
 If trc == hdr.cdth_trk1 Then trc = &AA - 1
 
 Inizio(fl.Handle, trc + 1, toc)
 sec2 = (toc.minute * 60) + toc.second
 Print "\n\e[0mEsecuzione audio della traccia n. "; trc &
 "   -   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 sono espressi in secondi.
' Il primo valore corrisponde alla distanza temporale dell'inizio della traccia audio dall'inizio dei dati audio, presenti nel CD-Audio;
' ovvero dall'inizio dell'audio e la fine della traccia, precedente a quella che deve essere eseguita, + 2 secondi senza dati.
 For i = sec1 To sec2 
   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
     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 Inizio(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