2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
14 char rbaudio_rcsid[] = "$Id: rbaudio.c,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $";
28 #define HSG_CD_PLAYBACK 0
29 #define MSF_CD_PLAYBACK 1
31 #define RBERR_WRITEPROTVIOL 0
32 #define RBERR_UNKNOWNUNIT 1
33 #define RBERR_DRIVENOTREADY 2
34 #define RBERR_UNKNOWNCOMMAND 3
35 #define RBERR_CRCERROR 4
36 #define RBERR_BADDRIVEREQ 5
37 #define RBERR_SEEKERROR 6
38 #define RBERR_UNKNOWNMEDIA 7
39 #define RBERR_SECTORNOTFOUND 8
40 #define RBERR_WRITEFAULT 10
41 #define RBERR_READFAULT 12
42 #define RBERR_GENERALFAILURE 13
43 #define RBERR_BADDISKCHANGE 15
45 #define RBSTAT_ERROR 0x8000
46 #define RBSTAT_BUSY 0x0200
47 #define RBSTAT_DONE 0x0100
52 // Request Header -----------------------------------------------------
54 typedef struct _REQHDR {
56 unsigned char subunit;
58 unsigned short status;
62 typedef struct _IOCTL {
64 unsigned short buffer_realoff;
65 unsigned short buffer_realseg;
66 unsigned short xferlen;
67 unsigned short sector;
72 // CD Information -----------------------------------------------------
75 char code; // Control Block Code
76 char track; // Track Number
77 long start; // Start of Track in MSF (Redbook)
78 unsigned char info; // Track Control Info
79 } CDTrackInfo = {11,2,0L,0};
84 } TrackInfoREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
88 unsigned long devstat;
94 } DevStatREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
104 } MedChgREQ = {{13,0,3,0,""}, {0,0,0,0,0}};
108 } StopREQ = {{13,0,133,0,""}};
112 } ResumeREQ = {{13,0,136,0,""}};
118 unsigned long lead_out;
119 } CDInfo = {10,0,0,0L};
124 } CDInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
128 char mode; // Play in HSG or MSF(RedBook)
129 unsigned long playbeg;
130 unsigned long playlen; // HSG Format
131 char empty; // Empty byte used so that all requests have same size
132 } PlayREQ = {{13,0,132,0,""},0,0,0,0};
136 unsigned char out0in;
137 unsigned char out0vol;
138 unsigned char out1in;
139 unsigned char out1vol;
140 unsigned char out2in;
141 unsigned char out2vol;
142 unsigned char out3in;
143 unsigned char out3vol;
144 } AUDChannelCtl = {3,0,0,0,0,0,0,0,0};
149 } AUDChannelCtlREQ = {{13,0,12,0,""},{0,0,0,0,0}};
153 unsigned char out0in;
154 unsigned char out0vol;
155 unsigned char out1in;
156 unsigned char out1vol;
157 unsigned char out2in;
158 unsigned char out2vol;
159 unsigned char out3in;
160 unsigned char out3vol;
161 } AUDChannelInfo = {4,0,0,0,0,0,0,0,0};
166 } AUDChannelInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
171 unsigned long headloc;
172 } HeadInfo = {1,MSF_CD_PLAYBACK,0L};
177 } HeadInfoREQ = {{13,0,3,0,""},{0,0,0,0,0}};
191 //@@} QChannelInfo = {12, MSF_CD_PLAYBACK, 2,0,0,0,0,0,0,0,0 };
196 //@@} QChannelInfoREQ = {{13, 0, 3, 0, ""}, {0,0,0,0,0}};
199 // Translation from RealMode->Protected mode buffer
201 typedef struct _REQ_xlatbuf { // Buffer for Real Mode Callback
208 // Other Data ---------------------------------------------------------
210 static unsigned char CDDriveLetter;
211 static unsigned long CDNumTracks,CDTrackStart[101];
212 static int RedBookEnabled = 0;
213 static int RedBookPlaybackType = MSF_CD_PLAYBACK;
214 static fix Playback_Start_Time = 0;
215 static fix Playback_Pause_Time = 0;
216 static fix Playback_Length = 0;
217 static int Playback_first_track,Playback_last_track;
220 // Prototypes ---------------------------------------------------------
222 int RBSendRequest(char *request, char *xferptr, int xferlen);
223 unsigned long msf_to_lsn(unsigned long msf);
224 unsigned long lsn_to_msf(unsigned long lsn);
227 // --------------------------------------------------------------------
229 void RBAInit(ubyte cd_drive_num) //drive a == 0, drive b == 1
233 // Detect presence of CD-ROM
236 int386(0x2f, ®s, ®s);
239 RBADisable(); // Disable RedBook
240 // Error("RBA Error: MSCDEX.EXE compatible driver couldn't be found.");
242 CDDriveLetter = cd_drive_num;
243 // CDDriveLetter = (unsigned char)regs.x.ecx;
246 RBACheckMediaChange();
248 RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
250 // If Door drive open, or no disc in CD-ROM, then Redbook is Disabled.
251 if ((DevStat.devstat&2048) || (DevStat.devstat&1) || RBAEnabled() == 0)
254 //@@ if (DevStat.devstat&4)
255 //@@ mprintf((0, "supports cooked and raw reading.\n"));
257 //@@ mprintf((0, "supports only cooked reading.\n"));
259 //@@ if (DevStat.devstat&256)
260 //@@ mprintf((0, "audio channel manipulation.\n"));
262 //@@ mprintf((0, "no audio channel manipulation.\n"));
264 if (DevStat.devstat&512) {
265 // RedBookPlaybackType = MSF_CD_PLAYBACK;
266 mprintf((0, "supports HSG and RedBook addressing mode.\n"));
269 // RedBookPlaybackType = HSG_CD_PLAYBACK;
270 mprintf((0, "supports HSG addressing only.\n"));
273 RedBookPlaybackType = MSF_CD_PLAYBACK;
278 //find out how many tracks on the CD, and their starting locations
279 void RBARegisterCD(void)
284 // Get CD Information First
285 RBACheckMediaChange();
287 error = RBSendRequest((char*)&CDInfoREQ, (char*)&CDInfo, sizeof(CDInfo));
289 if (error & RBSTAT_ERROR) { //error!
294 CDNumTracks = (CDInfo.last-CDInfo.first)+1;
296 // Define Track Starts
297 for (i=CDInfo.first; i<=CDNumTracks; i++)
299 CDTrackInfo.track = i;
300 RBSendRequest((char *)&TrackInfoREQ, (char*)&CDTrackInfo, sizeof(CDTrackInfo));
301 CDTrackStart[i] = CDTrackInfo.start;
304 CDTrackStart[i] = CDInfo.lead_out;
308 long RBAGetDeviceStatus()
310 RBSendRequest((char*)&DevStatREQ, (char*)&DevStat, sizeof(DevStat));
311 return (long)DevStat.devstat;
315 int RBAGetNumberOfTracks(void)
317 // Get CD Information
318 if (RBACheckMediaChange())
324 //returns the length in seconds of tracks first through last, inclusive
325 int get_track_time(int first,int last)
327 unsigned long playlen;
330 playlen = msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
331 playlen = lsn_to_msf(playlen);
332 min = (int)((playlen >> 16) & 0xFF);
333 sec = (int)((playlen >> 8) & 0xFF);
338 int RBAPlayTrack(int track)
340 Playback_Start_Time = 0;
343 if (track < CDInfo.first || track > CDInfo.last) {
344 mprintf((0,"CD not Redbook Compatible. Will not play track.\n"));
346 // Error("RBA Error: Track %d doesn't exist on CD!!!", track);
348 if (RBACheckMediaChange()) {
349 mprintf((0, "Unable to play track due to CD change.\n"));
353 // Play the track now!!!
354 PlayREQ.mode = RedBookPlaybackType;
355 PlayREQ.playbeg = CDTrackStart[track];
357 if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
359 msf_to_lsn(CDTrackStart[track+1]) - msf_to_lsn(CDTrackStart[track]);
362 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
364 PlayREQ.playlen = CDTrackStart[track+1] - CDTrackStart[track];
366 RBSendRequest((char *)&PlayREQ,NULL,0);
368 Playback_Length = i2f(get_track_time(track,track));
369 Playback_Start_Time = timer_get_fixed_seconds();
371 Playback_first_track = Playback_last_track = track;
373 mprintf( (0,"Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) );
378 //plays tracks first through last, inclusive
379 int RBAPlayTracks(int first, int last)
381 Playback_Start_Time = 0;
384 if (first < CDInfo.first || last > CDInfo.last) {
385 mprintf((0,"Invalid start or end track.\n"));
387 // Error("RBA Error: Track %d doesn't exist on CD!!!", track);
389 if (RBACheckMediaChange()) {
390 mprintf((0, "Unable to play track due to CD change.\n"));
394 // Play the track now!!!
395 PlayREQ.mode = RedBookPlaybackType;
396 PlayREQ.playbeg = CDTrackStart[first];
398 if (RedBookPlaybackType == MSF_CD_PLAYBACK) {
400 msf_to_lsn(CDTrackStart[last+1]) - msf_to_lsn(CDTrackStart[first]);
403 mprintf((1, "RBERR: No! No! No! This shouldn't happen (HSG_CD_PLAYBACK).\n"));
405 PlayREQ.playlen = CDTrackStart[last+1] - CDTrackStart[first];
407 RBSendRequest((char *)&PlayREQ,NULL,0);
409 Playback_Length = i2f(get_track_time(first,last));
410 Playback_Start_Time = timer_get_fixed_seconds();
412 Playback_first_track = first;
413 Playback_last_track = last;
415 mprintf( (0,"Playing Tracks %d-%d (len: %d secs)\n", first,last,f2i(Playback_Length)) );
423 RBSendRequest((char *)&StopREQ, NULL, 0);
424 Playback_Pause_Time = timer_get_fixed_seconds();
430 if (RBACheckMediaChange()) {
432 return RBA_MEDIA_CHANGED;
434 RBSendRequest((char *)&ResumeREQ, NULL, 0);
436 Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
444 if (RBACheckMediaChange())
447 RBSendRequest((char *)&StopREQ,NULL,0);
451 void RBASetStereoAudio(RBACHANNELCTL *channels)
453 AUDChannelCtl.out0in = channels->out0in;
454 AUDChannelCtl.out0vol = channels->out0vol;
455 AUDChannelCtl.out1in = channels->out1in;
456 AUDChannelCtl.out1vol = channels->out1vol;
457 AUDChannelCtl.out2in = AUDChannelCtl.out2vol = 0;
458 AUDChannelCtl.out3in = AUDChannelCtl.out3vol = 0;
460 RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
464 void RBASetQuadAudio(RBACHANNELCTL *channels)
466 AUDChannelCtl.out0in = (unsigned char)channels->out0in;
467 AUDChannelCtl.out0vol = (unsigned char)channels->out0vol;
468 AUDChannelCtl.out1in = (unsigned char)channels->out1in;
469 AUDChannelCtl.out1vol = (unsigned char)channels->out1vol;
470 AUDChannelCtl.out2in = (unsigned char)channels->out2in;
471 AUDChannelCtl.out2vol = (unsigned char)channels->out2vol;
472 AUDChannelCtl.out3in = (unsigned char)channels->out3in;
473 AUDChannelCtl.out3vol = (unsigned char)channels->out3vol;
475 RBSendRequest((char*)&AUDChannelCtlREQ, (char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
479 void RBAGetAudioInfo(RBACHANNELCTL *channels)
481 RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
483 channels->out0in = (int)AUDChannelInfo.out0in;
484 channels->out0vol = (int)AUDChannelInfo.out0vol;
485 channels->out1in = (int)AUDChannelInfo.out1in;
486 channels->out1vol = (int)AUDChannelInfo.out1vol;
487 channels->out2in = (int)AUDChannelInfo.out2in;
488 channels->out2vol = (int)AUDChannelInfo.out2vol;
489 channels->out3in = (int)AUDChannelInfo.out3in;
490 channels->out3vol = (int)AUDChannelInfo.out3vol;
495 void RBASetChannelVolume(int channel, int volume)
497 //RBACHANNELCTL channels;
499 RBSendRequest((char*)&AUDChannelInfoREQ, (char*)&AUDChannelInfo, sizeof(AUDChannelInfo));
501 AUDChannelCtl.out0in = AUDChannelInfo.out0in;
502 AUDChannelCtl.out0vol = AUDChannelInfo.out0vol;
503 AUDChannelCtl.out1in = AUDChannelInfo.out1in;
504 AUDChannelCtl.out1vol = AUDChannelInfo.out1vol;
505 AUDChannelCtl.out2in = AUDChannelInfo.out2in;
506 AUDChannelCtl.out2vol = AUDChannelInfo.out2vol;
507 AUDChannelCtl.out3in = AUDChannelInfo.out3in;
508 AUDChannelCtl.out3vol = AUDChannelInfo.out3vol;
510 if (channel == 0) AUDChannelCtl.out0vol = (unsigned char)volume;
511 if (channel == 1) AUDChannelCtl.out1vol = (unsigned char)volume;
512 if (channel == 2) AUDChannelCtl.out2vol = (unsigned char)volume;
513 if (channel == 3) AUDChannelCtl.out3vol = (unsigned char)volume;
515 RBSendRequest((char*)&AUDChannelCtlREQ,(char*)&AUDChannelCtl, sizeof(AUDChannelCtl));
520 void RBASetVolume(int volume)
522 RBASetChannelVolume(0,volume);
523 RBASetChannelVolume(1,volume);
527 long RBAGetHeadLoc(int *min, int *sec, int *frame)
531 if (RBACheckMediaChange())
532 return RBA_MEDIA_CHANGED;
534 HeadInfo.mode = RedBookPlaybackType;
535 RBSendRequest((char*)&HeadInfoREQ, (char*)&HeadInfo, sizeof(HeadInfo));
537 if (RedBookPlaybackType == MSF_CD_PLAYBACK)
538 loc = HeadInfo.headloc;
540 loc = lsn_to_msf(HeadInfo.headloc);
542 *min = (int)((loc >> 16) & 0xFF);
543 *sec = (int)((loc >> 8) & 0xFF);
544 *frame = (int)((loc >> 0) & 0xFF);
549 //return the track number currently playing. Useful if RBAPlayTracks()
550 //is called. Returns 0 if no track playing, else track number
554 int delta_time; //in seconds
556 if (!RBAPeekPlayStatus())
559 delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2);
561 for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) {
563 delta_time -= get_track_time(track,track);
569 Assert(track <= Playback_last_track);
575 int RBAPeekPlayStatus()
577 if (RBACheckMediaChange()) { //if media changed, then not playing
582 if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
589 if (RedBookEnabled < 1) return 0;
597 mprintf((0, "Enabling Redbook...\n"));
604 mprintf((0, "Disabling Redbook...\n"));
609 // RB functions: Internal to RBA library -----------------------------
611 //returns 1 if media has changed, else 0
612 int RBACheckMediaChange()
614 RBSendRequest((char *)&MedChgREQ, (char*)&MedChg, sizeof(MedChg));
616 if (MedChg.byte == 255 || MedChg.byte == 0) {
617 // New media in drive (or maybe no media in drive)
618 mprintf((0,"CD-ROM Media change detected\n"));
627 //returns 16-bit value. Bit 15 means error.
628 //WE NEED SYMBOLOIC CONSTANTS FOR ALL RETURN CODES & STATUS BITS
629 int RBSendRequest(char *request, char *xferptr, int xferlen)
632 dpmi_real_regs rregs;
635 REQ_xlatbuf *xlat_req; // Translated Buffer Request
636 char *xlat_xferptr; // Translated Buffer Transfer Buffer Ptr
638 unsigned short status;
640 if (!RedBookEnabled) return 0; // Don't send request if no RBA
642 memset(&rregs,0,sizeof(dpmi_real_regs));
644 // Get Temporary Real Mode Buffer for request translation
645 xlat_req = (REQ_xlatbuf *)dpmi_get_temp_low_buffer(128+xferlen);
646 memcpy(xlat_req, request, sizeof(REQHDR)+sizeof(IOCTL));
647 ioctl = (IOCTL *)(((char*)xlat_req)+sizeof(REQHDR));
649 // Set Transfer Buffer in IOCTL reqion of 'request'
650 if (xferlen && xferptr) {
651 xlat_xferptr = ((char*)xlat_req) + 128;
652 memcpy(xlat_xferptr, xferptr, xferlen);
654 ioctl->buffer_realoff = DPMI_real_offset(xlat_xferptr);
655 ioctl->buffer_realseg = DPMI_real_segment(xlat_xferptr);
656 ioctl->xferlen = xferlen;
659 // Setup and Send Request Packet
661 rregs.ecx = (unsigned)(CDDriveLetter);
662 rregs.es = DPMI_real_segment(xlat_req);
663 rregs.ebx = DPMI_real_offset(xlat_req);
664 dpmi_real_int386x(0x2f, &rregs);
666 // Return Translate Buffer to Protected mode 'xferptr'
667 if (xferlen && xferptr) {
668 memcpy(xferptr, xlat_xferptr, xferlen);
670 memcpy(request, xlat_req, sizeof(REQHDR)+sizeof(IOCTL));
673 status = ((REQHDR *)request)->status;
675 if (status & RBSTAT_ERROR) {
676 mprintf((0,"Error in SendRequest: %x.\n", status));
683 // Converts Logical Sector Number to Minutes Seconds Frame Format
685 unsigned long lsn_to_msf(unsigned long lsn)
687 unsigned long min,sec,frame;
696 return((min << 16) + (sec << 8) + (frame << 0));
699 // convert minutes seconds frame format to a logical sector number.
701 unsigned long msf_to_lsn(unsigned long msf)
703 unsigned long min,sec,frame;
705 min = (msf >> 16) & 0xFF;
706 sec = (msf >> 8) & 0xFF;
707 frame = (msf >> 0) & 0xFF;
709 return( (min * 60*75) + (sec * 75) + frame - 150 );