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(HAVE_DDK_NTDDCDRM_H)
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 was not compiled for this system\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();