Estrarre i dati audio di una traccia di un CD audio e crearne un file WAV con le risorse del API di libcdio paranoia

Da Gambas-it.org - Wikipedia.

Usando alcune risorse esterne della libreria "libcdio_paranoia" è possibile anche estrarre i dati audio grezzi di una traccia di un CD audio e crearne un file WAV.

E' necessario avere installate nel proprio sistema le librerie condivise "libcdio.so.18.0.0", "libcdio_cdda.so.2.0.0" e "libcdio_paranoia.so.2.0.0".

Nel codice che segue sarà richiamata la sola libreria condivisa: libcdio_paranoia.so.2.0.0

Private Const TRACCIA As Integer = 4   '' Imposta il numero della traccia audio di cui estrarre i dati audio
Private Const SEEK_SET As Integer = 0

Library "libcdio_paranoia:2.0.0"

Private Const CDIO_CD_FRAMESIZE_RAW As Integer = 2352
Private Enum CDIO_FS_AUDIO = 1, CDIO_FS_HIGH_SIERRA, CDIO_FS_ISO_9660, CDIO_FS_INTERACTIVE, CDIO_FS_HFS, CDIO_FS_UFS
Private Enum CDDA_MESSAGE_FORGETIT = 0, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_LOGIT, CD_FRAMESAMPLES = 588, MAXTRK = 100
Private Enum PARANOIA_MODE_DISABLE = 0, PARANOIA_MODE_VERIFY = 1, PARANOIA_MODE_FRAGMENT = 2, PARANOIA_MODE_OVERLAP = 4,
             PARANOIA_MODE_SCRATCH = 8, PARANOIA_MODE_REPAIR = 16, PARANOIA_MODE_NEVERSKIP = 32, PARANOIA_MODE_FULL = 255

' char ** cdio_get_devices_with_cap (char *ppsz_search_devices[], cdio_fs_anal_t capabilities, bool b_any)
' Get an array of device names in search_devices that have at least the capabilities listed by the capabities parameter.
Private Extern cdio_get_devices_with_cap(ppsz_search_devices As Pointer, capabilities As Integer, b_any As Boolean) As Pointer

' cdrom_drive_t *cdio_cddap_identify(const char *psz_device, int messagedest, char **ppsz_message)
' Returns a paranoia CD-ROM drive object with a CD-DA.
Private Extern cdio_cddap_identify(psz_device As String, messagedest As Integer, ppsz_message As Pointer) As Pointer

' void cdio_free_device_list (char * device_list[])
' Free device list returned.
Private Extern cdio_free_device_list(device_list As Pointer)

' void cdio_cddap_verbose_set(cdrom_drive_t *d, int err_action, int mes_action)
Private Extern cdio_cddap_verbose_set(d As Pointer, err_action As Integer, mes_action As Integer)

' int cdio_cddap_open(cdrom_drive_t *d)
Private Extern cdio_cddap_open(d As Pointer) As Integer

' cdrom_paranoia_t *cdio_paranoia_init(cdrom_drive_t *d)
' Get and initialize a new cdrom_paranoia object from cdrom_drive.
Private Extern cdio_paranoia_init(d As Pointer) As Pointer

' lsn_t cdio_cddap_track_firstsector(cdrom_drive_t *d, track_t i_track)
' Return the lsn for the start of track i_track.
Private Extern cdio_cddap_track_firstsector(d As Pointer, i_track As Integer) As Integer

' int cdio_cddap_sector_gettrack(cdrom_drive_t *d, lsn_t lsn)
' Return the track containing the given LSN.
Private Extern cdio_cddap_sector_gettrack(d As Pointer, lsn As Integer) As Integer

' lsn_t cdio_cddap_track_lastsector(cdrom_drive_t *d, track_t i_track)
' Get last lsn of the track.
Private Extern cdio_cddap_track_lastsector(d As Pointer, i_track As Integer) As Integer

' void cdio_paranoia_modeset(cdrom_paranoia_t *p, int mode_flags)
' Set the kind of repair you want to on for reading.
Private Extern cdio_paranoia_modeset(p As Pointer, mode_flags As Integer)

' lsn_t cdio_paranoia_seek(cdrom_paranoia_t *p, int32_t seek, int whence)
' Reposition reading offset.
Private Extern cdio_paranoia_seek(p As Pointer, _seek As Integer, whence As Integer) As Integer

' int16_t *cdio_paranoia_read(cdrom_paranoia_t *p, void(*callback)(long int, paranoia_cb_mode_t))
' Reads the next sector of audio data.
Private Extern cdio_paranoia_read(p As Pointer, callback As Pointer) As Pointer

