3 * Redbook Audio Library
21 #define HSG_CD_PLAYBACK 0
22 #define MSF_CD_PLAYBACK 1
24 #define RBERR_WRITEPROTVIOL 0
25 #define RBERR_UNKNOWNUNIT 1
26 #define RBERR_DRIVENOTREADY 2
27 #define RBERR_UNKNOWNCOMMAND 3
28 #define RBERR_CRCERROR 4
29 #define RBERR_BADDRIVEREQ 5
30 #define RBERR_SEEKERROR 6
31 #define RBERR_UNKNOWNMEDIA 7
32 #define RBERR_SECTORNOTFOUND 8
33 #define RBERR_WRITEFAULT 10
34 #define RBERR_READFAULT 12
35 #define RBERR_GENERALFAILURE 13
36 #define RBERR_BADDISKCHANGE 15
38 #define RBSTAT_ERROR 0x8000
39 #define RBSTAT_BUSY 0x0200
40 #define RBSTAT_DONE 0x0100
45 // Request Header -----------------------------------------------------
47 typedef struct _REQHDR {
49 unsigned char subunit;
51 unsigned short status;
55 typedef struct _IOCTL {
57 unsigned short buffer_realoff;
58 unsigned short buffer_realseg;
59 unsigned short xferlen;
60 unsigned short sector;
65 // CD Information -----------------------------------------------------
68 char code; // Control Block Code
69 char track; // Track Number
70 long start; // Start of Track in MSF (Redbook)
71 unsigned char info; // Track Control Info
72 } CDTrackInfo = {11,2,0L,0};
77 } TrackInfoREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
81 unsigned long devstat;
87 } DevStatREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
97 } MedChgREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
101 } StopREQ = {{13,0,133,0,""}};
105 } ResumeREQ = {{13,0,136,0,""}};
111 unsigned long lead_out;
112 } CDInfo = {10,0,0,0L};
117 } CDInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
121 char mode; // Play in HSG or MSF(RedBook)
122 unsigned long playbeg;
123 unsigned long playlen; // HSG Format
124 char empty; // Empty byte used so that all requests have same size
125 } PlayREQ = {{13,0,132,0,""},0,0,0,0};
129 unsigned char out0in;
130 unsigned char out0vol;
131 unsigned char out1in;
132 unsigned char out1vol;
133 unsigned char out2in;
134 unsigned char out2vol;
135 unsigned char out3in;
136 unsigned char out3vol;
137 } AUDChannelCtl = {3,0,0,0,0,0,0,0,0};
142 } AUDChannelCtlREQ = {{13,0,12,0,""},{0,0,0,0,0}};
146 unsigned char out0in;
147 unsigned char out0vol;
148 unsigned char out1in;
149 unsigned char out1vol;
150 unsigned char out2in;
151 unsigned char out2vol;
152 unsigned char out3in;
153 unsigned char out3vol;
154 } AUDChannelInfo = {4,0,0,0,0,0,0,0,0};
159 } AUDChannelInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
164 unsigned long headloc;
165 } HeadInfo = {1,MSF_CD_PLAYBACK,0L};
170 } HeadInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
184 //@@} QChannelInfo = {12, MSF_CD_PLAYBACK, 2,0,0,0,0,0,0,0,0 };
189 //@@} QChannelInfoREQ = {{13, 0, 3, 0, ""}, {0,0,0,0,0}};
192 // Translation from RealMode->Protected mode buffer
194 typedef struct _REQ_xlatbuf { // Buffer for Real Mode Callback
201 // Other Data ---------------------------------------------------------
203 static unsigned char CDDriveLetter;
204 static unsigned long CDNumTracks,CDTrackStart[101];
205 static int RedBookEnabled = 0;
206 static int RedBookPlaybackType = MSF_CD_PLAYBACK;
207 static fix Playback_Start_Time = 0;
208 static fix Playback_Pause_Time = 0;
209 static fix Playback_Length = 0;
212 // Prototypes ---------------------------------------------------------
214 int RBSendRequest(char *request, char *xferptr, int xferlen);
215 unsigned long msf_to_lsn(unsigned long msf);
216 unsigned long lsn_to_msf(unsigned long lsn);
219 // --------------------------------------------------------------------
221 void RBAInit(ubyte cd_drive_num) //drive a == 0, drive b == 1
225 // Detect presence of CD-ROM
228 int386(0x2f, ®s, ®s);
231 RBADisable(); // Disable RedBook
232 // Error("RBA Error: MSCDEX.EXE compatible driver couldn't be found.");
234 CDDriveLetter = cd_drive_num;
235 // CDDriveLetter = (unsigned char)regs.x.ecx;
238 RBACheckMediaChange();
240 RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
242 // If Door drive open, or no disc in CD-ROM, then Redbook is Disabled.
243 if ((DevStat.devstat&2048) || (DevStat.devstat&1) || RBAEnabled() == 0)
246 //@@ if (DevStat.devstat&4)
247 //@@ mprintf((0, "supports cooked and raw reading.\n"));
249 //@@ mprintf((0, "supports only cooked reading.\n"));
251 //@@ if (DevStat.devstat&256)
252 //@@ mprintf((0, "audio channel manipulation.\n"));
254 //@@ mprintf((0, "no audio channel manipulation.\n"));
256 if (DevStat.devstat&512) {
257 // RedBookPlaybackType = MSF_CD_PLAYBACK;
258 mprintf((0, "supports HSG and RedBook addressing mode.\n"));
261 // RedBookPlaybackType = HSG_CD_PLAYBACK;
262 mprintf((0, "supports HSG addressing only.\n"));
265 RedBookPlaybackType = MSF_CD_PLAYBACK;
270 //find out how many tracks on the CD, and their starting locations
271 void RBARegisterCD(void)
276 // Get CD Information First
277 RBACheckMediaChange();
279 error = RBSendRequest((char*)&CDInfoREQ, (char*)&CDInfo, sizeof(CDInfo));
281 if (error & RBSTAT_ERROR) { //error!
286 CDNumTracks = (CDInfo.last-CDInfo.first)+1;
288 // Define Track Starts
289 for (i=CDInfo.first; i<=CDNumTracks; i++)
291 CDTrackInfo.track = i;
292 RBSendRequest((char *)&TrackInfoREQ, (char*)&CDTrackInfo, sizeof(CDTrackInfo));
293 CDTrackStart[i] = CDTrackInfo.start;
296 CDTrackStart[i] = CDInfo.lead_out;
300 long RBAGetDeviceStatus()
302 RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
303 return (long)DevStat.devstat;
307 int RBAGetNumberOfTracks(void)
309 // Get CD Information
310 if (RBACheckMediaChange())
317 int RBAPlayTrack(int track)
319 unsigned long playlen;
322 Playback_Start_Time = 0;
325 if (track < CDInfo.first || track > CDInfo.last) {
326 mprintf((0,"CD not Redbook Compatible. Will not play track.\n"));
328 // Error("RBA Error: Track %d doesn't exist on CD!!!", track);
330 if (RBACheckMediaChange()) {
331 mprintf((0, "Unable to play track due to CD change.\n"));
335 // Play the track now!!!
336 PlayREQ.mode = RedBookPlaybackType;
337 PlayREQ.playbeg = CDTrackStart[track];
339 if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
341 msf_to_lsn(CDTrackStart[track+1]) - msf_to_lsn(CDTrackStart[track]);
344 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
346 PlayREQ.playlen = CDTrackStart[track+1] - CDTrackStart[track];
348 RBSendRequest((char *)&PlayREQ,NULL,0);
350 playlen = msf_to_lsn(CDTrackStart[track+1]) - msf_to_lsn(CDTrackStart[track]);
351 playlen = lsn_to_msf(playlen);
352 min = (int)((playlen >> 16) & 0xFF);
353 sec = (int)((playlen >> 8) & 0xFF);
355 Playback_Start_Time = timer_get_fixed_seconds();
356 Playback_Length = i2f(min*60+sec);
358 mprintf( (0,"Playing Track %d (len: %d secs)\n", track, min*60+sec) );
363 //plays tracks first through last, inclusive
364 int RBAPlayTracks(int first, int last)
366 unsigned long playlen;
369 Playback_Start_Time = 0;
372 if (first < CDInfo.first || last > CDInfo.last) {
373 mprintf((0,"Invalid start or end track.\n"));
375 // Error("RBA Error: Track %d doesn't exist on CD!!!", track);
377 if (RBACheckMediaChange()) {
378 mprintf((0, "Unable to play track due to CD change.\n"));
382 // Play the track now!!!
383 PlayREQ.mode = RedBookPlaybackType;
384 PlayREQ.playbeg = CDTrackStart[first];
386 if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
388 msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
391 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
393 PlayREQ.playlen = CDTrackStart[last+1] - CDTrackStart[first];
395 RBSendRequest((char *)&PlayREQ,NULL,0);
397 playlen = msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
398 playlen = lsn_to_msf(playlen);
399 min = (int)((playlen >> 16) & 0xFF);
400 sec = (int)((playlen >> 8) & 0xFF);
402 Playback_Start_Time = timer_get_fixed_seconds();
403 Playback_Length = i2f(min*60+sec);
405 mprintf( (0,"Playing Tracks %d-%d (len: %d secs)\n", first,last,min*60+sec) );
413 RBSendRequest((char *)&StopREQ, NULL, 0);
414 Playback_Pause_Time = timer_get_fixed_seconds();
420 if (RBACheckMediaChange()) {
422 return RBA_MEDIA_CHANGED;
424 RBSendRequest((char *)&ResumeREQ, NULL, 0);
426 Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
434 if (RBACheckMediaChange())
437 RBSendRequest((char *)&StopREQ,NULL,0);
441 void RBASetStereoAudio(RBACHANNELCTL *channels)
443 AUDChannelCtl.out0in = channels->out0in;
444 AUDChannelCtl.out0vol = channels->out0vol;
445 AUDChannelCtl.out1in = channels->out1in;
446 AUDChannelCtl.out1vol = channels->out1vol;
447 AUDChannelCtl.out2in = AUDChannelCtl.out2vol = 0;
448 AUDChannelCtl.out3in = AUDChannelCtl.out3vol = 0;
450 RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
454 void RBASetQuadAudio(RBACHANNELCTL *channels)
456 AUDChannelCtl.out0in = (unsigned char)channels->out0in;
457 AUDChannelCtl.out0vol = (unsigned char)channels->out0vol;
458 AUDChannelCtl.out1in = (unsigned char)channels->out1in;
459 AUDChannelCtl.out1vol = (unsigned char)channels->out1vol;
460 AUDChannelCtl.out2in = (unsigned char)channels->out2in;
461 AUDChannelCtl.out2vol = (unsigned char)channels->out2vol;
462 AUDChannelCtl.out3in = (unsigned char)channels->out3in;
463 AUDChannelCtl.out3vol = (unsigned char)channels->out3vol;
465 RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
469 void RBAGetAudioInfo(RBACHANNELCTL *channels)
471 RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
473 channels->out0in = (int)AUDChannelInfo.out0in;
474 channels->out0vol = (int)AUDChannelInfo.out0vol;
475 channels->out1in = (int)AUDChannelInfo.out1in;
476 channels->out1vol = (int)AUDChannelInfo.out1vol;
477 channels->out2in = (int)AUDChannelInfo.out2in;
478 channels->out2vol = (int)AUDChannelInfo.out2vol;
479 channels->out3in = (int)AUDChannelInfo.out3in;
480 channels->out3vol = (int)AUDChannelInfo.out3vol;
485 void RBASetChannelVolume(int channel, int volume)
487 //RBACHANNELCTL channels;
489 RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
491 AUDChannelCtl.out0in = AUDChannelInfo.out0in;
492 AUDChannelCtl.out0vol = AUDChannelInfo.out0vol;
493 AUDChannelCtl.out1in = AUDChannelInfo.out1in;
494 AUDChannelCtl.out1vol = AUDChannelInfo.out1vol;
495 AUDChannelCtl.out2in = AUDChannelInfo.out2in;
496 AUDChannelCtl.out2vol = AUDChannelInfo.out2vol;
497 AUDChannelCtl.out3in = AUDChannelInfo.out3in;
498 AUDChannelCtl.out3vol = AUDChannelInfo.out3vol;
500 if (channel == 0) AUDChannelCtl.out0vol = (unsigned char)volume;
501 if (channel == 1) AUDChannelCtl.out1vol = (unsigned char)volume;
502 if (channel == 2) AUDChannelCtl.out2vol = (unsigned char)volume;
503 if (channel == 3) AUDChannelCtl.out3vol = (unsigned char)volume;
505 RBSendRequest((char*)&AUDChannelCtlREQ,(char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
510 void RBASetVolume(int volume)
512 RBASetChannelVolume(0,volume);
513 RBASetChannelVolume(1,volume);
517 long RBAGetHeadLoc(int *min, int *sec, int *frame)
521 if (RBACheckMediaChange())
522 return RBA_MEDIA_CHANGED;
524 HeadInfo.mode = RedBookPlaybackType;
525 RBSendRequest((char*)&HeadInfoREQ, (char*)&HeadInfo, sizeof(HeadInfo));
527 if (RedBookPlaybackType == MSF_CD_PLAYBACK)
528 loc = HeadInfo.headloc;
530 loc = lsn_to_msf(HeadInfo.headloc);
532 *min = (int)((loc >> 16) & 0xFF);
533 *sec = (int)((loc >> 8) & 0xFF);
534 *frame = (int)((loc >> 0) & 0xFF);
540 int RBAPeekPlayStatus()
542 if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
549 if (RedBookEnabled < 1) return 0;
557 mprintf((0, "Enabling Redbook...\n"));
564 mprintf((0, "Disabling Redbook...\n"));
569 // RB functions: Internal to RBA library -----------------------------
571 //returns 1 if media has changed, else 0
572 int RBACheckMediaChange()
574 RBSendRequest((char *)&MedChgREQ, (char*)&MedChg, sizeof(MedChg));
576 if (MedChg.byte == 255 || MedChg.byte == 0) {
577 // New media in drive (or maybe no media in drive)
578 mprintf((0,"CD-ROM Media change detected\n"));
587 //returns 16-bit value. Bit 15 means error.
588 //WE NEED SYMBOLOIC CONSTANTS FOR ALL RETURN CODES & STATUS BITS
589 int RBSendRequest(char *request, char *xferptr, int xferlen)
592 dpmi_real_regs rregs;
595 REQ_xlatbuf *xlat_req; // Translated Buffer Request
596 char *xlat_xferptr; // Translated Buffer Transfer Buffer Ptr
598 unsigned short status;
600 if (!RedBookEnabled) return 0; // Don't send request if no RBA
602 memset(&rregs,0,sizeof(dpmi_real_regs));
604 // Get Temporary Real Mode Buffer for request translation
605 xlat_req = (REQ_xlatbuf *)dpmi_get_temp_low_buffer(128+xferlen);
606 memcpy(xlat_req, request, sizeof(REQHDR)+sizeof(IOCTL));
607 ioctl = (IOCTL *)(((char*)xlat_req)+sizeof(REQHDR));
609 // Set Transfer Buffer in IOCTL reqion of 'request'
610 if (xferlen && xferptr) {
611 xlat_xferptr = ((char*)xlat_req) + 128;
612 memcpy(xlat_xferptr, xferptr, xferlen);
614 ioctl->buffer_realoff = DPMI_real_offset(xlat_xferptr);
615 ioctl->buffer_realseg = DPMI_real_segment(xlat_xferptr);
616 ioctl->xferlen = xferlen;
619 // Setup and Send Request Packet
621 rregs.ecx = (unsigned)(CDDriveLetter);
622 rregs.es = DPMI_real_segment(xlat_req);
623 rregs.ebx = DPMI_real_offset(xlat_req);
624 dpmi_real_int386x(0x2f, &rregs);
626 // Return Translate Buffer to Protected mode 'xferptr'
627 if (xferlen && xferptr) {
628 memcpy(xferptr, xlat_xferptr, xferlen);
630 memcpy(request, xlat_req, sizeof(REQHDR)+sizeof(IOCTL));
633 status = ((REQHDR *)request)->status;
635 if (status & RBSTAT_ERROR) {
636 mprintf((0,"Error in SendRequest: %x.\n", status));
643 // Converts Logical Sector Number to Minutes Seconds Frame Format
645 unsigned long lsn_to_msf(unsigned long lsn)
647 unsigned long min,sec,frame;
656 return((min << 16) + (sec << 8) + (frame << 0));
659 // convert minutes seconds frame format to a logical sector number.
661 unsigned long msf_to_lsn(unsigned long msf)
663 unsigned long min,sec,frame;
665 min = (msf >> 16) & 0xFF;
666 sec = (msf >> 8) & 0xFF;
667 frame = (msf >> 0) & 0xFF;
669 return( (min * 60*75) + (sec * 75) + frame - 150 );