8 /* Linux implementation for reading CD digital audio */
14 #include <sys/ioctl.h>
15 #include <linux/cdrom.h>
25 static const char *cd_device
= "/dev/cdrom1";
27 static void *cdda_open_file(const char *fname
)
29 struct cdrom_tochdr cdtochdr
;
30 struct cdrom_tocentry cdtocentry
;
31 int startaddr
=-1, endaddr
=-1, idx
;
35 /* Make sure the filename has the appropriate URI and extract the track
37 if(strncmp(fname
, "cdda://", 7) != 0)
41 /* Open the block device and get the TOC header */
42 fd
= open(cd_device
, O_RDONLY
| O_NONBLOCK
);
43 if(fd
== -1) return NULL
;
45 ret
= ioctl(fd
, CDROMREADTOCHDR
, &cdtochdr
);
48 if(idx
< cdtochdr
.cdth_trk0
|| idx
>= cdtochdr
.cdth_trk1
)
53 /* Get the start address for the requested track, and the start address
54 * of the next track as the end point */
55 cdtocentry
.cdte_format
= CDROM_LBA
;
56 cdtocentry
.cdte_track
= idx
;
57 ret
= ioctl(fd
, CDROMREADTOCENTRY
, &cdtocentry
);
58 if(ret
!= -1 && !(cdtocentry
.cdte_ctrl
&CDROM_DATA_TRACK
))
59 startaddr
= cdtocentry
.cdte_addr
.lba
;
63 cdtocentry
.cdte_format
= CDROM_LBA
;
64 cdtocentry
.cdte_track
= ++idx
;
65 ret
= ioctl(fd
, CDROMREADTOCENTRY
, &cdtocentry
);
67 endaddr
= cdtocentry
.cdte_addr
.lba
;
70 if(ret
== -1 || startaddr
== -1 || endaddr
== -1)
76 dat
= malloc(sizeof(*dat
));
78 dat
->start
= startaddr
;
79 dat
->current
= startaddr
;
85 static ALboolean
cdda_get_format(void *instance
, ALenum
*format
, ALuint
*samplerate
, ALuint
*blocksize
)
87 /* These values are specified by the red-book audio standard and do not
89 *format
= AL_FORMAT_STEREO16
;
91 *blocksize
= CD_FRAMESIZE_RAW
;
97 static ALuint
cdda_decode(void *instance
, ALubyte
*data
, ALuint bytes
)
99 CDDAData
*self
= instance
;
102 while(bytes
-got
>= CD_FRAMESIZE_RAW
&& self
->current
< self
->end
)
104 struct cdrom_read_audio cdra
;
106 /* Read as many frames that are left that will fit */
107 cdra
.addr
.lba
= self
->current
;
108 cdra
.addr_format
= CDROM_LBA
;
109 cdra
.nframes
= (bytes
-got
) / CD_FRAMESIZE_RAW
;
112 if(cdra
.nframes
> self
->end
-self
->current
)
113 cdra
.nframes
= self
->end
-self
->current
;
115 if(ioctl(self
->fd
, CDROMREADAUDIO
, &cdra
) == -1)
118 memset(cdra
.buf
, 0, CD_FRAMESIZE_RAW
);
121 self
->current
+= cdra
.nframes
;
122 data
+= CD_FRAMESIZE_RAW
*cdra
.nframes
;
123 got
+= CD_FRAMESIZE_RAW
*cdra
.nframes
;
129 static ALboolean
cdda_rewind(void *instance
)
131 CDDAData
*self
= instance
;
133 self
->current
= self
->start
;
137 static void cdda_close(void *instance
)
139 CDDAData
*self
= instance
;
145 #elif defined(_WIN32)
146 /* Windows implementation for reading CD digital audio */
148 #include <ddk/ntddcdrm.h>
150 /* Defined by red-book standard; do not change! */
151 #define CD_FRAMESIZE_RAW (2352)
153 #define CDFRAMES_PERSEC (75)
154 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
155 #define FRAME_OF_ADDR(a) ((a)[1] * CDFRAMES_PERMIN + (a)[2] * CDFRAMES_PERSEC + (a)[3])
156 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
158 /* All of this is pretty similar to the Linux version, except using the WinAPI
159 * device functions instead of POSIX */
167 static const char *cd_device
= "D";
169 static void *cdda_open_file(const char *fname
)
171 /* Device filename is of the format "\\.\D:", where "D" is the letter of
173 const char cd_drv
[] = { '\\','\\','.','\\',*cd_device
,':', 0 };
180 if(strncmp(fname
, "cdda://", 7) != 0)
184 fd
= CreateFileA(cd_drv
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
185 if(fd
== (HANDLE
)-1) return NULL
;
187 ret
= DeviceIoControl(fd
, IOCTL_CDROM_READ_TOC
, NULL
, 0,
188 &toc
, sizeof(toc
), &br
, NULL
);
189 if(ret
== 0 || idx
< toc
.FirstTrack
|| idx
> toc
.LastTrack
||
190 (toc
.TrackData
[idx
-toc
.FirstTrack
].Control
&4))
196 dat
= malloc(sizeof(*dat
));
198 dat
->start
= FRAME_OF_TOC(toc
, idx
) - FRAME_OF_TOC(toc
, toc
.FirstTrack
);
199 dat
->current
= dat
->start
;
200 dat
->end
= FRAME_OF_TOC(toc
, idx
+1) - FRAME_OF_TOC(toc
, toc
.FirstTrack
);
205 static ALboolean
cdda_get_format(void *instance
, ALenum
*format
, ALuint
*samplerate
, ALuint
*blocksize
)
207 *format
= AL_FORMAT_STEREO16
;
209 *blocksize
= CD_FRAMESIZE_RAW
;
215 static ALuint
cdda_decode(void *instance
, ALubyte
*data
, ALuint bytes
)
217 CDDAData
*self
= instance
;
220 while(bytes
-got
>= CD_FRAMESIZE_RAW
&& self
->current
< self
->end
)
222 RAW_READ_INFO rdInfo
;
225 rdInfo
.DiskOffset
.QuadPart
= (LONGLONG
)self
->current
<<11;
226 rdInfo
.SectorCount
= (bytes
-got
) / CD_FRAMESIZE_RAW
;
227 rdInfo
.TrackMode
= CDDA
;
229 if(rdInfo
.SectorCount
> self
->end
-self
->current
)
230 rdInfo
.SectorCount
= self
->end
-self
->current
;
232 if(!DeviceIoControl(self
->fd
, IOCTL_CDROM_RAW_READ
,
233 &rdInfo
, sizeof(rdInfo
), data
, bytes
-got
,
236 rdInfo
.SectorCount
= 1;
237 memset(data
, 0, CD_FRAMESIZE_RAW
);
240 self
->current
+= rdInfo
.SectorCount
;
241 data
+= CD_FRAMESIZE_RAW
*rdInfo
.SectorCount
;
242 got
+= CD_FRAMESIZE_RAW
*rdInfo
.SectorCount
;
248 static ALboolean
cdda_rewind(void *instance
)
250 CDDAData
*self
= instance
;
252 self
->current
= self
->start
;
256 static void cdda_close(void *instance
)
258 CDDAData
*self
= instance
;
260 CloseHandle(self
->fd
);
266 static const char *cd_device
= "(unknown)";
268 static void *cdda_open_file(const char *fname
)
270 if(strncmp(fname
, "cdda://", 7) == 0)
271 fprintf(stderr
, "CD Digital Audio is not supported on this platform\n");
275 static ALboolean
cdda_get_format(void *instance
, ALenum
*format
, ALuint
*samplerate
, ALuint
*blocksize
)
281 static ALuint
cdda_decode(void *instance
, ALubyte
*data
, ALuint bytes
)
289 static ALboolean
cdda_rewind(void *instance
)
295 static void cdda_close(void *instance
)
303 static void eos_callback(void *param
, ALuint unused
)
305 *(volatile int*)param
= 1;
312 int main(int argc
, char **argv
)
319 if(argc
< 2 || (strcmp(argv
[1], "-cd-device") == 0 && argc
< 4))
321 fprintf(stderr
, "Usage %s [-cd-device <device>] cdda://<tracknum>\n", argv
[0]);
322 fprintf(stderr
, "Default CD device is %s\n", cd_device
);
326 if(strcmp(argv
[1], "-cd-device") != 0)
334 alureInstallDecodeCallbacks(-1, cdda_open_file
, NULL
, cdda_get_format
,
335 cdda_decode
, cdda_rewind
, cdda_close
);
337 if(!alureInitDevice(NULL
, NULL
))
339 fprintf(stderr
, "Failed to open OpenAL device: %s\n", alureGetErrorString());
343 alGenSources(1, &src
);
344 if(alGetError() != AL_NO_ERROR
)
346 fprintf(stderr
, "Failed to create OpenAL source!\n");
347 alureShutdownDevice();
351 alureStreamSizeIsMicroSec(AL_TRUE
);
353 stream
= alureCreateStreamFromFile(fname
, 250000, 0, NULL
);
356 fprintf(stderr
, "Could not load %s: %s\n", fname
, alureGetErrorString());
357 alDeleteSources(1, &src
);
359 alureShutdownDevice();
364 if(!alurePlaySourceStream(src
, stream
, NUM_BUFS
, 0, eos_callback
, (void*)&isdone
))
366 fprintf(stderr
, "Failed to play stream: %s\n", alureGetErrorString());
375 alureStopSource(src
, AL_FALSE
);
377 alDeleteSources(1, &src
);
378 alureDestroyStream(stream
, 0, NULL
);
380 alureShutdownDevice();