' char *cdio_cddap_errors(cdrom_drive_t *d)
' Returns the current error buffer.
Private Extern cdio_cddap_errors(d As Pointer) As Pointer

' char *cdio_cddap_messages(cdrom_drive_t *d)
' Returns the current message buffer.
Private Extern cdio_cddap_messages(d As Pointer) As Pointer

' void cdio_paranoia_free(cdrom_paranoia_t *p)
' Free any resources associated with p.
Private Extern cdio_paranoia_free(p As Pointer)

' int cdio_cddap_close(cdrom_drive_t *d)
' Closes d and releases all storage associated with it.
Private Extern cdio_cddap_close(d As Pointer) As Integer


Public Sub Main()
 
 Dim cdd, drives, p, buf As Pointer
 Dim err, mes As Pointer
 Dim lsn, cur, tra, llsn As Integer
 Dim fl As File
 
' Cerca un dispositivo con un CD-DA caricato:
 drives = cdio_get_devices_with_cap(0, CDIO_FS_AUDIO, False)
 
 If (drives > 0) And (Not IsNull(String@(Pointer@(drives)))) Then
' Se ha trovato un CD-ROM con un CD-DA caricato, allora utilizza la prima unità nell'elenco:
   cdd = cdio_cddap_identify(String@(Pointer@(drives)), 1, 0)
   Print "Unità CD-Da trovata e utilizzata: "; String@(Pointer@(drives))
 Else
   Error.Raise("Impossibile cercare o accedere ad un drive CD-ROM contenente un CD audio !")
 Endif
 
' Non si ha più bisogno della lista dei dispositivi, pertanto si libera la memoria:
 cdio_free_device_list(drives)
 
 If cdd == 0 Then Error.Raise("Impossibile identificare il disco CD audio !")
 
 cdio_cddap_verbose_set(cdd, CDDA_MESSAGE_PRINTIT, CDDA_MESSAGE_PRINTIT)
 
 If cdio_cddap_open(cdd) <> 0 Then Error.Raise("Impossibile aprire il disco !")
 
' Ora si leggono i frame del numero della traccia audio, indicato nella Costante "TRACCIA", del CD audio:
 p = cdio_paranoia_init(cdd)
 
 lsn = cdio_cddap_track_firstsector(cdd, TRACCIA)
 If lsn == -1 Then Error.Raise("Problemi nell'avvio dell'LSN !")
 
 tra = cdio_cddap_sector_gettrack(cdd, lsn)
 llsn = cdio_cddap_track_lastsector(cdd, tra)
 
' Il file WAV sarà salvato nella cartella "/tmp":
 fl = Open "/tmp/wave.wav" For Create
 
 cdio_paranoia_modeset(p, PARANOIA_MODE_FULL Xor PARANOIA_MODE_NEVERSKIP)
 cdio_paranoia_seek(p, lsn, SEEK_SET)
 
 Scrive_WAV_header(fl, (llsn - lsn + 1) * CDIO_CD_FRAMESIZE_RAW)
 
 For cur = lsn To llsn - 1
   buf = cdio_paranoia_read(p, 0)
   err = cdio_cddap_errors(cdd)
   mes = cdio_cddap_messages(cdd)
   If mes > 0 Then Print String@(mes)
   If err > 0 Then Print String@(err)
   If buf == 0 Then Error.Raise("Errore lettura di Paranoia !")
   Write #fl, buf, CDIO_CD_FRAMESIZE_RAW
 Next
 
 fl.Close
 cdio_paranoia_free(p)
 cdio_cddap_close(cdd)
 
End
 

Private Procedure Scrive_WAV_header(fl As File, byte As Integer)
 
                                 ' Indice:
 Write #fl, "RIFF"               '  0- 3
 Numeri(byte + 44 - 8, fl, 4)    '  4- 7
 Write #fl, "WAVEfmt "           '  8-15
 Numeri(16, fl, 4)               ' 16-19
 Numeri(1, fl, 2)                ' 20-21
 Numeri(2, fl, 2)                ' 22-23
 Numeri(44100, fl, 4)            ' 24-27
 Numeri(44100 * 2 * 2, fl, 4)    ' 28-31
 Numeri(4, fl, 2)                ' 32-33
 Numeri(16, fl, 2)               ' 34-35
 Write #fl, "data"               ' 36-39
 Numeri(byte, fl, 4)             ' 40-43
 
End
  
Private Procedure Numeri(num As Long, fi As File, bt As Integer)
 
 Dim i As Integer
 Dim b As Byte
 
 Repeat
   b = Shr(num, Shl(i, 3)) And &FF
   Write #fi, b As Byte
   Inc i
   Dec bt
 Until bt == 0
 
End