Eseguire una traccia di un CD audio con le funzioni esterne del API di libcdio e di ALSA
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 trccia 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 peciale 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) 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
- http://www.gnu.org/software/libcdio/
- http://www.gnu.org/software/libcdio/libcdio.html
- http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html
- https://en.wikipedia.org/wiki/Compact_Disc_Digital_Audio
- http://www.jmargolin.com/project/cdtermw.pdf
- http://whatis.techtarget.com/reference/Fast-Guide-to-CD-DVD