Add a function to get the number of samples from a stream
[alure.git] / examples / alurecdplay.c
blob721841b4f6110f3244053ddc377a1e4d3f00b73d
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
5 #include "AL/alure.h"
7 #ifdef __linux__
8 /* Linux implementation for reading CD digital audio */
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <errno.h>
14 #include <sys/ioctl.h>
15 #include <linux/cdrom.h>
18 typedef struct {
19 int fd;
20 int start;
21 int current;
22 int end;
23 } CDDAData;
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;
32 CDDAData *dat;
33 int fd, ret;
35 /* Make sure the filename has the appropriate URI and extract the track
36 * number */
37 if(strncmp(fname, "cdda://", 7) != 0)
38 return NULL;
39 idx = atoi(fname+7);
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);
46 if(ret != -1)
48 if(idx < cdtochdr.cdth_trk0 || idx >= cdtochdr.cdth_trk1)
49 ret = -1;
51 if(ret != -1)
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;
61 if(ret != -1)
63 cdtocentry.cdte_format = CDROM_LBA;
64 cdtocentry.cdte_track = ++idx;
65 ret = ioctl(fd, CDROMREADTOCENTRY, &cdtocentry);
66 if(ret != -1)
67 endaddr = cdtocentry.cdte_addr.lba;
70 if(ret == -1 || startaddr == -1 || endaddr == -1)
72 close(fd);
73 return NULL;
76 dat = malloc(sizeof(*dat));
77 dat->fd = fd;
78 dat->start = startaddr;
79 dat->current = startaddr;
80 dat->end = endaddr;
82 return dat;
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
88 * change */
89 *format = AL_FORMAT_STEREO16;
90 *samplerate = 44100;
91 *blocksize = CD_FRAMESIZE_RAW;
93 return AL_TRUE;
94 (void)instance;
97 static ALuint cdda_decode(void *instance, ALubyte *data, ALuint bytes)
99 CDDAData *self = instance;
101 ALuint got = 0;
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;
110 cdra.buf = data;
112 if(cdra.nframes > self->end-self->current)
113 cdra.nframes = self->end-self->current;
115 if(ioctl(self->fd, CDROMREADAUDIO, &cdra) == -1)
117 cdra.nframes = 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;
126 return got;
129 static ALboolean cdda_rewind(void *instance)
131 CDDAData *self = instance;
133 self->current = self->start;
134 return AL_TRUE;
137 static void cdda_close(void *instance)
139 CDDAData *self = instance;
141 close(self->fd);
142 free(self);
145 #elif defined(_WIN32)
146 /* Windows implementation for reading CD digital audio */
147 #include <windows.h>
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 */
160 typedef struct {
161 HANDLE fd;
162 DWORD start;
163 DWORD current;
164 DWORD end;
165 } CDDAData;
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
172 * the CD drive */
173 const char cd_drv[] = { '\\','\\','.','\\',*cd_device,':', 0 };
174 DWORD br, idx;
175 CDROM_TOC toc;
176 CDDAData *dat;
177 HANDLE fd;
178 int ret;
180 if(strncmp(fname, "cdda://", 7) != 0)
181 return NULL;
182 idx = atoi(fname+7);
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))
192 CloseHandle(fd);
193 return NULL;
196 dat = malloc(sizeof(*dat));
197 dat->fd = fd;
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);
202 return dat;
205 static ALboolean cdda_get_format(void *instance, ALenum *format, ALuint *samplerate, ALuint *blocksize)
207 *format = AL_FORMAT_STEREO16;
208 *samplerate = 44100;
209 *blocksize = CD_FRAMESIZE_RAW;
211 return AL_TRUE;
212 (void)instance;
215 static ALuint cdda_decode(void *instance, ALubyte *data, ALuint bytes)
217 CDDAData *self = instance;
219 ALuint got = 0;
220 while(bytes-got >= CD_FRAMESIZE_RAW && self->current < self->end)
222 RAW_READ_INFO rdInfo;
223 DWORD br;
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,
234 &br, NULL))
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;
245 return got;
248 static ALboolean cdda_rewind(void *instance)
250 CDDAData *self = instance;
252 self->current = self->start;
253 return AL_TRUE;
256 static void cdda_close(void *instance)
258 CDDAData *self = instance;
260 CloseHandle(self->fd);
261 free(self);
264 #else
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");
272 return NULL;
275 static ALboolean cdda_get_format(void *instance, ALenum *format, ALuint *samplerate, ALuint *blocksize)
277 return AL_FALSE;
278 (void)instance;
281 static ALuint cdda_decode(void *instance, ALubyte *data, ALuint bytes)
283 return 0;
284 (void)instance;
285 (void)data;
286 (void)bytes;
289 static ALboolean cdda_rewind(void *instance)
291 return AL_FALSE;
292 (void)instance;
295 static void cdda_close(void *instance)
297 (void)instance;
300 #endif
303 static void eos_callback(void *param, ALuint unused)
305 *(volatile int*)param = 1;
306 (void)unused;
309 #define NUM_BUFS 3
312 int main(int argc, char **argv)
314 volatile int isdone;
315 alureStream *stream;
316 const char *fname;
317 ALuint src;
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);
323 return 1;
326 if(strcmp(argv[1], "-cd-device") != 0)
327 fname = argv[1];
328 else
330 cd_device = argv[2];
331 fname = argv[3];
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());
340 return 1;
343 alGenSources(1, &src);
344 if(alGetError() != AL_NO_ERROR)
346 fprintf(stderr, "Failed to create OpenAL source!\n");
347 alureShutdownDevice();
348 return 1;
351 alureStreamSizeIsMicroSec(AL_TRUE);
353 stream = alureCreateStreamFromFile(fname, 250000, 0, NULL);
354 if(!stream)
356 fprintf(stderr, "Could not load %s: %s\n", fname, alureGetErrorString());
357 alDeleteSources(1, &src);
359 alureShutdownDevice();
360 return 1;
363 isdone = 0;
364 if(!alurePlaySourceStream(src, stream, NUM_BUFS, 0, eos_callback, (void*)&isdone))
366 fprintf(stderr, "Failed to play stream: %s\n", alureGetErrorString());
367 isdone = 1;
370 while(!isdone)
372 alureSleep(0.125);
373 alureUpdate();
375 alureStopSource(src, AL_FALSE);
377 alDeleteSources(1, &src);
378 alureDestroyStream(stream, 0, NULL);
380 alureShutdownDevice();
381 return 0;